feat(neo4j): project decorators, matching python - #143
Merged
Conversation
Decorators were captured as structured `TSDecorator` in the JSON from the start —
name, checker-resolved `qualified_name`, positional and keyword arguments, spans —
on types, callables, fields and parameters. None of it reached Neo4j: the whole
projection mentioned decorators once, in a comment. A query could see `@Controller`
in analysis.json and not in the graph.
Mirror python's `_project_decorator`:
- `:TSDecorator` merged on the resolved `qualified_name` when there is one, so
`@Get('/')` and `@Get('/:id')` collapse to a single node rather than two spellings
of one decorator.
- `TS_DECORATED_BY` from the decorated node to it, carrying the per-application
facts: `positional_arguments` (raw source fragments) and `keyword_arguments_json`
(sorted-key JSON, since Neo4j has no map property type — python encodes the same
field the same way, so the two projections stay diffable).
Like python's, the node carries no `_module` and is never pruned: it is shared
across modules, so anything application-specific stored on it would accumulate
across every project in the database.
Attached at types, callables and fields. Python attaches at classes and callables
only, but TS carries decorators on fields too and property decorators are a
first-class TS idiom (Angular `@Input`, TypeORM `@Column`), so dropping them would
lose the metadata most TS decorator queries are actually after. Parameters are not
projected as nodes in either analyzer, so parameter decorators stay unprojected.
SCHEMA_VERSION stays at 2.1.0 — every analyzer re-baselines together later.
This was referenced Sep 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #82.
Decorators have been captured as structured
TSDecoratorin the JSON since the beginning — name,checker-resolved
qualified_name, positional and keyword arguments, spans — on types, callables,fields and parameters. None of it reached Neo4j. The entire projection mentioned decorators once,
in a comment (
rows.ts:62), so a query could see@Controllerinanalysis.jsonand not in thegraph. Python has projected them since #128.
What this adds
Mirrors python's
_project_decorator(codeanalyzer/neo4j/project.py:645)::TSDecorator, merged on the resolvedqualified_namewhen the checker supplies one, so@Get('/')and@Get('/:id')land on one node instead of two spellings of the samedecorator.
TS_DECORATED_BYfrom the decorated node to it, carrying the per-application facts —positional_arguments(raw source fragments) andkeyword_arguments_json(sorted-key JSON,because Neo4j has no map property type; python encodes the same field the same way, so the two
projections stay diffable).
Like python's, the node carries no
_moduleand is never pruned — it is shared across modules, soanything application-specific on it would accumulate across every project in the database. The
per-application facts live on the relationship for exactly that reason.
One deliberate divergence from python
Python attaches at classes and callables. This attaches at types, callables and fields.
TS carries decorators on fields in the schema already, and property decorators are a first-class TS
idiom — Angular
@Input, TypeORM@Column, NestJS. Dropping them would lose the metadata most TSdecorator queries are actually after. Parameters are not projected as nodes in either analyzer, so
parameter decorators remain unprojected in both.
Verification
bun test— 238 pass, 0 fail (3 new)bun run test:container, 4 pass against live Neo4j, which is whatexercises the auto-derived
tsdecorator_nameuniqueness constraint in real DDLbun run typecheckclean,schema.neo4j.jsonregeneratedThe new test pins the two properties that row counts alone would not catch: the node is shared
across applications, and the arguments are per-application and therefore on the relationship.
Verified on the existing
sample-appfixture, which was already decorated:Note on #82's stated shape
The issue title proposes
decorators: string[]onTSCallable. That would be worse than whatthe analyzer already captures — it flattens structured decorators to bare names, drops the
arguments and the resolved FQN, and covers only callables when the schema carries decorators on
four node types. This implements the python-parity shape instead, which is what "full parity"
requires. Say the word if you want the flattened form as well as, or instead of, this.
SCHEMA_VERSIONstays at 2.1.0 — per the standing decision that only 2.0.0 is meaningful untilthe design settles and every analyzer re-baselines together.