@skillsmith/core 0.2.0 → 2.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.
Files changed (233) hide show
  1. package/README.md +233 -2
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/src/analysis/__tests__/incremental.test.d.ts +13 -0
  4. package/dist/src/analysis/__tests__/incremental.test.d.ts.map +1 -0
  5. package/dist/src/analysis/__tests__/incremental.test.js +515 -0
  6. package/dist/src/analysis/__tests__/incremental.test.js.map +1 -0
  7. package/dist/src/analysis/__tests__/integration.test.d.ts +14 -0
  8. package/dist/src/analysis/__tests__/integration.test.d.ts.map +1 -0
  9. package/dist/src/analysis/__tests__/integration.test.js +1059 -0
  10. package/dist/src/analysis/__tests__/integration.test.js.map +1 -0
  11. package/dist/src/analysis/__tests__/metrics.test.d.ts +9 -0
  12. package/dist/src/analysis/__tests__/metrics.test.d.ts.map +1 -0
  13. package/dist/src/analysis/__tests__/metrics.test.js +369 -0
  14. package/dist/src/analysis/__tests__/metrics.test.js.map +1 -0
  15. package/dist/src/analysis/__tests__/performance.test.d.ts +15 -0
  16. package/dist/src/analysis/__tests__/performance.test.d.ts.map +1 -0
  17. package/dist/src/analysis/__tests__/performance.test.js +402 -0
  18. package/dist/src/analysis/__tests__/performance.test.js.map +1 -0
  19. package/dist/src/analysis/adapters/__tests__/go.test.d.ts +12 -0
  20. package/dist/src/analysis/adapters/__tests__/go.test.d.ts.map +1 -0
  21. package/dist/src/analysis/adapters/__tests__/go.test.js +561 -0
  22. package/dist/src/analysis/adapters/__tests__/go.test.js.map +1 -0
  23. package/dist/src/analysis/adapters/__tests__/python.test.d.ts +11 -0
  24. package/dist/src/analysis/adapters/__tests__/python.test.d.ts.map +1 -0
  25. package/dist/src/analysis/adapters/__tests__/python.test.js +669 -0
  26. package/dist/src/analysis/adapters/__tests__/python.test.js.map +1 -0
  27. package/dist/src/analysis/adapters/__tests__/rust.test.d.ts +12 -0
  28. package/dist/src/analysis/adapters/__tests__/rust.test.d.ts.map +1 -0
  29. package/dist/src/analysis/adapters/__tests__/rust.test.js +676 -0
  30. package/dist/src/analysis/adapters/__tests__/rust.test.js.map +1 -0
  31. package/dist/src/analysis/adapters/__tests__/typescript.test.d.ts +14 -0
  32. package/dist/src/analysis/adapters/__tests__/typescript.test.d.ts.map +1 -0
  33. package/dist/src/analysis/adapters/__tests__/typescript.test.js +381 -0
  34. package/dist/src/analysis/adapters/__tests__/typescript.test.js.map +1 -0
  35. package/dist/src/analysis/adapters/base.d.ts +83 -0
  36. package/dist/src/analysis/adapters/base.d.ts.map +1 -0
  37. package/dist/src/analysis/adapters/base.js +40 -0
  38. package/dist/src/analysis/adapters/base.js.map +1 -0
  39. package/dist/src/analysis/adapters/factory.d.ts +150 -0
  40. package/dist/src/analysis/adapters/factory.d.ts.map +1 -0
  41. package/dist/src/analysis/adapters/factory.js +244 -0
  42. package/dist/src/analysis/adapters/factory.js.map +1 -0
  43. package/dist/src/analysis/adapters/go.d.ts +131 -0
  44. package/dist/src/analysis/adapters/go.d.ts.map +1 -0
  45. package/dist/src/analysis/adapters/go.js +414 -0
  46. package/dist/src/analysis/adapters/go.js.map +1 -0
  47. package/dist/src/analysis/adapters/index.d.ts +20 -0
  48. package/dist/src/analysis/adapters/index.d.ts.map +1 -0
  49. package/dist/src/analysis/adapters/index.js +23 -0
  50. package/dist/src/analysis/adapters/index.js.map +1 -0
  51. package/dist/src/analysis/adapters/java.d.ts +154 -0
  52. package/dist/src/analysis/adapters/java.d.ts.map +1 -0
  53. package/dist/src/analysis/adapters/java.js +407 -0
  54. package/dist/src/analysis/adapters/java.js.map +1 -0
  55. package/dist/src/analysis/adapters/python.d.ts +165 -0
  56. package/dist/src/analysis/adapters/python.d.ts.map +1 -0
  57. package/dist/src/analysis/adapters/python.js +475 -0
  58. package/dist/src/analysis/adapters/python.js.map +1 -0
  59. package/dist/src/analysis/adapters/rust.d.ts +116 -0
  60. package/dist/src/analysis/adapters/rust.d.ts.map +1 -0
  61. package/dist/src/analysis/adapters/rust.js +476 -0
  62. package/dist/src/analysis/adapters/rust.js.map +1 -0
  63. package/dist/src/analysis/adapters/typescript.d.ts +68 -0
  64. package/dist/src/analysis/adapters/typescript.d.ts.map +1 -0
  65. package/dist/src/analysis/adapters/typescript.js +79 -0
  66. package/dist/src/analysis/adapters/typescript.js.map +1 -0
  67. package/dist/src/analysis/aggregator.d.ts +193 -0
  68. package/dist/src/analysis/aggregator.d.ts.map +1 -0
  69. package/dist/src/analysis/aggregator.js +283 -0
  70. package/dist/src/analysis/aggregator.js.map +1 -0
  71. package/dist/src/analysis/cache.d.ts +180 -0
  72. package/dist/src/analysis/cache.d.ts.map +1 -0
  73. package/dist/src/analysis/cache.js +279 -0
  74. package/dist/src/analysis/cache.js.map +1 -0
  75. package/dist/src/analysis/file-streamer.d.ts +136 -0
  76. package/dist/src/analysis/file-streamer.d.ts.map +1 -0
  77. package/dist/src/analysis/file-streamer.js +291 -0
  78. package/dist/src/analysis/file-streamer.js.map +1 -0
  79. package/dist/src/analysis/incremental-parser.d.ts +186 -0
  80. package/dist/src/analysis/incremental-parser.d.ts.map +1 -0
  81. package/dist/src/analysis/incremental-parser.js +291 -0
  82. package/dist/src/analysis/incremental-parser.js.map +1 -0
  83. package/dist/src/analysis/incremental.d.ts +186 -0
  84. package/dist/src/analysis/incremental.d.ts.map +1 -0
  85. package/dist/src/analysis/incremental.js +247 -0
  86. package/dist/src/analysis/incremental.js.map +1 -0
  87. package/dist/src/analysis/index.d.ts +25 -3
  88. package/dist/src/analysis/index.d.ts.map +1 -1
  89. package/dist/src/analysis/index.js +45 -3
  90. package/dist/src/analysis/index.js.map +1 -1
  91. package/dist/src/analysis/language-detector.d.ts +92 -0
  92. package/dist/src/analysis/language-detector.d.ts.map +1 -0
  93. package/dist/src/analysis/language-detector.js +602 -0
  94. package/dist/src/analysis/language-detector.js.map +1 -0
  95. package/dist/src/analysis/memory-monitor.d.ts +199 -0
  96. package/dist/src/analysis/memory-monitor.d.ts.map +1 -0
  97. package/dist/src/analysis/memory-monitor.js +271 -0
  98. package/dist/src/analysis/memory-monitor.js.map +1 -0
  99. package/dist/src/analysis/metrics.d.ts +300 -0
  100. package/dist/src/analysis/metrics.d.ts.map +1 -0
  101. package/dist/src/analysis/metrics.js +537 -0
  102. package/dist/src/analysis/metrics.js.map +1 -0
  103. package/dist/src/analysis/router.d.ts +264 -0
  104. package/dist/src/analysis/router.d.ts.map +1 -0
  105. package/dist/src/analysis/router.js +398 -0
  106. package/dist/src/analysis/router.js.map +1 -0
  107. package/dist/src/analysis/tree-cache.d.ts +208 -0
  108. package/dist/src/analysis/tree-cache.d.ts.map +1 -0
  109. package/dist/src/analysis/tree-cache.js +288 -0
  110. package/dist/src/analysis/tree-cache.js.map +1 -0
  111. package/dist/src/analysis/tree-sitter/manager.d.ts +141 -0
  112. package/dist/src/analysis/tree-sitter/manager.d.ts.map +1 -0
  113. package/dist/src/analysis/tree-sitter/manager.js +239 -0
  114. package/dist/src/analysis/tree-sitter/manager.js.map +1 -0
  115. package/dist/src/analysis/types.d.ts +69 -6
  116. package/dist/src/analysis/types.d.ts.map +1 -1
  117. package/dist/src/analysis/types.js +23 -2
  118. package/dist/src/analysis/types.js.map +1 -1
  119. package/dist/src/analysis/worker-pool.d.ts +141 -0
  120. package/dist/src/analysis/worker-pool.d.ts.map +1 -0
  121. package/dist/src/analysis/worker-pool.js +418 -0
  122. package/dist/src/analysis/worker-pool.js.map +1 -0
  123. package/dist/src/analytics/schema.d.ts +1 -1
  124. package/dist/src/analytics/schema.d.ts.map +1 -1
  125. package/dist/src/analytics/schema.js +72 -0
  126. package/dist/src/analytics/schema.js.map +1 -1
  127. package/dist/src/api/cache.d.ts +24 -1
  128. package/dist/src/api/cache.d.ts.map +1 -1
  129. package/dist/src/api/cache.js +50 -2
  130. package/dist/src/api/cache.js.map +1 -1
  131. package/dist/src/api/client.d.ts +132 -2
  132. package/dist/src/api/client.d.ts.map +1 -1
  133. package/dist/src/api/client.js +214 -18
  134. package/dist/src/api/client.js.map +1 -1
  135. package/dist/src/api/index.d.ts +2 -0
  136. package/dist/src/api/index.d.ts.map +1 -1
  137. package/dist/src/api/index.js +7 -0
  138. package/dist/src/api/index.js.map +1 -1
  139. package/dist/src/api/types.d.ts +251 -0
  140. package/dist/src/api/types.d.ts.map +1 -0
  141. package/dist/src/api/types.js +9 -0
  142. package/dist/src/api/types.js.map +1 -0
  143. package/dist/src/benchmarks/memory/MemoryProfiler.d.ts.map +1 -1
  144. package/dist/src/benchmarks/memory/MemoryProfiler.js.map +1 -1
  145. package/dist/src/embeddings/index.d.ts.map +1 -1
  146. package/dist/src/embeddings/index.js.map +1 -1
  147. package/dist/src/errors.d.ts +1 -0
  148. package/dist/src/errors.d.ts.map +1 -1
  149. package/dist/src/errors.js +1 -0
  150. package/dist/src/errors.js.map +1 -1
  151. package/dist/src/index.d.ts +3 -3
  152. package/dist/src/index.d.ts.map +1 -1
  153. package/dist/src/index.js +4 -4
  154. package/dist/src/index.js.map +1 -1
  155. package/dist/src/repositories/IndexerRepository.d.ts.map +1 -1
  156. package/dist/src/repositories/IndexerRepository.js +1 -0
  157. package/dist/src/repositories/IndexerRepository.js.map +1 -1
  158. package/dist/src/repositories/SkillRepository.d.ts.map +1 -1
  159. package/dist/src/repositories/SkillRepository.js +1 -0
  160. package/dist/src/repositories/SkillRepository.js.map +1 -1
  161. package/dist/src/repositories/quarantine/QuarantineRepository.d.ts.map +1 -1
  162. package/dist/src/repositories/quarantine/QuarantineRepository.js.map +1 -1
  163. package/dist/src/repositories/quarantine/query-builder.d.ts.map +1 -1
  164. package/dist/src/repositories/quarantine/query-builder.js +1 -1
  165. package/dist/src/repositories/quarantine/query-builder.js.map +1 -1
  166. package/dist/src/scripts/__tests__/scan-imported-skills.test.js +3 -3
  167. package/dist/src/scripts/__tests__/scan-imported-skills.test.js.map +1 -1
  168. package/dist/src/scripts/github-import/index.js.map +1 -1
  169. package/dist/src/scripts/import-github-skills.js +1 -1
  170. package/dist/src/scripts/import-github-skills.js.map +1 -1
  171. package/dist/src/scripts/skill-scanner/reporter.d.ts.map +1 -1
  172. package/dist/src/scripts/skill-scanner/reporter.js.map +1 -1
  173. package/dist/src/scripts/skill-scanner/scanner.d.ts.map +1 -1
  174. package/dist/src/scripts/skill-scanner/scanner.js.map +1 -1
  175. package/dist/src/scripts/skill-scanner/trust-scorer.d.ts.map +1 -1
  176. package/dist/src/scripts/skill-scanner/trust-scorer.js.map +1 -1
  177. package/dist/src/scripts/validation/index.js +1 -2
  178. package/dist/src/scripts/validation/index.js.map +1 -1
  179. package/dist/src/scripts/validation/pipeline.d.ts.map +1 -1
  180. package/dist/src/scripts/validation/pipeline.js.map +1 -1
  181. package/dist/src/scripts/validation/types.d.ts +2 -2
  182. package/dist/src/security/scanner/SecurityScanner.d.ts.map +1 -1
  183. package/dist/src/security/scanner/SecurityScanner.js.map +1 -1
  184. package/dist/src/services/SearchService.d.ts.map +1 -1
  185. package/dist/src/services/SearchService.js +1 -0
  186. package/dist/src/services/SearchService.js.map +1 -1
  187. package/dist/src/session/SessionHealthMonitor.d.ts +1 -1
  188. package/dist/src/session/SessionHealthMonitor.d.ts.map +1 -1
  189. package/dist/src/session/SessionHealthMonitor.js +1 -1
  190. package/dist/src/session/SessionHealthMonitor.js.map +1 -1
  191. package/dist/src/telemetry/index.d.ts +1 -1
  192. package/dist/src/telemetry/index.d.ts.map +1 -1
  193. package/dist/src/telemetry/index.js +2 -2
  194. package/dist/src/telemetry/index.js.map +1 -1
  195. package/dist/src/telemetry/posthog.d.ts +27 -5
  196. package/dist/src/telemetry/posthog.d.ts.map +1 -1
  197. package/dist/src/telemetry/posthog.js +20 -5
  198. package/dist/src/telemetry/posthog.js.map +1 -1
  199. package/dist/src/types/skill.d.ts +3 -0
  200. package/dist/src/types/skill.d.ts.map +1 -1
  201. package/dist/src/types.d.ts +2 -1
  202. package/dist/src/types.d.ts.map +1 -1
  203. package/dist/src/types.js +2 -2
  204. package/dist/src/types.js.map +1 -1
  205. package/dist/tests/adapters-factory.test.d.ts +13 -0
  206. package/dist/tests/adapters-factory.test.d.ts.map +1 -0
  207. package/dist/tests/adapters-factory.test.js +308 -0
  208. package/dist/tests/adapters-factory.test.js.map +1 -0
  209. package/dist/tests/adapters-java.test.d.ts +13 -0
  210. package/dist/tests/adapters-java.test.d.ts.map +1 -0
  211. package/dist/tests/adapters-java.test.js +925 -0
  212. package/dist/tests/adapters-java.test.js.map +1 -0
  213. package/dist/tests/api/client.validation.test.d.ts +7 -0
  214. package/dist/tests/api/client.validation.test.d.ts.map +1 -0
  215. package/dist/tests/api/client.validation.test.js +183 -0
  216. package/dist/tests/api/client.validation.test.js.map +1 -0
  217. package/dist/tests/language-detector.test.d.ts +13 -0
  218. package/dist/tests/language-detector.test.d.ts.map +1 -0
  219. package/dist/tests/language-detector.test.js +674 -0
  220. package/dist/tests/language-detector.test.js.map +1 -0
  221. package/dist/tests/telemetry/posthog.test.d.ts +13 -0
  222. package/dist/tests/telemetry/posthog.test.d.ts.map +1 -0
  223. package/dist/tests/telemetry/posthog.test.js +600 -0
  224. package/dist/tests/telemetry/posthog.test.js.map +1 -0
  225. package/package.json +5 -6
  226. package/dist/src/security/RateLimiter.d.ts +0 -337
  227. package/dist/src/security/RateLimiter.d.ts.map +0 -1
  228. package/dist/src/security/RateLimiter.js +0 -782
  229. package/dist/src/security/RateLimiter.js.map +0 -1
  230. package/dist/src/security/scanner.d.ts +0 -151
  231. package/dist/src/security/scanner.d.ts.map +0 -1
  232. package/dist/src/security/scanner.js +0 -599
  233. package/dist/src/security/scanner.js.map +0 -1
