diff --git a/graph-ui/src/architecture/container-topology.test.ts b/graph-ui/src/architecture/container-topology.test.ts index 3cfa2db5f..2cef1596e 100644 --- a/graph-ui/src/architecture/container-topology.test.ts +++ b/graph-ui/src/architecture/container-topology.test.ts @@ -343,3 +343,9 @@ describe('Spring service connection evidence', () => { expect(links(result)).toEqual([]); }); }); + +it.each(['$endpoint', 'endpoint$'])('tracks a literal identifier through a transport wrapper: %s', parameter => { + const compose = 'services:\n web:\n build: ./web\n api:\n image: example/api\n'; + const source = `function load(${parameter}) { return fetch(${parameter}); }\nload("http://api:8080/items");`; + expect(links(buildContainerTopology([project(compose, { 'web/client.js': source })]))).toEqual(['web>api:http']); +}); diff --git a/graph-ui/src/architecture/container-topology.ts b/graph-ui/src/architecture/container-topology.ts index 51de635aa..b1735aaf7 100644 --- a/graph-ui/src/architecture/container-topology.ts +++ b/graph-ui/src/architecture/container-topology.ts @@ -300,7 +300,12 @@ function networkCall(name: string): boolean { || /^(?:requests|http|https|axios|urllib\.request|urllib3|grpc|redis|psycopg2?|pymysql|mysql|amqp|amqplib|net|socket|ConnectionMultiplexer|Dns)\./i.test(name) && /(?:get|post|put|patch|delete|request|urlopen|dial|newclient|connect|connectasync|createclient|createconnection|insecure_channel|secure_channel|gethostentry(?:async)?)$/i.test(name); } -const wordIn = (code: string, name: string): boolean => new RegExp(`\\b${name.replace(/[$]/g, '\\$&')}\\b`).test(code); +function wordIn(code: string, name: string): boolean { + for (const [identifier] of code.matchAll(/[A-Za-z_$][\w$]*/g)) { + if (identifier === name) return true; + } + return false; +} interface Wrapper { parameter: number; hint: string } /** Bounded local argument propagation to known transport APIs, not a function-name dictionary. */ function connectionWrappers(code: string, calls: Call[]): Map { diff --git a/graph-ui/src/architecture/repository-guide.test.ts b/graph-ui/src/architecture/repository-guide.test.ts index 0cb221754..a161a1216 100644 --- a/graph-ui/src/architecture/repository-guide.test.ts +++ b/graph-ui/src/architecture/repository-guide.test.ts @@ -64,3 +64,10 @@ it('follows only actual call edges across components and retains their evidence' expect(paths[0].edges[0]).toBe(map.evidence[0]); expect(entryRoutes(graph.nodes[2], map.evidence)).toEqual([]); }); + +it('keeps token boundaries when dropping markup from repository prose', () => { + const quote = sourceQuote('README.md', 'This repository provides useful source browsing and <