Skip to content

0.9.29: TypeScript leaks absolute-path slug ids into links source endpoints (C# fixed by #2250, TS not) #2262

Description

@vanjos

Follow-up to #2243. Thanks for the 0.9.29 fix (#2250), it cleaned up the C# case. TypeScript still leaks, in a slightly different spot.

What happens

In a TypeScript graph, a large number of links entries have a source that is an absolute-scan-path-derived slug rather than a real node id. The slug is the scan path lowercased with every run of non-alphanumerics collapsed to _, followed by the file's repo-relative path and the function name, for example abs_scan_path_slug_packages_account_src_components_paymentmethods_prepaybalancecontainer_invoicebalancesubsection. The target of the same edge is a clean scope-relative id (packages_account_src_settings_basesettingssection_constructrowwithid).

Two things about the leaked source:

C# vs TypeScript in the same version

Same repo, same graphify 0.9.29, two scopes:

  • C# scope, 79,336 nodes: 0 absolute-path slug endpoints.
  • TypeScript scope, 74,271 nodes / 165,872 edges: 7,143 absolute-path slug endpoints.

So the 0.9.29 relativization landed for the C# extractor but not the TypeScript one.

Why it matters

The slug is derived from the absolute scan path, so the same source tree built on two machines (or two checkout paths) produces different graph.json files. We commit graphs to git, so every rebuild on a different path shows a spurious diff, and the underlying non-determinism is general. Same reproducibility and cross-machine caching problem as #2243.

Repro

It shows up in a TypeScript monorepo (a packages/* layout with cross-package imports). A minimal two-file TS project does not trigger it, and neither does a class with arrow-property handlers calling across two files, so it correlates with the larger structure rather than a single syntactic form. The leaked callers are handler-style functions (invoicebalancesubsection, handletabclick, handleapply) whose containing symbol does not appear in nodes. Happy to share a fuller extract from the affected graph if useful.

Suggested fix

Apply the same relativization as #2243/#1899 to the TypeScript extractor's edge-source identifiers, and/or materialize the caller node so the edge references a real scope-relative id instead of an absolute path.

Activity

  1. vanjos commented on Jul 29, 2026

    @vanjos
    Author

    Any read on this? Happy to test a patch or provide a fuller repro if that helps.

  2. safishamsi commented on Jul 29, 2026

    @safishamsi
    Member

    Fixed in v0.9.30. The symbol-resolution pass now parses .tsx with the TSX grammar (it was using the plain TS grammar, so JSX misparsed and floated nested handlers to top level, emitting calls edges from an absolute-stem source with no node). A calls edge is also never emitted from an unowned source, and a general backstop canonicalizes any node-less absolute-derived endpoint. No node id or edge endpoint carries the scan-root slug now. https://github.com/Graphify-Labs/graphify/releases/tag/v0.9.30

  3. vanjos commented on Jul 29, 2026

    @vanjos
    Author

    Thanks, v0.9.30 does fix the case I filed: the .tsx-parsed-as-.ts handler misparse is gone, no more slug-source calls edges. Verified on the same TS project (74,271 nodes), zero of those remain.

    There's a residual of the same class the backstop was meant to cover, on a different edge type. imports / imports_from / references / re_exports edges still emit an absolute-path-derived slug as the target when the target resolves to no node. Counts on that project:

    • imports: 4,089
    • imports_from: 2,505
    • references: 67
    • re_exports: 16

    The bulk (~5,160) are imports to generated files that aren't in the checkout (e.g. src/**/graphql/generated/graphql.ts, a codegen output that's gitignored). graphify can't node a file that isn't on disk, so the endpoint comes out as the absolute-scan-path slug (<abs-scan-path>_..._graphql), which is machine-specific and differs per checkout path, the same reproducibility problem as the original report. The rest (~70) are references to markdown/docs.

    So the general backstop that canonicalizes node-less absolute-derived endpoints doesn't reach the imports/imports_from/references/re_exports path. Same fix idea: for a target that resolves to no node, either canonicalize it the way the backstop does elsewhere, or drop the edge, rather than leaving the absolute-derived slug.

    Happy to share a fuller extract if useful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions