Repository navigation
TypeScript extraction emits internal edges whose endpoint nodes are absent #2143
Description
Environment
graphifyy 0.9.25- macOS
- Expo / React Native TypeScript corpus
- code-only, undirected, no clustering
Reproduction
graphify extract . --out /tmp/graphify-diagnostic --no-cluster --code-only --force
graphify diagnose multigraph \
--graph /tmp/graphify-diagnostic/graphify-out/graph.json \
--undirected --max-examples 20After excluding 93 intentional external ref_* imports covered by #2130 and #2132, 21 missing endpoints remain. Every missing ID is internal to the scanned corpus.
Observed internal failures
Nested function callers are referenced but never emitted (8 edges)
One TSX component emits six calls edges whose source is a nested capture callback ID. No node with that ID exists, while the targets are valid emitted nodes. The callback calls functions for region/date ordering, preview cropping, crop variants, candidate extraction, vertical-region filtering, and translation.
The same pattern occurs for nested submit and changeDate functions in another TSX component.
Representative fixture shape:
import { target } from "./target";
export function Component() {
const capture = async () => target();
return null;
}Test-file caller IDs use an absent absolute-path form (6 edges)
Six indirect_call edges use source IDs shaped like:
users_<redacted>_src_components_additemscreen_test_tsx
The actual emitted file node uses a root-relative ID shaped like:
src_components_additemscreen_test
Equivalent mismatches occur across four TSX component test files.
Imported internal constants/assets have no target node (7 edges)
Missing targets represent:
- four exported scalar constants imported from TypeScript service modules
- one imported JSON asset
- two generated migration imports (a SQL module and migration journal JSON)
Representative fixture shapes:
// constants.ts
export const LIMIT = 14;
// consumer.ts
import { LIMIT } from "./constants";import data from "./data.json";Expected behavior
Every non-external edge endpoint should resolve to an emitted node. Depending on the construct, Graphify could:
- emit a node for the nested function, constant, asset, or generated module;
- anchor the edge to the nearest emitted containing function or file node; or
- omit the edge rather than emit structurally invalid extraction data.
Absolute-path caller IDs should be normalized consistently against the extraction root.
Impact
build_from_json() drops all 21 edges, removing genuine internal call and import relationships. After #2132 classifies external imports separately, these remain as true dangling_endpoint_edges.
Suggested regression assertions
- A nested TS/TSX callback calling an imported function has an emitted source endpoint.
- Test-file
indirect_calledges use the same root-relative file ID as the emitted file node. - Imported scalar constants and JSON/generated modules resolve to nodes or their containing file.
- After excluding classified external imports,
dangling_endpoint_edges == 0.
I can provide reduced real cache entries for these three patterns if useful.
Activity
itsLucas02 commentedon Oct 14, 2026
Adding a small Windows reproduction on graphifyy 0.9.89 for the nested-function omission described here. This fixture emits neither nested function nor their call edge; it does not produce a dangling endpoint in the raw output, so it isolates the missing-node part of the report.
Environment
10.0.26340, x643.13.11graphifyy==0.9.89tree-sitter==0.25.2tree-sitter-typescript==0.23.2Minimal reproduction
Create
repro/index.ts:Run:
Inspect
repro/graphify-out/graph.json:Observed
Extraction succeeds and reports 2 nodes, 1 edge. The raw output contains:
{ "nodes": [ {"id": "index", "label": "index.ts"}, {"id": "index_createqueuetools", "label": "createQueueTools()"} ], "edges": [ { "source": "index", "target": "index_createqueuetools", "relation": "contains" } ] }The JSON above is a projection of the actual output, omitting unrelated attributes. There is no node for
callVisitorupdateVisitAfterCall, and no call edge between them.Expected and impact
For symbol-level caller analysis, I expected nodes for both named nested arrow functions and a
callsedge fromcallVisittoupdateVisitAfterCall(the source call is on line 4).The same shape occurs in a real TypeScript hook: the outer hook and top-level helpers are represented, but its named nested handlers are absent, preventing tracing that handler-to-helper relationship. The reproduction above is self-contained and includes no proprietary application source.
Is omission of named nested typed-arrow functions an intended scope limitation, or should this be covered by the extraction fix for this issue? A regression asserting both nested nodes and their source-located call edge would cover this case.
Prepared with AI assistance; the version information and emitted nodes/edge were checked against a local run.