denoland / denoland/deno_graph
Lockfile pin to a JSR pre-release is dropped once upstream publishes a stable version
- Dominant language
- Rust
- Stars
- 137
- Forks
- 47
- Avg merge
- 2d 7h
- Merged PRs (30d)
- 4
Description
A lockfile entry mapping a wildcard requirement to a pre-release version stops being honored the moment the package publishes a stable release, so an upstream publish silently changes an existing build.
### How it happens
`jsr:@scope/a` parses to a `*` requirement, which never matches a pre-release in semver. For a package with no usable stable release, `resolve_version` falls back to considering pre-releases, so `@scope/a` -> `@scope/a@1.0.0-rc.2` gets written to the lockfile.
Once upstream publishes `2.0.0`, the package has a usable stable release, so the fallback no longer applies. Step 1 of `resolve_version` (in-graph/lockfile unification) then evaluates the pinned `1.0.0-rc.2` against `*`, which does not match, and resolution falls through to step 2 and picks `2.0.0` — despite the lockfile.
### Repro
Verified against `prerelease-fallback` (`tests/integration_test.rs`):
```rust
builder
.with_loader(|loader| {
loader.remote.add_source_with_text("file:///mod.ts", "import \"jsr:@scope/a\";");
// upstream has since published a stable release
add_jsr_package(loader, "@scope/a", &[
TestJsrVersion::new("1.0.0-rc.2"),
TestJsrVersion::new("2.0.0"),
]);
})
// the lockfile pinned the pre-release back when it was the only version
.lockfile_jsr_packages(BTreeMap::from([(
PackageReq::from_str("@scope/a").unwrap(),
PackageNv::from_str("@scope/a@1.0.0-rc.2").unwrap(),
)]));
```
Resolves to `@scope/a@2.0.0`; expected `@scope/a@1.0.0-rc.2`.
### Why it is not fixed in #661
The obvious fix — letting step 1 consider pre-releases for wildcard requirements — is one flag, but `existing_versions` cannot distinguish a lockfile pin from a version another requirement resolved a moment earlier in the same pass. So the same flag also means a bare `import "jsr:@scope/a"` unifies onto a pre-release whenever any *other* import in the graph opted into one, which is a worse surprise than the bug it fixes.
Fixing this properly means teaching step 1 that difference, rather than widening what a wildcard requirement matches.
Pre-existing; not introduced by #661.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with resolve_version and existing_versions, then run the prerelease-fallback case in tests/integration_test.rs using the lockfile_jsr_packages setup shown here. Trace how step 1 unifies in-graph versions and distinguish the lockfile pin from versions resolved by another import. Done means the pinned @scope/a@1.0.0-rc.2 remains selected after 2.0.0 is published without making a bare wildcard adopt unrelated pre-releases.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100