feat(schema)!: per-module language namespace in can:// ids - #115
Merged
Conversation
ids.ts hardcoded LANGUAGE = "typescript" and every code id descended from it, so a JavaScript module emitted can://typescript/<app>/lib/foo.js/fn. The first can:// segment is a language namespace, and for .js modules it named the wrong language -- on nodejs/node/lib, 406 pure-JavaScript modules all read can://typescript/. moduleIdOf now takes the app NAME and derives the namespace from the file key: .js/.jsx/.mjs/.cjs are javascript, everything else typescript. The match is anchored, so a directory named vendor.js does not change its .ts children, and .d.ts stays typescript rather than being read as a .ts suffix on a .d file. BREAKING for JavaScript ids: Neo4j MERGE keys on id, so re-projecting creates new nodes for JS modules instead of updating existing ones. A store built by an earlier version needs a rebuild, and saved queries referencing JS node ids must be updated. The analysis cache is unaffected -- ids are stamped per run because they embed --app-name, and the cached tree is id-free. The :Application anchor deliberately keeps typescript while owning javascript children. A mixed repository has no single language, so any single-anchor scheme names one language or none; a neutral anchor would move every id in every projection, and two anchors would break the single-anchor invariant (#43). Closes #114. Spec: docs/design/specs/js-language-namespace.md
Merged
rahlk
added a commit
that referenced
this pull request
Sep 3, 2026
ANALYZER_VERSION moves with package.json because it is the cache-invalidation key (utils/cache.ts): this release changes artifact text capture (#117) and per-module id namespacing (#115), so a warm 1.1.0 cache must not be reused against it. Neo4j schema contract is 2.0.0 as of #144 — collapsed back from 2.1.0, since intermediate 2.x versions are not meaningful until every analyzer re-baselines together. Released as a MINOR despite three breaking-marked commits (#115, #117, #144), by explicit decision. The release notes lead with those breaks and their migrations.
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 #114. Spec:
docs/design/specs/js-language-namespace.md(committed in this PR).src/schema/ids.tshardcodedconst LANGUAGE = "typescript"and every code id descended from it,so a JavaScript module emitted
can://typescript/<app>/lib/foo.js/fn. The firstcan://segment isdefined as a language namespace, and for
.jsmodules it named the wrong language. Measured onnodejs/node/lib: all 406 pure-JavaScript modules readcan://typescript/….moduleIdOfnow takes the app name and derives the namespace from the file key:.js/.jsx/.mjs/.cjsarejavascript, everything elsetypescript.assignIds.tsis the onlycaller, so the change is contained to one construction site.
BREAKING
JavaScript ids change. Neo4j
MERGEkeys on id, so re-projecting creates new nodes for JSmodules instead of updating existing ones — a store built by an earlier version needs a rebuild, and
saved Cypher or persisted references to JS node ids must be updated.
The analysis cache is unaffected: ids are stamped per run because they embed
--app-name, andthe cached tree is id-free.
The accepted inconsistency
The
:Applicationanchor keeps sayingtypescriptwhile owningjavascriptchildren. Chosenknowingly over the alternatives: a neutral anchor (
can://app/<app>, mirroring the artifact layer)is the more coherent end state but moves every id in every projection rather than only JS ones,
and two anchors would break the single-
:Applicationinvariant that carries analyzer identity(#43). A mixed repository has no single language, so any single-anchor scheme names one language or
none. Moving to a neutral anchor stays open as a follow-up.
Verification
test/ids.test.ts); typecheck, build, andgen:schemaall clean.
test/checker-guard.test.tsfailed becausethey hardcoded
can://typescript/…for a.jsmodule. That fixture holds both a.tsand a.jsmodule, so it now serves as the two-namespaces-in-one-run assertion..d.tsresolves totypescript(not read as a.tssuffix on a.dfile),and the regex is anchored so a directory named
vendor.jsdoes not make its.tschildrenJavaScript.
can://artifact/,can://javascript/,can://typescript/.