MCPcopy Create free account
hub / github.com/colbymchenry/codegraph / eventEmitterEdges

Function eventEmitterEdges

src/resolution/callback-synthesizer.ts:329–387  ·  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

327
328/** Phase 2: string-keyed EventEmitter channels (on('e', fn) ↔ emit('e')). */
329async function eventEmitterEdges(ctx: ResolutionContext, onYield: MaybeYield): Promise<Edge[]> {
330 let scannedFiles = 0;
331 const emitsByEvent = new Map<string, Set<string>>(); // event → dispatcher node ids
332 const handlersByEvent = new Map<string, Map<string, string>>(); // event → handler id → registration site (file:line)
333
334 let scanned = 0;
335 for (const file of ctx.getAllFiles()) {
336 if ((++scannedFiles & 15) === 0) await onYield();
337 if ((++scanned & 255) === 0) await onYield(); // #1091: yield mid-scan on huge graphs
338 const content = ctx.readFile(file);
339 if (!content) continue;
340 const hasEmit = content.includes('.emit(') || content.includes('.fire(') || content.includes('.dispatchEvent(');
341 const hasOn = content.includes('.on(') || content.includes('.once(') || content.includes('.addListener(');
342 if (!hasEmit && !hasOn) continue;
343 const nodesInFile = ctx.getNodesInFile(file);
344 const lineOf = makeLineAt(content, 1);
345
346 if (hasEmit) {
347 EMIT_RE.lastIndex = 0;
348 let m: RegExpExecArray | null;
349 while ((m = EMIT_RE.exec(content))) {
350 const disp = enclosingFn(nodesInFile, lineOf(m.index));
351 if (!disp) continue;
352 const set = emitsByEvent.get(m[1]!) ?? new Set<string>();
353 set.add(disp.id); emitsByEvent.set(m[1]!, set);
354 }
355 }
356 if (hasOn) {
357 ON_RE.lastIndex = 0;
358 let m: RegExpExecArray | null;
359 while ((m = ON_RE.exec(content))) {
360 const handlerName = m[2] || m[3];
361 if (!handlerName) continue;
362 const handler = ctx.getNodesByName(handlerName).find((n) => n.kind === 'function' || n.kind === 'method');
363 if (!handler) continue;
364 const map = handlersByEvent.get(m[1]!) ?? new Map<string, string>();
365 map.set(handler.id, `${file}:${lineOf(m.index)}`); handlersByEvent.set(m[1]!, map);
366 }
367 }
368 }
369
370 const edges: Edge[] = [];
371 const seen = new Set<string>();
372 for (const [event, dispatchers] of emitsByEvent) {
373 const handlers = handlersByEvent.get(event);
374 if (!handlers) continue;
375 // Precision guard: a generic event name with many handlers/dispatchers can't
376 // be matched without receiver-type info (Phase 3) — skip rather than over-link.
377 if (dispatchers.size > EVENT_FANOUT_CAP || handlers.size > EVENT_FANOUT_CAP) continue;
378 for (const d of dispatchers) for (const [h, registeredAt] of handlers) {
379 if (d === h) continue;
380 const key = `${d}>${h}`;
381 if (seen.has(key)) continue;
382 seen.add(key);
383 edges.push({ source: d, target: h, kind: 'calls', provenance: 'heuristic', metadata: { synthesizedBy: 'event-emitter', event, registeredAt } });
384 }
385 }
386 return edges;

Callers 1

Calls 11

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

Tested by

no test coverage detected