@getmikk/core 1.8.3 → 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 (42) hide show
  1. package/package.json +6 -4
  2. package/src/constants.ts +285 -0
  3. package/src/contract/contract-generator.ts +7 -0
  4. package/src/contract/index.ts +2 -3
  5. package/src/contract/lock-compiler.ts +66 -35
  6. package/src/contract/lock-reader.ts +30 -5
  7. package/src/contract/schema.ts +21 -0
  8. package/src/error-handler.ts +432 -0
  9. package/src/graph/cluster-detector.ts +52 -22
  10. package/src/graph/confidence-engine.ts +85 -0
  11. package/src/graph/graph-builder.ts +298 -255
  12. package/src/graph/impact-analyzer.ts +132 -119
  13. package/src/graph/index.ts +4 -0
  14. package/src/graph/memory-manager.ts +186 -0
  15. package/src/graph/query-engine.ts +76 -0
  16. package/src/graph/risk-engine.ts +86 -0
  17. package/src/graph/types.ts +89 -65
  18. package/src/index.ts +2 -0
  19. package/src/parser/change-detector.ts +99 -0
  20. package/src/parser/go/go-extractor.ts +18 -8
  21. package/src/parser/go/go-parser.ts +2 -0
  22. package/src/parser/index.ts +86 -36
  23. package/src/parser/javascript/js-extractor.ts +1 -1
  24. package/src/parser/javascript/js-parser.ts +2 -0
  25. package/src/parser/oxc-parser.ts +708 -0
  26. package/src/parser/oxc-resolver.ts +83 -0
  27. package/src/parser/tree-sitter/parser.ts +19 -10
  28. package/src/parser/types.ts +100 -73
  29. package/src/parser/typescript/ts-extractor.ts +229 -589
  30. package/src/parser/typescript/ts-parser.ts +16 -171
  31. package/src/parser/typescript/ts-resolver.ts +11 -1
  32. package/src/search/bm25.ts +16 -4
  33. package/src/utils/minimatch.ts +1 -1
  34. package/tests/contract.test.ts +2 -2
  35. package/tests/dead-code.test.ts +7 -7
  36. package/tests/esm-resolver.test.ts +75 -0
  37. package/tests/graph.test.ts +20 -20
  38. package/tests/helpers.ts +11 -6
  39. package/tests/impact-classified.test.ts +37 -41
  40. package/tests/parser.test.ts +7 -5
  41. package/tests/ts-parser.test.ts +27 -52
  42. package/test-output.txt +0 -373
@@ -0,0 +1,83 @@
1
+ import { ResolverFactory } from 'oxc-resolver';
2
+ import path from 'node:path';
3
+ import fs from 'node:fs';
4
+ import type { ParsedFile } from './types.js';
5
+
6
+ /**
7
+ * OxcResolver — Rust-backed compiler-grade module resolution.
8
+ *
9
+ * Resolution strategy:
10
+ * 1. If the resolved path is inside projectRoot → return project-relative posix path
11
+ * 2. If the resolved path is outside projectRoot (node_modules, monorepo peer) → return ''
12
+ * (external deps produce no graph edges; they're not in our file set)
13
+ * 3. On any error → return '' (unresolved, no false edges)
14
+ *
15
+ * All returned paths use forward slashes and are ABSOLUTE, matching what
16
+ * parseFiles passes to parse(). graph-builder only creates import edges when
17
+ * the target path exists as a file node — so no path format inconsistency can
18
+ * create false positive edges.
19
+ */
20
+ export class OxcResolver {
21
+ private resolver: any;
22
+ private readonly normalizedRoot: string;
23
+
24
+ constructor(private readonly projectRoot: string) {
25
+ this.normalizedRoot = path.resolve(projectRoot).replace(/\\/g, '/');
26
+
27
+ const tsconfigPath = path.resolve(projectRoot, 'tsconfig.json');
28
+ const hasTsConfig = fs.existsSync(tsconfigPath);
29
+
30
+ this.resolver = new ResolverFactory({
31
+ extensions: ['.ts', '.tsx', '.js', '.jsx', '.json', '.mjs', '.cjs', '.mts', '.cts'],
32
+ mainFields: ['module', 'main', 'jsnext:main'],
33
+ mainFiles: ['index', 'main', 'app'],
34
+ conditionNames: ['import', 'require', 'node', 'default', 'types', 'browser'],
35
+ symlinks: true,
36
+ modules: ['node_modules'],
37
+ tsconfig: hasTsConfig ? {
38
+ configFile: tsconfigPath,
39
+ references: 'auto',
40
+ } : undefined,
41
+ });
42
+ }
43
+
44
+ /**
45
+ * Resolve a single import source string relative to fromFile.
46
+ * fromFile MUST be an absolute path (as produced by parseFiles).
47
+ * Returns an absolute posix path, or '' if unresolvable/external.
48
+ */
49
+ public resolve(source: string, fromFile: string): string {
50
+ try {
51
+ const absFrom = path.isAbsolute(fromFile)
52
+ ? fromFile
53
+ : path.resolve(this.projectRoot, fromFile);
54
+ const dir = path.dirname(absFrom);
55
+
56
+ const result = this.resolver.sync(dir, source);
57
+ if (!result?.path) return '';
58
+
59
+ const resolved = result.path.replace(/\\/g, '/');
60
+
61
+ // Only include files within our project root in the graph.
62
+ // node_modules, hoisted workspace deps, etc. are external.
63
+ if (!resolved.startsWith(this.normalizedRoot + '/') && resolved !== this.normalizedRoot) {
64
+ return '';
65
+ }
66
+
67
+ return resolved;
68
+ } catch {
69
+ return '';
70
+ }
71
+ }
72
+
73
+ /** Resolve all imports for a batch of files in one pass */
74
+ public resolveBatch(files: ParsedFile[]): ParsedFile[] {
75
+ return files.map(file => ({
76
+ ...file,
77
+ imports: file.imports.map(imp => ({
78
+ ...imp,
79
+ resolvedPath: this.resolve(imp.source, file.path),
80
+ })),
81
+ }));
82
+ }
83
+ }
@@ -109,8 +109,8 @@ function findFirstChild(node: any, predicate: (n: any) => boolean): any {
109
109
  function assignCallsToFunctions(
110
110
  functions: ParsedFunction[],
111
111
  callEntries: Array<{ name: string; line: number }>
112
- ): string[] {
113
- const unassigned: string[] = []
112
+ ): Array<{ name: string; line: number }> {
113
+ const unassigned: Array<{ name: string; line: number }> = []
114
114
  for (const { name, line } of callEntries) {
115
115
  // Find the innermost (smallest range) function that contains this line
116
116
  let best: ParsedFunction | null = null
@@ -125,11 +125,11 @@ function assignCallsToFunctions(
125
125
  }
126
126
  }
127
127
  if (best) {
128
- if (!best.calls.includes(name)) {
129
- best.calls.push(name)
128
+ if (!best.calls.some(c => c.name === name && c.line === line)) {
129
+ best.calls.push({ name, line, type: 'function' })
130
130
  }
131
131
  } else {
132
- unassigned.push(name)
132
+ unassigned.push({ name, line })
133
133
  }
134
134
  }
135
135
  return unassigned
@@ -187,10 +187,13 @@ export class TreeSitterParser extends BaseParser {
187
187
  // --- Calls: record name and line position ---
188
188
  if (captures['call.name']) {
189
189
  const callNode = captures['call.name']
190
- callEntries.push({
191
- name: callNode.text,
192
- line: (callNode.startPosition?.row ?? 0) + 1,
193
- })
190
+ const name = callNode.text
191
+ if (name) {
192
+ callEntries.push({
193
+ name,
194
+ line: (callNode.startPosition?.row ?? 0) + 1,
195
+ })
196
+ }
194
197
  continue
195
198
  }
196
199
 
@@ -282,7 +285,9 @@ export class TreeSitterParser extends BaseParser {
282
285
  startLine,
283
286
  endLine,
284
287
  methods: [],
288
+ properties: [],
285
289
  isExported: isExportedByLanguage(ext, clsName, nodeText),
290
+ hash: hashContent(nodeText),
286
291
  })
287
292
  }
288
293
  }
@@ -305,7 +310,7 @@ export class TreeSitterParser extends BaseParser {
305
310
  returnType: 'void',
306
311
  isExported: false, // Don't export the synthetic module function
307
312
  isAsync: false,
308
- calls: Array.from(new Set(unassignedCalls)),
313
+ calls: unassignedCalls.map(c => ({ name: c.name, line: c.line, type: 'function' })),
309
314
  hash: '',
310
315
  purpose: 'Module-level initialization code',
311
316
  edgeCasesHandled: [],
@@ -333,6 +338,8 @@ export class TreeSitterParser extends BaseParser {
333
338
  file: filePath,
334
339
  })),
