@getmikk/core 2.0.13 → 2.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/README.md +4 -4
  2. package/package.json +2 -1
  3. package/src/analysis/index.ts +9 -0
  4. package/src/analysis/taint-analysis.ts +419 -0
  5. package/src/analysis/type-flow.ts +247 -0
  6. package/src/cache/incremental-cache.ts +278 -0
  7. package/src/cache/index.ts +1 -0
  8. package/src/contract/contract-generator.ts +31 -3
  9. package/src/contract/contract-reader.ts +1 -0
  10. package/src/contract/lock-compiler.ts +125 -12
  11. package/src/contract/schema.ts +4 -0
  12. package/src/error-handler.ts +2 -1
  13. package/src/graph/cluster-detector.ts +2 -4
  14. package/src/graph/dead-code-detector.ts +303 -117
  15. package/src/graph/graph-builder.ts +21 -161
  16. package/src/graph/impact-analyzer.ts +1 -0
  17. package/src/graph/index.ts +2 -0
  18. package/src/graph/rich-function-index.ts +1080 -0
  19. package/src/graph/symbol-table.ts +252 -0
  20. package/src/hash/hash-store.ts +1 -0
  21. package/src/index.ts +4 -0
  22. package/src/parser/base-extractor.ts +19 -0
  23. package/src/parser/boundary-checker.ts +31 -12
  24. package/src/parser/error-recovery.ts +647 -0
  25. package/src/parser/function-body-extractor.ts +248 -0
  26. package/src/parser/go/go-extractor.ts +249 -676
  27. package/src/parser/index.ts +138 -295
  28. package/src/parser/language-registry.ts +57 -0
  29. package/src/parser/oxc-parser.ts +166 -28
  30. package/src/parser/oxc-resolver.ts +179 -11
  31. package/src/parser/parser-constants.ts +1 -0
  32. package/src/parser/rust/rust-extractor.ts +109 -0
  33. package/src/parser/tree-sitter/parser.ts +400 -66
  34. package/src/parser/tree-sitter/queries.ts +106 -10
  35. package/src/parser/types.ts +20 -1
  36. package/src/search/bm25.ts +21 -8
  37. package/src/search/direct-search.ts +472 -0
  38. package/src/search/embedding-provider.ts +249 -0
  39. package/src/search/index.ts +12 -0
  40. package/src/search/semantic-search.ts +435 -0
  41. package/src/security/index.ts +1 -0
  42. package/src/security/scanner.ts +342 -0
  43. package/src/utils/artifact-transaction.ts +1 -0
  44. package/src/utils/atomic-write.ts +1 -0
  45. package/src/utils/errors.ts +89 -4
  46. package/src/utils/fs.ts +150 -65
  47. package/src/utils/json.ts +1 -0
  48. package/src/utils/language-registry.ts +96 -5
  49. package/src/utils/minimatch.ts +49 -6
  50. package/src/utils/path.ts +26 -0
  51. package/tests/dead-code.test.ts +3 -2
  52. package/tests/direct-search.test.ts +435 -0
  53. package/tests/error-recovery.test.ts +143 -0
  54. package/tests/fixtures/simple-api/src/index.ts +1 -1
  55. package/tests/go-parser.test.ts +19 -335
  56. package/tests/js-parser.test.ts +18 -1089
  57. package/tests/language-registry-all.test.ts +276 -0
  58. package/tests/language-registry.test.ts +6 -4
  59. package/tests/parse-diagnostics.test.ts +9 -96
  60. package/tests/parser.test.ts +42 -771
  61. package/tests/polyglot-parser.test.ts +117 -0
  62. package/tests/rich-function-index.test.ts +703 -0
  63. package/tests/tree-sitter-parser.test.ts +108 -80
  64. package/tests/ts-parser.test.ts +8 -8
  65. package/tests/verification.test.ts +175 -0
  66. package/src/parser/base-parser.ts +0 -16
  67. package/src/parser/go/go-parser.ts +0 -43
  68. package/src/parser/javascript/js-extractor.ts +0 -278
  69. package/src/parser/javascript/js-parser.ts +0 -101
  70. package/src/parser/typescript/ts-extractor.ts +0 -447
  71. package/src/parser/typescript/ts-parser.ts +0 -36
