Skip to content

fix(npm): handle legacy array-form engines field - #47

Merged
andrew merged 1 commit into
git-pkgs:mainfrom
acidghost:fix/npm-legacy-engines-array
Jul 26, 2026
Merged

fix(npm): handle legacy array-form engines field#47
andrew merged 1 commit into
git-pkgs:mainfrom
acidghost:fix/npm-legacy-engines-array

Conversation

@acidghost

Copy link
Copy Markdown
Contributor

Problem

FetchVersions / FetchPackage fail with a JSON unmarshal error for packages whose versions publish engines as a legacy array instead of the modern object form, making the whole package unusable through the client:

json: cannot unmarshal array into Go struct field versionInfo.versions.engines of type map[string]string

lodash is the notable example — its 0.x releases publish engines as an array.

Reproducer

# Public registry: early lodash versions use an array for engines
curl -s https://registry.npmjs.org/lodash \
  | jq -c '.versions | to_entries[] | select(.value.engines | type=="array") | {version: .key, engines: .value.engines}'

returns 38 entries like:

{"version":"0.1.0","engines":["node","rhino"]}
{"version":"0.2.0","engines":["node","rhino"]}
...

so FetchVersions(ctx, "lodash") errors out instead of returning versions.

Root cause

versionInfo.Engines is typed map[string]string, but the npm packument historically allows engines to be either an object ({"node": ">=4"}) or a legacy array (["node","rhino"]). The array form cannot unmarshal into a map, failing the entire packageResponse decode.

Fix

Relax Engines to interface{}, matching the existing handling of other variable-shape npm fields (Homepage, License, Keywords, Funding). The value only flows opaquely into Version.Metadata["engines"] — no logic depends on the map shape — so both the array and object forms now round-trip without changing behavior.

Testing

Added TestFetchVersions_LegacyEnginesArray, which serves both forms (legacy array ["node","rhino"] and modern object {"node": ">=10"}) from a test server and asserts they round-trip into Version.Metadata["engines"] as []interface{} and map[string]interface{} respectively, with FetchVersions returning no error.

Early npm packages (e.g. lodash 0.x) publish `engines` as a legacy
array (`["node","rhino"]`) instead of the modern object form
(`{"node":">=4"}`). The struct typed it as `map[string]string`, so
unmarshalling failed with "cannot unmarshal array into Go struct field
versionInfo.versions.engines of type map[string]string" and
FetchVersions returned an error for the entire package.

Relax the field to `interface{}` (matching Homepage, License, Keywords
and Funding, which already tolerate variable shapes). The value only
flows opaquely into Version.Metadata["engines"], so both the array and
object forms now round-trip without affecting any logic.

Adds TestFetchVersions_LegacyEnginesArray as a regression test covering
both the legacy array and modern object forms.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the npm registry client’s packument decoding to tolerate legacy npm metadata where versions[].engines is published as an array (e.g. early lodash 0.x), preventing FetchVersions / FetchPackage from failing with a JSON unmarshal error.

Changes:

  • Relax versionInfo.Engines from map[string]string to interface{} to accept both object and array JSON shapes.
  • Add a regression test covering both legacy array-form engines and modern object-form engines, asserting both round-trip into Version.Metadata["engines"].

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
internal/npm/npm.go Makes the engines field decode tolerant of legacy array-form values by loosening the Go type.
internal/npm/npm_test.go Adds coverage to ensure both legacy and modern engines shapes parse and are preserved in version metadata.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/npm/npm.go
Maintainers []maintainerInfo `json:"maintainers"`
NpmUser map[string]interface{} `json:"_npmUser"`
Engines map[string]string `json:"engines"`
Engines interface{} `json:"engines"`
@andrew
andrew merged commit 059c125 into git-pkgs:main Jul 26, 2026
2 checks passed
@acidghost
acidghost deleted the fix/npm-legacy-engines-array branch July 27, 2026 15:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants