MCPcopy
hub / github.com/colbymchenry/codegraph / eventEmitterEdges

Function eventEmitterEdges

src/resolution/callback-synthesizer.ts:282–338  ·  view source on GitHub ↗

Phase 2: string-keyed EventEmitter channels (on('e', fn) ↔ emit('e')).

(ctx: ResolutionContext, onYield: MaybeYield)

Source from the content-addressed store, hash-verified

280
281/** Phase 2: string-keyed EventEmitter channels (on('e', fn) ↔ emit('e')). */
282async function eventEmitterEdges(ctx: ResolutionContext, onYield: MaybeYield): Promise<Edge[]> {
283 const emitsByEvent = new Map<string, Set<string>>(); // event → dispatcher node ids
284 const handlersByEvent = new Map<string, Map<string, string>>(); // event → handler id → registration site (file:line)
285
286 let scanned = 0;
287 for (const file of ctx.getAllFiles()) {
288 if ((++scanned & 255) === 0) await onYield(); // #1091: yield mid-scan on huge graphs
289 const content = ctx.readFile(file);
290 if (!content) continue;
291 const hasEmit = content.includes('.emit(') || content.includes('.fire(') || content.includes('.dispatchEvent(');
292 const hasOn = content.includes('.on(') || content.includes('.once(') || content.includes('.addListener(');
293 if (!hasEmit && !hasOn) continue;
294 const nodesInFile = ctx.getNodesInFile(file);
295 const lineOf = (idx: number) => content.slice(0, idx).split('\n').length;
296
297 if (hasEmit) {
298 EMIT_RE.lastIndex = 0;
299 let m: RegExpExecArray | null;
300 while ((m = EMIT_RE.exec(content))) {
301 const disp = enclosingFn(nodesInFile, lineOf(m.index));
302 if (!disp) continue;
303 const set = emitsByEvent.get(m[1]!) ?? new Set<string>();
304 set.add(disp.id); emitsByEvent.set(m[1]!, set);
305 }
306 }
307 if (hasOn) {
308 ON_RE.lastIndex = 0;
309 let m: RegExpExecArray | null;
310 while ((m = ON_RE.exec(content))) {
311 const handlerName = m[2] || m[3];
312 if (!handlerName) continue;
313 const handler = ctx.getNodesByName(handlerName).find((n) => n.kind === 'function' || n.kind === 'method');
314 if (!handler) continue;
315 const map = handlersByEvent.get(m[1]!) ?? new Map<string, string>();
316 map.set(handler.id, `${file}:${lineOf(m.index)}`); handlersByEvent.set(m[1]!, map);
317 }
318 }
319 }
320
321 const edges: Edge[] = [];
322 const seen = new Set<string>();
323 for (const [event, dispatchers] of emitsByEvent) {
324 const handlers = handlersByEvent.get(event);
325 if (!handlers) continue;
326 // Precision guard: a generic event name with many handlers/dispatchers can't
327 // be matched without receiver-type info (Phase 3) — skip rather than over-link.
328 if (dispatchers.size > EVENT_FANOUT_CAP || handlers.size > EVENT_FANOUT_CAP) continue;
329 for (const d of dispatchers) for (const [h, registeredAt] of handlers) {
330 if (d === h) continue;
331 const key = `${d}>${h}`;
332 if (seen.has(key)) continue;
333 seen.add(key);
334 edges.push({ source: d, target: h, kind: 'calls', provenance: 'heuristic', metadata: { synthesizedBy: 'event-emitter', event, registeredAt } });
335 }
336 }
337 return edges;
338}
339

Callers 1

synthesizeCallbackEdgesFunction · 0.85

Calls 10

enclosingFnFunction · 0.85
readFileMethod · 0.80
setMethod · 0.80
hasMethod · 0.80
lineOfFunction · 0.70
getAllFilesMethod · 0.65
getNodesInFileMethod · 0.65
execMethod · 0.65
getMethod · 0.65
getNodesByNameMethod · 0.65

Tested by

no test coverage detected