#62

Accept string-shorthand repository field on npm packages

OpenSteel-techwants to merge intofrom

Summary

Fixes #61. opensrc path next-auth (and any package whose registry response includes a version with a string-shorthand repository field) failed with Error: error decoding response body. Root cause: NpmRepository was modeled as a single struct, but package.json's repository field is either an object ({ url, directory, ... }) or a plain string shorthand. The npm registry preserves whichever shape was published, so one shorthand entry tanks the whole response.

Concrete reproducer that was failing before this PR:

$ opensrc path next-auth
Error: error decoding response body

next-auth 3.29.3 and 3.29.7 publish "repository": "https://github.com/nextauthjs/next-auth.git" as a string. Those two entries among the 725-version registry response were enough to fail the whole decode, even though current versions use the object form.

Approach

Model NpmRepository as a #[serde(untagged)] enum with two variants — Object { url, directory } and Shorthand(String) — plus thin url() / directory() accessors so the call site in extract_repo_url stays unchanged in shape. The existing URL normalization in extract_repo_url already handles git+, git+ssh://, and github: prefixes, so it covers the common shorthand URLs without further changes.

Tests

Four new unit tests in packages/opensrc/cli/src/core/registries/npm.rs:

  • test_npm_repository_decodes_object_form — object shape still decodes.
  • test_npm_repository_decodes_string_shorthand — regression test naming next-auth 3.29.3 / 3.29.7.
  • test_npm_response_decodes_with_mixed_repository_shapes — full NpmResponse with a mix of object-form, string-form, and missing repository entries. Mirrors the exact shape that broke opensrc path next-auth.
  • test_extract_repo_url_handles_string_shorthand — extract_repo_url correctly normalizes a shorthand URL.

cargo test, cargo fmt --check, and cargo clippy --all-targets -- -D warnings all pass locally.

End-to-end verification

Built a local release binary against this branch:

$ ./target/release/opensrc path next-auth
/Users/.../nextauthjs/next-auth/4.24.14

$ ./target/release/opensrc path zod
/Users/.../colinhacks/zod/3.25.76

$ ./target/release/opensrc path @auth/core
/Users/.../nextauthjs/next-auth/0.34.3

next-auth now resolves; unrelated packages still resolve.

Notes

  • No public API change.
  • The Shorthand variant intentionally accepts any string and feeds it through the same URL-normalization path as the object form's url. Edge shorthands like the bare "owner/repo" form aren't expanded here — they were never handled before either, and a follow-up could address them if desired. The next-auth case (a full URL string) works because of the existing normalizer.

Activity

3
vercel[bot]5mo ago

@Steel-tech is attempting to deploy a commit to the Vercel Labs Team on Vercel.

A member of the Team first needs to authorize it.

azullus3mo ago

Ran into the same error decoding response body independently with velite, which surfaces a variant this PR doesn't fully cover yet: the bare owner/repo shorthand.

$ curl -s https://registry.npmjs.org/velite | jq -r '.versions | to_entries[] | select(.value.repository | type == "string") | "\(.key) \(.value.repository)"'
1.0.0-alpha.2 zce/velite
1.0.0-alpha.3 zce/velite

This is the form the npm docs document as the default shorthand ("repository": "npm/npm" → GitHub). The untagged enum in this PR fixes deserialization for it — which is what poisons the whole packument today — but if a bare-shorthand version is the one being resolved, extract_repo_url passes zce/velite through unchanged (it only normalizes git+ / git:// / ssh:// / github:-prefixed strings), so the resulting clone URL is invalid.

Suggestion: in the normalization path, treat a scheme-less string matching ^[^/@#:]+/[^/@#:]+$ as https://github.com/{s} (npm's documented default host), and add it as a test case alongside the full-URL string form.

Either way, the enum change alone already unblocks both next-auth and velite for normal resolution, since their latest versions publish the object form — thanks for the fix.

(analysis assisted by Claude Code)