335
340
  routes: [],
341
+ variables: [],
342
+ calls: [],
336
343
  hash: hashContent(content),
337
344
  parsedAt: Date.now(),
338
345
  }
@@ -355,6 +362,8 @@ export class TreeSitterParser extends BaseParser {
355
362
  imports: [],
356
363
  exports: [],
357
364
  routes: [],
365
+ variables: [],
366
+ calls: [],
358
367
  hash: hashContent(content),
359
368
  parsedAt: Date.now(),
360
369
  }
@@ -1,103 +1,130 @@
1
1
  /**
2
2
  * Parser types — data shapes that flow through the entire Mikk system.
3
- * Parser produces them, graph consumes them, contract stores them.
4
3
  */
5
4
 
6
5
  /** A single parameter in a function signature */
7
6
  export interface ParsedParam {
8
- name: string
9
- type: string
10
- optional: boolean
11
- defaultValue?: string
7
+ name: string;
8
+ type: string;
9
+ optional: boolean;
10
+ defaultValue?: string;
12
11
  }
13
12
 
13
+ /** A single call expression found in code (Mikk 2.0) */
14
+ export interface CallExpression {
15
+ name: string;
16
+ line: number;
17
+ type: 'function' | 'method' | 'property';
18
+ arguments?: string[];
19
+ }
20
+
21
+ /** A detailed function declaration */
14
22
  export interface ParsedFunction {
15
- id: string // "fn:auth/verify.ts:verifyToken"
16
- name: string // "verifyToken"
17
- file: string // "src/auth/verify.ts"
18
- moduleId?: string
19
- startLine: number // 14
20
- endLine: number // 28
21
- params: ParsedParam[] // [{name: "token", type: "string"}]
22
- returnType: string // "boolean"
23
- isExported: boolean // true
24
- isAsync: boolean // false
25
- isGenerator?: boolean // true for function* / async function*
26
- typeParameters?: string[] // ["T", "U"] for generic functions
27
- calls: string[] // ["jwtDecode", "findUser"]
28
- hash: string // SHA-256 of the function body
29
- purpose: string // Extracted from JSDoc or comments
30
- edgeCasesHandled: string[] // Found conditions like 'if (!x) return'
31
- errorHandling: { line: number, type: 'try-catch' | 'throw', detail: string }[]
32
- detailedLines: { startLine: number, endLine: number, blockType: string }[]
23
+ id: string; // unique normalized ID (file::name)
24
+ name: string;
25
+ file: string;
26
+ moduleId?: string;
27
+ startLine: number;
28
+ endLine: number;
29
+ params: ParsedParam[];
30
+ returnType: string;
31
+ isExported: boolean;
32
+ isAsync: boolean;
33
+ isGenerator?: boolean;
34
+ typeParameters?: string[];
35
+ calls: CallExpression[]; // Behavioral tracking (Upgraded from string[])
36
+ hash: string;
37
+ purpose: string;
38
+ edgeCasesHandled: string[];
39
+ errorHandling: { line: number; type: 'try-catch' | 'throw'; detail: string }[];
40
+ detailedLines: { startLine: number; endLine: number; blockType: string }[];
33
41
  }
34
42
 
35
43
  /** A single import statement */
36
44
  export interface ParsedImport {
37
- source: string // "../../utils/jwt"
38
- resolvedPath: string // "src/utils/jwt.ts" (absolute within project)
39
- names: string[] // ["jwtDecode", "jwtSign"]
40
- isDefault: boolean // false
41
- isDynamic: boolean // false
45
+ source: string;
46
+ resolvedPath: string;
47
+ names: string[];
48
+ isDefault: boolean;
49
+ isDynamic: boolean;
42
50
  }
43
51
 
44
52
  /** A single exported symbol */
45
53
  export interface ParsedExport {
46
- name: string // "verifyToken"
47
- type: 'function' | 'class' | 'const' | 'type' | 'default' | 'interface'
48
- file: string
54
+ name: string;
55
+ type: 'function' | 'class' | 'const' | 'type' | 'default' | 'interface' | 'variable';
56
+ file: string;
57
+ }
58
+
59
+ /** A single variable or property */
60
+ export interface ParsedVariable {
61
+ id: string;
62
+ name: string;
63
+ type: string;
64
+ file: string;
65
+ line: number;
66
+ isExported: boolean;
67
+ isStatic?: boolean;
68
+ purpose?: string;
49
69
  }
