@getmikk/core 1.2.0 → 1.3.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 (44) hide show
  1. package/README.md +431 -0
  2. package/package.json +6 -2
  3. package/src/contract/contract-generator.ts +85 -85
  4. package/src/contract/contract-reader.ts +28 -28
  5. package/src/contract/contract-writer.ts +114 -114
  6. package/src/contract/index.ts +12 -12
  7. package/src/contract/lock-compiler.ts +221 -221
  8. package/src/contract/lock-reader.ts +34 -34
  9. package/src/contract/schema.ts +147 -147
  10. package/src/graph/cluster-detector.ts +312 -312
  11. package/src/graph/graph-builder.ts +211 -211
  12. package/src/graph/impact-analyzer.ts +55 -55
  13. package/src/graph/index.ts +4 -4
  14. package/src/graph/types.ts +59 -59
  15. package/src/hash/file-hasher.ts +30 -30
  16. package/src/hash/hash-store.ts +119 -119
  17. package/src/hash/index.ts +3 -3
  18. package/src/hash/tree-hasher.ts +20 -20
  19. package/src/index.ts +12 -12
  20. package/src/parser/base-parser.ts +16 -16
  21. package/src/parser/boundary-checker.ts +211 -211
  22. package/src/parser/index.ts +46 -46
  23. package/src/parser/types.ts +90 -90
  24. package/src/parser/typescript/ts-extractor.ts +543 -543
  25. package/src/parser/typescript/ts-parser.ts +41 -41
  26. package/src/parser/typescript/ts-resolver.ts +86 -86
  27. package/src/utils/errors.ts +42 -42
  28. package/src/utils/fs.ts +75 -75
  29. package/src/utils/fuzzy-match.ts +186 -186
  30. package/src/utils/logger.ts +36 -36
  31. package/src/utils/minimatch.ts +19 -19
  32. package/tests/contract.test.ts +134 -134
  33. package/tests/fixtures/simple-api/package.json +5 -5
  34. package/tests/fixtures/simple-api/src/auth/middleware.ts +9 -9
  35. package/tests/fixtures/simple-api/src/auth/verify.ts +6 -6
  36. package/tests/fixtures/simple-api/src/index.ts +9 -9
  37. package/tests/fixtures/simple-api/src/utils/jwt.ts +3 -3
  38. package/tests/fixtures/simple-api/tsconfig.json +8 -8
  39. package/tests/fuzzy-match.test.ts +142 -142
  40. package/tests/graph.test.ts +169 -169
  41. package/tests/hash.test.ts +49 -49
  42. package/tests/helpers.ts +83 -83
  43. package/tests/parser.test.ts +218 -218
  44. package/tsconfig.json +15 -15
