mapra 0.1.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.
- package/LICENSE +21 -0
- package/README.md +144 -0
- package/dist/analyzer/blast-radius.d.ts +27 -0
- package/dist/analyzer/blast-radius.d.ts.map +1 -0
- package/dist/analyzer/blast-radius.js +58 -0
- package/dist/analyzer/blast-radius.js.map +1 -0
- package/dist/analyzer/churn.d.ts +26 -0
- package/dist/analyzer/churn.d.ts.map +1 -0
- package/dist/analyzer/churn.js +96 -0
- package/dist/analyzer/churn.js.map +1 -0
- package/dist/analyzer/co-change.d.ts +60 -0
- package/dist/analyzer/co-change.d.ts.map +1 -0
- package/dist/analyzer/co-change.js +153 -0
- package/dist/analyzer/co-change.js.map +1 -0
- package/dist/analyzer/conventions.d.ts +22 -0
- package/dist/analyzer/conventions.d.ts.map +1 -0
- package/dist/analyzer/conventions.js +81 -0
- package/dist/analyzer/conventions.js.map +1 -0
- package/dist/analyzer/git-hash.d.ts +11 -0
- package/dist/analyzer/git-hash.d.ts.map +1 -0
- package/dist/analyzer/git-hash.js +25 -0
- package/dist/analyzer/git-hash.js.map +1 -0
- package/dist/analyzer/graph-utils.d.ts +46 -0
- package/dist/analyzer/graph-utils.d.ts.map +1 -0
- package/dist/analyzer/graph-utils.js +145 -0
- package/dist/analyzer/graph-utils.js.map +1 -0
- package/dist/analyzer/index.d.ts +34 -0
- package/dist/analyzer/index.d.ts.map +1 -0
- package/dist/analyzer/index.js +63 -0
- package/dist/analyzer/index.js.map +1 -0
- package/dist/cli/hooks.d.ts +24 -0
- package/dist/cli/hooks.d.ts.map +1 -0
- package/dist/cli/hooks.js +126 -0
- package/dist/cli/hooks.js.map +1 -0
- package/dist/cli/index.d.ts +18 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +818 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/plan-parser.d.ts +20 -0
- package/dist/cli/plan-parser.d.ts.map +1 -0
- package/dist/cli/plan-parser.js +104 -0
- package/dist/cli/plan-parser.js.map +1 -0
- package/dist/cli/shim.d.ts +3 -0
- package/dist/cli/shim.d.ts.map +1 -0
- package/dist/cli/shim.js +33 -0
- package/dist/cli/shim.js.map +1 -0
- package/dist/cli/templates.d.ts +28 -0
- package/dist/cli/templates.d.ts.map +1 -0
- package/dist/cli/templates.js +91 -0
- package/dist/cli/templates.js.map +1 -0
- package/dist/encoder/encode.d.ts +17 -0
- package/dist/encoder/encode.d.ts.map +1 -0
- package/dist/encoder/encode.js +270 -0
- package/dist/encoder/encode.js.map +1 -0
- package/dist/encoder/layer-infrastructure.d.ts +17 -0
- package/dist/encoder/layer-infrastructure.d.ts.map +1 -0
- package/dist/encoder/layer-infrastructure.js +232 -0
- package/dist/encoder/layer-infrastructure.js.map +1 -0
- package/dist/encoder/layer-labels.d.ts +18 -0
- package/dist/encoder/layer-labels.d.ts.map +1 -0
- package/dist/encoder/layer-labels.js +172 -0
- package/dist/encoder/layer-labels.js.map +1 -0
- package/dist/encoder/layer-terrain.d.ts +18 -0
- package/dist/encoder/layer-terrain.d.ts.map +1 -0
- package/dist/encoder/layer-terrain.js +135 -0
- package/dist/encoder/layer-terrain.js.map +1 -0
- package/dist/encoder/layout.d.ts +53 -0
- package/dist/encoder/layout.d.ts.map +1 -0
- package/dist/encoder/layout.js +178 -0
- package/dist/encoder/layout.js.map +1 -0
- package/dist/encoder/parse-strand-header.d.ts +29 -0
- package/dist/encoder/parse-strand-header.d.ts.map +1 -0
- package/dist/encoder/parse-strand-header.js +59 -0
- package/dist/encoder/parse-strand-header.js.map +1 -0
- package/dist/encoder/spatial-text-encode.d.ts +22 -0
- package/dist/encoder/spatial-text-encode.d.ts.map +1 -0
- package/dist/encoder/spatial-text-encode.js +199 -0
- package/dist/encoder/spatial-text-encode.js.map +1 -0
- package/dist/encoder/strand-format-encode-v1.d.ts +16 -0
- package/dist/encoder/strand-format-encode-v1.d.ts.map +1 -0
- package/dist/encoder/strand-format-encode-v1.js +296 -0
- package/dist/encoder/strand-format-encode-v1.js.map +1 -0
- package/dist/encoder/strand-format-encode.d.ts +21 -0
- package/dist/encoder/strand-format-encode.d.ts.map +1 -0
- package/dist/encoder/strand-format-encode.js +562 -0
- package/dist/encoder/strand-format-encode.js.map +1 -0
- package/dist/encoder/text-encode.d.ts +13 -0
- package/dist/encoder/text-encode.d.ts.map +1 -0
- package/dist/encoder/text-encode.js +123 -0
- package/dist/encoder/text-encode.js.map +1 -0
- package/dist/query/blast-radius.d.ts +14 -0
- package/dist/query/blast-radius.d.ts.map +1 -0
- package/dist/query/blast-radius.js +81 -0
- package/dist/query/blast-radius.js.map +1 -0
- package/dist/query/cache.d.ts +29 -0
- package/dist/query/cache.d.ts.map +1 -0
- package/dist/query/cache.js +138 -0
- package/dist/query/cache.js.map +1 -0
- package/dist/query/index.d.ts +2 -0
- package/dist/query/index.d.ts.map +1 -0
- package/dist/query/index.js +46 -0
- package/dist/query/index.js.map +1 -0
- package/dist/query/resolve.d.ts +7 -0
- package/dist/query/resolve.d.ts.map +1 -0
- package/dist/query/resolve.js +24 -0
- package/dist/query/resolve.js.map +1 -0
- package/dist/query/risk-profile.d.ts +30 -0
- package/dist/query/risk-profile.d.ts.map +1 -0
- package/dist/query/risk-profile.js +94 -0
- package/dist/query/risk-profile.js.map +1 -0
- package/dist/query/test-map.d.ts +13 -0
- package/dist/query/test-map.d.ts.map +1 -0
- package/dist/query/test-map.js +43 -0
- package/dist/query/test-map.js.map +1 -0
- package/dist/scanner/index.d.ts +51 -0
- package/dist/scanner/index.d.ts.map +1 -0
- package/dist/scanner/index.js +480 -0
- package/dist/scanner/index.js.map +1 -0
- package/dist/scanner/workspace.d.ts +11 -0
- package/dist/scanner/workspace.d.ts.map +1 -0
- package/dist/scanner/workspace.js +243 -0
- package/dist/scanner/workspace.js.map +1 -0
- package/package.json +52 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Text Encoder — generates a structured text representation of the same graph
|
|
3
|
+
* Used as the baseline comparison for the visual encoding experiment.
|
|
4
|
+
* This represents "the best text can do" — well-structured YAML-like format.
|
|
5
|
+
*/
|
|
6
|
+
export function encodeToText(graph, analysis, options) {
|
|
7
|
+
const bare = options?.bare ?? false;
|
|
8
|
+
let text = "";
|
|
9
|
+
// Layer 0: Identity
|
|
10
|
+
text += `# ${graph.projectName}\n`;
|
|
11
|
+
text += `Framework: ${graph.framework}\n`;
|
|
12
|
+
text += `Files: ${graph.totalFiles} | Lines: ${graph.totalLines.toLocaleString()}\n`;
|
|
13
|
+
text += `Modules: ${graph.modules.length}\n\n`;
|
|
14
|
+
// Layer 1: Module overview
|
|
15
|
+
text += `## Modules\n`;
|
|
16
|
+
for (const mod of graph.modules.sort((a, b) => b.totalLines - a.totalLines)) {
|
|
17
|
+
const entryCount = mod.entryPoints.length;
|
|
18
|
+
text += `- ${mod.name} (${mod.path}): ${mod.nodeCount} files, ${mod.totalLines} lines`;
|
|
19
|
+
if (entryCount > 0)
|
|
20
|
+
text += ` [${entryCount} entry points]`;
|
|
21
|
+
text += `\n`;
|
|
22
|
+
}
|
|
23
|
+
// Risk analysis (structural — skip in bare mode)
|
|
24
|
+
if (!bare && analysis && analysis.risk.length > 0) {
|
|
25
|
+
text += `\n## Risk (Change With Care)\n`;
|
|
26
|
+
const top = analysis.risk.slice(0, 8);
|
|
27
|
+
for (const r of top) {
|
|
28
|
+
text += `- ${r.nodeId}: ${r.affectedCount} affected, depth ${r.maxDepth}, ${r.modulesAffected} modules, amp ${r.amplificationRatio.toFixed(1)}\n`;
|
|
29
|
+
}
|
|
30
|
+
const remaining = analysis.risk.length - top.length;
|
|
31
|
+
if (remaining > 0) {
|
|
32
|
+
text += ` ... and ${remaining} more with blast radius > 1\n`;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
// Layer 2: Routes and API
|
|
36
|
+
const apiRoutes = graph.nodes.filter((n) => n.type === "api-route");
|
|
37
|
+
const pages = graph.nodes.filter((n) => n.type === "route");
|
|
38
|
+
if (apiRoutes.length > 0) {
|
|
39
|
+
text += `\n## API Routes (${apiRoutes.length})\n`;
|
|
40
|
+
for (const route of apiRoutes) {
|
|
41
|
+
const methods = route.framework?.metadata?.methods?.join(", ") || "?";
|
|
42
|
+
const routePath = route.framework?.metadata?.routePath ||
|
|
43
|
+
route.path;
|
|
44
|
+
text += `- ${methods} ${routePath} (${route.lines} lines)\n`;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (pages.length > 0) {
|
|
48
|
+
text += `\n## Pages (${pages.length})\n`;
|
|
49
|
+
for (const page of pages) {
|
|
50
|
+
const routePath = page.framework?.metadata?.routePath ||
|
|
51
|
+
page.path;
|
|
52
|
+
const client = page.framework?.metadata?.isClientComponent
|
|
53
|
+
? " [client]"
|
|
54
|
+
: "";
|
|
55
|
+
text += `- ${routePath}${client} (${page.lines} lines)\n`;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// Components
|
|
59
|
+
const components = graph.nodes.filter((n) => n.type === "component");
|
|
60
|
+
if (components.length > 0) {
|
|
61
|
+
text += `\n## Components (${components.length})\n`;
|
|
62
|
+
const sorted = bare
|
|
63
|
+
? [...components].sort((a, b) => a.name.localeCompare(b.name))
|
|
64
|
+
: [...components].sort((a, b) => b.complexity - a.complexity);
|
|
65
|
+
for (const comp of sorted.slice(0, 20)) {
|
|
66
|
+
if (bare) {
|
|
67
|
+
text += `- ${comp.name} (${comp.lines} lines)\n`;
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
text += `- ${comp.name} (${comp.lines} lines, complexity: ${comp.complexity.toFixed(2)})\n`;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (components.length > 20) {
|
|
74
|
+
text += ` ... and ${components.length - 20} more\n`;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// Schema
|
|
78
|
+
const schemas = graph.nodes.filter((n) => n.type === "schema");
|
|
79
|
+
if (schemas.length > 0) {
|
|
80
|
+
text += `\n## Data Schema\n`;
|
|
81
|
+
for (const schema of schemas) {
|
|
82
|
+
const models = schema.framework?.metadata?.models || [];
|
|
83
|
+
text += `- ${schema.name}: ${models.length} models (${models.join(", ")})\n`;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Structural analysis sections — skip in bare mode
|
|
87
|
+
if (!bare) {
|
|
88
|
+
// High-complexity files
|
|
89
|
+
const complex = graph.nodes
|
|
90
|
+
.filter((n) => n.type !== "test" && n.type !== "config")
|
|
91
|
+
.sort((a, b) => b.complexity - a.complexity)
|
|
92
|
+
.slice(0, 10);
|
|
93
|
+
text += `\n## Complexity Hotspots (top 10)\n`;
|
|
94
|
+
for (const node of complex) {
|
|
95
|
+
text += `- ${node.path} (${node.lines} lines, ${node.imports.length} imports)\n`;
|
|
96
|
+
}
|
|
97
|
+
// Dependencies — most connected files
|
|
98
|
+
const edgeCounts = new Map();
|
|
99
|
+
for (const edge of graph.edges) {
|
|
100
|
+
edgeCounts.set(edge.to, (edgeCounts.get(edge.to) || 0) + 1);
|
|
101
|
+
}
|
|
102
|
+
const mostImported = [...edgeCounts.entries()]
|
|
103
|
+
.sort((a, b) => b[1] - a[1])
|
|
104
|
+
.slice(0, 10);
|
|
105
|
+
if (mostImported.length > 0) {
|
|
106
|
+
text += `\n## Most Depended-On Files\n`;
|
|
107
|
+
for (const [fileId, count] of mostImported) {
|
|
108
|
+
text += `- ${fileId} (imported by ${count} files)\n`;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// Test coverage
|
|
112
|
+
const testEdges = graph.edges.filter((e) => e.type === "tests");
|
|
113
|
+
const testedFiles = new Set(testEdges.map((e) => e.to));
|
|
114
|
+
const testableFiles = graph.nodes.filter((n) => n.type !== "test" && n.type !== "config");
|
|
115
|
+
const coveragePercent = testableFiles.length > 0
|
|
116
|
+
? ((testedFiles.size / testableFiles.length) * 100).toFixed(0)
|
|
117
|
+
: "0";
|
|
118
|
+
text += `\n## Test Coverage\n`;
|
|
119
|
+
text += `${testEdges.length} test files covering ${testedFiles.size}/${testableFiles.length} files (${coveragePercent}%)\n`;
|
|
120
|
+
}
|
|
121
|
+
return text;
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=text-encode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-encode.js","sourceRoot":"","sources":["../../src/encoder/text-encode.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAUH,MAAM,UAAU,YAAY,CAC1B,KAAkB,EAClB,QAAwB,EACxB,OAA2B;IAE3B,MAAM,IAAI,GAAG,OAAO,EAAE,IAAI,IAAI,KAAK,CAAC;IACpC,IAAI,IAAI,GAAG,EAAE,CAAC;IAEd,oBAAoB;IACpB,IAAI,IAAI,KAAK,KAAK,CAAC,WAAW,IAAI,CAAC;IACnC,IAAI,IAAI,cAAc,KAAK,CAAC,SAAS,IAAI,CAAC;IAC1C,IAAI,IAAI,UAAU,KAAK,CAAC,UAAU,aAAa,KAAK,CAAC,UAAU,CAAC,cAAc,EAAE,IAAI,CAAC;IACrF,IAAI,IAAI,YAAY,KAAK,CAAC,OAAO,CAAC,MAAM,MAAM,CAAC;IAE/C,2BAA2B;IAC3B,IAAI,IAAI,cAAc,CAAC;IACvB,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5E,MAAM,UAAU,GAAG,GAAG,CAAC,WAAW,CAAC,MAAM,CAAC;QAC1C,IAAI,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,MAAM,GAAG,CAAC,SAAS,WAAW,GAAG,CAAC,UAAU,QAAQ,CAAC;QACvF,IAAI,UAAU,GAAG,CAAC;YAAE,IAAI,IAAI,KAAK,UAAU,gBAAgB,CAAC;QAC5D,IAAI,IAAI,IAAI,CAAC;IACf,CAAC;IAED,iDAAiD;IACjD,IAAI,CAAC,IAAI,IAAI,QAAQ,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClD,IAAI,IAAI,gCAAgC,CAAC;QACzC,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACtC,KAAK,MAAM,CAAC,IAAI,GAAG,EAAE,CAAC;YACpB,IAAI,IAAI,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,aAAa,oBAAoB,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,eAAe,iBAAiB,CAAC,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;QACpJ,CAAC;QACD,MAAM,SAAS,GAAG,QAAQ,CAAC,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;QACpD,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;YAClB,IAAI,IAAI,aAAa,SAAS,+BAA+B,CAAC;QAChE,CAAC;IACH,CAAC;IAED,0BAA0B;IAC1B,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC;IACpE,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC;IAE5D,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzB,IAAI,IAAI,oBAAoB,SAAS,CAAC,MAAM,KAAK,CAAC;QAClD,KAAK,MAAM,KAAK,IAAI,SAAS,EAAE,CAAC;YAC9B,MAAM,OAAO,GACV,KAAK,CAAC,SAAS,EAAE,QAAmC,EAAE,OAAO,EAAE,IAAI,CAClE,IAAI,CACL,IAAI,GAAG,CAAC;YACX,MAAM,SAAS,GACZ,KAAK,CAAC,SAAS,EAAE,QAAmC,EAAE,SAAS;gBAChE,KAAK,CAAC,IAAI,CAAC;YACb,IAAI,IAAI,KAAK,OAAO,IAAI,SAAS,KAAK,KAAK,CAAC,KAAK,WAAW,CAAC;QAC/D,CAAC;IACH,CAAC;IAED,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,IAAI,IAAI,eAAe,KAAK,CAAC,MAAM,KAAK,CAAC;QACzC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,SAAS,GACZ,IAAI,CAAC,SAAS,EAAE,QAAmC,EAAE,SAAS;gBAC/D,IAAI,CAAC,IAAI,CAAC;YACZ,MAAM,MAAM,GACV,IAAI,CAAC,SAAS,EAAE,QACjB,EAAE,iBAAiB;gBAClB,CAAC,CAAC,WAAW;gBACb,CAAC,CAAC,EAAE,CAAC;YACP,IAAI,IAAI,KAAK,SAAS,GAAG,MAAM,KAAK,IAAI,CAAC,KAAK,WAAW,CAAC;QAC5D,CAAC;IACH,CAAC;IAED,aAAa;IACb,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC;IACrE,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,IAAI,IAAI,oBAAoB,UAAU,CAAC,MAAM,KAAK,CAAC;QACnD,MAAM,MAAM,GAAG,IAAI;YACjB,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAC9D,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC;QAChE,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;YACvC,IAAI,IAAI,EAAE,CAAC;gBACT,IAAI,IAAI,KAAK,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,KAAK,WAAW,CAAC;YACnD,CAAC;iBAAM,CAAC;gBACN,IAAI,IAAI,KAAK,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,KAAK,uBAAuB,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;YAC9F,CAAC;QACH,CAAC;QACD,IAAI,UAAU,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;YAC3B,IAAI,IAAI,aAAa,UAAU,CAAC,MAAM,GAAG,EAAE,SAAS,CAAC;QACvD,CAAC;IACH,CAAC;IAED,SAAS;IACT,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;IAC/D,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,IAAI,IAAI,oBAAoB,CAAC;QAC7B,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,MAAM,MAAM,GACT,MAAM,CAAC,SAAS,EAAE,QAAkC,EAAE,MAAM,IAAI,EAAE,CAAC;YACtE,IAAI,IAAI,KAAK,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,MAAM,YAAY,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;QAC/E,CAAC;IACH,CAAC;IAED,mDAAmD;IACnD,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,wBAAwB;QACxB,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK;aACxB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC;aACvD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC;aAC3C,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAEhB,IAAI,IAAI,qCAAqC,CAAC;QAC9C,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;YAC3B,IAAI,IAAI,KAAK,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,KAAK,WAAW,IAAI,CAAC,OAAO,CAAC,MAAM,aAAa,CAAC;QACnF,CAAC;QAED,sCAAsC;QACtC,MAAM,UAAU,GAAG,IAAI,GAAG,EAAkB,CAAC;QAC7C,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YAC/B,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAC9D,CAAC;QACD,MAAM,YAAY,GAAG,CAAC,GAAG,UAAU,CAAC,OAAO,EAAE,CAAC;aAC3C,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;aAC3B,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAEhB,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5B,IAAI,IAAI,+BAA+B,CAAC;YACxC,KAAK,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,YAAY,EAAE,CAAC;gBAC3C,IAAI,IAAI,KAAK,MAAM,iBAAiB,KAAK,WAAW,CAAC;YACvD,CAAC;QACH,CAAC;QAED,gBAAgB;QAChB,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC;QAChE,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACxD,MAAM,aAAa,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CACtC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ,CAChD,CAAC;QACF,MAAM,eAAe,GACnB,aAAa,CAAC,MAAM,GAAG,CAAC;YACtB,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,GAAG,aAAa,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;YAC9D,CAAC,CAAC,GAAG,CAAC;QAEV,IAAI,IAAI,sBAAsB,CAAC;QAC/B,IAAI,IAAI,GAAG,SAAS,CAAC,MAAM,wBAAwB,WAAW,CAAC,IAAI,IAAI,aAAa,CAAC,MAAM,WAAW,eAAe,MAAM,CAAC;IAC9H,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { MapraCache } from "./cache.js";
|
|
2
|
+
export interface BlastRadiusResult {
|
|
3
|
+
file: string;
|
|
4
|
+
directImporters: number;
|
|
5
|
+
affectedCount: number;
|
|
6
|
+
amplificationRatio: number;
|
|
7
|
+
cascadeDepth: number;
|
|
8
|
+
modulesAffected: number;
|
|
9
|
+
affectedModules: string[];
|
|
10
|
+
cascadePath: string[];
|
|
11
|
+
}
|
|
12
|
+
export declare function queryBlastRadius(fileId: string, cache: MapraCache): BlastRadiusResult;
|
|
13
|
+
export declare function formatBlastRadius(result: BlastRadiusResult, json: boolean): string;
|
|
14
|
+
//# sourceMappingURL=blast-radius.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blast-radius.d.ts","sourceRoot":"","sources":["../../src/query/blast-radius.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,iBAAiB,CA4CrF;AA+BD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,CAoBlF"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// src/query/blast-radius.ts
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import { buildReverseAdjacency, bfsWithParents, getModuleId } from "../analyzer/graph-utils.js";
|
|
4
|
+
export function queryBlastRadius(fileId, cache) {
|
|
5
|
+
// Use pre-computed risk data when available; only run BFS for cascade path
|
|
6
|
+
const precomputed = cache.analysis.risk.find(r => r.nodeId === fileId);
|
|
7
|
+
// Lazy BFS — only run when we need cascade path or fallback data
|
|
8
|
+
let _bfsResult;
|
|
9
|
+
let _reverseAdj;
|
|
10
|
+
function getBfs() {
|
|
11
|
+
if (!_bfsResult) {
|
|
12
|
+
const testNodeIds = new Set(cache.graph.nodes.filter(n => n.type === "test").map(n => n.id));
|
|
13
|
+
_reverseAdj = buildReverseAdjacency(cache.graph.edges, true, testNodeIds);
|
|
14
|
+
_bfsResult = bfsWithParents(fileId, _reverseAdj);
|
|
15
|
+
}
|
|
16
|
+
return _bfsResult;
|
|
17
|
+
}
|
|
18
|
+
const directImporters = precomputed?.directImporters ?? (() => {
|
|
19
|
+
getBfs();
|
|
20
|
+
return _reverseAdj.get(fileId)?.size ?? 0;
|
|
21
|
+
})();
|
|
22
|
+
const affectedCount = precomputed?.affectedCount ?? getBfs().depths.size;
|
|
23
|
+
const amplificationRatio = precomputed?.amplificationRatio ??
|
|
24
|
+
(directImporters > 0 ? Math.round((affectedCount / directImporters) * 10) / 10 : 0);
|
|
25
|
+
const cascadeDepth = precomputed?.maxDepth ?? (getBfs().depths.size > 0 ? Math.max(...getBfs().depths.values()) : 0);
|
|
26
|
+
const affectedModules = precomputed?.affectedModuleNames ??
|
|
27
|
+
[...new Set([...getBfs().depths.keys()].map(getModuleId))].sort();
|
|
28
|
+
const modulesAffected = precomputed?.modulesAffected ?? affectedModules.length;
|
|
29
|
+
// Cascade path always needs BFS
|
|
30
|
+
const { depths, parents } = getBfs();
|
|
31
|
+
return {
|
|
32
|
+
file: fileId,
|
|
33
|
+
directImporters,
|
|
34
|
+
affectedCount,
|
|
35
|
+
amplificationRatio,
|
|
36
|
+
cascadeDepth,
|
|
37
|
+
modulesAffected,
|
|
38
|
+
affectedModules,
|
|
39
|
+
cascadePath: reconstructCascadePath(fileId, depths, parents),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/** Trace from deepest BFS node back to start to get the longest cascade chain. */
|
|
43
|
+
function reconstructCascadePath(startId, depths, parents) {
|
|
44
|
+
if (depths.size === 0)
|
|
45
|
+
return [];
|
|
46
|
+
let deepestNode = "";
|
|
47
|
+
let maxDepth = 0;
|
|
48
|
+
for (const [node, depth] of depths) {
|
|
49
|
+
if (depth > maxDepth) {
|
|
50
|
+
maxDepth = depth;
|
|
51
|
+
deepestNode = node;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const chain = [];
|
|
55
|
+
let current = deepestNode;
|
|
56
|
+
while (current !== startId) {
|
|
57
|
+
chain.unshift(current);
|
|
58
|
+
const parent = parents.get(current);
|
|
59
|
+
if (!parent)
|
|
60
|
+
break;
|
|
61
|
+
current = parent;
|
|
62
|
+
}
|
|
63
|
+
return chain;
|
|
64
|
+
}
|
|
65
|
+
export function formatBlastRadius(result, json) {
|
|
66
|
+
if (json)
|
|
67
|
+
return JSON.stringify(result, null, 2);
|
|
68
|
+
const lines = [];
|
|
69
|
+
lines.push(`${result.file} \u00D7${result.directImporters}\u2192${result.affectedCount} d${result.cascadeDepth} amp${result.amplificationRatio} ${result.modulesAffected}mod`);
|
|
70
|
+
if (result.affectedModules.length > 0) {
|
|
71
|
+
lines.push(`affected modules: ${result.affectedModules.join(", ")}`);
|
|
72
|
+
}
|
|
73
|
+
if (result.cascadePath.length > 0) {
|
|
74
|
+
const chain = result.cascadePath
|
|
75
|
+
.map((f, i) => `${path.basename(f)}(d${i + 1})`)
|
|
76
|
+
.join(" \u2192 ");
|
|
77
|
+
lines.push(`cascade: ${chain}`);
|
|
78
|
+
}
|
|
79
|
+
return lines.join("\n");
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=blast-radius.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blast-radius.js","sourceRoot":"","sources":["../../src/query/blast-radius.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { StrandGraph } from "../scanner/index.js";
|
|
2
|
+
import type { GraphAnalysis } from "../analyzer/index.js";
|
|
3
|
+
export interface MapraCache {
|
|
4
|
+
version: 1;
|
|
5
|
+
generated: string;
|
|
6
|
+
gitHead?: string;
|
|
7
|
+
graph: StrandGraph;
|
|
8
|
+
analysis: GraphAnalysis;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Serialize StrandGraph + GraphAnalysis to .mapra-cache.json.
|
|
12
|
+
* Uses atomic write (tmp + rename) for safety.
|
|
13
|
+
*/
|
|
14
|
+
export declare function writeCache(targetPath: string, graph: StrandGraph, analysis: GraphAnalysis, gitHead?: string): void;
|
|
15
|
+
/**
|
|
16
|
+
* Load and validate .mapra-cache.json from the given directory,
|
|
17
|
+
* walking up to git root if not found in startDir.
|
|
18
|
+
*/
|
|
19
|
+
export declare function loadCache(startDir?: string): MapraCache;
|
|
20
|
+
/**
|
|
21
|
+
* Check if cache is stale relative to current git HEAD.
|
|
22
|
+
* Returns a warning string, or null if current/not-git.
|
|
23
|
+
*/
|
|
24
|
+
export declare function checkStaleness(cache: MapraCache): string | null;
|
|
25
|
+
/**
|
|
26
|
+
* Append `.mapra-cache.json` to .gitignore if not already present.
|
|
27
|
+
*/
|
|
28
|
+
export declare function ensureCacheInGitignore(targetPath: string): void;
|
|
29
|
+
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/query/cache.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAG1D,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,CAAC,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,WAAW,CAAC;IACnB,QAAQ,EAAE,aAAa,CAAC;CACzB;AAaD;;;GAGG;AACH,wBAAgB,UAAU,CACxB,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,WAAW,EAClB,QAAQ,EAAE,aAAa,EACvB,OAAO,CAAC,EAAE,MAAM,GACf,IAAI,CAwBN;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,UAAU,CAiCvD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAuB/D;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAa/D"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// src/query/cache.ts — mapra cache management
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
import { execSync } from "child_process";
|
|
5
|
+
/**
|
|
6
|
+
* Serialize StrandGraph + GraphAnalysis to .mapra-cache.json.
|
|
7
|
+
* Uses atomic write (tmp + rename) for safety.
|
|
8
|
+
*/
|
|
9
|
+
export function writeCache(targetPath, graph, analysis, gitHead) {
|
|
10
|
+
const cache = {
|
|
11
|
+
version: 1,
|
|
12
|
+
generated: new Date().toISOString(),
|
|
13
|
+
...(gitHead !== undefined ? { gitHead } : {}),
|
|
14
|
+
graph,
|
|
15
|
+
analysis: {
|
|
16
|
+
...analysis,
|
|
17
|
+
churn: Object.fromEntries(analysis.churn),
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
const cachePath = path.join(targetPath, ".mapra-cache.json");
|
|
21
|
+
const tmpPath = cachePath + ".tmp";
|
|
22
|
+
const json = JSON.stringify(cache, null, 2);
|
|
23
|
+
fs.writeFileSync(tmpPath, json, "utf-8");
|
|
24
|
+
try {
|
|
25
|
+
fs.renameSync(tmpPath, cachePath);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
fs.writeFileSync(cachePath, json, "utf-8");
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
try {
|
|
32
|
+
fs.unlinkSync(tmpPath);
|
|
33
|
+
}
|
|
34
|
+
catch { /* already renamed or gone */ }
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Load and validate .mapra-cache.json from the given directory,
|
|
39
|
+
* walking up to git root if not found in startDir.
|
|
40
|
+
*/
|
|
41
|
+
export function loadCache(startDir) {
|
|
42
|
+
const dir = startDir ?? process.cwd();
|
|
43
|
+
const cachePath = findCacheFile(dir);
|
|
44
|
+
if (!cachePath) {
|
|
45
|
+
throw new Error("No .mapra-cache.json found. Run 'mapra generate' first.");
|
|
46
|
+
}
|
|
47
|
+
let raw;
|
|
48
|
+
try {
|
|
49
|
+
raw = fs.readFileSync(cachePath, "utf-8");
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
throw new Error("No .mapra-cache.json found. Run 'mapra generate' first.");
|
|
53
|
+
}
|
|
54
|
+
let data;
|
|
55
|
+
try {
|
|
56
|
+
data = JSON.parse(raw);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
throw new Error(".mapra-cache.json is corrupted. Run 'mapra generate' to rebuild.");
|
|
60
|
+
}
|
|
61
|
+
if (data.version !== 1) {
|
|
62
|
+
throw new Error("Cache format is incompatible. Run 'mapra generate' to rebuild.");
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
...data,
|
|
66
|
+
analysis: {
|
|
67
|
+
...data.analysis,
|
|
68
|
+
churn: new Map(Object.entries(data.analysis.churn)),
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Check if cache is stale relative to current git HEAD.
|
|
74
|
+
* Returns a warning string, or null if current/not-git.
|
|
75
|
+
*/
|
|
76
|
+
export function checkStaleness(cache) {
|
|
77
|
+
if (!cache.gitHead)
|
|
78
|
+
return null;
|
|
79
|
+
try {
|
|
80
|
+
const currentHead = execSync("git rev-parse HEAD", {
|
|
81
|
+
encoding: "utf-8",
|
|
82
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
83
|
+
}).trim();
|
|
84
|
+
if (currentHead === cache.gitHead)
|
|
85
|
+
return null;
|
|
86
|
+
try {
|
|
87
|
+
const count = execSync(`git rev-list --count ${cache.gitHead}..HEAD`, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
88
|
+
return `\u26A0 cache generated before ${count} commits \u2014 run 'mapra generate' to refresh`;
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return "\u26A0 cache may be stale \u2014 run 'mapra generate' to refresh";
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Append `.mapra-cache.json` to .gitignore if not already present.
|
|
100
|
+
*/
|
|
101
|
+
export function ensureCacheInGitignore(targetPath) {
|
|
102
|
+
const gitignorePath = path.join(targetPath, ".gitignore");
|
|
103
|
+
const entry = ".mapra-cache.json";
|
|
104
|
+
let content = "";
|
|
105
|
+
try {
|
|
106
|
+
content = fs.readFileSync(gitignorePath, "utf-8");
|
|
107
|
+
}
|
|
108
|
+
catch { /* file doesn't exist yet */ }
|
|
109
|
+
if (content.split("\n").some(line => line.trim() === entry))
|
|
110
|
+
return;
|
|
111
|
+
const separator = content.length > 0 && !content.endsWith("\n") ? "\n" : "";
|
|
112
|
+
fs.writeFileSync(gitignorePath, content + separator + entry + "\n", "utf-8");
|
|
113
|
+
}
|
|
114
|
+
/** Walk up from startDir to git root looking for .mapra-cache.json. */
|
|
115
|
+
function findCacheFile(startDir) {
|
|
116
|
+
let dir = path.resolve(startDir);
|
|
117
|
+
let gitRoot = null;
|
|
118
|
+
try {
|
|
119
|
+
gitRoot = path.resolve(execSync("git rev-parse --show-toplevel", {
|
|
120
|
+
cwd: dir,
|
|
121
|
+
encoding: "utf-8",
|
|
122
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
123
|
+
}).trim());
|
|
124
|
+
}
|
|
125
|
+
catch { /* not a git repo */ }
|
|
126
|
+
while (true) {
|
|
127
|
+
const candidate = path.join(dir, ".mapra-cache.json");
|
|
128
|
+
if (fs.existsSync(candidate))
|
|
129
|
+
return candidate;
|
|
130
|
+
if (gitRoot && path.resolve(dir) === gitRoot)
|
|
131
|
+
return null;
|
|
132
|
+
const parent = path.dirname(dir);
|
|
133
|
+
if (parent === dir)
|
|
134
|
+
return null;
|
|
135
|
+
dir = parent;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/query/cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/query/index.ts"],"names":[],"mappings":"AASA,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAyCnE"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// src/query/index.ts
|
|
2
|
+
import { loadCache, checkStaleness } from "./cache.js";
|
|
3
|
+
import { resolveFile } from "./resolve.js";
|
|
4
|
+
import { queryBlastRadius, formatBlastRadius } from "./blast-radius.js";
|
|
5
|
+
import { queryTestMap, formatTestMap } from "./test-map.js";
|
|
6
|
+
import { queryRiskProfile, formatRiskProfile } from "./risk-profile.js";
|
|
7
|
+
const QUERY_TYPES = ["blast_radius", "risk_profile", "test_map"];
|
|
8
|
+
export async function runQueryCommand(args) {
|
|
9
|
+
const jsonFlag = args.includes("--json");
|
|
10
|
+
const positionalArgs = args.filter(a => !a.startsWith("--"));
|
|
11
|
+
const queryType = positionalArgs[0];
|
|
12
|
+
const fileArg = positionalArgs[1];
|
|
13
|
+
if (!queryType || !fileArg) {
|
|
14
|
+
console.error("Usage: mapra query <type> <file> [--json]");
|
|
15
|
+
console.error(`Types: ${QUERY_TYPES.join(", ")}`);
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
if (!QUERY_TYPES.includes(queryType)) {
|
|
19
|
+
console.error(`Error: Unknown query type '${queryType}'. Available: ${QUERY_TYPES.join(", ")}`);
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
const cache = loadCache();
|
|
23
|
+
const staleness = checkStaleness(cache);
|
|
24
|
+
if (staleness)
|
|
25
|
+
console.error(staleness);
|
|
26
|
+
const nodeIds = cache.graph.nodes.map(n => n.id);
|
|
27
|
+
const fileId = resolveFile(nodeIds, fileArg);
|
|
28
|
+
switch (queryType) {
|
|
29
|
+
case "blast_radius": {
|
|
30
|
+
const result = queryBlastRadius(fileId, cache);
|
|
31
|
+
console.log(formatBlastRadius(result, jsonFlag));
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
case "test_map": {
|
|
35
|
+
const result = queryTestMap(fileId, cache);
|
|
36
|
+
console.log(formatTestMap(result, jsonFlag));
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
case "risk_profile": {
|
|
40
|
+
const result = queryRiskProfile(fileId, cache);
|
|
41
|
+
console.log(formatRiskProfile(result, jsonFlag));
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/query/index.ts"],"names":[],"mappings":"AAAA,qBAAqB;AACrB,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACxE,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAExE,MAAM,WAAW,GAAG,CAAC,cAAc,EAAE,cAAc,EAAE,UAAU,CAAU,CAAC;AAE1E,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAc;IAClD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACzC,MAAM,cAAc,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;IAC7D,MAAM,SAAS,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC;IACpC,MAAM,OAAO,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC;IAElC,IAAI,CAAC,SAAS,IAAI,CAAC,OAAO,EAAE,CAAC;QAC3B,OAAO,CAAC,KAAK,CAAC,2CAA2C,CAAC,CAAC;QAC3D,OAAO,CAAC,KAAK,CAAC,UAAU,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAClD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,SAAuC,CAAC,EAAE,CAAC;QACnE,OAAO,CAAC,KAAK,CAAC,8BAA8B,SAAS,iBAAiB,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAChG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC;IAC1B,MAAM,SAAS,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IACxC,IAAI,SAAS;QAAE,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IAExC,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACjD,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAE7C,QAAQ,SAAS,EAAE,CAAC;QAClB,KAAK,cAAc,CAAC,CAAC,CAAC;YACpB,MAAM,MAAM,GAAG,gBAAgB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;YAC/C,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;YACjD,MAAM;QACR,CAAC;QACD,KAAK,UAAU,CAAC,CAAC,CAAC;YAChB,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;YAC3C,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;YAC7C,MAAM;QACR,CAAC;QACD,KAAK,cAAc,CAAC,CAAC,CAAC;YACpB,MAAM,MAAM,GAAG,gBAAgB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;YAC/C,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;YACjD,MAAM;QACR,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve user-provided file path against cached node IDs.
|
|
3
|
+
* Supports exact match, backslash normalization, and suffix matching.
|
|
4
|
+
* Throws with actionable error messages on ambiguous or missing matches.
|
|
5
|
+
*/
|
|
6
|
+
export declare function resolveFile(nodeIds: string[], input: string): string;
|
|
7
|
+
//# sourceMappingURL=resolve.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/query/resolve.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAqBpE"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// src/query/resolve.ts
|
|
2
|
+
/**
|
|
3
|
+
* Resolve user-provided file path against cached node IDs.
|
|
4
|
+
* Supports exact match, backslash normalization, and suffix matching.
|
|
5
|
+
* Throws with actionable error messages on ambiguous or missing matches.
|
|
6
|
+
*/
|
|
7
|
+
export function resolveFile(nodeIds, input) {
|
|
8
|
+
const normalized = input.replace(/\\/g, "/").replace(/^\//, "");
|
|
9
|
+
// Exact match (O(1) via Set)
|
|
10
|
+
const idSet = new Set(nodeIds);
|
|
11
|
+
if (idSet.has(normalized))
|
|
12
|
+
return normalized;
|
|
13
|
+
// Suffix match
|
|
14
|
+
const suffix = "/" + normalized;
|
|
15
|
+
const matches = nodeIds.filter(id => id.endsWith(suffix));
|
|
16
|
+
if (matches.length === 1)
|
|
17
|
+
return matches[0];
|
|
18
|
+
if (matches.length > 1) {
|
|
19
|
+
const candidates = matches.map(m => " " + m).join("\n");
|
|
20
|
+
throw new Error(`Multiple matches for '${input}':\n${candidates}\nSpecify the full path.`);
|
|
21
|
+
}
|
|
22
|
+
throw new Error(`'${input}' not found in cache. Check the path or run 'mapra generate'.`);
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=resolve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/query/resolve.ts"],"names":[],"mappings":"AAAA,uBAAuB;AAEvB;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,OAAiB,EAAE,KAAa;IAC1D,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAEhE,6BAA6B;IAC7B,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC;IAC/B,IAAI,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC;QAAE,OAAO,UAAU,CAAC;IAE7C,eAAe;IACf,MAAM,MAAM,GAAG,GAAG,GAAG,UAAU,CAAC;IAChC,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;IAE1D,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,OAAO,CAAC,CAAC,CAAE,CAAC;IAE7C,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACzD,MAAM,IAAI,KAAK,CACb,yBAAyB,KAAK,OAAO,UAAU,0BAA0B,CAC1E,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,IAAI,KAAK,+DAA+D,CAAC,CAAC;AAC5F,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { MapraCache } from "./cache.js";
|
|
2
|
+
import { type TestMapResult } from "./test-map.js";
|
|
3
|
+
export interface RiskProfileResult {
|
|
4
|
+
file: string;
|
|
5
|
+
risk: {
|
|
6
|
+
directImporters: number;
|
|
7
|
+
affectedCount: number;
|
|
8
|
+
cascadeDepth: number;
|
|
9
|
+
amplificationRatio: number;
|
|
10
|
+
modulesAffected: number;
|
|
11
|
+
affectedModules: string[];
|
|
12
|
+
} | null;
|
|
13
|
+
churn: {
|
|
14
|
+
commits30d: number;
|
|
15
|
+
linesAdded: number;
|
|
16
|
+
linesRemoved: number;
|
|
17
|
+
lastCommitMsg: string;
|
|
18
|
+
} | null;
|
|
19
|
+
coChangePartners: Array<{
|
|
20
|
+
file: string;
|
|
21
|
+
count: number;
|
|
22
|
+
confidence: number;
|
|
23
|
+
linked: boolean;
|
|
24
|
+
}>;
|
|
25
|
+
tests: TestMapResult;
|
|
26
|
+
conventionViolations: string[];
|
|
27
|
+
}
|
|
28
|
+
export declare function queryRiskProfile(fileId: string, cache: MapraCache): RiskProfileResult;
|
|
29
|
+
export declare function formatRiskProfile(result: RiskProfileResult, json: boolean): string;
|
|
30
|
+
//# sourceMappingURL=risk-profile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"risk-profile.d.ts","sourceRoot":"","sources":["../../src/query/risk-profile.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAgB,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AAEjE,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE;QACJ,eAAe,EAAE,MAAM,CAAC;QACxB,aAAa,EAAE,MAAM,CAAC;QACtB,YAAY,EAAE,MAAM,CAAC;QACrB,kBAAkB,EAAE,MAAM,CAAC;QAC3B,eAAe,EAAE,MAAM,CAAC;QACxB,eAAe,EAAE,MAAM,EAAE,CAAC;KAC3B,GAAG,IAAI,CAAC;IACT,KAAK,EAAE;QACL,UAAU,EAAE,MAAM,CAAC;QACnB,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,MAAM,CAAC;QACrB,aAAa,EAAE,MAAM,CAAC;KACvB,GAAG,IAAI,CAAC;IACT,gBAAgB,EAAE,KAAK,CAAC;QACtB,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,EAAE,OAAO,CAAC;KACjB,CAAC,CAAC;IACH,KAAK,EAAE,aAAa,CAAC;IACrB,oBAAoB,EAAE,MAAM,EAAE,CAAC;CAChC;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,iBAAiB,CAiDrF;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,CAuDlF"}
|