@@ -0,0 +1,674 @@
1
+ /**
2
+ * SMI-1340: Language Detector Tests
3
+ *
4
+ * Tests for the LanguageDetector class, verifying:
5
+ * - Shebang detection
6
+ * - Content pattern analysis
7
+ * - Statistical keyword analysis
8
+ * - Confidence scoring
9
+ *
10
+ * @see docs/architecture/multi-language-analysis.md
11
+ */
12
+ import { describe, it, expect } from 'vitest';
13
+ import { LanguageDetector, detectLanguage } from '../src/analysis/language-detector.js';
14
+ describe('LanguageDetector', () => {
15
+ describe('shebang detection', () => {
16
+ const detector = new LanguageDetector();
17
+ it('detects Python from shebang', () => {
18
+ const result = detector.detect('#!/usr/bin/python\nprint("hello")');
19
+ expect(result.language).toBe('python');
20
+ expect(result.confidence).toBe(1.0);
21
+ expect(result.method).toBe('shebang');
22
+ expect(result.evidence[0]).toContain('#!/usr/bin/python');
23
+ });
24
+ it('detects Python3 from shebang', () => {
25
+ const result = detector.detect('#!/usr/bin/python3\nimport sys');
26
+ expect(result.language).toBe('python');
27
+ expect(result.confidence).toBe(1.0);
28
+ expect(result.method).toBe('shebang');
29
+ });
30
+ it('detects Python from env shebang', () => {
31
+ const result = detector.detect('#!/usr/bin/env python\nprint("hello")');
32
+ expect(result.language).toBe('python');
33
+ expect(result.confidence).toBe(1.0);
34
+ });
35
+ it('detects Node.js from shebang', () => {
36
+ const result = detector.detect('#!/usr/bin/node\nconsole.log("hello")');
37
+ expect(result.language).toBe('javascript');
38
+ expect(result.confidence).toBe(1.0);
39
+ });
40
+ it('detects Node.js from env shebang', () => {
41
+ const result = detector.detect('#!/usr/bin/env node\nmodule.exports = {}');
42
+ expect(result.language).toBe('javascript');
43
+ expect(result.confidence).toBe(1.0);
44
+ });
45
+ it('detects TypeScript from ts-node shebang', () => {
46
+ const result = detector.detect('#!/usr/bin/env ts-node\nconst x: number = 1');
47
+ expect(result.language).toBe('typescript');
48
+ expect(result.confidence).toBe(1.0);
49
+ });
50
+ it('detects TypeScript from deno shebang', () => {
51
+ const result = detector.detect('#!/usr/bin/env deno run\nDeno.writeTextFile("test.txt", "hello")');
52
+ expect(result.language).toBe('typescript');
53
+ expect(result.confidence).toBe(1.0);
54
+ });
55
+ it('detects TypeScript from bun shebang', () => {
56
+ const result = detector.detect('#!/usr/bin/env bun\nawait Bun.write("output.txt", "hello")');
57
+ expect(result.language).toBe('typescript');
58
+ expect(result.confidence).toBe(1.0);
59
+ });
60
+ it('detects TypeScript from npx tsx shebang', () => {
61
+ const result = detector.detect('#!/usr/bin/env npx tsx\nconst x: string = "hello"');
62
+ expect(result.language).toBe('typescript');
63
+ expect(result.confidence).toBe(1.0);
64
+ });
65
+ it('returns null for unknown shebang', () => {
66
+ const result = detector.detect('#!/bin/bash\necho "hello"');
67
+ // Should not detect as our supported languages
68
+ expect(result.method).not.toBe('shebang');
69
+ });
70
+ it('returns null for no shebang', () => {
71
+ const result = detector.detectByShebang('print("hello")');
72
+ expect(result.language).toBeNull();
73
+ expect(result.confidence).toBe(0);
74
+ });
75
+ });
76
+ describe('pattern detection', () => {
77
+ const detector = new LanguageDetector();
78
+ describe('TypeScript patterns', () => {
79
+ it('detects type-only imports', () => {
80
+ const content = `
81
+ import type { User } from './types'
82
+ import type { Config } from './config'
83
+
84
+ const user: User = { id: 1, name: 'test' }
85
+ `;
86
+ const result = detector.detect(content);
87
+ expect(result.language).toBe('typescript');
88
+ expect(result.method).toBe('pattern');
89
+ expect(result.evidence).toContain('type-only import');
90
+ });
91
+ it('detects interface declarations', () => {
92
+ const content = `
93
+ interface User {
94
+ id: number
95
+ name: string
96
+ }
97
+ `;
98
+ const result = detector.detect(content);
99
+ expect(result.language).toBe('typescript');
100
+ expect(result.evidence).toContain('interface declaration');
101
+ });
102
+ it('detects type annotations', () => {
103
+ const content = `
104
+ const count: number = 0
105
+ const name: string = 'test'
106
+ function greet(name: string): void {}
107
+ const callback: (x: number) => boolean = (x) => x > 0
108
+ `;
109
+ const result = detector.detect(content);
110
+ // Type annotations might not be strong enough on their own
111
+ // Combined with other patterns they should detect TypeScript
112
+ expect(['typescript', null]).toContain(result.language);
113
+ if (result.language === 'typescript') {
114
+ expect(result.evidence).toContain('type annotation');
115
+ }
116
+ });
117
+ });
118
+ describe('Python patterns', () => {
119
+ it('detects from-import statements', () => {
120
+ const content = `
121
+ from typing import List, Dict
122
+ from dataclasses import dataclass
123
+
124
+ @dataclass
125
+ class User:
126
+ name: str
127
+ `;
128
+ const result = detector.detect(content);
129
+ expect(result.language).toBe('python');
130
+ expect(result.method).toBe('pattern');
131
+ expect(result.evidence).toContain('from import');
132
+ });
133
+ it('detects function definitions', () => {
134
+ const content = `
135
+ def greet(name: str) -> str:
136
+ return f"Hello, {name}"
137
+
138
+ async def fetch_data():
139
+ pass
140
+ `;
141
+ const result = detector.detect(content);
142
+ expect(result.language).toBe('python');
143
+ expect(result.evidence).toContain('function definition');
144
+ });
145
+ it('detects main guard', () => {
146
+ const content = `
147
+ def main():
148
+ print("Hello")
149
+
150
+ if __name__ == "__main__":
151
+ main()
152
+ `;
153
+ const result = detector.detect(content);
154
+ expect(result.language).toBe('python');
155
+ expect(result.confidence).toBeGreaterThan(0.5);
156
+ });
157
+ it('detects class definitions', () => {
158
+ const content = `
159
+ class UserService:
160
+ def __init__(self):
161
+ self.users = []
162
+
163
+ def get_user(self, id):
164
+ return self.users[id]
165
+ `;
166
+ const result = detector.detect(content);
167
+ expect(result.language).toBe('python');
168
+ expect(result.evidence).toContain('class definition');
169
+ });
170
+ it('detects elif keyword', () => {
171
+ const content = `
172
+ if x > 10:
173
+ print("big")
174
+ elif x > 5:
175
+ print("medium")
176
+ else:
177
+ print("small")
178
+ `;
179
+ const result = detector.detect(content);
180
+ expect(result.language).toBe('python');
181
+ expect(result.evidence).toContain('elif keyword');
182
+ });
183
+ });
184
+ describe('Go patterns', () => {
185
+ it('detects package declaration', () => {
186
+ const content = `
187
+ package main
188
+
189
+ import "fmt"
190
+
191
+ func main() {
192
+ fmt.Println("Hello")
193
+ }
194
+ `;
195
+ const result = detector.detect(content);
196
+ expect(result.language).toBe('go');
197
+ expect(result.method).toBe('pattern');
198
+ expect(result.evidence).toContain('package declaration');
199
+ });
200
+ it('detects method receivers', () => {
201
+ const content = `
202
+ type User struct {
203
+ Name string
204
+ }
205
+
206
+ func (u *User) Greet() string {
207
+ return "Hello, " + u.Name
208
+ }
209
+ `;
210
+ const result = detector.detect(content);
211
+ expect(result.language).toBe('go');
212
+ expect(result.evidence).toContain('method with receiver');
213
+ });
214
+ it('detects short variable declaration', () => {
215
+ const content = `
216
+ func main() {
217
+ name := "test"
218
+ count := 0
219
+ }
220
+ `;
221
+ const result = detector.detect(content);
222
+ expect(result.language).toBe('go');
223
+ expect(result.evidence).toContain('short variable declaration');
224
+ });
225
+ it('detects goroutines and channels', () => {
226
+ const content = `
227
+ func main() {
228
+ ch := make(chan int)
229
+ go processData(ch)
230
+ defer close(ch)
231
+ }
232
+ `;
233
+ const result = detector.detect(content);
234
+ expect(result.language).toBe('go');
235
+ });
236
+ });
237
+ describe('Rust patterns', () => {
238
+ it('detects use statements', () => {
239
+ const content = `
240
+ use std::io::Read;
241
+ use std::collections::HashMap;
242
+
243
+ fn main() {
244
+ let map: HashMap<String, i32> = HashMap::new();
245
+ }
246
+ `;
247
+ const result = detector.detect(content);
248
+ expect(result.language).toBe('rust');
249
+ expect(result.method).toBe('pattern');
250
+ expect(result.evidence).toContain('use statement');
251
+ });
252
+ it('detects impl blocks', () => {
253
+ const content = `
254
+ struct User {
255
+ name: String,
256
+ }
257
+
258
+ impl User {
259
+ fn new(name: String) -> Self {
260
+ User { name }
261
+ }
262
+ }
263
+ `;
264
+ const result = detector.detect(content);
265
+ expect(result.language).toBe('rust');
266
+ expect(result.evidence).toContain('impl block');
267
+ });
268
+ it('detects derive macros', () => {
269
+ const content = `
270
+ #[derive(Debug, Clone, Serialize)]
271
+ struct Config {
272
+ host: String,
273
+ port: u16,
274
+ }
275
+ `;
276
+ const result = detector.detect(content);
277
+ expect(result.language).toBe('rust');
278
+ expect(result.evidence).toContain('derive macro');
279
+ });
280
+ it('detects Result and Option types', () => {
281
+ const content = `
282
+ fn read_file(path: &str) -> Result<String, io::Error> {
283
+ let content: Option<String> = Some("test".to_string());
284
+ content.unwrap()
285
+ }
286
+ `;
287
+ const result = detector.detect(content);
288
+ expect(result.language).toBe('rust');
289
+ });
290
+ it('detects match expressions', () => {
291
+ const content = `
292
+ fn get_value(opt: Option<i32>) -> i32 {
293
+ match opt {
294
+ Some(x) => x,
295
+ None => 0,
296
+ }
297
+ }
298
+ `;
299
+ const result = detector.detect(content);
300
+ expect(result.language).toBe('rust');
301
+ expect(result.evidence).toContain('match expression');
302
+ });
303
+ });
304
+ describe('Java patterns', () => {
305
+ it('detects package declaration', () => {
306
+ const content = `
307
+ package com.example.app;
308
+
309
+ import java.util.List;
310
+
311
+ public class Main {
312
+ public static void main(String[] args) {
313
+ System.out.println("Hello");
314
+ }
315
+ }
316
+ `;
317
+ const result = detector.detect(content);
318
+ expect(result.language).toBe('java');
319
+ expect(result.method).toBe('pattern');
320
+ expect(result.evidence).toContain('package declaration');
321
+ });
322
+ it('detects public class', () => {
323
+ const content = `
324
+ public class UserService {
325
+ private List<User> users;
326
+
327
+ public User findById(int id) {
328
+ return users.get(id);
329
+ }
330
+ }
331
+ `;
332
+ const result = detector.detect(content);
333
+ expect(result.language).toBe('java');
334
+ expect(result.evidence).toContain('public class');
335
+ });
336
+ it('detects Override annotation', () => {
337
+ const content = `
338
+ class User {
339
+ @Override
340
+ public String toString() {
341
+ return "User{}";
342
+ }
343
+ }
344
+ `;
345
+ const result = detector.detect(content);
346
+ expect(result.language).toBe('java');
347
+ expect(result.evidence).toContain('Override annotation');
348
+ });
349
+ it('detects System.out.println', () => {
350
+ const content = `
351
+ public class Main {
352
+ public void test() {
353
+ System.out.println("test");
354
+ System.out.print("test");
355
+ }
356
+
357
+ public static void main(String[] args) {
358
+ new Main().test();
359
+ }
360
+ }
361
+ `;
362
+ const result = detector.detect(content);
363
+ expect(result.language).toBe('java');
364
+ expect(result.evidence).toContain('System.out');
365
+ });
366
+ });
367
+ describe('JavaScript patterns', () => {
368
+ it('detects ES module imports', () => {
369
+ const content = `
370
+ import React from 'react'
371
+ import { useState, useEffect } from 'react'
372
+
373
+ export default function App() {
374
+ return <div>Hello</div>
375
+ }
376
+ `;
377
+ const result = detector.detect(content);
378
+ // Should upgrade to TypeScript since TypeScript patterns match too
379
+ expect(['typescript', 'javascript']).toContain(result.language);
380
+ });
381
+ it('detects CommonJS require', () => {
382
+ const content = `
383
+ const fs = require('fs')
384
+ const path = require('path')
385
+
386
+ module.exports = {
387
+ readFile: fs.readFileSync
388
+ }
389
+ `;
390
+ const result = detector.detect(content);
391
+ expect(['typescript', 'javascript']).toContain(result.language);
392
+ });
393
+ it('detects async/await', () => {
394
+ const content = `
395
+ async function fetchData() {
396
+ const response = await fetch('/api/data')
397
+ return await response.json()
398
+ }
399
+ `;
400
+ const result = detector.detect(content);
401
+ expect(['typescript', 'javascript']).toContain(result.language);
402
+ });
403
+ });
404
+ });
405
+ describe('statistical detection', () => {
406
+ const detector = new LanguageDetector();
407
+ it('detects Python by keyword frequency', () => {
408
+ const content = `
409
+ def function_one():
410
+ pass
411
+
412
+ def function_two():
413
+ if True:
414
+ return None
415
+ elif False:
416
+ raise Exception
417
+ else:
418
+ yield 1
419
+
420
+ class MyClass:
421
+ def __init__(self):
422
+ self.value = True
423
+ `;
424
+ const result = detector.detect(content);
425
+ expect(result.language).toBe('python');
426
+ });
427
+ it('detects Go by keyword frequency', () => {
428
+ const content = `
429
+ package main
430
+
431
+ func main() {
432
+ var x int
433
+ const y = 10
434
+
435
+ for i := range []int{1, 2, 3} {
436
+ go processItem(i)
437
+ }
438
+
439
+ select {
440
+ case <-done:
441
+ return
442
+ }
443
+ }
444
+ `;
445
+ const result = detector.detect(content);
446
+ expect(result.language).toBe('go');
447
+ });
448
+ it('returns lower confidence for statistical detection', () => {
449
+ // Content that relies only on statistical detection
450
+ const content = `
451
+ fn test
452
+ let mut
453
+ const static
454
+ struct enum
455
+ impl use
456
+ `;
457
+ const result = detector.detectByStatistics(content);
458
+ expect(result.confidence).toBeLessThanOrEqual(0.7);
459
+ });
460
+ });
461
+ describe('confidence threshold', () => {
462
+ it('uses default minimum confidence', () => {
463
+ const detector = new LanguageDetector();
464
+ expect(detector.getMinConfidence()).toBe(0.3);
465
+ });
466
+ it('respects custom minimum confidence', () => {
467
+ const detector = new LanguageDetector({ minConfidence: 0.5 });
468
+ expect(detector.getMinConfidence()).toBe(0.5);
469
+ });
470
+ it('can update confidence threshold', () => {
471
+ const detector = new LanguageDetector();
472
+ detector.setMinConfidence(0.8);
473
+ expect(detector.getMinConfidence()).toBe(0.8);
474
+ });
475
+ it('clamps confidence to valid range', () => {
476
+ const detector = new LanguageDetector();
477
+ detector.setMinConfidence(1.5);
478
+ expect(detector.getMinConfidence()).toBe(1);
479
+ detector.setMinConfidence(-0.5);
480
+ expect(detector.getMinConfidence()).toBe(0);
481
+ });
482
+ it('returns null when confidence below threshold', () => {
483
+ const detector = new LanguageDetector({ minConfidence: 0.99 });
484
+ // Even strong patterns might not reach 0.99
485
+ const result = detector.detect('const x = 1');
486
+ // Should likely return null due to high threshold
487
+ if (result.confidence < 0.99) {
488
+ expect(result.language).toBeNull();
489
+ }
490
+ });
491
+ });
492
+ describe('detectLanguage convenience function', () => {
493
+ it('detects language from content', () => {
494
+ const result = detectLanguage('#!/usr/bin/env python\nprint("hello")');
495
+ expect(result.language).toBe('python');
496
+ expect(result.confidence).toBe(1.0);
497
+ });
498
+ it('accepts options', () => {
499
+ const result = detectLanguage('const x = 1', { minConfidence: 0.1 });
500
+ expect(result).toBeDefined();
501
+ });
502
+ });
503
+ describe('edge cases', () => {
504
+ const detector = new LanguageDetector();
505
+ it('handles empty content', () => {
506
+ const result = detector.detect('');
507
+ expect(result.language).toBeNull();
508
+ expect(result.confidence).toBe(0);
509
+ expect(result.method).toBe('none');
510
+ });
511
+ it('handles whitespace-only content', () => {
512
+ const result = detector.detect(' \n\t\n ');
513
+ expect(result.language).toBeNull();
514
+ expect(result.confidence).toBe(0);
515
+ });
516
+ it('handles content with no clear language', () => {
517
+ const result = detector.detect('Hello World\nThis is just text\nNo code here');
518
+ expect(result.confidence).toBeLessThan(0.5);
519
+ });
520
+ it('handles binary-like content', () => {
521
+ const result = detector.detect('\x00\x01\x02\x03');
522
+ expect(result.confidence).toBeLessThan(0.5);
523
+ });
524
+ it('handles mixed language patterns', () => {
525
+ // Content with both Python and JavaScript patterns
526
+ const content = `
527
+ def function():
528
+ pass
529
+
530
+ function test() {
531
+ return 1
532
+ }
533
+ `;
534
+ const result = detector.detect(content);
535
+ // Should pick one, likely Python due to stronger patterns
536
+ expect(result.language).not.toBeNull();
537
+ expect(result.confidence).toBeGreaterThan(0);
538
+ });
539
+ });
540
+ describe('real-world examples', () => {
541
+ const detector = new LanguageDetector();
542
+ it('detects Python Django view', () => {
543
+ const content = `
544
+ from django.http import JsonResponse
545
+ from django.views import View
546
+
547
+ class UserView(View):
548
+ def get(self, request, user_id):
549
+ user = User.objects.get(id=user_id)
550
+ return JsonResponse({'name': user.name})
551
+
552
+ def post(self, request):
553
+ data = json.loads(request.body)
554
+ return JsonResponse({'status': 'ok'})
555
+ `;
556
+ const result = detector.detect(content);
557
+ expect(result.language).toBe('python');
558
+ expect(result.confidence).toBeGreaterThan(0.5);
559
+ });
560
+ it('detects TypeScript React component', () => {
561
+ const content = `
562
+ import React, { useState, useEffect } from 'react'
563
+ import type { User } from './types'
564
+
565
+ interface Props {
566
+ userId: number
567
+ }
568
+
569
+ export const UserProfile: React.FC<Props> = ({ userId }) => {
570
+ const [user, setUser] = useState<User | null>(null)
571
+
572
+ useEffect(() => {
573
+ fetchUser(userId).then(setUser)
574
+ }, [userId])
575
+
576
+ if (!user) return <div>Loading...</div>
577
+
578
+ return <div>{user.name}</div>
579
+ }
580
+ `;
581
+ const result = detector.detect(content);
582
+ expect(result.language).toBe('typescript');
583
+ expect(result.confidence).toBeGreaterThan(0.5);
584
+ });
585
+ it('detects Go HTTP handler', () => {
586
+ const content = `
587
+ package handlers
588
+
589
+ import (
590
+ "encoding/json"
591
+ "net/http"
592
+ )
593
+
594
+ type User struct {
595
+ ID int \`json:"id"\`
596
+ Name string \`json:"name"\`
597
+ }
598
+
599
+ func GetUser(w http.ResponseWriter, r *http.Request) {
600
+ user := &User{ID: 1, Name: "test"}
601
+
602
+ w.Header().Set("Content-Type", "application/json")
603
+ json.NewEncoder(w).Encode(user)
604
+ }
605
+ `;
606
+ const result = detector.detect(content);
607
+ expect(result.language).toBe('go');
608
+ expect(result.confidence).toBeGreaterThan(0.5);
609
+ });
610
+ it('detects Rust CLI application', () => {
611
+ const content = `
612
+ use clap::Parser;
613
+ use std::fs;
614
+ use std::io::Result;
615
+
616
+ #[derive(Parser, Debug)]
617
+ #[command(author, version, about)]
618
+ struct Args {
619
+ #[arg(short, long)]
620
+ input: String,
621
+
622
+ #[arg(short, long, default_value_t = false)]
623
+ verbose: bool,
624
+ }
625
+
626
+ fn main() -> Result<()> {
627
+ let args = Args::parse();
628
+
629
+ let content = fs::read_to_string(&args.input)?;
630
+
631
+ if args.verbose {
632
+ println!("Read {} bytes", content.len());
633
+ }
634
+
635
+ Ok(())
636
+ }
637
+ `;
638
+ const result = detector.detect(content);
639
+ expect(result.language).toBe('rust');
640
+ expect(result.confidence).toBeGreaterThan(0.5);
641
+ });
642
+ it('detects Java Spring Boot controller', () => {
643
+ const content = `
644
+ package com.example.demo.controller;
645
+
646
+ import org.springframework.web.bind.annotation.*;
647
+ import org.springframework.beans.factory.annotation.Autowired;
648
+ import java.util.List;
649
+
650
+ @RestController
651
+ @RequestMapping("/api/users")
652
+ public class UserController {
653
+
654
+ @Autowired
655
+ private UserService userService;
656
+
657
+ @GetMapping("/{id}")
658
+ public User getUser(@PathVariable Long id) {
659
+ return userService.findById(id);
660
+ }
661
+
662
+ @PostMapping
663
+ public User createUser(@RequestBody User user) {
664
+ return userService.save(user);
665
+ }
666
+ }
667
+ `;
668
+ const result = detector.detect(content);
669
+ expect(result.language).toBe('java');
670
+ expect(result.confidence).toBeGreaterThan(0.5);
671
+ });
672
+ });
673
+ });
674
+ //# sourceMappingURL=language-detector.test.js.map