(ctx: ResolutionContext, onYield: MaybeYield)
| 2803 | } |
| 2804 | |
| 2805 | async function mediatrDispatchEdges(ctx: ResolutionContext, onYield: MaybeYield): Promise<Edge[]> { |
| 2806 | let scannedFiles = 0; |
| 2807 | // Pass 1 — request/notification type → the Handle method of each handler class. |
| 2808 | const handlers = new Map<string, Node[]>(); |
| 2809 | for (const file of ctx.getAllFiles()) { |
| 2810 | if ((++scannedFiles & 15) === 0) await onYield(); |
| 2811 | if (!MEDIATR_CS_EXT.test(file)) continue; |
| 2812 | const content = ctx.readFile(file); |
| 2813 | if (!content || (!content.includes('IRequestHandler<') && !content.includes('INotificationHandler<'))) continue; |
| 2814 | const lines = content.split('\n'); |
| 2815 | const nodesInFile = ctx.getNodesInFile(file); |
| 2816 | for (const cls of nodesInFile) { |
| 2817 | if (cls.kind !== 'class') continue; |
| 2818 | const decl = lines.slice(cls.startLine - 1, cls.startLine - 1 + MEDIATR_HANDLER_DECL_LOOKAHEAD).join('\n'); |
| 2819 | const m = MEDIATR_HANDLER_BASE_RE.exec(decl); |
| 2820 | if (!m) continue; |
| 2821 | const type = m[1]!; |
| 2822 | const end = cls.endLine ?? cls.startLine; |
| 2823 | const handle = nodesInFile.find( |
| 2824 | (n) => n.kind === 'method' && n.name === 'Handle' && n.startLine >= cls.startLine && n.startLine <= end |
| 2825 | ); |
| 2826 | if (!handle) continue; |
| 2827 | let arr = handlers.get(type); |
| 2828 | if (!arr) { arr = []; handlers.set(type, arr); } |
| 2829 | arr.push(handle); |
| 2830 | } |
| 2831 | } |
| 2832 | if (!handlers.size) return []; |
| 2833 | |
| 2834 | // Pass 2 — link each mediator-ish .Send(x)/.Publish(x) → the handler of x's type. |
| 2835 | const edges: Edge[] = []; |
| 2836 | const seen = new Set<string>(); |
| 2837 | for (const file of ctx.getAllFiles()) { |
| 2838 | if ((++scannedFiles & 15) === 0) await onYield(); |
| 2839 | if (!MEDIATR_CS_EXT.test(file)) continue; |
| 2840 | const content = ctx.readFile(file); |
| 2841 | if (!content || (!content.includes('.Send(') && !content.includes('.Publish('))) continue; |
| 2842 | const safe = stripCommentsForRegex(content, 'csharp'); |
| 2843 | const safeLines = safe.split('\n'); |
| 2844 | const nodesInFile = ctx.getNodesInFile(file); |
| 2845 | MEDIATR_DISPATCH_RE.lastIndex = 0; |
| 2846 | let m: RegExpExecArray | null; |
| 2847 | let added = 0; |
| 2848 | while ((m = MEDIATR_DISPATCH_RE.exec(safe)) && added < MEDIATR_FANOUT_CAP) { |
| 2849 | if (!MEDIATR_RECEIVER_RE.test(m[1]!)) continue; // not a mediator (MessagingCenter, HttpClient, …) |
| 2850 | const line = safe.slice(0, m.index).split('\n').length; |
| 2851 | const disp = enclosingFn(nodesInFile, line); |
| 2852 | if (!disp) continue; |
| 2853 | const type = resolveMediatrArgType(m[2]!, safeLines, disp.startLine, line); |
| 2854 | if (!type) continue; |
| 2855 | const targets = handlers.get(type); |
| 2856 | if (!targets) continue; |
| 2857 | for (const target of targets) { |
| 2858 | if (target.id === disp.id) continue; |
| 2859 | const key = `${disp.id}>${target.id}`; |
| 2860 | if (seen.has(key)) continue; |
| 2861 | seen.add(key); |
| 2862 | edges.push({ |
no test coverage detected