@codeflow-map/core 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.
@@ -0,0 +1,351 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.typescriptAnalyzer = void 0;
4
+ const extractorUtils_1 = require("../extractorUtils");
5
+ exports.typescriptAnalyzer = {
6
+ functionQuery: `
7
+ (function_declaration
8
+ name: (identifier) @fn.name
9
+ parameters: (formal_parameters) @fn.params
10
+ return_type: (type_annotation)? @fn.return_type) @fn.decl
11
+
12
+ (lexical_declaration
13
+ (variable_declarator
14
+ name: (identifier) @fn.name
15
+ value: [(arrow_function) (function_expression)] @fn.decl))
16
+
17
+ (export_statement
18
+ declaration: (lexical_declaration
19
+ (variable_declarator
20
+ name: (identifier) @fn.name
21
+ value: [(arrow_function) (function_expression)] @fn.decl)))
22
+
23
+ (method_definition
24
+ name: (property_identifier) @fn.name
25
+ parameters: (formal_parameters) @fn.params
26
+ return_type: (type_annotation)? @fn.return_type) @fn.decl
27
+
28
+ (call_expression
29
+ function: (identifier) @hook.name
30
+ arguments: (arguments
31
+ [(arrow_function) (function_expression)] @fn.decl)
32
+ (#match? @hook.name "^use"))
33
+
34
+ (generator_function_declaration
35
+ name: (identifier) @fn.name
36
+ parameters: (formal_parameters) @fn.params
37
+ return_type: (type_annotation)? @fn.return_type) @fn.decl
38
+
39
+ (lexical_declaration
40
+ (variable_declarator
41
+ name: (identifier) @fn.name
42
+ value: (generator_function) @fn.decl))
43
+
44
+ (export_statement
45
+ declaration: (lexical_declaration
46
+ (variable_declarator
47
+ name: (identifier) @fn.name
48
+ value: (generator_function) @fn.decl)))
49
+
50
+ (function_expression
51
+ name: (identifier) @fn.name
52
+ parameters: (formal_parameters) @fn.params) @fn.decl
53
+
54
+ (lexical_declaration
55
+ (variable_declarator
56
+ name: (identifier) @fn.name
57
+ value: (call_expression
58
+ function: [(parenthesized_expression
59
+ [(function_expression)(arrow_function)] @fn.decl)
60
+ (arrow_function) @fn.decl
61
+ (function_expression) @fn.decl])))
62
+
63
+ (expression_statement
64
+ (call_expression
65
+ function: (parenthesized_expression
66
+ [(function_expression)(arrow_function)] @fn.decl)))
67
+
68
+ (assignment_expression
69
+ left: (member_expression
70
+ object: (identifier) @worker.obj
71
+ property: (property_identifier) @worker.prop)
72
+ right: [(arrow_function) (function_expression)] @worker.fn
73
+ (#match? @worker.obj "^self$")
74
+ (#match? @worker.prop "^onmessage$"))
75
+
76
+ (call_expression
77
+ function: (member_expression
78
+ object: (identifier) @worker.obj
79
+ property: (property_identifier) @worker.method)
80
+ arguments: (arguments
81
+ (string) @worker.event
82
+ [(arrow_function) (function_expression)] @worker.fn)
83
+ (#match? @worker.obj "^self$")
84
+ (#match? @worker.method "^addEventListener$")
85
+ (#match? @worker.event "message"))
86
+
87
+ (lexical_declaration
88
+ (variable_declarator
89
+ name: (identifier) @fn.name
90
+ value: (call_expression
91
+ function: (identifier) @wrapper.name
92
+ arguments: (arguments
93
+ [(arrow_function) (function_expression)] @fn.decl)
94
+ (#match? @wrapper.name "^(forwardRef|memo)$"))))
95
+
96
+ (lexical_declaration
97
+ (variable_declarator
98
+ name: (identifier) @fn.name
99
+ value: (call_expression
100
+ function: (identifier) @wrapper.outer
101
+ arguments: (arguments
102
+ (call_expression
103
+ function: (identifier) @wrapper.inner
104
+ arguments: (arguments
105
+ [(arrow_function) (function_expression)] @fn.decl)))
106
+ (#match? @wrapper.outer "^(memo|forwardRef)$"))))
107
+
108
+ (export_statement
109
+ declaration: (lexical_declaration
110
+ (variable_declarator
111
+ name: (identifier) @fn.name
112
+ value: (call_expression
113
+ function: (identifier) @wrapper.name
114
+ arguments: (arguments
115
+ [(arrow_function) (function_expression)] @fn.decl)
116
+ (#match? @wrapper.name "^(forwardRef|memo)$")))))
117
+
118
+ (export_statement
119
+ declaration: (lexical_declaration
120
+ (variable_declarator
121
+ name: (identifier) @fn.name
122
+ value: (call_expression
123
+ function: (identifier) @wrapper.outer
124
+ arguments: (arguments
125
+ (call_expression
126
+ function: (identifier) @wrapper.inner
127
+ arguments: (arguments
128
+ [(arrow_function) (function_expression)] @fn.decl)))
129
+ (#match? @wrapper.outer "^(memo|forwardRef)$")))))
130
+
131
+ (public_field_definition
132
+ name: (property_identifier) @fn.name
133
+ value: [(arrow_function)(function_expression)] @fn.decl)
134
+ `,
135
+ callQuery: `
136
+ (call_expression
137
+ function: [(identifier)(member_expression)] @call.name) @call.expr
138
+
139
+ (new_expression
140
+ constructor: (identifier) @call.name) @call.expr
141
+
142
+ (call_expression
143
+ function: (member_expression
144
+ property: (property_identifier) @call.name
145
+ (#match? @call.name "^(then|catch|finally)$"))) @call.expr
146
+ `,
147
+ extractFunction(match, filePath, languageId) {
148
+ const declCapture = match.captures.find((c) => c.name === 'fn.decl');
149
+ if (!declCapture) {
150
+ const workerFn = match.captures.find((c) => c.name === 'worker.fn');
151
+ if (workerFn) {
152
+ const workerProp = match.captures.find((c) => c.name === 'worker.prop');
153
+ const node = workerFn.node;
154
+ const rawName = workerProp ? 'onmessage_handler' : `message_handler_${node.startPosition.row}`;
155
+ const paramsList = (0, extractorUtils_1.getParamsFromFunctionNode)(node, true);
156
+ return {
157
+ id: `${filePath}::${rawName}::${node.startPosition.row}`,
158
+ name: rawName,
159
+ filePath,
160
+ startLine: node.startPosition.row,
161
+ endLine: node.endPosition.row,
162
+ params: paramsList,
163
+ returnType: null,
164
+ isAsync: (0, extractorUtils_1.checkIsAsync)(node),
165
+ isExported: false,
166
+ isEntryPoint: true,
167
+ language: languageId,
168
+ kind: 'function'
169
+ };
170
+ }
171
+ return null;
172
+ }
173
+ const nameCapture = match.captures.find((c) => c.name === 'fn.name');
174
+ const hookNameCapture = match.captures.find((c) => c.name === 'hook.name');
175
+ // For wrapper patterns (memo, forwardRef), only capture the first argument
176
+ const wrapperCapture = match.captures.find((c) => c.name === 'wrapper.name' || c.name === 'wrapper.outer');
177
+ if (wrapperCapture) {
178
+ const argsParent = declCapture.node.parent;
179
+ if (argsParent?.type === 'arguments') {
180
+ const firstArg = argsParent.namedChildren[0];
181
+ if (firstArg.id !== declCapture.node.id) {
182
+ return null;
183
+ }
184
+ }
185
+ }
186
+ let rawName = "anonymous";
187
+ let kind = 'function';
188
+ let parentFunctionId;
189
+ if (nameCapture) {
190
+ rawName = nameCapture.node.text;
191
+ if (declCapture.node.type === 'function_expression' && nameCapture.node.parent?.id === declCapture.node.id) {
192
+ if (declCapture.node.parent?.type === 'variable_declarator') {
193
+ return null;
194
+ }
195
+ parentFunctionId = (0, extractorUtils_1.getEnclosingFunctionId)(declCapture.node, filePath) ?? undefined;
196
+ }
197
+ if (declCapture.node.type === 'method_definition') {
198
+ kind = 'method';
199
+ }
200
+ }
201
+ else if (hookNameCapture) {
202
+ const startLine = declCapture.node.startPosition.row;
203
+ rawName = `${hookNameCapture.node.text}_callback_${startLine}`;
204
+ kind = 'hook';
205
+ }
206
+ // Detect IIFE: fn.decl is inside a call_expression (the IIFE invocation)
207
+ // Skip IIFE detection for wrapper patterns (forwardRef, memo)
208
+ const declParent = declCapture.node.parent;
209
+ const isParenWrapped = declParent?.type === 'parenthesized_expression';
210
+ const callParent = isParenWrapped ? declParent.parent : declParent;
211
+ if (callParent?.type === 'call_expression' && !wrapperCapture) {
212
+ if (rawName === 'anonymous') {
213
+ rawName = `iife_${declCapture.node.startPosition.row}`;
214
+ }
215
+ kind = 'iife';
216
+ }
217
+ else if (rawName === "anonymous" && !hookNameCapture) {
218
+ return null;
219
+ }
220
+ // Prefix with enclosing scope chain (parent functions, classes, and object literals)
221
+ const scopeChain = (0, extractorUtils_1.getEnclosingScopeChain)(declCapture.node, extractorUtils_1.JS_FN_SCOPE_TYPES, extractorUtils_1.JS_CLASS_SCOPE_TYPES);
222
+ if (scopeChain.length > 0) {
223
+ rawName = `${scopeChain.map(s => s.name).join('.')}.${rawName}`;
224
+ if (kind === 'method' && scopeChain[scopeChain.length - 1].type === 'object') {
225
+ // method_definition inside object literal is still a method
226
+ }
227
+ }
228
+ // For wrapper patterns, use the outermost wrapper call_expression range
229
+ let rangeNode = declCapture.node;
230
+ if (wrapperCapture) {
231
+ let target = declCapture.node.parent;
232
+ while (target?.type === 'arguments') {
233
+ const callExpr = target.parent;
234
+ if (callExpr?.type !== 'call_expression')
235
+ break;
236
+ const fn = callExpr.childForFieldName('function');
237
+ const isKnownWrapper = fn && ((fn.type === 'identifier' && /^(forwardRef|memo)$/.test(fn.text)) ||
238
+ (fn.type === 'member_expression' && /^(forwardRef|memo)$/.test(fn.childForFieldName('property')?.text ?? '')));
239
+ if (!isKnownWrapper)
240
+ break;
241
+ rangeNode = callExpr;
242
+ target = callExpr.parent;
243
+ }
244
+ }
245
+ const paramsCapture = match.captures.find((c) => c.name === 'fn.params');
246
+ const returnCapture = match.captures.find((c) => c.name === 'fn.return_type');
247
+ const isExported = (0, extractorUtils_1.checkExportedByParentWalk)(declCapture.node);
248
+ const paramsList = paramsCapture
249
+ ? (0, extractorUtils_1.buildJsParams)(paramsCapture.node, true)
250
+ : (0, extractorUtils_1.getParamsFromFunctionNode)(declCapture.node, true);
251
+ let finalReturnType = returnCapture ? returnCapture.node.text.replace(/^:\s*/, '') : null;
252
+ if (!finalReturnType) {
253
+ const returnValues = (0, extractorUtils_1.getReturnValues)(declCapture.node, extractorUtils_1.JS_NESTED_FN_TYPES);
254
+ if (returnValues.length > 0) {
255
+ finalReturnType = returnValues.join(' | ');
256
+ }
257
+ }
258
+ return {
259
+ id: `${filePath}::${rawName}::${rangeNode.startPosition.row}`,
260
+ name: rawName,
261
+ filePath,
262
+ startLine: rangeNode.startPosition.row,
263
+ endLine: rangeNode.endPosition.row,
264
+ params: paramsList,
265
+ returnType: finalReturnType,
266
+ isAsync: (0, extractorUtils_1.checkIsAsync)(declCapture.node),
267
+ isExported,
268
+ isEntryPoint: false,
269
+ language: languageId,
270
+ kind,
271
+ ...(parentFunctionId ? { parentFunctionId } : {})
272
+ };
273
+ },
274
+ extractCall(match, filePath) {
275
+ const nameCapture = match.captures.find((c) => c.name === 'call.name');
276
+ const exprCapture = match.captures.find((c) => c.name === 'call.expr');
277
+ if (nameCapture && exprCapture) {
278
+ let calleeName = nameCapture.node.text;
279
+ if (nameCapture.node.type === 'member_expression') {
280
+ const prop = nameCapture.node.childForFieldName('property');
281
+ if (prop)
282
+ calleeName = prop.text;
283
+ }
284
+ // Unwrap .bind(): handleWorkerMessage.bind(this) → ref to handleWorkerMessage
285
+ if (calleeName === 'bind' && nameCapture.node.type === 'member_expression') {
286
+ const obj = nameCapture.node.childForFieldName('object');
287
+ if (obj?.type === 'identifier') {
288
+ return {
289
+ callerFilePath: filePath,
290
+ calleeName: obj.text,
291
+ line: exprCapture.node.startPosition.row,
292
+ callType: (0, extractorUtils_1.isInsidePromiseCombinator)(exprCapture.node) ? 'concurrent' : 'ref',
293
+ isRef: true
294
+ };
295
+ }
296
+ }
297
+ const callType = (0, extractorUtils_1.isInsidePromiseCombinator)(exprCapture.node) ? 'concurrent' : undefined;
298
+ const directCall = {
299
+ callerFilePath: filePath,
300
+ calleeName,
301
+ line: exprCapture.node.startPosition.row,
302
+ ...(callType ? { callType } : {})
303
+ };
304
+ // Extract function reference arguments from any call
305
+ const refCalls = [];
306
+ const callNode = exprCapture.node;
307
+ const argsNode = callNode.childForFieldName('arguments');
308
+ if (argsNode) {
309
+ for (const arg of argsNode.namedChildren) {
310
+ let refName = null;
311
+ if (arg.type === 'identifier') {
312
+ refName = arg.text;
313
+ }
314
+ else if (arg.type === 'member_expression') {
315
+ const obj = arg.childForFieldName('object');
316
+ const prop = arg.childForFieldName('property');
317
+ if (obj?.type === 'this' && prop) {
318
+ refName = prop.text;
319
+ }
320
+ }
321
+ else if (arg.type === 'call_expression') {
322
+ // Unwrap .bind(): handleWorkerMessage.bind(this) -> handleWorkerMessage
323
+ const fn = arg.childForFieldName('function');
324
+ if (fn?.type === 'member_expression') {
325
+ const prop = fn.childForFieldName('property');
326
+ const obj = fn.childForFieldName('object');
327
+ if (prop?.text === 'bind' && obj?.type === 'identifier') {
328
+ refName = obj.text;
329
+ }
330
+ }
331
+ }
332
+ if (refName) {
333
+ const refCallType = (0, extractorUtils_1.isInsidePromiseCombinator)(callNode) ? 'concurrent' : 'ref';
334
+ refCalls.push({
335
+ callerFilePath: filePath,
336
+ calleeName: refName,
337
+ line: callNode.startPosition.row,
338
+ callType: refCallType,
339
+ isRef: true
340
+ });
341
+ }
342
+ }
343
+ }
344
+ if (refCalls.length > 0) {
345
+ return [directCall, ...refCalls];
346
+ }
347
+ return directCall;
348
+ }
349
+ return null;
350
+ }
351
+ };
@@ -0,0 +1,4 @@
1
+ import Parser from 'web-tree-sitter';
2
+ import { SupportedLanguage } from '../types';
3
+ export declare function initTreeSitter(wasmDirectory: string): Promise<void>;
4
+ export declare function loadLanguage(lang: SupportedLanguage, wasmDirectory: string): Promise<Parser.Language>;
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.initTreeSitter = initTreeSitter;
40
+ exports.loadLanguage = loadLanguage;
41
+ const web_tree_sitter_1 = __importDefault(require("web-tree-sitter"));
42
+ const path = __importStar(require("path"));
43
+ let initialized = false;
44
+ const languageCache = new Map();
45
+ async function initTreeSitter(wasmDirectory) {
46
+ if (initialized)
47
+ return;
48
+ await web_tree_sitter_1.default.init({
49
+ locateFile: () => path.join(wasmDirectory, 'tree-sitter.wasm'),
50
+ });
51
+ initialized = true;
52
+ }
53
+ async function loadLanguage(lang, wasmDirectory) {
54
+ if (languageCache.has(lang)) {
55
+ return languageCache.get(lang);
56
+ }
57
+ const wasmName = lang === 'jsx' ? 'javascript' : lang;
58
+ const wasmPath = path.join(wasmDirectory, `tree-sitter-${wasmName}.wasm`);
59
+ const language = await web_tree_sitter_1.default.Language.load(wasmPath);
60
+ languageCache.set(lang, language);
61
+ return language;
62
+ }
@@ -0,0 +1,2 @@
1
+ import { FunctionNode, RawCall, CallEdge } from '../types';
2
+ export declare function buildCallGraph(nodes: FunctionNode[], rawCalls: RawCall[]): CallEdge[];
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildCallGraph = buildCallGraph;
4
+ function buildCallGraph(nodes, rawCalls) {
5
+ const byName = new Map();
6
+ const bySuffix = new Map();
7
+ for (const n of nodes) {
8
+ byName.set(n.name, n);
9
+ const dotIdx = n.name.lastIndexOf('.');
10
+ if (dotIdx >= 0) {
11
+ const suffix = n.name.substring(dotIdx + 1);
12
+ if (!bySuffix.has(suffix))
13
+ bySuffix.set(suffix, []);
14
+ bySuffix.get(suffix).push(n);
15
+ }
16
+ }
17
+ const edges = [];
18
+ for (const call of rawCalls) {
19
+ let callee = byName.get(call.calleeName);
20
+ // Find the innermost function whose range contains the call line,
21
+ // excluding the callee itself to avoid self-loops (e.g. go func(){...}())
22
+ const callerCandidates = nodes.filter(n => n.filePath === call.callerFilePath &&
23
+ call.line >= n.startLine &&
24
+ call.line <= n.endLine &&
25
+ n !== callee);
26
+ const caller = callerCandidates.length > 0
27
+ ? callerCandidates.reduce((best, n) => (n.endLine - n.startLine) < (best.endLine - best.startLine) ? n : best)
28
+ : undefined;
29
+ if (!caller)
30
+ continue;
31
+ if (!callee) {
32
+ const candidates = bySuffix.get(call.calleeName);
33
+ if (candidates && candidates.length === 1) {
34
+ const candidate = candidates[0];
35
+ // Suffix matching requires shared receiver prefix between caller and callee
36
+ const callerDot = caller.name.lastIndexOf('.');
37
+ const calleeDot = candidate.name.lastIndexOf('.');
38
+ if (callerDot >= 0 && calleeDot >= 0) {
39
+ const callerPrefix = caller.name.substring(0, callerDot);
40
+ const calleePrefix = candidate.name.substring(0, calleeDot);
41
+ if (callerPrefix === calleePrefix) {
42
+ callee = candidate;
43
+ }
44
+ }
45
+ }
46
+ }
47
+ if (!callee)
48
+ continue;
49
+ // Deduplicate: skip if an edge with same from, to, and line already exists
50
+ if (edges.some(e => e.from === caller.id && e.to === callee.id && e.line === call.line))
51
+ continue;
52
+ const edge = { from: caller.id, to: callee.id, line: call.line };
53
+ if (call.callType)
54
+ edge.callType = call.callType;
55
+ edges.push(edge);
56
+ }
57
+ return edges;
58
+ }
@@ -0,0 +1,2 @@
1
+ import { FunctionNode, CallEdge } from '../types';
2
+ export declare function detectEntryPoints(nodes: FunctionNode[], edges: CallEdge[]): void;
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.detectEntryPoints = detectEntryPoints;
4
+ function detectEntryPoints(nodes, edges) {
5
+ const nonSelfEdges = edges.filter(e => e.from !== e.to);
6
+ const calledIds = new Set(nonSelfEdges.map(e => e.to));
7
+ const callerIds = new Set(nonSelfEdges.map(e => e.from));
8
+ for (const node of nodes) {
9
+ node.isEntryPoint = node.isEntryPoint || (!calledIds.has(node.id) && callerIds.has(node.id));
10
+ }
11
+ }
@@ -0,0 +1,5 @@
1
+ import { FunctionNode, CallEdge, Flow } from '../types';
2
+ export declare function partitionFlows(nodes: FunctionNode[], edges: CallEdge[]): {
3
+ flows: Flow[];
4
+ orphans: string[];
5
+ };
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.partitionFlows = partitionFlows;
4
+ function partitionFlows(nodes, edges) {
5
+ const flows = [];
6
+ const visited = new Set();
7
+ const nonSelfEdges = edges.filter(e => e.from !== e.to);
8
+ // 1. BFS from every entry point (in-degree=0, out-degree>0)
9
+ const entryIds = findEntryIds(new Set(nodes.map(n => n.id)), nonSelfEdges);
10
+ for (const eid of entryIds) {
11
+ const reachable = bfs(eid, nonSelfEdges);
12
+ flows.push({ id: eid, entryPoint: eid, nodeIds: [...reachable] });
13
+ reachable.forEach(id => visited.add(id));
14
+ }
15
+ // 2. Iteratively find mini-flows in unvisited subgraph
16
+ let remaining = new Set(nodes.map(n => n.id).filter(id => !visited.has(id)));
17
+ while (remaining.size > 0) {
18
+ const subEdges = nonSelfEdges.filter(e => remaining.has(e.from) && remaining.has(e.to));
19
+ const subEntries = findEntryIds(remaining, subEdges);
20
+ if (subEntries.length === 0)
21
+ break;
22
+ for (const eid of subEntries) {
23
+ const reachable = bfs(eid, subEdges);
24
+ flows.push({ id: eid, entryPoint: eid, nodeIds: [...reachable] });
25
+ reachable.forEach(id => visited.add(id));
26
+ }
27
+ remaining = new Set([...remaining].filter(id => !visited.has(id)));
28
+ }
29
+ // 3. Remaining nodes with edges (pure cycles) → pick one per component
30
+ remaining = new Set([...remaining]);
31
+ if (remaining.size > 0) {
32
+ const subEdges = nonSelfEdges.filter(e => remaining.has(e.from) && remaining.has(e.to));
33
+ const nodesWithEdges = new Set();
34
+ for (const e of subEdges) {
35
+ nodesWithEdges.add(e.from);
36
+ nodesWithEdges.add(e.to);
37
+ }
38
+ const componentVisited = new Set();
39
+ for (const nid of nodesWithEdges) {
40
+ if (componentVisited.has(nid))
41
+ continue;
42
+ // BFS bidirectional to find connected component
43
+ const component = bfsBidirectional(nid, subEdges);
44
+ component.forEach(id => componentVisited.add(id));
45
+ // Pick the first node as synthetic entry
46
+ const syntheticEntry = nid;
47
+ const reachable = bfs(syntheticEntry, subEdges);
48
+ // Include all component members even if not forward-reachable
49
+ component.forEach(id => reachable.add(id));
50
+ flows.push({ id: syntheticEntry, entryPoint: syntheticEntry, nodeIds: [...reachable] });
51
+ reachable.forEach(id => visited.add(id));
52
+ }
53
+ remaining = new Set([...remaining].filter(id => !visited.has(id)));
54
+ }
55
+ // 4. True orphans — zero edges in the full graph
56
+ const orphans = [...remaining];
57
+ return { flows, orphans };
58
+ }
59
+ function findEntryIds(nodeIds, edges) {
60
+ const calledIds = new Set();
61
+ const callerIds = new Set();
62
+ for (const e of edges) {
63
+ if (nodeIds.has(e.from) && nodeIds.has(e.to)) {
64
+ calledIds.add(e.to);
65
+ callerIds.add(e.from);
66
+ }
67
+ }
68
+ return [...nodeIds].filter(id => !calledIds.has(id) && callerIds.has(id));
69
+ }
70
+ function bfs(startId, edges) {
71
+ const visited = new Set([startId]);
72
+ const queue = [startId];
73
+ while (queue.length) {
74
+ const current = queue.shift();
75
+ for (const edge of edges) {
76
+ if (edge.from === current && !visited.has(edge.to)) {
77
+ visited.add(edge.to);
78
+ queue.push(edge.to);
79
+ }
80
+ }
81
+ }
82
+ return visited;
83
+ }
84
+ function bfsBidirectional(startId, edges) {
85
+ const visited = new Set([startId]);
86
+ const queue = [startId];
87
+ while (queue.length) {
88
+ const current = queue.shift();
89
+ for (const edge of edges) {
90
+ if (edge.from === current && !visited.has(edge.to)) {
91
+ visited.add(edge.to);
92
+ queue.push(edge.to);
93
+ }
94
+ if (edge.to === current && !visited.has(edge.from)) {
95
+ visited.add(edge.from);
96
+ queue.push(edge.from);
97
+ }
98
+ }
99
+ }
100
+ return visited;
101
+ }
@@ -0,0 +1,6 @@
1
+ export * from './types';
2
+ export * from './analyzer/treeSitter';
3
+ export * from './analyzer/index';
4
+ export * from './graph/callGraphBuilder';
5
+ export * from './graph/entryPointDetector';
6
+ export * from './graph/flowPartitioner';
package/dist/index.js ADDED
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./types"), exports);
18
+ __exportStar(require("./analyzer/treeSitter"), exports);
19
+ __exportStar(require("./analyzer/index"), exports);
20
+ __exportStar(require("./graph/callGraphBuilder"), exports);
21
+ __exportStar(require("./graph/entryPointDetector"), exports);
22
+ __exportStar(require("./graph/flowPartitioner"), exports);