fix(register): resolve cross-package + transitive-version + cache-mismatch canonical lookups#151
Conversation
…match canonical lookups The canonical resolver had three related bugs causing 'Base resource not found' errors when packages have different versions of the same dependency. This commit consolidates three iterations (vrq, 7y9, pkt) of the fix into a single change. ## Bug 1: kbv.basis@1.8.0 .index.json has null id entries de.basisprofil.r4@1.5.4 ships an .index.json with null-id ImplementationGuide entries. parseIndex treats any null id as fatal and rejects the entire .index.json — leaving the package with 0 indexed resources. Cross-package base-type lookups for KBV profiles that inherit from de.basisprofil.r4 fail at transform time. Fix (vrq): when manager.search returns 0 for a package, scan the canonical-manager's node_modules cache directly. Equivalent to scanDirectoryForResources but tolerant of malformed .index.json. Implemented as scanNodeModulesPackage with a nodeModulesPath parameter computed from the focusedPackages cache key. ## Bug 2: top-level + transitive package version mismatch When the top level loads de.basisprofil.r4@1.6.0-ballot2 and a dependency (kbv.basis) needs de.basisprofil.r4@1.5.4, npm installs the older version into a nested location: node_modules/kbv.basis/node_modules/de.basisprofil.r4/. The original scanNodeModulesPackage only checked the flat top-level path → returned wrong-version content. Fix (7y9): scanNodeModulesPackage now reads the package.json version from the flat path, and if it doesn't match the requested version, scans nested node_modules/<parent>/node_modules/<dep>/ paths. Returns the first one whose version matches; falls back to flat path if no match (graceful degradation). ## Bug 3: APIBuilder.localTgzPackage cache-key divergence When packages are added via APIBuilder.localTgzPackage() (or addTgzPackage on the manager), the canonical-manager's cache hash is computed from constructor packages ONLY. addTgzPackage adds packages to the cache record but doesn't change the hash. Codegen however computed nodeModulesPath from ALL focusedPackages → divergent hashes → computed path doesn't exist → fallback never triggers → silent failure. Concrete failure: polaris dental builder uses .localTgzPackage(praxis) + .localTgzPackage(dental). manager.search returned 0 for de.basisprofil.r4@1.6.0-ballot2 even though the resource existed in the actual cache directory. Fix (pkt): when the computed nodeModulesPath does not exist, scan ALL sibling cache record directories under <workdir>/canonical-manager-cache/ and try each one. The warning message identifies which cache record was actually scanned. ## Tests - test/unit/typeschema/versioned-canonical.test.ts (5 tests): cross-package base type lookup with kbv.basis@1.8.0 + de.basisprofil.r4@1.5.4 against null-id .index.json scenario - test/unit/typeschema/transitive-version-mismatch.test.ts (5 tests): - Integration: 1.6.0-ballot2 top-level + kbv.basis@1.8.0 → no errors - Synthetic: scanNodeModulesPackage prefers nested 1.5.4 over flat 1.6.0-ballot2 - Synthetic: scanNodeModulesPackage scans sibling cache dirs when computed path missing All 10 tests pass on upstream/main.
|
Thanks for pointing to the kbv-r4 example and the patch-on-load pattern. I tested all three of the failure cases this PR addresses against
One related caution from our side: in Polaris we previously had a Happy to drop the resolver code paths that are covered by |
|
Currently, I will investigate a problem deeply to find a good way how it should work with conflict versions and also how to leave a control on the user’s side. I will return here soon. Looks like it should be a different approach for regular and core packages. |
Replace the explicit hl7.fhir.r4.core dep (now pulled transitively) with de.basisprofil.r4@1.6.0-ballot2 and kbv.basis@1.8.0 to exercise the cross-package version graph from PR #151.
|
Hi @sussdorff! Did I understand your setup correctly: you want types for
specified like this: .fromPackage("kbv.ita.for", "1.3.1")
.fromPackage("de.basisprofil.r4", "1.6.0-ballot2")
.fromPackage("kbv.basis", "1.8.0")and you hit: If so — the failure is in For cases like this we already maintain a hardcoded skip list in I also improved the error message for this case so the next person hits a clearer signal: All three changes are in #153 — please give it a look and let me know if it covers your case. |
Summary
Three related canonical-resolver bugs caused 'Base resource not found' errors when packages have different versions of the same dependency. Discovered while running codegen on @polaris (fhir-de) which loads
de.basisprofil.r4@1.6.0-ballot2(top-level) pluskbv.basis@1.8.0(transitive, depends onde.basisprofil.r4@1.5.4) plus FHIR IG packages vialocalTgzPackage().This PR consolidates three iterative fixes from our cognovis fork (vrq → 7y9 → pkt) into a single change.
Bugs Fixed
1. Null-id
.index.jsonrejection causes 0 indexed resourcesde.basisprofil.r4@1.5.4ships an.index.jsonwith null-idImplementationGuideentries.parseIndextreats any null id as fatal and rejects the entire.index.json— leaving the package with 0 indexed resources. Cross-package base-type lookups for KBV profiles (e.g.KBV_PR_Base_Observation_Care_Level→observation-de-pflegegrad) fail at transform time.Fix: When
manager.searchreturns 0 for a focused package, fall back to a direct directory scan of the canonical-manager'snode_modulescache (scanNodeModulesPackage). Equivalent to the manager's ownscanDirectoryForResourcesbut tolerant of malformed.index.json.2. Top-level + transitive package version mismatch
When the top level loads
de.basisprofil.r4@1.6.0-ballot2and a transitive dep (kbv.basis) needsde.basisprofil.r4@1.5.4, npm/bun installs the older version into a nested location:node_modules/kbv.basis/node_modules/de.basisprofil.r4/. The originalscanNodeModulesPackageonly checked the flat top-level path → returned wrong-version content silently.Fix:
scanNodeModulesPackagenow readspackage.jsonversion from the flat path; if it doesn't match the requested version, scans nestednode_modules/<parent>/node_modules/<dep>/paths. Returns the first one whose version matches; falls back to flat path with a warning if no exact match.3. APIBuilder.localTgzPackage cache-key divergence
When packages are added via
APIBuilder.localTgzPackage()(ormanager.addTgzPackage()), the canonical-manager's cache hash is computed from constructor packages only.addTgzPackageadds packages to the cache record but doesn't change the hash. Codegen however computednodeModulesPathfrom ALLfocusedPackages(those returned bymanager.init(), including tgz adds) → divergent hashes → computed path doesn't exist → fallback never triggers → silent 0-resource failure.Concrete failure: a build like
```ts
new APIBuilder()
.fromPackage('hl7.fhir.r4.core', '4.0.1')
.fromPackage('de.basisprofil.r4', '1.6.0-ballot2')
.localTgzPackage('vendor/de.cognovis.fhir.dental-0.17.0.tgz')
.typescript({})
.outputTo('generated/dental')
```
returns
Base resource not found 'http://fhir.de/StructureDefinition/observation-de-pflegegrad'despite the resource existing in the actual cache.Fix: When the computed
nodeModulesPathdoes not exist, scan ALL sibling cache record directories under<workdir>/canonical-manager-cache/and try each one. The warning message identifies which cache record was actually scanned.Test plan
10 tests across 2 files, all passing:
test/unit/typeschema/versioned-canonical.test.ts(5 tests):kbv.basis@1.8.0+de.basisprofil.r4@1.5.4.index.jsonscenario via real package downloadstest/unit/typeschema/transitive-version-mismatch.test.ts(5 tests):1.6.0-ballot2top-level +kbv.basis@1.8.0resolves without errorsscanNodeModulesPackageprefers nested1.5.4over flat1.6.0-ballot2scanNodeModulesPackagescans sibling cache dirs when computed path missingRun with:
```bash
bun test test/unit/typeschema/versioned-canonical.test.ts test/unit/typeschema/transitive-version-mismatch.test.ts
```
Background
This branch consolidates three iterative fixes from our
cognovis/mira-adaptersfork:codegen-vrq: introducednodeModulesPathfallback (Bug 1)codegen-7y9: added nested-path version-aware scan (Bug 2)codegen-pkt: added sibling-cache scan forlocalTgzPackagelayouts (Bug 3)The single squashed commit makes review simpler. Happy to split into per-bug commits if preferred.
We've been running this on production codegen for polaris — verified 11/11 builders complete cleanly.