@@ -1,218 +1,218 @@
1
- import { describe, it, expect } from 'bun:test'
2
- import { TypeScriptParser } from '../src/parser/typescript/ts-parser'
3
- import { TypeScriptExtractor } from '../src/parser/typescript/ts-extractor'
4
- import { TypeScriptResolver } from '../src/parser/typescript/ts-resolver'
5
- import { getParser } from '../src/parser/index'
6
- import { UnsupportedLanguageError } from '../src/utils/errors'
7
-
8
- describe('TypeScriptExtractor', () => {
9
- it('extracts function declarations', () => {
10
- const extractor = new TypeScriptExtractor('src/auth.ts', `
11
- export function verifyToken(token: string): boolean {
12
- return true
13
- }
14
- `)
15
- const fns = extractor.extractFunctions()
16
- expect(fns).toHaveLength(1)
17
- expect(fns[0].name).toBe('verifyToken')
18
- expect(fns[0].isExported).toBe(true)
19
- expect(fns[0].params[0].name).toBe('token')
20
- expect(fns[0].params[0].type).toBe('string')
21
- expect(fns[0].returnType).toBe('boolean')
22
- })
23
-
24
- it('extracts arrow functions assigned to const', () => {
25
- const extractor = new TypeScriptExtractor('src/utils.ts', `
26
- export const greet = (name: string): string => {
27
- return 'Hello ' + name
28
- }
29
- `)
30
- const fns = extractor.extractFunctions()
31
- expect(fns).toHaveLength(1)
32
- expect(fns[0].name).toBe('greet')
33
- expect(fns[0].isExported).toBe(true)
34
- })
35
-
36
- it('extracts async functions', () => {
37
- const extractor = new TypeScriptExtractor('src/db.ts', `
38
- export async function findUser(id: string): Promise<User> {
39
- return await db.find(id)
40
- }
41
- `)
42
- const fns = extractor.extractFunctions()
43
- expect(fns[0].isAsync).toBe(true)
44
- })
45
-
46
- it('extracts call expressions from function bodies', () => {
47
- const extractor = new TypeScriptExtractor('src/auth.ts', `
48
- import { jwtDecode } from './jwt'
49
- function verifyToken(token: string) {
50
- const decoded = jwtDecode(token)
51
- console.log(decoded)
52
- return decoded.exp > Date.now()
53
- }
54
- `)
55
- const fns = extractor.extractFunctions()
56
- expect(fns[0].calls).toContain('jwtDecode')
57
- })
58
-
59
- it('extracts imports', () => {
60
- const extractor = new TypeScriptExtractor('src/auth.ts', `
61
- import { jwtDecode, jwtSign } from '../utils/jwt'
62
- import express from 'express'
63
- `)
64
- const imports = extractor.extractImports()
65
- expect(imports).toHaveLength(2)
66
- expect(imports[0].source).toBe('../utils/jwt')
67
- expect(imports[0].names).toContain('jwtDecode')
68
- expect(imports[0].names).toContain('jwtSign')
69
- expect(imports[1].source).toBe('express')
70
- expect(imports[1].isDefault).toBe(true)
71
- })
72
-
73
- it('extracts named exports', () => {
74
- const extractor = new TypeScriptExtractor('src/auth.ts', `
75
- export function verifyToken() {}
76
- export const SECRET = 'abc'
77
- export class AuthService {}
78
- `)
79
- const exports = extractor.extractExports()
80
- expect(exports.length).toBeGreaterThanOrEqual(3)
81
- expect(exports.find(e => e.name === 'verifyToken')?.type).toBe('function')
82
- expect(exports.find(e => e.name === 'SECRET')?.type).toBe('const')
83
- expect(exports.find(e => e.name === 'AuthService')?.type).toBe('class')
84
- })
85
-
86
- it('extracts classes with methods', () => {
87
- const extractor = new TypeScriptExtractor('src/service.ts', `
88
- export class AuthService {
89
- verify(token: string): boolean {
90
- return true
91
- }
92
- async refresh(): Promise<string> {
93
- return 'new-token'
94
- }
95
- }
96
- `)
97
- const classes = extractor.extractClasses()
98
- expect(classes).toHaveLength(1)
99
- expect(classes[0].name).toBe('AuthService')
100
- expect(classes[0].methods).toHaveLength(2)
101
- expect(classes[0].methods[0].name).toBe('AuthService.verify')
102
- expect(classes[0].methods[1].isAsync).toBe(true)
103
- expect(classes[0].isExported).toBe(true)
104
- })
105
-
106
- it('skips type-only imports', () => {
107
- const extractor = new TypeScriptExtractor('src/auth.ts', `
108
- import type { User } from './types'
109
- import { verifyToken } from './verify'
110
- `)
111
- const imports = extractor.extractImports()
112
- expect(imports).toHaveLength(1)
113
- expect(imports[0].source).toBe('./verify')
114
- })
115
-
116
- it('handles optional parameters', () => {
117
- const extractor = new TypeScriptExtractor('src/utils.ts', `
118
- function greet(name: string, greeting?: string) {}
119
- `)
120
- const fns = extractor.extractFunctions()
121
- expect(fns[0].params[1].optional).toBe(true)
122
- })
123
-
124
- it('handles default parameter values', () => {
125
- const extractor = new TypeScriptExtractor('src/utils.ts', `
126
- function config(port: number = 3000) {}
127
- `)
128
- const fns = extractor.extractFunctions()
129
- expect(fns[0].params[0].optional).toBe(true)
130
- expect(fns[0].params[0].defaultValue).toBe('3000')
131
- })
132
- })
133
-
134
- describe('TypeScriptParser', () => {
135
- const parser = new TypeScriptParser()
136
-
137
- it('returns correct language', () => {
138
- const result = parser.parse('src/test.ts', 'const x = 1')
139
- expect(result.language).toBe('typescript')
140
- })
141
-
142
- it('parses a complete file', () => {
143
- const result = parser.parse('src/auth.ts', `
144
- import { jwtDecode } from '../utils/jwt'
145
- export function verifyToken(token: string): boolean {
146
- return jwtDecode(token).exp > Date.now()
147
- }
148
- `)
149
- expect(result.functions).toHaveLength(1)
150
- expect(result.imports).toHaveLength(1)
151
- expect(result.exports).toHaveLength(1)
152
- expect(result.hash).toBeDefined()
153
- expect(result.path).toBe('src/auth.ts')
154
- })
155
-
156
- it('supports .tsx extension', () => {
157
- expect(parser.getSupportedExtensions()).toContain('.tsx')
158
- })
159
- })
160
-
161
- describe('TypeScriptResolver', () => {
162
- it('resolves relative imports', () => {
163
- const resolver = new TypeScriptResolver('/project')
164
- const result = resolver.resolve(
165
- { source: '../utils/jwt', names: ['jwtDecode'], resolvedPath: '', isDefault: false, isDynamic: false },
166
- 'src/auth/verify.ts',
167
- ['src/utils/jwt.ts']
168
- )
169
- expect(result.resolvedPath).toBe('src/utils/jwt.ts')
170
- })
171
-
172
- it('resolves path aliases', () => {
173
- const resolver = new TypeScriptResolver('/project', {
174
- '@/*': ['src/*'],
175
- })
176
- const result = resolver.resolve(
177
- { source: '@/utils/jwt', names: ['jwtDecode'], resolvedPath: '', isDefault: false, isDynamic: false },
178
- 'src/auth/verify.ts',
179
- ['src/utils/jwt.ts']
180
- )
181
- expect(result.resolvedPath).toBe('src/utils/jwt.ts')
182
- })
183
-
184
- it('skips external packages', () => {
185
- const resolver = new TypeScriptResolver('/project')
186
- const result = resolver.resolve(
187
- { source: 'express', names: ['default'], resolvedPath: '', isDefault: true, isDynamic: false },
188
- 'src/index.ts'
189
- )
190
- expect(result.resolvedPath).toBe('')
191
- })
192
-
193
- it('handles index files', () => {
194
- const resolver = new TypeScriptResolver('/project')
195
- const result = resolver.resolve(
196
- { source: '../utils', names: ['helper'], resolvedPath: '', isDefault: false, isDynamic: false },
197
- 'src/auth/verify.ts',
198
- ['src/utils/index.ts']
199
- )
200
- expect(result.resolvedPath).toBe('src/utils/index.ts')
201
- })
202
- })
203
-
204
- describe('getParser', () => {
205
- it('returns TypeScriptParser for .ts files', () => {
206
- const parser = getParser('src/auth.ts')
207
- expect(parser).toBeInstanceOf(TypeScriptParser)
208
- })
209
-
210
- it('returns TypeScriptParser for .tsx files', () => {
211
- const parser = getParser('src/App.tsx')
212
- expect(parser).toBeInstanceOf(TypeScriptParser)
213
- })
214
-
215
- it('throws for unsupported extensions', () => {
216
- expect(() => getParser('src/auth.rb')).toThrow(UnsupportedLanguageError)
217
- })
218
- })
1
+ import { describe, it, expect } from 'bun:test'
2
+ import { TypeScriptParser } from '../src/parser/typescript/ts-parser'
3
+ import { TypeScriptExtractor } from '../src/parser/typescript/ts-extractor'
4
+ import { TypeScriptResolver } from '../src/parser/typescript/ts-resolver'
5
+ import { getParser } from '../src/parser/index'
6
+ import { UnsupportedLanguageError } from '../src/utils/errors'
7
+
8
+ describe('TypeScriptExtractor', () => {
9
+ it('extracts function declarations', () => {
10
+ const extractor = new TypeScriptExtractor('src/auth.ts', `
11
+ export function verifyToken(token: string): boolean {
12
+ return true
13
+ }
14
+ `)
15
+ const fns = extractor.extractFunctions()
16
+ expect(fns).toHaveLength(1)
17
+ expect(fns[0].name).toBe('verifyToken')
18
+ expect(fns[0].isExported).toBe(true)
19
+ expect(fns[0].params[0].name).toBe('token')
20
+ expect(fns[0].params[0].type).toBe('string')
21
+ expect(fns[0].returnType).toBe('boolean')
22
+ })
23
+
24
+ it('extracts arrow functions assigned to const', () => {
25
+ const extractor = new TypeScriptExtractor('src/utils.ts', `
26
+ export const greet = (name: string): string => {
27
+ return 'Hello ' + name
28
+ }
29
+ `)
30
+ const fns = extractor.extractFunctions()
31
+ expect(fns).toHaveLength(1)
32
+ expect(fns[0].name).toBe('greet')
33
+ expect(fns[0].isExported).toBe(true)
34
+ })
35
+
36
+ it('extracts async functions', () => {
37
+ const extractor = new TypeScriptExtractor('src/db.ts', `
38
+ export async function findUser(id: string): Promise<User> {
39
+ return await db.find(id)
40
+ }
41
+ `)
42
+ const fns = extractor.extractFunctions()
43
+ expect(fns[0].isAsync).toBe(true)
44
+ })
45
+
46
+ it('extracts call expressions from function bodies', () => {
47
+ const extractor = new TypeScriptExtractor('src/auth.ts', `
48
+ import { jwtDecode } from './jwt'
49
+ function verifyToken(token: string) {
50
+ const decoded = jwtDecode(token)
51
+ console.log(decoded)
52
+ return decoded.exp > Date.now()
53
+ }
54
+ `)
55
+ const fns = extractor.extractFunctions()
56
+ expect(fns[0].calls).toContain('jwtDecode')
57
+ })
58
+
59
+ it('extracts imports', () => {
60
+ const extractor = new TypeScriptExtractor('src/auth.ts', `
61
+ import { jwtDecode, jwtSign } from '../utils/jwt'
62
+ import express from 'express'
63
+ `)
64
+ const imports = extractor.extractImports()
65
+ expect(imports).toHaveLength(2)
66
+ expect(imports[0].source).toBe('../utils/jwt')
67
+ expect(imports[0].names).toContain('jwtDecode')
68
+ expect(imports[0].names).toContain('jwtSign')
69
+ expect(imports[1].source).toBe('express')
70
+ expect(imports[1].isDefault).toBe(true)
71
+ })
72
+
73
+ it('extracts named exports', () => {
74
+ const extractor = new TypeScriptExtractor('src/auth.ts', `
75
+ export function verifyToken() {}
76
+ export const SECRET = 'abc'
77
+ export class AuthService {}
78
+ `)
79
+ const exports = extractor.extractExports()
80
+ expect(exports.length).toBeGreaterThanOrEqual(3)
81
+ expect(exports.find(e => e.name === 'verifyToken')?.type).toBe('function')
82
+ expect(exports.find(e => e.name === 'SECRET')?.type).toBe('const')
83
+ expect(exports.find(e => e.name === 'AuthService')?.type).toBe('class')
84
+ })
85
+
86
+ it('extracts classes with methods', () => {
87
+ const extractor = new TypeScriptExtractor('src/service.ts', `
88
+ export class AuthService {
89
+ verify(token: string): boolean {
90
+ return true
91
+ }
92
+ async refresh(): Promise<string> {
93
+ return 'new-token'
94
+ }
95
+ }
96
+ `)
97
+ const classes = extractor.extractClasses()
98
+ expect(classes).toHaveLength(1)
99
+ expect(classes[0].name).toBe('AuthService')
100
+ expect(classes[0].methods).toHaveLength(2)
101
+ expect(classes[0].methods[0].name).toBe('AuthService.verify')
102
+ expect(classes[0].methods[1].isAsync).toBe(true)
103
+ expect(classes[0].isExported).toBe(true)
104
+ })
105
+
106
+ it('skips type-only imports', () => {
107
+ const extractor = new TypeScriptExtractor('src/auth.ts', `
108
+ import type { User } from './types'
109
+ import { verifyToken } from './verify'
110
+ `)
111
+ const imports = extractor.extractImports()
112
+ expect(imports).toHaveLength(1)
113
+ expect(imports[0].source).toBe('./verify')
114
+ })
115
+
116
+ it('handles optional parameters', () => {
117
+ const extractor = new TypeScriptExtractor('src/utils.ts', `
118
+ function greet(name: string, greeting?: string) {}
119
+ `)
120
+ const fns = extractor.extractFunctions()
121
+ expect(fns[0].params[1].optional).toBe(true)
122
+ })
123
+
124
+ it('handles default parameter values', () => {
125
+ const extractor = new TypeScriptExtractor('src/utils.ts', `
126
+ function config(port: number = 3000) {}
127
+ `)
128
+ const fns = extractor.extractFunctions()
129
+ expect(fns[0].params[0].optional).toBe(true)
130
+ expect(fns[0].params[0].defaultValue).toBe('3000')
131
+ })
132
+ })
133
+
134
+ describe('TypeScriptParser', () => {
135
+ const parser = new TypeScriptParser()
136
+
137
+ it('returns correct language', () => {
138
+ const result = parser.parse('src/test.ts', 'const x = 1')
139
+ expect(result.language).toBe('typescript')
140
+ })
141
+
142
+ it('parses a complete file', () => {
143
+ const result = parser.parse('src/auth.ts', `
144
+ import { jwtDecode } from '../utils/jwt'
145
+ export function verifyToken(token: string): boolean {
146
+ return jwtDecode(token).exp > Date.now()
147
+ }
148
+ `)
149
+ expect(result.functions).toHaveLength(1)
150
+ expect(result.imports).toHaveLength(1)
151
+ expect(result.exports).toHaveLength(1)
152
+ expect(result.hash).toBeDefined()
153
+ expect(result.path).toBe('src/auth.ts')
154
+ })
155
+
156
+ it('supports .tsx extension', () => {
157
+ expect(parser.getSupportedExtensions()).toContain('.tsx')
158
+ })
159
+ })
160
+
161
+ describe('TypeScriptResolver', () => {
162
+ it('resolves relative imports', () => {
163
+ const resolver = new TypeScriptResolver('/project')
164
+ const result = resolver.resolve(
165
+ { source: '../utils/jwt', names: ['jwtDecode'], resolvedPath: '', isDefault: false, isDynamic: false },
166
+ 'src/auth/verify.ts',
167
+ ['src/utils/jwt.ts']
168
+ )
169
+ expect(result.resolvedPath).toBe('src/utils/jwt.ts')
170
+ })
171
+
172
+ it('resolves path aliases', () => {
173
+ const resolver = new TypeScriptResolver('/project', {
174
+ '@/*': ['src/*'],
175
+ })
176
+ const result = resolver.resolve(
177
+ { source: '@/utils/jwt', names: ['jwtDecode'], resolvedPath: '', isDefault: false, isDynamic: false },
178
+ 'src/auth/verify.ts',
179
+ ['src/utils/jwt.ts']
180
+ )
181
+ expect(result.resolvedPath).toBe('src/utils/jwt.ts')
182
+ })
183
+
184
+ it('skips external packages', () => {
185
+ const resolver = new TypeScriptResolver('/project')
186
+ const result = resolver.resolve(
187
+ { source: 'express', names: ['default'], resolvedPath: '', isDefault: true, isDynamic: false },
188
+ 'src/index.ts'
189
+ )
190
+ expect(result.resolvedPath).toBe('')
191
+ })
192
+
193
+ it('handles index files', () => {
194
+ const resolver = new TypeScriptResolver('/project')
195
+ const result = resolver.resolve(
196
+ { source: '../utils', names: ['helper'], resolvedPath: '', isDefault: false, isDynamic: false },
197
+ 'src/auth/verify.ts',
198
+ ['src/utils/index.ts']
199
+ )
200
+ expect(result.resolvedPath).toBe('src/utils/index.ts')
201
+ })
202
+ })
203
+
204
+ describe('getParser', () => {
205
+ it('returns TypeScriptParser for .ts files', () => {
206
+ const parser = getParser('src/auth.ts')
207
+ expect(parser).toBeInstanceOf(TypeScriptParser)
208
+ })
209
+
210
+ it('returns TypeScriptParser for .tsx files', () => {
211
+ const parser = getParser('src/App.tsx')
212
+ expect(parser).toBeInstanceOf(TypeScriptParser)
213
+ })
214
+
215
+ it('throws for unsupported extensions', () => {
216
+ expect(() => getParser('src/auth.rb')).toThrow(UnsupportedLanguageError)
217
+ })
218
+ })
package/tsconfig.json CHANGED
@@ -1,15 +1,15 @@
1
- {
2
- "extends": "../../tsconfig.base.json",
3
- "compilerOptions": {
4
- "outDir": "dist",
5
- "rootDir": "src"
6
- },
7
- "include": [
8
- "src/**/*"
9
- ],
10
- "exclude": [
11
- "node_modules",
12
- "dist",
13
- "tests"
14
- ]
15
- }
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "dist",
5
+ "rootDir": "src"
6
+ },
7
+ "include": [
8
+ "src/**/*"
9
+ ],
10
+ "exclude": [
11
+ "node_modules",
12
+ "dist",
13
+ "tests"
14
+ ]
15
+ }