@@ -0,0 +1,248 @@
1
+ import { readFile } from 'node:fs/promises'
2
+ import { existsSync } from 'node:fs'
3
+ import type { RichFunction } from '../graph/rich-function-index.js'
4
+
5
+ export interface FunctionBody {
6
+ id: string
7
+ name: string
8
+ file: string
9
+ startLine: number
10
+ endLine: number
11
+ body: string
12
+ fullSource: string
13
+ isComplete: boolean
14
+ trimmedLines: number
15
+ }
16
+
17
+ export interface BodyExtractOptions {
18
+ maxLines?: number
19
+ includeComments?: boolean
20
+ includeImports?: boolean
21
+ contextLines?: number
22
+ }
23
+
24
+ export class FunctionBodyExtractor {
25
+ private cache: Map<string, string> = new Map()
26
+ private bodyCache: Map<string, FunctionBody> = new Map()
27
+
28
+ async extractBody(fn: RichFunction, options: BodyExtractOptions = {}): Promise<FunctionBody | null> {
29
+ const cacheKey = `${fn.id}:${fn.startLine}:${fn.endLine}`
30
+
31
+ if (this.bodyCache.has(cacheKey)) {
32
+ return this.bodyCache.get(cacheKey)!
33
+ }
34
+
35
+ const { maxLines = 500, contextLines = 0 } = options
36
+
37
+ const source = await this.readSource(fn.file)
38
+ if (!source) {
39
+ return null
40
+ }
41
+
42
+ const lines = source.split('\n')
43
+
44
+ const startLine = Math.max(0, fn.startLine - 1 - contextLines)
45
+ const endLine = Math.min(lines.length, fn.endLine + contextLines)
46
+
47
+ const bodyLines = lines.slice(startLine, endLine)
48
+ const body = bodyLines.join('\n')
49
+
50
+ const isComplete = fn.endLine <= lines.length && bodyLines.length >= fn.endLine - fn.startLine
51
+
52
+ const result: FunctionBody = {
53
+ id: fn.id,
54
+ name: fn.name,
55
+ file: fn.file,
56
+ startLine: fn.startLine,
57
+ endLine: fn.endLine,
58
+ body,
59
+ fullSource: source,
60
+ isComplete,
61
+ trimmedLines: lines.length - endLine,
62
+ }
63
+
64
+ this.bodyCache.set(cacheKey, result)
65
+ return result
66
+ }
67
+
68
+ async extractBodies(fns: RichFunction[], options: BodyExtractOptions = {}): Promise<Map<string, FunctionBody>> {
69
+ const results = new Map<string, FunctionBody>()
70
+
71
+ for (const fn of fns) {
72
+ const body = await this.extractBody(fn, options)
73
+ if (body) {
74
+ results.set(fn.id, body)
75
+ }
76
+ }
77
+
78
+ return results
79
+ }
80
+
81
+ async extractBodiesByIds(ids: string[], getFn: (id: string) => RichFunction | undefined, options: BodyExtractOptions = {}): Promise<Map<string, FunctionBody>> {
82
+ const results = new Map<string, FunctionBody>()
83
+
84
+ const fns = ids.map(id => getFn(id)).filter(Boolean) as RichFunction[]
85
+ const bodies = await this.extractBodies(fns, options)
86
+
87
+ for (const [id, body] of bodies) {
88
+ results.set(id, body)
89
+ }
90
+
91
+ return results
92
+ }
93
+
94
+ private async readSource(filePath: string): Promise<string | null> {
95
+ if (this.cache.has(filePath)) {
96
+ return this.cache.get(filePath)!
97
+ }
98
+
99
+ if (!existsSync(filePath)) {
100
+ return null
101
+ }
102
+
103
+ try {
104
+ const content = await readFile(filePath, 'utf-8')
105
+ this.cache.set(filePath, content)
106
+ return content
107
+ } catch {
108
+ return null
109
+ }
110
+ }
111
+
112
+ clearCache(): void {
113
+ this.cache.clear()
114
+ this.bodyCache.clear()
115
+ }
116
+
117
+ getCachedCount(): number {
118
+ return this.cache.size
119
+ }
120
+
121
+ getBodyCacheCount(): number {
122
+ return this.bodyCache.size
123
+ }
124
+ }
125
+
126
+ export interface ExtractResult {
127
+ success: boolean
128
+ body?: string
129
+ signature?: string
130
+ params?: string
131
+ returnType?: string
132
+ docComment?: string
133
+ error?: string
134
+ }
135
+
136
+ export async function extractFunction(
137
+ filePath: string,
138
+ functionName: string,
139
+ options: BodyExtractOptions = {}
140
+ ): Promise<ExtractResult> {
141
+ if (!existsSync(filePath)) {
142
+ return { success: false, error: 'File not found' }
143
+ }
144
+
145
+ try {
146
+ const content = await readFile(filePath, 'utf-8')
147
+ const lines = content.split('\n')
148
+
149
+ let startLine = -1
150
+ let endLine = -1
151
+ let braceCount = 0
152
+ let inFunction = false
153
+ let foundOpening = false
154
+
155
+ for (let i = 0; i < lines.length; i++) {
156
+ const line = lines[i]
157
+
158
+ if (!inFunction && (line.includes(`function ${functionName}`) || line.includes(`const ${functionName}`) || line.includes(`${functionName}(`) || line.includes(`async ${functionName}`))) {
159
+ if (line.includes(`function ${functionName}`) || line.includes(`async function ${functionName}`) || line.match(new RegExp(`(const|let|var)\\s+${functionName}\\s*=`)) || line.match(new RegExp(`${functionName}\\s*\\(`))) {
160
+ inFunction = true
161
+ startLine = i
162
+ }
163
+ }
164
+
165
+ if (inFunction) {
166
+ for (const char of line) {
167
+ if (char === '{') {
168
+ braceCount++
169
+ foundOpening = true
170
+ } else if (char === '}') {
171
+ braceCount--
172
+ }
173
+ }
174
+
175
+ if (foundOpening && braceCount === 0) {
176
+ endLine = i + 1
177
+ break
178
+ }
179
+ }
180
+ }
181
+
182
+ if (startLine === -1 || endLine === -1) {
183
+ return { success: false, error: 'Function not found' }
184
+ }
185
+
186
+ const bodyLines = lines.slice(startLine, endLine)
187
+ const body = bodyLines.join('\n')
188
+
189
+ const maxLines = options.maxLines || 500
190
+ const trimmedBody = bodyLines.length > maxLines
191
+ ? bodyLines.slice(0, maxLines).join('\n') + '\n// ... (truncated)'
192
+ : body
193
+
194
+ return {
195
+ success: true,
196
+ body: trimmedBody,
197
+ signature: extractSignature(bodyLines[0] || ''),
198
+ docComment: extractDocComment(lines, startLine),
199
+ }
200
+ } catch (err: any) {
201
+ return { success: false, error: err.message }
202
+ }
203
+ }
204
+
205
+ function extractSignature(line: string): string {
206
+ const match = line.match(/(?:async\s+)?(?:function\s+)?(?:\w+\s+)?(\w+)\s*\([^)]*\)\s*(?::\s*\w+)?/)
207
+ return match ? match[0] : line.trim()
208
+ }
209
+
210
+ function extractDocComment(lines: string[], startLine: number): string | undefined {
211
+ if (startLine === 0) return undefined
212
+
213
+ const prevLine = lines[startLine - 1]
214
+ if (prevLine && (prevLine.includes('/**') || prevLine.includes('///') || prevLine.startsWith('//'))) {
215
+ const commentLines: string[] = []
216
+ let i = startLine - 1
217
+
218
+ while (i >= 0) {
219
+ const line = lines[i]
220
+ if (line.includes('/**') || line.includes("'''")) {
221
+ commentLines.unshift(line)
222
+ break
223
+ } else if (line.trim().startsWith('*') || line.trim().startsWith('//')) {
224
+ commentLines.unshift(line)
225
+ } else {
226
+ break
227
+ }
228
+ i--
229
+ }
230
+
231
+ return commentLines.join('\n') || undefined
232
+ }
233
+
234
+ return undefined
235
+ }
236
+
237
+ export function trimBody(body: string, maxLines: number): string {
238
+ const lines = body.split('\n')
239
+ if (lines.length <= maxLines) return body
240
+
241
+ return lines.slice(0, maxLines).join('\n') + '\n// ...'
242
+ }
243
+
244
+ export function getBodyPreview(body: string, maxChars: number = 200): string {
245
+ const firstLine = body.split('\n')[0]
246
+ if (firstLine.length <= maxChars) return firstLine
247
+ return firstLine.slice(0, maxChars) + '...'
248
+ }