gitblast 0.0.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.
Files changed (49) hide show
  1. package/dist/analyzer.d.ts +12 -0
  2. package/dist/analyzer.d.ts.map +1 -0
  3. package/dist/analyzer.js +140 -0
  4. package/dist/analyzer.js.map +1 -0
  5. package/dist/ast-analyzer.d.ts +12 -0
  6. package/dist/ast-analyzer.d.ts.map +1 -0
  7. package/dist/ast-analyzer.js +274 -0
  8. package/dist/ast-analyzer.js.map +1 -0
  9. package/dist/blast-radius.d.ts +17 -0
  10. package/dist/blast-radius.d.ts.map +1 -0
  11. package/dist/blast-radius.js +82 -0
  12. package/dist/blast-radius.js.map +1 -0
  13. package/dist/call-graph.d.ts +8 -0
  14. package/dist/call-graph.d.ts.map +1 -0
  15. package/dist/call-graph.js +252 -0
  16. package/dist/call-graph.js.map +1 -0
  17. package/dist/call-resolver.d.ts +18 -0
  18. package/dist/call-resolver.d.ts.map +1 -0
  19. package/dist/call-resolver.js +130 -0
  20. package/dist/call-resolver.js.map +1 -0
  21. package/dist/file-scanner.d.ts +2 -0
  22. package/dist/file-scanner.d.ts.map +1 -0
  23. package/dist/file-scanner.js +42 -0
  24. package/dist/file-scanner.js.map +1 -0
  25. package/dist/function-blast-radius.d.ts +7 -0
  26. package/dist/function-blast-radius.d.ts.map +1 -0
  27. package/dist/function-blast-radius.js +52 -0
  28. package/dist/function-blast-radius.js.map +1 -0
  29. package/dist/git.d.ts +27 -0
  30. package/dist/git.d.ts.map +1 -0
  31. package/dist/git.js +69 -0
  32. package/dist/git.js.map +1 -0
  33. package/dist/index.d.ts +3 -0
  34. package/dist/index.d.ts.map +1 -0
  35. package/dist/index.js +385 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/tool-runner.d.ts +65 -0
  38. package/dist/tool-runner.d.ts.map +1 -0
  39. package/dist/tool-runner.js +178 -0
  40. package/dist/tool-runner.js.map +1 -0
  41. package/dist/types.d.ts +92 -0
  42. package/dist/types.d.ts.map +1 -0
  43. package/dist/types.js +2 -0
  44. package/dist/types.js.map +1 -0
  45. package/dist/web/assets/index-BEQJnpxt.js +17 -0
  46. package/dist/web/assets/index-CqmVdlle.css +1 -0
  47. package/dist/web/index.html +14 -0
  48. package/dist/web/vite.svg +1 -0
  49. package/package.json +44 -0
