@getmikk/core 1.2.0 → 1.3.1
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.
- package/README.md +431 -0
- package/package.json +6 -2
- package/src/contract/contract-generator.ts +85 -85
- package/src/contract/contract-reader.ts +28 -28
- package/src/contract/contract-writer.ts +114 -114
- package/src/contract/index.ts +12 -12
- package/src/contract/lock-compiler.ts +221 -221
- package/src/contract/lock-reader.ts +34 -34
- package/src/contract/schema.ts +147 -147
- package/src/graph/cluster-detector.ts +312 -312
- package/src/graph/graph-builder.ts +211 -211
- package/src/graph/impact-analyzer.ts +55 -55
- package/src/graph/index.ts +4 -4
- package/src/graph/types.ts +59 -59
- package/src/hash/file-hasher.ts +30 -30
- package/src/hash/hash-store.ts +119 -119
- package/src/hash/index.ts +3 -3
- package/src/hash/tree-hasher.ts +20 -20
- package/src/index.ts +12 -12
- package/src/parser/base-parser.ts +16 -16
- package/src/parser/boundary-checker.ts +211 -211
- package/src/parser/index.ts +46 -46
- package/src/parser/types.ts +90 -90
- package/src/parser/typescript/ts-extractor.ts +543 -543
- package/src/parser/typescript/ts-parser.ts +41 -41
- package/src/parser/typescript/ts-resolver.ts +86 -86
- package/src/utils/errors.ts +42 -42
- package/src/utils/fs.ts +75 -75
- package/src/utils/fuzzy-match.ts +186 -186
- package/src/utils/logger.ts +36 -36
- package/src/utils/minimatch.ts +19 -19
- package/tests/contract.test.ts +134 -134
- package/tests/fixtures/simple-api/package.json +5 -5
- package/tests/fixtures/simple-api/src/auth/middleware.ts +9 -9
- package/tests/fixtures/simple-api/src/auth/verify.ts +6 -6
- package/tests/fixtures/simple-api/src/index.ts +9 -9
- package/tests/fixtures/simple-api/src/utils/jwt.ts +3 -3
- package/tests/fixtures/simple-api/tsconfig.json +8 -8
- package/tests/fuzzy-match.test.ts +142 -142
- package/tests/graph.test.ts +169 -169
- package/tests/hash.test.ts +49 -49
- package/tests/helpers.ts +83 -83
- package/tests/parser.test.ts +218 -218
- package/tsconfig.json +15 -15
|
@@ -1,114 +1,114 @@
|
|
|
1
|
-
import * as fs from 'node:fs/promises'
|
|
2
|
-
import * as path from 'node:path'
|
|
3
|
-
import type { MikkContract } from './schema.js'
|
|
4
|
-
import { hashContent } from '../hash/file-hasher.js'
|
|
5
|
-
|
|
6
|
-
const VERSION = '
|
|
7
|
-
|
|
8
|
-
export interface UpdateResult {
|
|
9
|
-
updated: boolean
|
|
10
|
-
reason?: string
|
|
11
|
-
requiresConfirmation?: boolean
|
|
12
|
-
proposedChanges?: object
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* ContractWriter — writes mikk.json to disk.
|
|
17
|
-
* Implements the permission model (never / ask / explicit).
|
|
18
|
-
*/
|
|
19
|
-
export class ContractWriter {
|
|
20
|
-
/** Write a new mikk.json — safe for first write */
|
|
21
|
-
async writeNew(contract: MikkContract, outputPath: string): Promise<void> {
|
|
22
|
-
await fs.mkdir(path.dirname(outputPath), { recursive: true })
|
|
23
|
-
const json = JSON.stringify(contract, null, 2)
|
|
24
|
-
await fs.writeFile(outputPath, json, 'utf-8')
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/** Update an existing mikk.json respecting overwrite mode */
|
|
28
|
-
async update(
|
|
29
|
-
existing: MikkContract,
|
|
30
|
-
updates: Partial<MikkContract>,
|
|
31
|
-
outputPath: string
|
|
32
|
-
): Promise<UpdateResult> {
|
|
33
|
-
const mode = existing.overwrite?.mode ?? 'never'
|
|
34
|
-
|
|
35
|
-
if (mode === 'never') {
|
|
36
|
-
return { updated: false, reason: 'overwrite mode is never' }
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (mode === 'ask') {
|
|
40
|
-
return {
|
|
41
|
-
updated: false,
|
|
42
|
-
requiresConfirmation: true,
|
|
43
|
-
proposedChanges: this.diffContracts(existing, updates),
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (mode === 'explicit') {
|
|
48
|
-
const updated = this.mergeContracts(existing, updates)
|
|
49
|
-
updated.overwrite = {
|
|
50
|
-
...updated.overwrite,
|
|
51
|
-
lastOverwrittenBy: VERSION,
|
|
52
|
-
lastOverwrittenAt: new Date().toISOString(),
|
|
53
|
-
}
|
|
54
|
-
await this.writeNew(updated, outputPath)
|
|
55
|
-
await this.writeAuditLog(existing, updated, outputPath)
|
|
56
|
-
return { updated: true }
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
return { updated: false, reason: 'unknown mode' }
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/** Merge two contracts — updates overwrite existing fields */
|
|
63
|
-
private mergeContracts(existing: MikkContract, updates: Partial<MikkContract>): MikkContract {
|
|
64
|
-
return {
|
|
65
|
-
...existing,
|
|
66
|
-
...updates,
|
|
67
|
-
declared: {
|
|
68
|
-
...existing.declared,
|
|
69
|
-
...(updates.declared || {}),
|
|
70
|
-
modules: updates.declared?.modules || existing.declared.modules,
|
|
71
|
-
constraints: updates.declared?.constraints || existing.declared.constraints,
|
|
72
|
-
decisions: updates.declared?.decisions || existing.declared.decisions,
|
|
73
|
-
},
|
|
74
|
-
project: {
|
|
75
|
-
...existing.project,
|
|
76
|
-
...(updates.project || {}),
|
|
77
|
-
},
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/** Compute diff between two contracts */
|
|
82
|
-
private diffContracts(existing: MikkContract, updates: Partial<MikkContract>): object {
|
|
83
|
-
return {
|
|
84
|
-
before: existing.declared,
|
|
85
|
-
after: updates.declared || existing.declared,
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/** Write audit log to .mikk/overwrite-history.json */
|
|
90
|
-
private async writeAuditLog(
|
|
91
|
-
before: MikkContract,
|
|
92
|
-
after: MikkContract,
|
|
93
|
-
contractPath: string
|
|
94
|
-
): Promise<void> {
|
|
95
|
-
const historyPath = path.join(path.dirname(contractPath), '.mikk', 'overwrite-history.json')
|
|
96
|
-
let history: object[] = []
|
|
97
|
-
|
|
98
|
-
try {
|
|
99
|
-
const existing = await fs.readFile(historyPath, 'utf-8')
|
|
100
|
-
history = JSON.parse(existing)
|
|
101
|
-
} catch {
|
|
102
|
-
// No history file yet
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
history.push({
|
|
106
|
-
timestamp: new Date().toISOString(),
|
|
107
|
-
before: before.declared,
|
|
108
|
-
after: after.declared,
|
|
109
|
-
})
|
|
110
|
-
|
|
111
|
-
await fs.mkdir(path.dirname(historyPath), { recursive: true })
|
|
112
|
-
await fs.writeFile(historyPath, JSON.stringify(history, null, 2), 'utf-8')
|
|
113
|
-
}
|
|
114
|
-
}
|
|
1
|
+
import * as fs from 'node:fs/promises'
|
|
2
|
+
import * as path from 'node:path'
|
|
3
|
+
import type { MikkContract } from './schema.js'
|
|
4
|
+
import { hashContent } from '../hash/file-hasher.js'
|
|
5
|
+
|
|
6
|
+
const VERSION = '@getmikk/cli@1.2.1'
|
|
7
|
+
|
|
8
|
+
export interface UpdateResult {
|
|
9
|
+
updated: boolean
|
|
10
|
+
reason?: string
|
|
11
|
+
requiresConfirmation?: boolean
|
|
12
|
+
proposedChanges?: object
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* ContractWriter — writes mikk.json to disk.
|
|
17
|
+
* Implements the permission model (never / ask / explicit).
|
|
18
|
+
*/
|
|
19
|
+
export class ContractWriter {
|
|
20
|
+
/** Write a new mikk.json — safe for first write */
|
|
21
|
+
async writeNew(contract: MikkContract, outputPath: string): Promise<void> {
|
|
22
|
+
await fs.mkdir(path.dirname(outputPath), { recursive: true })
|
|
23
|
+
const json = JSON.stringify(contract, null, 2)
|
|
24
|
+
await fs.writeFile(outputPath, json, 'utf-8')
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Update an existing mikk.json respecting overwrite mode */
|
|
28
|
+
async update(
|
|
29
|
+
existing: MikkContract,
|
|
30
|
+
updates: Partial<MikkContract>,
|
|
31
|
+
outputPath: string
|
|
32
|
+
): Promise<UpdateResult> {
|
|
33
|
+
const mode = existing.overwrite?.mode ?? 'never'
|
|
34
|
+
|
|
35
|
+
if (mode === 'never') {
|
|
36
|
+
return { updated: false, reason: 'overwrite mode is never' }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (mode === 'ask') {
|
|
40
|
+
return {
|
|
41
|
+
updated: false,
|
|
42
|
+
requiresConfirmation: true,
|
|
43
|
+
proposedChanges: this.diffContracts(existing, updates),
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (mode === 'explicit') {
|
|
48
|
+
const updated = this.mergeContracts(existing, updates)
|
|
49
|
+
updated.overwrite = {
|
|
50
|
+
...updated.overwrite,
|
|
51
|
+
lastOverwrittenBy: VERSION,
|
|
52
|
+
lastOverwrittenAt: new Date().toISOString(),
|
|
53
|
+
}
|
|
54
|
+
await this.writeNew(updated, outputPath)
|
|
55
|
+
await this.writeAuditLog(existing, updated, outputPath)
|
|
56
|
+
return { updated: true }
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return { updated: false, reason: 'unknown mode' }
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Merge two contracts — updates overwrite existing fields */
|
|
63
|
+
private mergeContracts(existing: MikkContract, updates: Partial<MikkContract>): MikkContract {
|
|
64
|
+
return {
|
|
65
|
+
...existing,
|
|
66
|
+
...updates,
|
|
67
|
+
declared: {
|
|
68
|
+
...existing.declared,
|
|
69
|
+
...(updates.declared || {}),
|
|
70
|
+
modules: updates.declared?.modules || existing.declared.modules,
|
|
71
|
+
constraints: updates.declared?.constraints || existing.declared.constraints,
|
|
72
|
+
decisions: updates.declared?.decisions || existing.declared.decisions,
|
|
73
|
+
},
|
|
74
|
+
project: {
|
|
75
|
+
...existing.project,
|
|
76
|
+
...(updates.project || {}),
|
|
77
|
+
},
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Compute diff between two contracts */
|
|
82
|
+
private diffContracts(existing: MikkContract, updates: Partial<MikkContract>): object {
|
|
83
|
+
return {
|
|
84
|
+
before: existing.declared,
|
|
85
|
+
after: updates.declared || existing.declared,
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Write audit log to .mikk/overwrite-history.json */
|
|
90
|
+
private async writeAuditLog(
|
|
91
|
+
before: MikkContract,
|
|
92
|
+
after: MikkContract,
|
|
93
|
+
contractPath: string
|
|
94
|
+
): Promise<void> {
|
|
95
|
+
const historyPath = path.join(path.dirname(contractPath), '.mikk', 'overwrite-history.json')
|
|
96
|
+
let history: object[] = []
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
const existing = await fs.readFile(historyPath, 'utf-8')
|
|
100
|
+
history = JSON.parse(existing)
|
|
101
|
+
} catch {
|
|
102
|
+
// No history file yet
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
history.push({
|
|
106
|
+
timestamp: new Date().toISOString(),
|
|
107
|
+
before: before.declared,
|
|
108
|
+
after: after.declared,
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
await fs.mkdir(path.dirname(historyPath), { recursive: true })
|
|
112
|
+
await fs.writeFile(historyPath, JSON.stringify(history, null, 2), 'utf-8')
|
|
113
|
+
}
|
|
114
|
+
}
|
package/src/contract/index.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
export {
|
|
2
|
-
MikkContractSchema, MikkLockSchema,
|
|
3
|
-
MikkModuleSchema, MikkDecisionSchema, MikkOverwriteSchema,
|
|
4
|
-
MikkLockFunctionSchema, MikkLockModuleSchema, MikkLockFileSchema,
|
|
5
|
-
type MikkContract, type MikkLock, type MikkModule, type MikkDecision,
|
|
6
|
-
type MikkLockFunction, type MikkLockModule, type MikkLockFile,
|
|
7
|
-
} from './schema.js'
|
|
8
|
-
export { LockCompiler } from './lock-compiler.js'
|
|
9
|
-
export { ContractWriter, type UpdateResult } from './contract-writer.js'
|
|
10
|
-
export { ContractReader } from './contract-reader.js'
|
|
11
|
-
export { LockReader } from './lock-reader.js'
|
|
12
|
-
export { ContractGenerator } from './contract-generator.js'
|
|
1
|
+
export {
|
|
2
|
+
MikkContractSchema, MikkLockSchema,
|
|
3
|
+
MikkModuleSchema, MikkDecisionSchema, MikkOverwriteSchema,
|
|
4
|
+
MikkLockFunctionSchema, MikkLockModuleSchema, MikkLockFileSchema,
|
|
5
|
+
type MikkContract, type MikkLock, type MikkModule, type MikkDecision,
|
|
6
|
+
type MikkLockFunction, type MikkLockModule, type MikkLockFile,
|
|
7
|
+
} from './schema.js'
|
|
8
|
+
export { LockCompiler } from './lock-compiler.js'
|
|
9
|
+
export { ContractWriter, type UpdateResult } from './contract-writer.js'
|
|
10
|
+
export { ContractReader } from './contract-reader.js'
|
|
11
|
+
export { LockReader } from './lock-reader.js'
|
|
12
|
+
export { ContractGenerator } from './contract-generator.js'
|