(ctx: ResolutionContext, onYield: MaybeYield)
| 75 | } |
| 76 | |
| 77 | export async function goframeRouteEdges(ctx: ResolutionContext, onYield: MaybeYield): Promise<Edge[]> { |
| 78 | let scanned255 = 0; |
| 79 | // Route nodes the goframe extractor created, keyed by their package-qualified |
| 80 | // request type (`cash.ListReq`). `wanted` holds every key a handler signature |
| 81 | // could match — the qualified form plus its bare type fallback. |
| 82 | const routesByReqType = new Map<string, Node[]>(); |
| 83 | const wanted = new Set<string>(); |
| 84 | for (const route of (ctx.iterateNodesByKind?.('route') ?? ctx.getNodesByKind('route'))) { |
| 85 | if ((++scanned255 & 63) === 0) await onYield(); |
| 86 | if (route.language !== 'go') continue; |
| 87 | const marker = route.qualifiedName.indexOf(GOFRAME_ROUTE_MARKER); |
| 88 | if (marker < 0) continue; |
| 89 | const joinKey = route.qualifiedName.slice(marker + GOFRAME_ROUTE_MARKER.length); |
| 90 | if (!joinKey) continue; |
| 91 | let arr = routesByReqType.get(joinKey); |
| 92 | if (!arr) { arr = []; routesByReqType.set(joinKey, arr); } |
| 93 | arr.push(route); |
| 94 | wanted.add(joinKey); |
| 95 | const dot = joinKey.lastIndexOf('.'); |
| 96 | if (dot >= 0) wanted.add(joinKey.slice(dot + 1)); // bare fallback |
| 97 | } |
| 98 | if (routesByReqType.size === 0) return []; |
| 99 | |
| 100 | // Handler candidates: Go methods whose signature takes a wanted request type by |
| 101 | // pointer, indexed by every matching (qualified + bare) form so a route can |
| 102 | // match precisely on `pkg.Type` and fall back to the bare `Type`. |
| 103 | const handlersByKey = new Map<string, Node[]>(); |
| 104 | for (const method of (ctx.iterateNodesByKind?.('method') ?? ctx.getNodesByKind('method'))) { |
| 105 | if ((++scanned255 & 63) === 0) await onYield(); |
| 106 | if (method.language !== 'go' || !method.signature) continue; |
| 107 | for (const t of pointerParamTypes(method.signature)) { |
| 108 | if (!wanted.has(t)) continue; |
| 109 | let arr = handlersByKey.get(t); |
| 110 | if (!arr) { arr = []; handlersByKey.set(t, arr); } |
| 111 | arr.push(method); |
| 112 | } |
| 113 | } |
| 114 | |
| 115 | const edges: Edge[] = []; |
| 116 | const seen = new Set<string>(); |
| 117 | let added = 0; |
| 118 | for (const [joinKey, routes] of routesByReqType) { |
| 119 | const bare = joinKey.includes('.') ? joinKey.slice(joinKey.lastIndexOf('.') + 1) : joinKey; |
| 120 | // Precise package-qualified match first; bare type only as a fallback (covers |
| 121 | // a same-package handler or an aliased import where the bare name is unique). |
| 122 | const candidates = handlersByKey.get(joinKey) ?? handlersByKey.get(bare); |
| 123 | if (!candidates || candidates.length === 0) continue; |
| 124 | const requestType = bare; |
| 125 | for (const route of routes) { |
| 126 | const handler = selectHandler(candidates, route.filePath); |
| 127 | if (!handler || route.id === handler.id) continue; |
| 128 | const key = `${route.id}>${handler.id}`; |
| 129 | if (seen.has(key) || added >= FANOUT_CAP) continue; |
| 130 | seen.add(key); |
| 131 | edges.push({ |
| 132 | source: route.id, |
| 133 | target: handler.id, |
| 134 | kind: 'calls', |
no test coverage detected