Repository navigation
Conversation
v0.56 changes graph meaning (framework resolvers, synthesis, VB.NET, grammar bumps), so the extraction version moves once, first, to 23. A v0.55 index (schema 10, extraction version 22) is Outdated and upgrades through the existing full migration; v0.55 reports a version-23 index as Future and writes nothing. - upgrade_from_v055 covers the paths upgrade_from_v054 covers (CLI sync, catch-up, the cold MCP session's daemon upgrade, an older uninit stays Uninitialized, a failed upgrade still refuses, interrupted builds, tombstones and missing databases are skipped) over a fixture the official v0.55.2 linux-musl binary wrote; checksum, attestation and commands are in its README. The shared helpers move to tests/upgrade_support unchanged. - upgrade_from_schema_9 downgrades a fresh init of the mini corpus to schema 9 instead of planting rows from 3878d52, so its comparison with a fresh init checks only the migration. The release fixture keeps a third test that checks the in-place migration alone.
A database whose schema_versions records a version above this binary's was written by a newer codegraph (OD-19). Reads, status, sync, MCP, the daemon, init and index --force now refuse it before they change a byte, and the error names both versions. ensure_schema_and_migrations returns MigrationError::NewerSchema before it creates a table, migrates or analyzes. The newer version may sit only in an un-checkpointed write-ahead log. The writers that tolerate a live log (an incremental sync, the daemon's recovery of a dead owner's log) and every rebuild entry (before an explicit init removes a copied project's state slots, before a lockless root gets its lock, before building is published) decide from schema_probe: the main file plus the log's committed frames, applied in memory the way SQLite's recovery does, so no -wal or -shm is created or folded. A file that is not SQLite is still replaced; a database whose version cannot be read is refused, and the error says nothing changed. uninit --force skips the check and stays the way to discard such an index; the refusal offers it only for the states uninit accepts. status reports the index as current with initialized false and both versions.
test_engine() returned an engine over /tmp/cg-mcp-engine-{pid}-{seq} and
never removed the directory, so a reused pid opened a stale index and
engine tests failed locally. TestEngine derefs to the engine, removes a
stale directory before it creates one, and on drop closes the engine
before removing its directory.
The member-path std gate deferred on 2026-10-03 needs no code: the v0.55 port of upstream #2169 already keeps a receiver whose declared type the project does not declare (PathBuf, &Path, std::path::PathBuf) from binding a project method by name. The probe pins it: index_root.join, base.join, p.join and q.join stay unresolved and reach no LineIndex::join, while env.set() still reaches EnvGuard::set.
Takes dependabot #334 except tree-sitter-swift: tree-sitter 0.27.1, tree-sitter-language 0.1.9, erlang 0.21.0, php 0.25.1, scala =0.26.3, cfml 0.26.43, and the windows-sys reselection. Every golden, the four precision corpora and this repository index identically before and after. tree-sitter-swift stays at =0.7.3 (OD-B4). 0.7.4 fixes the scanner line behind upstream #2683 (`1UL << 32`, which MSVC folds to 0, so the Windows build reads `try! Foo()` as a call of `! Foo`), but it also regenerates the grammar: a call after an infix or prefix operator (`a + b.c()`, `!a.b()`) comes out as another tree, which the walker does not read as a member call. Upgrading it lost an exact-match edge in the swift golden and turned two Alamofire callees into whole expressions; the upgrade waits for that walker change. The #2683 probe pins calls after try!, try? and try. It is ignored on Windows until the upgrade; with the scanner line changed to what MSVC compiles it to, it fails on Linux the way upstream observed on Windows. No real Windows host was used. The grammar manifest records the held version, and languages.md the Windows limitation.
A Current index beside the uninit tombstone (an init that published Current but could not remove it) refused uninit --force with CurrentTombstoned. With a schema a newer codegraph wrote, init refuses that index and names uninit --force, so the refusal pointed at a command that then failed. Uninit's corroboration now accepts the tombstone: it discards the index like any other Current one and keeps the tombstone, so only init rebuilds it. Covered with and without a newer schema, in the store and through the CLI.
A Current index beside the uninit tombstone was reported as tombstoned before its schema was read, so status named `codegraph init` as the recovery even when a newer codegraph wrote the database, and init then refused it. When the tombstone is present, the corroboration now reads the schema version first (the write-ahead-log-aware probe, which touches no file); a positively newer version is FutureSchema, which offers uninit --force. Any other outcome is still CurrentTombstoned, so the same-version contract and its init recovery are unchanged.
A refusal of a newer codegraph's index offered `uninit --force` for every state uninit accepts, but on a Current namespace uninit first corroborates the database's extraction stamp. With the stamp missing, malformed or not this binary's, the offered command then failed. The offer now runs the same read-only corroboration uninit runs before its first change, for the tombstone precedence in the read gate and for the rebuild prologue. A newer schema with a bad stamp, tombstoned or not, is refused without the offer.
status's human output for an index a newer codegraph wrote always named `uninit --force`, even when the refusal it reports does not offer it because uninit would refuse the index (a missing or foreign extraction stamp). It now follows the refusal: upgrade, plus the uninit recovery only where uninit would discard the index.
Moving the shared helpers to tests/upgrade_support left upgrade_from_v054 and upgrade_from_v055 using ExtractionStatus and Store only in their unix-only tests, so the Windows Clippy job failed on unused imports. The import is now cfg(unix).
The groundwork the routers of the next commits draw their edges with (F1, upstream #2129 and #2395). No registered resolver uses the new pieces yet, so every golden and the express, typescript-large and self-index graphs are unchanged. - EdgeKind::Navigates, stored as text like every edge kind. Callers, callees, the named-symbol flow and a flow's boundary follow it, as upstream's traversal, FLOW_EDGE_KINDS and CONTINUATION_KINDS do. - A ResolvedRef can carry an EdgeShape (upstream edgeKind, metadata and alsoTargets): the edges' kind, the metadata on the target's edge, and more targets, each its own edge under the same kind and confidence. ResolvedRef::new builds the plain resolution every resolver returned before. - frameworks/package_deps reads the dependencies a project declares in its root package.json and up to 24 nested ones: one or two directories down, then deeper above JavaScript-family code, never inside node_modules. React and NestJS detect through it, so an app whose manifest is in frontend/, apps/web/ or src/Web/ClientApp/ is detected without its fallback evidence. Upstream package-deps.ts is unchanged between b635dd46 and e8ddc4e2. - A framework can name its app dependencies (appDependencies). When a manifest declares one, its extractor runs only on files whose nearest package.json naming such a framework names it. The frameworks row a sync compares adds a digest of where each gated framework runs, so a manifest edit that moves an app re-extracts every file, as a framework that came or went does. The JS control inputs digest every manifest detection may read, so a sync notices one appearing beside no script. - The trait gains cross_file_nodes and navigation (upstream #2400 and #2425), both None by default; nothing calls them until a router implements them. The sync matrix gains two cases: React declared in an app's manifest, and in a manifest one directory down above no script. The gating sync case waits for the first gated framework (W7-07).
An edge a framework resolver made now names that resolver in
metadata.framework (upstream #2159), as a synthesized edge names its
pass in synthesizedBy: a Tauri invoke('greet') says `tauri`, a Godot
autoload call `godot`. The orchestrator adds it to every result a
framework's resolve returns, before the confidence race, so a lower
confidence framework result that wins later carries it too. When a
resolution reaches several targets, only the edge to the first carries
it, as upstream's does; the others keep the metadata the resolver gave
each.
Goldens: godot's four autoload calls and routers' one Vue-resolved call
gain the key; no edge, node or reference comes or goes. The same holds
for this repository (2 edges) and typescript-large (590 edges, all
`react`).
Framework detection reads the manifests one or two directories down from every indexed file, whatever its language (upstream #2395), and the JS control-input row digests every such candidate. The watcher re-read that row only when a JS-family file came or went, so a file of another language arriving in a new directory left the row behind a fresh index: 39 cases of the sync matrix (CFML, C, C++, C#, Dart, Go, Java, Kotlin, Liquid, Lua, Objective-C, PHP, Python, Rust and Solidity edits) failed under the watch driver. The graph itself converged, since a sync with changes re-detects the frameworks anyway. The control-input check now counts any file that comes or goes as touching the JS kind. typescript_react_dependency_beside_an_added_script covers it directly: React declared in frontend/package.json is detected once frontend/build.py is indexed, and the sync records that manifest as a fresh index does.
…ts; typed styled components
React resolution now follows upstream b635dd46 (#2120's React half,
#2240, #2429, #841, #2475's static half and #2420's wrapper half):
- A component, hook or context the file imports is the import's: a
UI kit's `<Button>` or @tanstack/react-query's `useQuery` no longer
binds a project namesake, and a project module's is import
resolution's to find (#2120, #2475).
- A hook nested inside another function is never a target, and the
file's own hook comes first (#2120). In a JS/TS module another file's
hook or context is reached only through an import, never by bare name
(#2240), and `f?.()` is a bare call, so an optional call no longer
binds a class member or a property.
- A PascalCase name a .tsx/.jsx file declares as a module-level value
is that declaration wherever the file uses it: the function the value
wraps (`observer(function X() {…})`, `forwardRef(Inner) as …`), else
the declaration, never another app's same-named component (#2420).
The lazy loader half needs W7-09's reader.
- A PascalCase const declared through forwardRef/memo (bare or `React.`)
or a styled-components/emotion tag (`styled.x`…``, `styled(Base)`…``,
typed `styled.x<P>`…``, #2429) is a `component` node, and an inline
render function's calls are the component's (#841). React's extract no
longer mints regex component/hook duplicates of tree-sitter nodes; it
emits routes only, now from .tsx/.jsx files too.
js_scan ports the bracket and string scanner upstream reads declarations
with, iteratively, so deeply nested source cannot exhaust the stack.
Goldens: routers gains the four .tsx page and data-router routes and one
route -> Layout edge; synthesis loses the regex `useCartStore` hook node
(log row in docs/equivalence.md). Precision: express-medium identical;
this repository +4 routes, -8 regex hook duplicates, and a Svelte
`onclose?.()` is now unresolved, as upstream; typescript-large and
LibreChat sampled 20 + 20 rows each, no wrong edge introduced.
LibreChat loses 919 reference sites in the interim: 857 behind the
`~/` alias its client/tsconfig.json declares (Rust reads only the root
tsconfig; upstream #2165's nearest-config aliases come with W7-09) and
62 rendering a module whose default export is `memo(X)` (#2563's
static half, W7-09). Upstream landed #2120 before #2165 the same way.
The sync matrix gains tsx_route_element_body_edit and
tsx_wrapper_value_body_edit; the react_dependency cases observe a data
router's route instead of a regex hook node. Upstream's later react.ts
rows #2737 (PORT) and #2738 (PARTIAL) are left for the next round.
…sioning
In a TS/JS module the provider convention (`*Service`, `*Controller`, …)
now takes only the reference's own class or the convention file beside
it (users.controller.ts -> users.service.ts); anything farther is import
resolution's or name matching's (upstream #2246). Nest's own specs'
`TransientService` no longer reaches an integration app's, and a
decorator call `@Controller('users')` no longer instantiates a PHP class
named `Controller`.
A NestJS route is named by the path a request takes to it (upstream
#2117). post_extract reads each `NestFactory.create` bootstrap and
renames the routes of its app's controllers in NestJS's order,
`/{prefix}/{v}{version}/{RouterModule prefix}/{path}`:
- `exclude` paths (plain, `{ path, method }`, `{/*wildcard}`, `(.*)`,
`*`) skip the prefix and keep their version;
- a route's version is its own `@Version` (found past decorators with
object arguments), else its class's `@Version` or `@Controller({
version })`, else `defaultVersion`; `VERSION_NEUTRAL` has none, and
header or media-type versioning adds no segment;
- a computed prefix is not guessed; each app takes the bootstrap with
the longest root above it, a seed script that creates the app without
serving it does not displace the one that configures it, and an app
that sets nothing keeps its routes as written;
- only a route whose decorator sits in a `@Controller` is renamed.
A route keeps its id and qualified name. One an earlier pass renamed
that nothing renames any more (a removed global prefix, version or
RouterModule entry) gets the name its file gives it back, so a sync
converges with a fresh index; before, a removed RouterModule entry left
the old name, as colby's sync does. The decorator regexes are compiled
once, since the pass now scans controller files on every sync.
Goldens: servers' NestJS route becomes `GET /api/v1/users/:id` (log row
in docs/equivalence.md). Precision: express-medium and typescript-large
identical; this repository renames that route and drops the PHP
`Controller` guess; nestjs/nest's samples and specs move 43 provider
references from another app's class to the spec's own and resolve 14
more. Its 302 routes without a bootstrap of their own take the `/api` of
the one root-level e2e spec that sets one, by upstream's bootstrapFor.
A sync of typescript-large declaring @nestjs/core takes 2.8% longer
(median of three).
The sync matrix gains four NestJS cases and checks node names. The
Express and cross-tier halves of upstream's test come with W7-19a and
W7-20.
…component Nuxt's file routes move from the Vue resolver to their own `nuxt` resolver (upstream #2130), detected by a `nuxt`, `nuxt3` or `@nuxt/kit` dependency or a `nuxt.config.*` file, and gated to the apps whose nearest manifest declares one (#2129). A plain Vue app keeps its views in a `pages/` folder just as often, and those are components a router config names, not addresses: halo's console no longer makes a route of each of them. Every path gets a leading `/` before the `pages/`, `server/api/` and `middleware/` searches, so a Nuxt app at the repository root finds its endpoints and middleware as a nested one does; a top-level `pages/index.vue` is `/`, and a `server/api/` file takes a page's param syntax (`[id]` -> `:id`, `[...slug]` -> `*slug`; `index` is the folder's path, `/api` at the top). A page route `calls` the component its own file is (#2267, new `frameworks/page_component.rs`): the route says so by the file's basename, and the resolver lands it on that file's component only, never on a same-named one elsewhere (every `index.vue` is a component named `index`). The viewer's routes list counts a component as a route's handler, so a Nuxt page is listed with the component that serves it. Goldens: routers' Nuxt index route becomes `/` (its id moves) and its two pages gain a `framework` edge each (log row in docs/equivalence.md). Precision: express-medium and typescript-large identical; elk's 59 pages each reach their own component, its root route is `/` and its four `server/api/` routes appear; halo's console loses 12 routes that were components. The sync matrix gains three Nuxt cases: a `nuxt.config.ts` appearing, an app's manifest coming to declare Nuxt, and a page's template edit. The Astro half of #2267 comes with W7-19a, and #2130's Vue Router admin-template half with W7-11. Upstream's later change to vue.ts (#2633, 986de95c: `@/`/`~/` imports left to the import resolver) is left for the next round.
Explore opens with the call path among the symbols the query names (upstream's Flow section, #365): when two or more named callables are connected by a chain of three or more, one numbered line per symbol and one `↓` line per hop come before the header. The search is named_symbol_flow's, the one the viewer's Flow rides, so of two equally long routes the Flow takes the one through the symbols the query names (#2453's explore half). A hop names the conditions its call runs under (#1655). The new codegraph-graph branch_guards module reads them from the caller's current source with the extractor's grammars: the if/else and ternary arms, switch cases, `&&` / `||` short circuits and catch blocks around the call site up to the enclosing function, and the early exits before it, negated, for JavaScript, TypeScript, Swift, Python, Java, Kotlin, C#, Go, C, C++ and Objective-C callers: `↓ calls (when !busy && ready)`. A caller file that changed since it was indexed, or one above 256 KiB, gets no condition rather than one read at a moved line. Upstream's rules read fwcd's Kotlin grammar and an older Go grammar; this port reads kotlin-ng's tree (arms without control_structure_body, return_expression, a bare break or continue as an identifier) and tree-sitter-go 0.25's statement_list for the same labels, and passes every guard label of upstream's branch-guards tests. codegraph-graph now depends on codegraph-extract for the grammars. A synthesized hop reads as the dynamic dispatch it is, in the Flow and in the codegraph_node trail: `dynamic: interface → impl @src/pdf-exporter.ts:5` (new synthesized_hop module, upstream's synthesized-hop.ts at cd97bb37, #2428). The index has no synthesized edges until the synthesis stage (W7-14), so only the engine test shows the tag for now. Not in this commit: the dynamic-dispatch links among the named symbols (W7-26), boundary and polymorphic-dispatch announcements for a flow that does not connect (the port keeps its boundary scan over the explore seeds), and the spine's weight in file ranking and body windows (W7-25). Upstream's interface cases of #2453 need interface-impl edges and come with W7-14. Upstream rows left for the next round: #2750 (f5dfa21f, PORT, named-symbol-flow.ts), #2769's branch-guards hunk (6bd217a2, PARTIAL, the Steps status reader this commit does not port), and the explore rows #2783 (1890b10c, PARTIAL), #2789 (214ef50e, PORT) and #2557 (5da4a41d, PARTIAL). Goldens and the MCP fixtures are unchanged: the mini fixture's query `Counter add runDemo` connects no chain of three, because runDemo calls add directly and the walk reaches it first.
A `NestFactory.create` in a test file (an e2e spec) with no `src/`
segment above it now belongs to the directory above its test folder
(`integration/security/` for `integration/security/e2e/csrf.spec.ts`),
or to the directory holding it. Within one app root, the bootstraps of
the app's entry points decide over a test's: an e2e spec's
`setGlobalPrefix('api')` does not rename the routes `src/main.ts`
serves.
W7-02 ported upstream b635dd46's appRootOf and bootstrapFor as they
are. They put every such spec at the repository root and prefer the
bootstrap that configures the app, so on nestjs/nest one integration
app's spec gave its `/api` to 306 routes of ten other integration apps
and packages/core's specs. With this change only
integration/security's own routes and sample/24's keep `/api`.
express-medium and typescript-large are identical, and no golden
changes. This is a deliberate difference from upstream (KEEP-RUST,
recorded in KNOWN_DIFFS.md). It answers the W7a sub-wave gate's first
round.
W7-04 (9ef9f69) names the resolver on the edges a framework resolution draws (`metadata.framework`, upstream #2159) without adding a sync case of its own. The plan requires one for every commit that changes crates/codegraph-resolve/src. The matrix harness gains `edge_metadata_after`, which requires an edge of the fresh index to hold a value under a metadata key. Each sync is already compared with the fresh index by every metadata key. Two cases use it: - tauri_command_body_edit_keeps_the_invoke_edges_resolver_name: an edit to a Tauri command's body re-resolves the TypeScript `invoke('greet')` at its site, and the edge keeps `framework: "tauri"`. - tsx_framework_edge_keeps_its_resolvers_name: a `.tsx` file is re-extracted, and the React-resolved edge to the function a component value wraps keeps `framework: "react"`. Both cases fail if W7-04's naming is removed (the fresh index lacks the key). They also fail if the incremental resolution drops the name (the sync differs from the fresh index). Only test files change.
Next.js gets a resolver of its own, as upstream F3 (9c6bc23b) split
nextjs.ts out of react.ts. React's old rule read any `pages/` or `app/`
file with a default export as a page, kept route groups in the path
(`/(commonLayout)/tools/custom`) and drew no edge.
- A page is a route named by the path a request takes: a `(group)` is no
segment, `[slug]` is `:slug`, `[...all]` and `[[...all]]` are `:all*`.
It `calls` its default export. A `route.ts` handler is one
`METHOD /path` endpoint per exported method, referencing it; a
`pages/api/` file is `ANY /path`. Layouts, `_app`, configs, tests and
parallel or intercepting routes are no route.
- Next is detected by a `next` dependency, or a `next.config.*` beside a
route file, and names `next` as its app dependency (#2129): in a
monorepo it reads only the apps whose manifest declares it.
- `router.push` / `replace` / `prefetch`, `redirect`,
`permanentRedirect` and `NextResponse.redirect(new URL(...))` under a
Next app become a `navigates` edge to the page their href names, with
the `href` and `navMethod` in its metadata; each arm of a conditional
is an edge. The href readers and the matcher are upstream's Expo Router
ones, in a new shared `route_href` module: a string, a template whose
holes fill parameters, a `{ pathname }` object, a local `const href`.
A computed href, a relative one or a path no page serves stays
unresolved.
- When several pages serve one path (two apps of a monorepo with
`/login`), a call goes to its own app's page; several or none there
is nothing. Upstream keeps the last page in node order, which a sync
changes (KEEP-RUST, recorded in KNOWN_DIFFS.md).
The routers golden is rewritten (log row in docs/equivalence.md): the
three Next page routes take upstream's ids and gain their `calls` edges,
`GET /api/auth` appears, and the Dashboard's `router.push('/')` becomes
a `navigates` edge. On Dify's Next.js web the 31 page routes lose their
route-group segments, each calls its own file's component, and 27
navigation calls reach the pages they name; express-medium,
typescript-large and LibreChat are identical, and halo loses the
`/module` route React's rule made of a Vue console file.
Not in this commit: `<Link href>` and `<a href>` (a synthesizer, not a
call), the Screens and Steps builders that read these edges, and the
sync retry for a navigation call whose page appears later in another
file (upstream #2425).
Expo Router gets a resolver of its own (upstream F2, expo-router.ts at
b635dd46, with #2135's API routes).
- A file under `app/` or `src/app/` is a screen named by its path: a
`(group)` is no segment, `[id]` and `[...rest]` stay as written, an
`index` is its directory, a platform suffix (`.ios.tsx`) is dropped.
The route `calls` the file's default export. Layouts and the other `_`
and `+` files are no screen, except `+not-found`. A `+api` file is one
`GET /path`-style endpoint per exported method, referencing it (#2135).
- Expo Router is detected by an `expo-router` dependency, or an
`app/_layout` beside an `app.config.*`, and names `expo-router` as its
app dependency (#2129).
- `router.push`, `replace`, `navigate` and `dismissTo`, and a project's
own wrapper named for the verb (`safePush`), become a `navigates` edge
to the screen their href names, through the shared href readers; a
relative href is read from the calling screen's directory, and a
wrapper is remembered as `via`.
- A call matches only the screens of its own Expo app, the longest
directory above an `app/` that holds it; a file under no Expo app is
not read, and a path two screens of that app serve stays unresolved
(KEEP-RUST, recorded in KNOWN_DIFFS.md). Upstream keeps one table for
the project and reads every file: in the routers corpus, the React
Router app's `navigate('/')` reached the Expo app's `/` screen.
The Next.js resolver's duplicate-path rule moves into route_href as a
shared `ExactTable`, and the routers' unit tests share one test context.
The routers golden is rewritten (log row in docs/equivalence.md): the
Expo app's `/` and `/settings` screens and its `GET /api/hello`
endpoint appear, and the index screen's `router.push('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/settings')`
becomes a `navigates` edge.
A monorepo app declares its own `paths` (`@/*`, `~/*`) in a tsconfig.json or jsconfig.json below the repository root, and only the root's aliases were read: LibreChat's `~/hooks`, bulletproof-react's per-app `@/lib/auth` stayed unresolved, and since imported names wait for import resolution (#2120) their calls, tags and imports lost their edges. - An import tries the aliases of the config nearest the importing file first (walking up its directories, the root excluded; a directory counts only when its tsconfig.json or jsconfig.json yields aliases, so a lone tsconfig.base.json does not), then the root's. Upstream #2165 (getNearestAliases). Both resolution contexts cache the lookup per directory. - An importing path that is absolute or climbs with `..` never walks. - Sync re-resolves the JS class when an ancestor directory's tsconfig.json or jsconfig.json appears, changes or goes away, or a config one of them extends changes. Precision against the previous commit: express, typescript-large, this repository, adifyweb and mantis identical; bulletproof-react and LibreChat lose no site and gain 898 and 6665, each in its own app or package.
React Router routes were read by a v1.0 regex scan that named a nested
route by its own segment (`login`, `c/:conversationId?`) and never read a
route object whose path is a constant or whose page loads lazily, and a
navigation call (`navigate('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/login')`, `history.push(…)`) resolved to
nothing.
- A structural scanner, ported from upstream's `scanRoutes` (the part
that reads one file), reads `<Route>` elements and the route objects of
a file that makes a router: nested paths compose, an index route is
its parent's address, a route around others is the layout they render
inside (`references` with `layout: true`), `lazy: () => import(…)`
renders the module's default or `Component` export, an `<Outlet />`
route renders nothing of its own, and `path={paths.x}` is a route the
file does not spell out. A path built from a constant
(`paths.app.root.path`) is named in `post_extract`, which now runs
before resolution in index, migration, sync and the orphan sweep, as
upstream's does, so a fresh index and a sync agree.
- A `react-router` resolver turns `history.push`/`replace`,
`navigate(…)`, `router.navigate(…)` and `redirect(…)` into `navigates`
edges carrying the href, matched against the routes of the caller's own
app: a literal, a template with holes, a `{ pathname }` object, a local
`const href`, `paths.x.getHref(id)`, every arm of a conditional.
Upstream F4 (#2165, #2187, #2400, #2423, #2436, #2558, #2670).
- KEEP-RUST: a path two routes of one app serve stays unresolved rather
than taking the first in node order, and a route file nested past 256
levels yields no routes rather than running the stack out.
Precision against the previous commit: express, typescript-large,
adifyweb and mantis identical; this repository changes only the routers
fixture, as its golden; bulletproof-react gains ten constant-path routes
and three navigation edges; LibreChat's 14 relative routes become 16
composed ones and seven navigation calls resolve. Upstream's first-wins
table would resolve one more call (bulletproof's `navigate('/app')`).
A data-router `lazy` loader that picks the export it renders (`const {
TestTab } = await import('./tabs'); return { Component: TestTab }`) was
read for its first import, so the route rendered the module's default or
`Component` export, and a barrel of tabs has neither. A module-level value
a lazy wrapper loads (`Loadable(lazy(() => import('./Register')))`) was
bound by name, so a tag naming it reached another app's same-named page.
- The loader readers, ported from upstream (#2480), follow what a loader
hands back: a destructured or `.then`-picked export, `(await
import('./x')).Page`, a module binding, `Promise.all`, an `async lazy()`
method, React Router 7's object of lazy properties, the page an
`element` shows past a guard, or a component of the route file. The
route's reference names the pick (`import:./x#Page`), which the React
resolver finds through barrels, only for a React route's reference. A
loader that hands back `Outlet` renders nothing of its own (#2670); one
that hands over only a `loader` makes no page.
- A PascalCase value the file declares is, wherever the file uses it, the
component of the module its lazy wrapper loads, through `Loadable`,
`dynamic(…, options)`, a loader option or either branch of a
conditional (#2420, #2452's route half).
- KEEP-RUST: a loader nested past 256 levels links nothing rather than
running the stack out.
Precision against the previous commit: express, typescript-large, this
repository, adifyweb, bulletproof-react and mantis identical; LibreChat's
two tags naming a same-file `lazy()` value (`<AccountSettings>`,
`<BookmarkNav>`) reach the component of the module it loads instead of the
value itself.
A route table written in one file and handed to the router in another
(`useRoutes(routes)`, `createBrowserRouter([MainRoutes, LoginRoutes])`,
`AppRoutes.map((route) => <Route {...route} />)`) made no routes: the
scanner read only the route objects of a file that makes a router
itself.
- The table pass, ported from upstream (#2400, #2423, #2668, #2672),
starts from every non-test file that imports React Router and hands a
table over, finds the table where it is declared, imported or
re-exported, or as what a function a value calls returns (through
`useMemo` too), and makes its route objects, and the tables they name,
routes under the path they are mounted at. They render inside the
layouts around the mount only when the table is written in the same
file. A navigation call binds to these routes as to any other.
- The routes are cross-file nodes (`route:<file>:<line>:table:<path>`):
a pass after the constant naming compares the routes the tables give
now with the stored ones, before anything resolves, in an index, a
migration, every sync and the orphan sweep. The edges that reached a
removed route are resolved again; a sync withdraws its markers only
when the pass changes the store, and a re-extraction of a file keeps
the references of the routes in it.
- KEEP-RUST: a route whose name or references changed is deleted and
inserted again with its new references, where upstream renames it in
place and keeps the old ones; a file nested past the scan bound hands
over no table either.
- The table readers blank comments byte for byte, so an offset found
past a comment holding non-ASCII text still indexes the file.
Precision against the previous commit: express, typescript-large, this
repository, adifyweb and bulletproof-react identical; LibreChat gains
the three routes of `client/src/routes/Dashboard.tsx`, which its router
names among another table's children, and mantis the eight routes of
its `MainRoutes.jsx` and `LoginRoutes.jsx`; nothing removed.
A tag whose name the file imports from a module of the project kept
import resolution's or name matching's target: the module's first
exported function (`getToolIconType` for `<ToolIcon>`), a memo
wrapper's bodyless node, a type alias, or a same-named declaration
elsewhere, and nothing when the module's default export wraps the
component (`export default memo(HoverButtons)`) or an index forwards it
(`export { default as X } from './X'`).
- React binds such a tag, ported from upstream (#2563), to the
component the module exports under the imported name: through an
index that forwards it, past a wrapper around it, or through a value
holding another module's component. Where import resolution already
reaches that component, its edge stays.
- KEEP-RUST: where the module exports no component under the name, the
tag keeps import resolution's edge; upstream's `jsx-render` pass
draws nothing there.
- The module surface a dependent re-resolves on covers the imports a
module's own exports forward, read the way React reads them (each
declarator of a statement, past a type annotation; row format 3), so
an index that re-points what it forwards moves the tags behind it on
sync, as a fresh index does.
Precision against the previous commit: express, typescript-large, this
repository and mantis identical. Every change is a tsx tag's
`references` edge, now the module's (`framework`, 0.9): LibreChat 181
sites (75 bound where they were unresolved, 47 retargeted, 59 the same
target), adifyweb 1038 (944, 90, 4), bulletproof-react 434 (the same
target); no tag lost its edge.
A TanStack Router app had no routes in the graph: its
`createFileRoute('/posts/$postId')` pages and `createRoute({
getParentRoute, path })` trees were plain calls, and its
`navigate({ to })` and `throw redirect({ to })` calls stayed unresolved.
- A resolver, ported from upstream, extracts one route per address.
File routes take their path from the literal (`$x` is `:x`, a
trailing `$` a splat; pathless `_layout` segments and `(group)`
folders are not in the URL); an index route wins its address over
the layout around it, and a layout file (`*.route.tsx`, or one that
renders `<Outlet />`) is no page. Code routes are composed through
`getParentRoute` within their file. Each route calls its component.
- `navigate`, `redirect`, `router.navigate` and `Route.navigate` bind
to the route of their own app that the `to` pattern names, as a
`navigates` edge with `href` and `navMethod`. The resolver is
detected by any of the five TanStack Router and Start packages and
runs after React Router and before Next.js, as upstream orders the
routers.
- KEEP-RUST: a pattern two routes of one app serve stays unresolved,
as React Router's does; upstream keeps the first in node order, which
a sync changes.
- KEEP-RUST: a framework result inside a module that exports nothing is
dropped like a name match's, so a route tree in a `main.tsx` that only
renders is reached only from that file.
Precision against the previous commit: express, typescript-large,
LibreChat, adifyweb, bulletproof-react and mantis identical; this
repository gains its routers fixture's two TanStack routes. Five
TanStack Router examples gain 27 routes, 26 route-to-component edges
and 3 navigations. One component edge picks another file's same-named
function in the directory, as upstream's component rule does.
A Vue app's routes table was plain data:
`createRouter({ routes: [...] })` and an admin template's
`constantRoutes` named no route, and `router.push({ name: 'login' })`,
the way Vue apps navigate, stayed unresolved.
- A resolver, ported from upstream, reads the routes tables of a file
that builds a router, imports `vue-router`, writes a `routes` array
or sits in a `router/` directory: one route per entry, a child's
path joined onto its parent's (`children` read only from an array
written in place). A route references its parents' components as
layouts; a parent that a child's path claims or that redirects is a
frame, not a page.
- A route calls the component its import takes: a named import the
module's exported node of that name, through export lists and
re-exports; a default import or a lazy `() => import(...)` the
module's default export, a `.vue` file's own component. The module
is the one the router file's import resolves to, or, through an
alias import resolution misses, the one file of the app whose path
ends the same way. KEEP-RUST: the route's reference to an identifier
is `component:<name>`, which only this resolver answers, so an import
it cannot follow binds no namesake; upstream takes the module's first
component-like function and falls back to any component of the name.
- `router.push`, `router.replace` and `this.$router.push` bind by name
to the route of their own app that the table names so, else by path
to a config route or a Nuxt page, as a `navigates` edge with `href`
and `navMethod` (and `by: name`). KEEP-RUST: a destination is read
only when it is whole (a string, a template whose holes fill whole
segments, a conditional of them, a local initialized to one, an
object's own `name` or `path`), so `'/redirect' + fullPath` reaches
nothing; and a name or a path that more than one entry of an app
carries stays unresolved, where upstream keeps the first.
- The resolver is detected by `vue-router`, `nuxt` or `nuxt3` and runs
after Next.js and before Expo Router, as upstream orders the routers.
A bare `navigateTo` now passes the name filter as a navigation call,
and the Vue resolver answers it first as a Nuxt auto-import, an edge
to its own caller, as upstream does at the audited revision.
Precision against the previous commit: express, typescript-large,
LibreChat, adifyweb, bulletproof-react and mantis identical; this
repository gains its routers fixture's Vue routes. Across six Vue apps
and the fixture: 92 routes, 92 route-to-view edges and 62 layout
references, each to the component its entry names, and 15 navigations.
A SvelteKit app's pages were plain components:
`src/routes/+page.svelte` and `src/routes/blog/[slug]/+page.svelte`
named no route, `goto('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/login')` and `redirect(303, '/')` stayed
unresolved, and some `$lib/...` imports reached nothing.
- A `svelte` resolver, ported from upstream, makes each `+page.svelte`
under a `routes/` directory a route named by its directory: a
`(group)` folder is no part of the address and a parameter's
`=matcher` no part of its name (upstream #2131), `[[lang]]` is
`:lang?` and `[...rest]` is `*rest`. A layout, an error page, an
endpoint or a loader is no route.
- It resolves what Svelte's and SvelteKit's conventions name: a rune is
the compiler's, `$app/*` and `$env/*` are SvelteKit's own modules,
`$lib/x` is the file `src/lib/x` of the importing file's app, and a
capitalized call names a component. KEEP-RUST: `$lib` is read from
the importing file's app rather than the repository root, a store
subscription binds only the store its own component declares, and a
component call binds only the one component of its name in the
calling file's directory or, failing that, in the project.
- A `sveltekit-router` resolver binds `goto` (destination first) and
`redirect` (destination after the status) to the page of their own
app that serves the path, as a `navigates` edge with `href` and
`navMethod`. A page whose last segment is `[[optional]]` also serves
the shorter path unless another page serves it. KEEP-RUST: a
destination is read only when it is whole, as Vue Router reads it, an
absolute URL reaches nothing, and a path two pages of one app serve
stays unresolved, where upstream keeps the first page in node order.
- Both are detected by their dependencies (Svelte also by any `.svelte`
file) and run after Next.js and before Vue Router, as upstream orders
them. `<a href>` links and a page's join with its loader are synthesis
passes, which come later.
Precision against the previous commit: express, typescript-large,
LibreChat, adifyweb, bulletproof-react and mantis identical; this
repository gains its routers fixture's SvelteKit routes. Across three
SvelteKit apps (sveltejs/realworld, shadcn-svelte, svelte.dev): 45
routes, 17 navigations each to the page its destination names, and 48
`$lib` imports that reached nothing now reach their app's file.
An Angular app's routes were plain arrays: `{ path: 'login', component:
AuthComponent }` named no route, and a lazily loaded routes file's
paths were never seen under the path that loads it.
- An `angular-router` resolver, ported from upstream (#2113, #2133),
reads the routes arrays of each TypeScript file that imports
`@angular/router`: an array typed `Routes` or `Route[]`, handed to
`RouterModule.forRoot/forChild` or `provideRouter`, a routes file's
`export default [...]`, or a `const` that says `as Routes`. Each
screen is a route that references the component it renders (a
`component` class through import resolution, the `@Component` class
a `loadComponent` import picks) and the layouts around it in its file
(`layout: true`). A route with `children` is a layout, a screen only
where no child claims its address; a redirect, a `**` path, a custom
`matcher` and a computed path are none.
- After extraction, each file's routes are renamed under the path that
lazy-loads the file: a `loadChildren` import, followed through
barrels (an Nx library's `src/index.ts`) and an NgModule's routing
modules, with constant segments (`internalRoutes.account.path`, an
enum member, a class's static field, `$localize`) read from their
declaration. A file mounted from two places sits under the first in
path order, as in upstream. The renaming runs in every sync, so an
edited mount or constant renames routes in files the edit did not
touch.
- KEEP-RUST: `children` and a constant's nested object are read only
where written in place, a cycle of mounts settles on each file's own
chain, and a chain of local aliases is followed at most eight hops.
- Navigation calls, template links and the viewer's Angular parts come
in later commits.
Precision against the previous commit: express, typescript-large,
LibreChat, adifyweb, bulletproof-react and mantis identical; this
repository gains its routers fixture's two Angular routes. Across five
Angular apps (angular-realworld-example-app, ngx-admin,
jira-clone-angular, angular-spotify, ghostfolio): 168 routes, each
checked against its routes file, and nothing removed.
An Angular app's `this.router.navigate(['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/login'])` reached nothing: its routes were nodes since the previous commit, but no call was bound to them. - The `angular-router` resolver, ported from upstream (#2113, #2133), binds `navigate`, `navigateByUrl`, `createUrlTree` and `parseUrl` on the router to the route of the caller's app its destination names, a `navigates` edge with the `href` and `navMethod`. The destination is a command array, a static string, `'/editor/' + slug`, a route constant or one whose arrow function builds the commands, a property of the calling class, or any of those `.concat(…)`. A computed command is a hole, and a first command without a leading `/` is read from the root unless it starts with `.`; a call with `relativeTo` reaches nothing. - An app is the directory of an `angular.json` or `nx.json` above a routes file, else its conventional app root, and a file under no app uses the only app's routes; an app whose routes all hold unread constants still counts. A redirect gives its path the route it leads to, and `/` that nothing serves takes the root `**` redirect's. - KEEP-RUST: the table does not depend on node or file order. A path two routes of an app serve stays unresolved, a redirect is followed through the others at most four hops, and a path whose redirects lead to two routes reaches neither; upstream keeps the first in node order. `'/editor/' + slug` is read only when the literal ends with `/` and one operand follows, a conditional or template is read whole, a relative arm or one that leaves the site (`//host/…`) drops out before matching, and a route or redirect with a constant the cross-file pass could not read is in no table. - Five sync shapes still diverge from a fresh index (a competitor route or redirect in another file, an edited route constant, a route that appears where a parked call points, a workspace marker that appears); they are kept as seeds for the sync relinking commit. Precision against the previous commit: express, typescript-large, LibreChat, adifyweb, bulletproof-react and mantis identical; this repository's routers fixture gains its Angular navigation. Across five Angular apps: 40 navigations (angular-realworld-example-app 10, jira-clone-angular 3, ghostfolio 27), each checked against its call.
A per-piece mutation run of React Router's routes and navigation found
four pieces whose removal left every test green: a navigation needs a
call, a route answers its own app's calls, and both a sync and a
migration name a route a constant spells before anything resolves.
- A `references` reference at a `history.push('/shipping')` site
resolves to nothing; the same site as a call navigates.
- A route of `frontend/src/App.js` answers the calls of `frontend/`,
where the existing case had only the repository's root app.
- A sync case edits a routes file whose route a constant names and
whose `redirect('/app')` names the route: the sync renames the
re-extracted route before it resolves the call, as a fresh index does.
- An outdated-index migration keeps that call's `navigates` edge and
equals `index --force`.
Each new assertion fails when its piece is removed and passes on the
code as it is.
The v0.56.0 regeneration log in docs/equivalence.md had no command for three routers rows (React Router, Vue Router, Svelte and SvelteKit), and two rows named their commit by a shorter title than the commit's. The rows now carry `scripts/regen-goldens.sh --write routers` and the exact titles. These commits took b635dd46's behavior for an upstream row, or ported part of a change and left the rest to a later commit, without saying so in their own message: - `feat(resolve): React Router routes and navigation` takes React's routing at e8ddc4e2 without #2737 (PORT: Pattern 1's `ownDeclarations` and `namesType`) and #2738 (PARTIAL: `wrappedComponentName`, the nested `React.memo(forwardRef(…))` wrapper), and writes #2769's five escapes (PARTIAL) as b635dd46 did. The three rows are left for the next round, and with #2738 the one `React.memo(forwardRef(…))` site that `fix(resolve): an import alias is the nearest tsconfig's` measured. The sync halves of #2560 and #2452 go to the commit that relinks routes on sync, and #2132 with #2187's Link-tag fix to the link synthesis pass. - `feat(resolve): Vue Router routes and navigation` keeps the audited revision's bare `navigateTo` in a Nuxt app. Upstream #2826 (58eb57a3, after the audited range) makes it a navigation and adds an `ownsReference` hook; the next audit takes it. - `feat(resolve): Angular routes and the components they render` and `feat(resolve): Angular navigation` port angular-router.ts only. #2113's template synthesizer and its callback-synthesizer change, and #2114 and #2398, go to the template synthesis pass; the viewer parts of #2113 (route roots, Screens) and of #2114 and #2398 (Steps) to the viewer commit. The navigation hook's documentation in framework.rs named only Next.js and Expo Router; every router resolver answers it now.
This branch has not been deployed
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.
Summary
The v0.56.0 follow-up program: the work split out of v0.55 (OD-18) plus the
items that came after it. This PR stays a draft while the waves land; each
wave is pushed when its local gate passes.
Planned scope:
resolvers)
codegraph context, upstream #1617, and the v1.5.0 performance strategiesW0 (pushed)
graph meaning, so users rebuild once.
upgrade_from_v055covers thev0.55 → v0.56 path (a v0.55 index is
Outdated{22}and upgrades to agraph equal to a fresh
init).StoreError::FutureSchemafrom every opening path (sync, daemon catch-up,MCP sessions,
rebuild), probed WAL-aware before any writable open. Therecovery text offers
uninitonly whereuninitwould succeed.uninit --forcecan still discard such an index.uninit --forcebeside a leftover tombstone: a current index next tothe uninit tombstone is now discarded (the tombstone stays, so only
initrebuilds it), and a newer schema is reported before the tombstone.
std-typed receivers (
PathBuf,&Path) stay unresolved.tree-sitter-swift,which stays at 0.7.3 (0.7.4 regenerates the grammar; documented in
docs/grammar-manifest.md).Verification (W0, HEAD 5cb5f32)
make check: 239 suites, 6135 passed, 0 failed, 1 ignored.make pre-ci(pre-push hook): passed, including the viewer bundle bytecheck and the release-archive smoke.
scripts/regen-goldens.sh --check); graphdiffs over the precision corpora identical before and after the dependency
updates.
within budget.
Not in this PR yet
W7-W10. This description is updated as each wave is pushed.