@@ -0,0 +1,252 @@
1
+ /**
2
+ * Walk the full AST of a parsed file to extract function scopes and call sites.
3
+ * Uses a scope stack to track which function each call site belongs to.
4
+ */
5
+ export function extractCallData(fileId, rootNode, exportedNames) {
6
+ const scopes = [];
7
+ const callSites = [];
8
+ // Module-level scope for top-level code
9
+ const moduleScope = {
10
+ id: `${fileId}::<module>`,
11
+ name: "<module>",
12
+ fileId,
13
+ kind: "module",
14
+ line: 1,
15
+ isExported: false,
16
+ };
17
+ scopes.push(moduleScope);
18
+ const scopeStack = [moduleScope.id];
19
+ function currentScope() {
20
+ return scopeStack[scopeStack.length - 1];
21
+ }
22
+ function pushScope(scope) {
23
+ scopes.push(scope);
24
+ scopeStack.push(scope.id);
25
+ }
26
+ function popScope() {
27
+ if (scopeStack.length > 1)
28
+ scopeStack.pop();
29
+ }
30
+ /**
31
+ * Try to extract a function name from a variable_declarator parent.
32
+ * Handles: const foo = () => {}, const foo = function() {}
33
+ */
34
+ function getAssignedName(node) {
35
+ const parent = node.parent;
36
+ if (parent?.type === "variable_declarator") {
37
+ const nameNode = parent.childForFieldName("name");
38
+ if (nameNode)
39
+ return nameNode.text;
40
+ }
41
+ return null;
42
+ }
43
+ /**
44
+ * Check if a node is inside an export_statement.
45
+ */
46
+ function isExported(node) {
47
+ let current = node.parent;
48
+ while (current) {
49
+ if (current.type === "export_statement")
50
+ return true;
51
+ // Stop at program level
52
+ if (current.type === "program")
53
+ break;
54
+ current = current.parent;
55
+ }
56
+ return false;
57
+ }
58
+ function walk(node) {
59
+ let didPush = false;
60
+ switch (node.type) {
61
+ case "function_declaration":
62
+ case "generator_function_declaration": {
63
+ const name = node.childForFieldName("name")?.text;
64
+ if (name) {
65
+ const exported = isExported(node) || exportedNames.has(name);
66
+ pushScope({
67
+ id: `${fileId}::${name}`,
68
+ name,
69
+ fileId,
70
+ kind: "function",
71
+ line: node.startPosition.row + 1,
72
+ isExported: exported,
73
+ });
74
+ didPush = true;
75
+ }
76
+ break;
77
+ }
78
+ case "class_declaration": {
79
+ const name = node.childForFieldName("name")?.text;
80
+ if (name) {
81
+ const exported = isExported(node) || exportedNames.has(name);
82
+ pushScope({
83
+ id: `${fileId}::${name}`,
84
+ name,
85
+ fileId,
86
+ kind: "class",
87
+ line: node.startPosition.row + 1,
88
+ isExported: exported,
89
+ });
90
+ didPush = true;
91
+ }
92
+ break;
93
+ }
94
+ case "method_definition": {
95
+ const name = node.childForFieldName("name")?.text;
96
+ if (name) {
97
+ // Find enclosing class name
98
+ let className = "";
99
+ let p = node.parent;
100
+ while (p) {
101
+ if (p.type === "class_declaration" || p.type === "class") {
102
+ className = p.childForFieldName("name")?.text ?? "";
103
+ break;
104
+ }
105
+ p = p.parent;
106
+ }
107
+ const qualifiedName = className ? `${className}.${name}` : name;
108
+ pushScope({
109
+ id: `${fileId}::${qualifiedName}`,
110
+ name: qualifiedName,
111
+ fileId,
112
+ kind: "method",
113
+ line: node.startPosition.row + 1,
114
+ isExported: false, // methods aren't directly exported
115
+ });
116
+ didPush = true;
117
+ }
118
+ break;
119
+ }
120
+ case "arrow_function":
121
+ case "function_expression":
122
+ case "generator_function": {
123
+ // Only create a named scope if assigned to a variable
124
+ const assignedName = getAssignedName(node);
125
+ if (assignedName) {
126
+ const exported = isExported(node) || exportedNames.has(assignedName);
127
+ pushScope({
128
+ id: `${fileId}::${assignedName}`,
129
+ name: assignedName,
130
+ fileId,
131
+ kind: node.type === "arrow_function" ? "arrow" : "function",
132
+ line: node.startPosition.row + 1,
133
+ isExported: exported,
134
+ });
135
+ didPush = true;
136
+ }
137
+ // Anonymous arrow fns: their calls belong to the enclosing scope
138
+ break;
139
+ }
140
+ case "call_expression": {
141
+ const fnNode = node.childForFieldName("function");
142
+ if (fnNode) {
143
+ const site = parseCallExpression(fnNode, node, currentScope());
144
+ if (site)
145
+ callSites.push(site);
146
+ }
147
+ break;
148
+ }
149
+ case "new_expression": {
150
+ const ctorNode = node.childForFieldName("constructor");
151
+ if (ctorNode) {
152
+ const name = ctorNode.text;
153
+ // Skip complex expressions
154
+ if (isSimpleIdentifier(name) || isDottedAccess(name)) {
155
+ const { objectName, propertyName } = parseDottedName(name);
156
+ callSites.push({
157
+ calledName: name,
158
+ objectName,
159
+ propertyName,
160
+ enclosingScope: currentScope(),
161
+ line: node.startPosition.row + 1,
162
+ isJsx: false,
163
+ isNew: true,
164
+ });
165
+ }
166
+ }
167
+ break;
168
+ }
169
+ case "jsx_self_closing_element":
170
+ case "jsx_opening_element": {
171
+ const nameNode = node.childForFieldName("name");
172
+ if (nameNode) {
173
+ const name = nameNode.text;
174
+ // Only user components (uppercase first letter), not HTML elements
175
+ if (name[0] && name[0] === name[0].toUpperCase() && /^[A-Z]/.test(name)) {
176
+ const { objectName, propertyName } = parseDottedName(name);
177
+ callSites.push({
178
+ calledName: name,
179
+ objectName,
180
+ propertyName,
181
+ enclosingScope: currentScope(),
182
+ line: node.startPosition.row + 1,
183
+ isJsx: true,
184
+ isNew: false,
185
+ });
186
+ }
187
+ }
188
+ break;
189
+ }
190
+ }
191
+ // Walk children
192
+ for (let i = 0; i < node.childCount; i++) {
193
+ const child = node.child(i);
194
+ if (child)
195
+ walk(child);
196
+ }
197
+ if (didPush)
198
+ popScope();
199
+ }
200
+ walk(rootNode);
201
+ return { fileId, scopes, callSites };
202
+ }
203
+ function parseCallExpression(fnNode, callNode, enclosingScope) {
204
+ const text = fnNode.text;
205
+ // Skip template literals, computed access, etc.
206
+ if (text.includes("`") || text.includes("["))
207
+ return null;
208
+ if (fnNode.type === "identifier") {
209
+ return {
210
+ calledName: text,
211
+ enclosingScope,
212
+ line: callNode.startPosition.row + 1,
213
+ isJsx: false,
214
+ isNew: false,
215
+ };
216
+ }
217
+ if (fnNode.type === "member_expression") {
218
+ const obj = fnNode.childForFieldName("object");
219
+ const prop = fnNode.childForFieldName("property");
220
+ if (obj && prop) {
221
+ // Only handle simple dotted access: foo.bar(), not foo().bar()
222
+ if (obj.type === "identifier" || obj.type === "member_expression") {
223
+ return {
224
+ calledName: text,
225
+ objectName: obj.text,
226
+ propertyName: prop.text,
227
+ enclosingScope,
228
+ line: callNode.startPosition.row + 1,
229
+ isJsx: false,
230
+ isNew: false,
231
+ };
232
+ }
233
+ }
234
+ }
235
+ return null;
236
+ }
237
+ function isSimpleIdentifier(name) {
238
+ return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
239
+ }
240
+ function isDottedAccess(name) {
241
+ return /^[a-zA-Z_$][a-zA-Z0-9_$.]*$/.test(name);
242
+ }
243
+ function parseDottedName(name) {
244
+ const dotIndex = name.indexOf(".");
245
+ if (dotIndex === -1)
246
+ return {};
247
+ return {
248
+ objectName: name.slice(0, dotIndex),
249
+ propertyName: name.slice(dotIndex + 1),
250
+ };
251
+ }
252
+ //# sourceMappingURL=call-graph.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"call-graph.js","sourceRoot":"","sources":["../src/call-graph.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { CallEdge, FileCallData, ImportInfo, ExportInfo } from "./types.js";
2
+ interface FileInfo {
3
+ fileId: string;
4
+ imports: ImportInfo[];
5
+ exports: ExportInfo[];
6
+ }
7
+ /**
8
+ * Resolve raw call sites into cross-file CallEdge entries.
9
+ *
10
+ * For each call site in each file:
11
+ * 1. Check if the called name matches an imported symbol
12
+ * 2. If so, create a CallEdge from caller scope → target file's export
13
+ *
14
+ * Handles: named imports, default imports, namespace imports (X.foo()).
15
+ */
16
+ export declare function resolveCallEdges(callDataByFile: Map<string, FileCallData>, fileInfoMap: Map<string, FileInfo>): CallEdge[];
17
+ export {};
18
+ //# sourceMappingURL=call-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"call-resolver.d.ts","sourceRoot":"","sources":["../src/call-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,QAAQ,EACR,YAAY,EACZ,UAAU,EACV,UAAU,EAEX,MAAM,YAAY,CAAC;AAEpB,UAAU,QAAQ;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,OAAO,EAAE,UAAU,EAAE,CAAC;CACvB;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,EACzC,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,GACjC,QAAQ,EAAE,CAuCZ"}
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Resolve raw call sites into cross-file CallEdge entries.
3
+ *
4
+ * For each call site in each file:
5
+ * 1. Check if the called name matches an imported symbol
6
+ * 2. If so, create a CallEdge from caller scope → target file's export
7
+ *
8
+ * Handles: named imports, default imports, namespace imports (X.foo()).
9
+ */
10
+ export function resolveCallEdges(callDataByFile, fileInfoMap) {
11
+ const edges = [];
12
+ // Build export lookup: targetFileId → Map<exportName, ExportInfo>
13
+ const exportLookup = new Map();
14
+ for (const [fileId, info] of fileInfoMap) {
15
+ const exMap = new Map();
16
+ for (const exp of info.exports) {
17
+ exMap.set(exp.name, exp);
18
+ }
19
+ exportLookup.set(fileId, exMap);
20
+ }
21
+ for (const [fileId, callData] of callDataByFile) {
22
+ const fileInfo = fileInfoMap.get(fileId);
23
+ if (!fileInfo)
24
+ continue;
25
+ // Build import resolution map for this file:
26
+ // localName → { targetFileId, exportName }
27
+ const importMap = buildImportMap(fileInfo.imports);
28
+ for (const site of callData.callSites) {
29
+ const resolved = resolveCallSite(site, importMap, exportLookup);
30
+ if (resolved) {
31
+ edges.push({
32
+ caller: site.enclosingScope,
33
+ callee: resolved.targetScopeId,
34
+ callExpression: site.isJsx
35
+ ? `<${site.calledName} />`
36
+ : site.isNew
37
+ ? `new ${site.calledName}()`
38
+ : `${site.calledName}()`,
39
+ line: site.line,
40
+ });
41
+ }
42
+ }
43
+ }
44
+ return edges;
45
+ }
46
+ function buildImportMap(imports) {
47
+ const map = new Map();
48
+ for (const imp of imports) {
49
+ if (!imp.resolvedPath || imp.isExternal)
50
+ continue;
51
+ if (imp.isNamespace) {
52
+ // import * as Utils from './utils'
53
+ // The namespace identifier maps to the whole module
54
+ for (const name of imp.names) {
55
+ map.set(name, {
56
+ targetFileId: imp.resolvedPath,
57
+ exportName: "*",
58
+ isNamespace: true,
59
+ });
60
+ }
61
+ }
62
+ else if (imp.isDefault) {
63
+ // import Foo from './foo'
64
+ for (const name of imp.names) {
65
+ map.set(name, {
66
+ targetFileId: imp.resolvedPath,
67
+ exportName: "default",
68
+ isNamespace: false,
69
+ });
70
+ }
71
+ }
72
+ else {
73
+ // import { foo, bar } from './module'
74
+ for (const name of imp.names) {
75
+ map.set(name, {
76
+ targetFileId: imp.resolvedPath,
77
+ exportName: name,
78
+ isNamespace: false,
79
+ });
80
+ }
81
+ }
82
+ }
83
+ return map;
84
+ }
85
+ function resolveCallSite(site, importMap, exportLookup) {
86
+ // Case 1: Direct call to imported name — foo()
87
+ if (!site.objectName) {
88
+ const mapping = importMap.get(site.calledName);
89
+ if (!mapping)
90
+ return null;
91
+ // Verify the target file has this export
92
+ const targetExports = exportLookup.get(mapping.targetFileId);
93
+ if (!targetExports)
94
+ return null;
95
+ if (mapping.exportName === "default") {
96
+ // Default import — find the default export or a named export matching the import name
97
+ const defaultExport = targetExports.get("default");
98
+ if (defaultExport) {
99
+ return { targetScopeId: `${mapping.targetFileId}::${defaultExport.name}` };
100
+ }
101
+ // Fallback: look for an export matching the called name
102
+ const named = targetExports.get(site.calledName);
103
+ if (named) {
104
+ return { targetScopeId: `${mapping.targetFileId}::${site.calledName}` };
105
+ }
106
+ return null;
107
+ }
108
+ const exp = targetExports.get(mapping.exportName);
109
+ if (exp) {
110
+ return { targetScopeId: `${mapping.targetFileId}::${exp.name}` };
111
+ }
112
+ return null;
113
+ }
114
+ // Case 2: Namespace call — Utils.foo()
115
+ if (site.objectName && site.propertyName) {
116
+ const mapping = importMap.get(site.objectName);
117
+ if (!mapping || !mapping.isNamespace)
118
+ return null;
119
+ const targetExports = exportLookup.get(mapping.targetFileId);
120
+ if (!targetExports)
121
+ return null;
122
+ const exp = targetExports.get(site.propertyName);
123
+ if (exp) {
124
+ return { targetScopeId: `${mapping.targetFileId}::${exp.name}` };
125
+ }
126
+ return null;
127
+ }
128
+ return null;
129
+ }
130
+ //# sourceMappingURL=call-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"call-resolver.js","sourceRoot":"","sources":["../src/call-resolver.ts"],"names":[],"mappings":"AAcA;;;;;;;;GAQG;AACH,MAAM,UAAU,gBAAgB,CAC9B,cAAyC,EACzC,WAAkC;IAElC,MAAM,KAAK,GAAe,EAAE,CAAC;IAE7B,kEAAkE;IAClE,MAAM,YAAY,GAAG,IAAI,GAAG,EAAmC,CAAC;IAChE,KAAK,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,WAAW,EAAE,CAAC;QACzC,MAAM,KAAK,GAAG,IAAI,GAAG,EAAsB,CAAC;QAC5C,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAC/B,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAC3B,CAAC;QACD,YAAY,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAClC,CAAC;IAED,KAAK,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,cAAc,EAAE,CAAC;QAChD,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACzC,IAAI,CAAC,QAAQ;YAAE,SAAS;QAExB,6CAA6C;QAC7C,2CAA2C;QAC3C,MAAM,SAAS,GAAG,cAAc,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAEnD,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,SAAS,EAAE,CAAC;YACtC,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;YAChE,IAAI,QAAQ,EAAE,CAAC;gBACb,KAAK,CAAC,IAAI,CAAC;oBACT,MAAM,EAAE,IAAI,CAAC,cAAc;oBAC3B,MAAM,EAAE,QAAQ,CAAC,aAAa;oBAC9B,cAAc,EAAE,IAAI,CAAC,KAAK;wBACxB,CAAC,CAAC,IAAI,IAAI,CAAC,UAAU,KAAK;wBAC1B,CAAC,CAAC,IAAI,CAAC,KAAK;4BACV,CAAC,CAAC,OAAO,IAAI,CAAC,UAAU,IAAI;4BAC5B,CAAC,CAAC,GAAG,IAAI,CAAC,UAAU,IAAI;oBAC5B,IAAI,EAAE,IAAI,CAAC,IAAI;iBAChB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAQD,SAAS,cAAc,CACrB,OAAqB;IAErB,MAAM,GAAG,GAAG,IAAI,GAAG,EAAyB,CAAC;IAE7C,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;QAC1B,IAAI,CAAC,GAAG,CAAC,YAAY,IAAI,GAAG,CAAC,UAAU;YAAE,SAAS;QAElD,IAAI,GAAG,CAAC,WAAW,EAAE,CAAC;YACpB,mCAAmC;YACnC,oDAAoD;YACpD,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC;gBAC7B,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE;oBACZ,YAAY,EAAE,GAAG,CAAC,YAAY;oBAC9B,UAAU,EAAE,GAAG;oBACf,WAAW,EAAE,IAAI;iBAClB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;aAAM,IAAI,GAAG,CAAC,SAAS,EAAE,CAAC;YACzB,0BAA0B;YAC1B,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC;gBAC7B,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE;oBACZ,YAAY,EAAE,GAAG,CAAC,YAAY;oBAC9B,UAAU,EAAE,SAAS;oBACrB,WAAW,EAAE,KAAK;iBACnB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;aAAM,CAAC;YACN,sCAAsC;YACtC,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC;gBAC7B,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE;oBACZ,YAAY,EAAE,GAAG,CAAC,YAAY;oBAC9B,UAAU,EAAE,IAAI;oBAChB,WAAW,EAAE,KAAK;iBACnB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,eAAe,CACtB,IAAiB,EACjB,SAAqC,EACrC,YAAkD;IAElD,+CAA+C;IAC/C,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;QACrB,MAAM,OAAO,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC/C,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAC;QAE1B,yCAAyC;QACzC,MAAM,aAAa,GAAG,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QAC7D,IAAI,CAAC,aAAa;YAAE,OAAO,IAAI,CAAC;QAEhC,IAAI,OAAO,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;YACrC,sFAAsF;YACtF,MAAM,aAAa,GAAG,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YACnD,IAAI,aAAa,EAAE,CAAC;gBAClB,OAAO,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC,YAAY,KAAK,aAAa,CAAC,IAAI,EAAE,EAAE,CAAC;YAC7E,CAAC;YACD,wDAAwD;YACxD,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YACjD,IAAI,KAAK,EAAE,CAAC;gBACV,OAAO,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC,YAAY,KAAK,IAAI,CAAC,UAAU,EAAE,EAAE,CAAC;YAC1E,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,GAAG,GAAG,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QAClD,IAAI,GAAG,EAAE,CAAC;YACR,OAAO,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC,YAAY,KAAK,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC;QACnE,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,uCAAuC;IACvC,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;QACzC,MAAM,OAAO,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC/C,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,WAAW;YAAE,OAAO,IAAI,CAAC;QAElD,MAAM,aAAa,GAAG,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QAC7D,IAAI,CAAC,aAAa;YAAE,OAAO,IAAI,CAAC;QAEhC,MAAM,GAAG,GAAG,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACjD,IAAI,GAAG,EAAE,CAAC;YACR,OAAO,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC,YAAY,KAAK,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC;QACnE,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function scanFiles(rootDir: string): Promise<string[]>;
2
+ //# sourceMappingURL=file-scanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-scanner.d.ts","sourceRoot":"","sources":["../src/file-scanner.ts"],"names":[],"mappings":"AA0BA,wBAAsB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAoBlE"}
@@ -0,0 +1,42 @@
1
+ import { globby } from "globby";
2
+ import ignore from "ignore";
3
+ import { readFileSync, existsSync } from "node:fs";
4
+ import { join } from "node:path";
5
+ const SOURCE_PATTERNS = [
6
+ "**/*.ts",
7
+ "**/*.tsx",
8
+ "**/*.js",
9
+ "**/*.jsx",
10
+ ];
11
+ const DEFAULT_IGNORE = [
12
+ "node_modules",
13
+ "dist",
14
+ "build",
15
+ ".next",
16
+ ".nuxt",
17
+ ".turbo",
18
+ "coverage",
19
+ "__tests__",
20
+ "*.test.*",
21
+ "*.spec.*",
22
+ "*.d.ts",
23
+ ];
24
+ export async function scanFiles(rootDir) {
25
+ // Read .gitignore if it exists
26
+ const gitignorePath = join(rootDir, ".gitignore");
27
+ const ig = ignore();
28
+ ig.add(DEFAULT_IGNORE);
29
+ if (existsSync(gitignorePath)) {
30
+ const gitignoreContent = readFileSync(gitignorePath, "utf-8");
31
+ ig.add(gitignoreContent);
32
+ }
33
+ const files = await globby(SOURCE_PATTERNS, {
34
+ cwd: rootDir,
35
+ absolute: false,
36
+ ignore: DEFAULT_IGNORE,
37
+ gitignore: true,
38
+ });
39
+ // Additional filtering through ignore
40
+ return ig.filter(files).sort();
41
+ }
42
+ //# sourceMappingURL=file-scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-scanner.js","sourceRoot":"","sources":["../src/file-scanner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,eAAe,GAAG;IACtB,SAAS;IACT,UAAU;IACV,SAAS;IACT,UAAU;CACX,CAAC;AAEF,MAAM,cAAc,GAAG;IACrB,cAAc;IACd,MAAM;IACN,OAAO;IACP,OAAO;IACP,OAAO;IACP,QAAQ;IACR,UAAU;IACV,WAAW;IACX,UAAU;IACV,UAAU;IACV,QAAQ;CACT,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAe;IAC7C,+BAA+B;IAC/B,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IAClD,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC;IACpB,EAAE,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAEvB,IAAI,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QAC9B,MAAM,gBAAgB,GAAG,YAAY,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;QAC9D,EAAE,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAC3B,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,eAAe,EAAE;QAC1C,GAAG,EAAE,OAAO;QACZ,QAAQ,EAAE,KAAK;QACf,MAAM,EAAE,cAAc;QACtB,SAAS,EAAE,IAAI;KAChB,CAAC,CAAC;IAEH,sCAAsC;IACtC,OAAO,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;AACjC,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { CallEdge, FunctionScope, FunctionBlastRadiusResult } from "./types.js";
2
+ /**
3
+ * Compute the function-level blast radius for a given export.
4
+ * BFS through reverse call edges: who calls this function, who calls those callers, etc.
5
+ */
6
+ export declare function computeFunctionBlastRadius(fileId: string, functionName: string, scopes: FunctionScope[], callEdges: CallEdge[]): FunctionBlastRadiusResult;
7
+ //# sourceMappingURL=function-blast-radius.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"function-blast-radius.d.ts","sourceRoot":"","sources":["../src/function-blast-radius.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,QAAQ,EACR,aAAa,EACb,yBAAyB,EAE1B,MAAM,YAAY,CAAC;AAEpB;;;GAGG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,aAAa,EAAE,EACvB,SAAS,EAAE,QAAQ,EAAE,GACpB,yBAAyB,CAwD3B"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Compute the function-level blast radius for a given export.
3
+ * BFS through reverse call edges: who calls this function, who calls those callers, etc.
4
+ */
5
+ export function computeFunctionBlastRadius(fileId, functionName, scopes, callEdges) {
6
+ const targetId = `${fileId}::${functionName}`;
7
+ // Build reverse adjacency: callee → callers
8
+ const reverseEdges = new Map();
9
+ for (const edge of callEdges) {
10
+ const callers = reverseEdges.get(edge.callee) ?? [];
11
+ callers.push(edge.caller);
12
+ reverseEdges.set(edge.callee, callers);
13
+ }
14
+ // Build scope lookup for result enrichment
15
+ const scopeMap = new Map();
16
+ for (const scope of scopes) {
17
+ scopeMap.set(scope.id, scope);
18
+ }
19
+ // BFS
20
+ const visited = new Set();
21
+ const queue = [{ scopeId: targetId, depth: 0 }];
22
+ const affected = [];
23
+ visited.add(targetId);
24
+ while (queue.length > 0) {
25
+ const current = queue.shift();
26
+ const callers = reverseEdges.get(current.scopeId) ?? [];
27
+ for (const callerId of callers) {
28
+ if (visited.has(callerId))
29
+ continue;
30
+ visited.add(callerId);
31
+ const scope = scopeMap.get(callerId);
32
+ const parts = callerId.split("::");
33
+ const callerFileId = parts.slice(0, -1).join("::");
34
+ const callerFnName = parts[parts.length - 1];
35
+ affected.push({
36
+ functionId: callerId,
37
+ fileId: scope?.fileId ?? callerFileId,
38
+ functionName: scope?.name ?? callerFnName,
39
+ depth: current.depth + 1,
40
+ });
41
+ queue.push({ scopeId: callerId, depth: current.depth + 1 });
42
+ }
43
+ }
44
+ // Sort by depth then alphabetically
45
+ affected.sort((a, b) => a.depth - b.depth || a.functionId.localeCompare(b.functionId));
46
+ return {
47
+ sourceFunction: targetId,
48
+ sourceFile: fileId,
49
+ affected,
50
+ };
51
+ }
52
+ //# sourceMappingURL=function-blast-radius.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"function-blast-radius.js","sourceRoot":"","sources":["../src/function-blast-radius.ts"],"names":[],"mappings":"AAOA;;;GAGG;AACH,MAAM,UAAU,0BAA0B,CACxC,MAAc,EACd,YAAoB,EACpB,MAAuB,EACvB,SAAqB;IAErB,MAAM,QAAQ,GAAG,GAAG,MAAM,KAAK,YAAY,EAAE,CAAC;IAE9C,4CAA4C;IAC5C,MAAM,YAAY,GAAG,IAAI,GAAG,EAAoB,CAAC;IACjD,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;QAC7B,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QACpD,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC1B,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,CAAC;IAED,2CAA2C;IAC3C,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAyB,CAAC;IAClD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IAChC,CAAC;IAED,MAAM;IACN,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,MAAM,KAAK,GAAyC,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;IACtF,MAAM,QAAQ,GAA+B,EAAE,CAAC;IAEhD,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAEtB,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,EAAG,CAAC;QAC/B,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;QAExD,KAAK,MAAM,QAAQ,IAAI,OAAO,EAAE,CAAC;YAC/B,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;gBAAE,SAAS;YACpC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YAEtB,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YACrC,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACnC,MAAM,YAAY,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACnD,MAAM,YAAY,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YAE7C,QAAQ,CAAC,IAAI,CAAC;gBACZ,UAAU,EAAE,QAAQ;gBACpB,MAAM,EAAE,KAAK,EAAE,MAAM,IAAI,YAAY;gBACrC,YAAY,EAAE,KAAK,EAAE,IAAI,IAAI,YAAY;gBACzC,KAAK,EAAE,OAAO,CAAC,KAAK,GAAG,CAAC;aACzB,CAAC,CAAC;YAEH,KAAK,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC;IAED,oCAAoC;IACpC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;IAEvF,OAAO;QACL,cAAc,EAAE,QAAQ;QACxB,UAAU,EAAE,MAAM;QAClB,QAAQ;KACT,CAAC;AACJ,CAAC"}
package/dist/git.d.ts ADDED
@@ -0,0 +1,27 @@
1
+ export interface GitInfo {
2
+ isGitRepo: boolean;
3
+ currentBranch: string | null;
4
+ branches: string[];
5
+ recentCommits: {
6
+ hash: string;
7
+ subject: string;
8
+ author: string;
9
+ date: string;
10
+ }[];
11
+ }
12
+ export interface GitDiff {
13
+ files: {
14
+ path: string;
15
+ status: "added" | "modified" | "deleted" | "renamed";
16
+ }[];
17
+ fromRef: string;
18
+ toRef: string;
19
+ }
20
+ export declare function isGitRepo(cwd: string): boolean;
21
+ export declare function getGitInfo(cwd: string): GitInfo;
22
+ export declare function getGitDiff(cwd: string, fromRef: string, toRef: string): GitDiff;
23
+ export declare function checkoutBranch(cwd: string, branch: string): {
24
+ success: boolean;
25
+ error?: string;
26
+ };
27
+ //# sourceMappingURL=git.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,OAAO;IACtB,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,aAAa,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAClF;AAED,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,CAAA;KAAE,EAAE,CAAC;IAChF,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAUD,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAE9C;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAwB/C;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAgB/E;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAiBhG"}
package/dist/git.js ADDED
@@ -0,0 +1,69 @@
1
+ import { execSync } from "node:child_process";
2
+ import { existsSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ function gitExec(cmd, cwd) {
5
+ try {
6
+ return execSync(cmd, { cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
7
+ }
8
+ catch {
9
+ return "";
10
+ }
11
+ }
12
+ export function isGitRepo(cwd) {
13
+ return existsSync(join(cwd, ".git"));
14
+ }
15
+ export function getGitInfo(cwd) {
16
+ if (!isGitRepo(cwd)) {
17
+ return { isGitRepo: false, currentBranch: null, branches: [], recentCommits: [] };
18
+ }
19
+ const currentBranch = gitExec("git rev-parse --abbrev-ref HEAD", cwd) || null;
20
+ const branchOutput = gitExec("git branch --format='%(refname:short)'", cwd);
21
+ const branches = branchOutput
22
+ ? branchOutput.split("\n").map((b) => b.trim().replace(/^'|'$/g, "")).filter(Boolean)
23
+ : [];
24
+ const logOutput = gitExec('git log --oneline --format="%H|%s|%an|%aI" -20', cwd);
25
+ const recentCommits = logOutput
26
+ ? logOutput.split("\n").filter(Boolean).map((line) => {
27
+ const [hash, subject, author, date] = line.split("|");
28
+ return { hash: hash.slice(0, 8), subject, author, date };
29
+ })
30
+ : [];
31
+ return { isGitRepo: true, currentBranch, branches, recentCommits };
32
+ }
33
+ export function getGitDiff(cwd, fromRef, toRef) {
34
+ const output = gitExec(`git diff --name-status ${fromRef}..${toRef}`, cwd);
35
+ const files = output
36
+ ? output.split("\n").filter(Boolean).map((line) => {
37
+ const [statusChar, ...pathParts] = line.split("\t");
38
+ const path = pathParts.join("\t"); // handle paths with tabs (rare)
39
+ let status = "modified";
40
+ if (statusChar.startsWith("A"))
41
+ status = "added";
42
+ else if (statusChar.startsWith("D"))
43
+ status = "deleted";
44
+ else if (statusChar.startsWith("R"))
45
+ status = "renamed";
46
+ return { path, status };
47
+ })
48
+ : [];
49
+ return { files, fromRef, toRef };
50
+ }
51
+ export function checkoutBranch(cwd, branch) {
52
+ // Safety check: refuse if there are uncommitted changes
53
+ const status = gitExec("git status --porcelain", cwd);
54
+ if (status) {
55
+ return { success: false, error: "Uncommitted changes detected. Commit or stash before switching branches." };
56
+ }
57
+ try {
58
+ execSync(`git checkout ${branch}`, {
59
+ cwd,
60
+ encoding: "utf-8",
61
+ stdio: ["pipe", "pipe", "pipe"],
62
+ });
63
+ return { success: true };
64
+ }
65
+ catch (err) {
66
+ return { success: false, error: err.stderr?.toString().slice(0, 200) || "Failed to checkout branch" };
67
+ }
68
+ }
69
+ //# sourceMappingURL=git.js.map