50
70
 
51
71
  /** A parsed class */
52
72
  export interface ParsedClass {
53
- id: string
54
- name: string
55
- file: string
56
- moduleId?: string
57
- startLine: number
58
- endLine: number
59
- methods: ParsedFunction[]
60
- isExported: boolean
61
- decorators?: string[] // ["Injectable", "Controller"]
62
- typeParameters?: string[] // ["T"] for generic classes
63
- purpose?: string
64
- edgeCasesHandled?: string[]
65
- errorHandling?: { line: number, type: 'try-catch' | 'throw', detail: string }[]
73
+ id: string;
74
+ name: string;
75
+ file: string;
76
+ moduleId?: string;
77
+ startLine: number;
78
+ endLine: number;
79
+ methods: ParsedFunction[];
80
+ properties: ParsedVariable[];
81
+ extends?: string;
82
+ implements?: string[];
83
+ isExported: boolean;
84
+ decorators?: string[];
85
+ typeParameters?: string[];
86
+ hash: string;
87
+ purpose?: string;
88
+ edgeCasesHandled?: string[];
89
+ errorHandling?: { line: number; type: 'try-catch' | 'throw'; detail: string }[];
66
90
  }
67
91
 
68
- /** A detected HTTP route registration (Express/Koa/Hono style) */
69
- export interface ParsedRoute {
70
- method: string // "GET", "POST", "PUT", "DELETE", "USE", etc.
71
- path: string // "/upload", "/:shortId", "/api"
72
- handler: string // "createZap" or "anonymous"
73
- middlewares: string[] // ["uploadLimiter", "upload.single"]
74
- file: string // "src/Routes/zap.routes.ts"
75
- line: number // 15
92
+ /** A generic declaration (interface, type aliase, etc.) */
93
+ export interface ParsedGeneric {
94
+ id: string;
95
+ name: string;
96
+ type: string; // "interface" | "type"
97
+ file: string;
98
+ startLine: number;
99
+ endLine: number;
100
+ isExported: boolean;
101
+ typeParameters?: string[];
102
+ hash: string;
103
+ purpose?: string;
76
104
  }
77
105
 
78
- /** A generic declaration like interface, type, or constant with metadata */
79
- export interface ParsedGeneric {
80
- id: string
81
- name: string
82
- type: string // "interface" | "type" | "const"
83
- file: string
84
- startLine: number
85
- endLine: number
86
- isExported: boolean
87
- typeParameters?: string[] // ["T", "K"] for generic interfaces/types
88
- purpose?: string
106
+ /** A detected HTTP route registration */
107
+ export interface ParsedRoute {
108
+ method: string;
109
+ path: string;
110
+ handler: string;
111
+ middlewares: string[];
112
+ file: string;
113
+ line: number;
89
114
  }
90
115
 
91
116
  /** Everything extracted from a single file */
92
117
  export interface ParsedFile {
93
- path: string // "src/auth/verify.ts"
94
- language: 'python' | 'go' | 'typescript' | 'javascript' | 'java' | 'c' | 'cpp' | 'csharp' | 'rust' | 'php' | 'ruby' | 'unknown'
95
- functions: ParsedFunction[]
96
- classes: ParsedClass[]
97
- generics: ParsedGeneric[]
98
- imports: ParsedImport[]
99
- exports: ParsedExport[]
100
- routes: ParsedRoute[] // Detected HTTP route registrations
101
- hash: string // SHA-256 of the entire file content
102
- parsedAt: number // Date.now()
118
+ path: string; // normalized absolute path
119
+ language: 'python' | 'go' | 'typescript' | 'javascript' | 'java' | 'c' | 'cpp' | 'csharp' | 'rust' | 'php' | 'ruby' | 'unknown';
120
+ functions: ParsedFunction[];
121
+ classes: ParsedClass[];
122
+ variables: ParsedVariable[];
123
+ generics: ParsedGeneric[];
124
+ imports: ParsedImport[];
125
+ exports: ParsedExport[];
126
+ routes: ParsedRoute[];
127
+ calls: CallExpression[]; // module-level calls
128
+ hash: string;
129
+ parsedAt: number;
103
130
  }