refactor(pkg-deps): consume typed dependency-graph fields, drop GenericJSON decoder - #22
Merged
Merged
Conversation
…icJSON decoder
The backend moved `packageDependencies.dependencies.transitive` from
four `GenericJSON` fields (`dag`, `conflicts`, `circularDependencies`,
`environmentConstraints`) to typed siblings (`dependencyGraph`,
`dependencyConflicts`, `circularDependencyCycles`,
`environmentMarkers`). The legacy fields are `@deprecated` but still
served. This PR migrates the client to the typed surface ahead of
removal, swapping the input source without changing the public
envelope contract.
Summary:
- Service layer reads the typed fields. `TransitiveDependencySummary`
loses the four GenericJSON fields, gains typed ones; new types
exported: `DependencyGraph` / `DependencyGraphNode` /
`DependencyGraphEdge`, `DependencyConflict` /
`DependencyConflictEdge`, `CircularDependencyCycle`,
`EnvironmentMarker`.
- `package-dependencies-response.ts` loses ~100 lines of
`decodeDag` / `decodeNodes` / `decodeEdges` / `decodeObjectNode`
plus the tuple-vs-object tolerance. Direct-version lookup and
importer provenance walk the typed `DependencyGraph` directly.
- `buildTypedConflicts` / `decodeConflictEntryForEnvelope` /
`buildTypedCycles` / `decodeCycleEntryForEnvelope` collapse to
straight `.map()` calls. No raw-fallback path — backend contract
is typed by construction.
- `groups.environmentConstraints: UntypedGenericJSON[]` (the last
raw passthrough in the envelope) becomes `environmentMarkers:
[{type, value, raw}]`. Verbose-terminal output upgrades from
`JSON.stringify` per marker to `type: value` one-liners.
- `formatConflictsAndCycles`: drops the `isTypedConflictArray` /
`isTypedCycleArray` guards and their JSON-blob fallbacks. Conflict
table + cycle chain rendering behaviour is preserved.
The public envelope shape is unchanged for `transitive.conflicts`
(`{name, requiredVersions}[]`) and `transitive.circularDependencies`
(`{cycle: string[]}[]`). `groups.environmentConstraints` renames to
`groups.environmentMarkers` with a typed content shape — clean break
over ambiguity; consumers branch on the presence of the typed
fields.
The typed `dependencyGraph` sits on the service-level result for a
future `pkg deps-dag` command; this tool's envelope deliberately
doesn't surface it.
Also:
- `--lifecycle` was already on the wire (shipped previously), so no
follow-on change needed there.
- `UntypedGenericJSON` type is retained — still used by
`ChangelogEntryDetail.metadata`.
Testing:
- 1326 tests pass. Test fixtures migrated from the legacy
tuple/object DAG shapes to the typed form; raw-fallback /
garbage-shape tolerance tests removed.
- Typecheck / lint / build clean.
Live-smoke:
- `pkg deps npm:express --transitive --verbose` — 28 direct + 67
transitive resolved; importer provenance renders correctly.
- `pkg deps npm:jest --transitive --verbose` — all 16 known
conflicts render in the conflicts table with correct
`name: range1, range2, …` formatting.
- `pkg deps pypi:requests --groups --verbose` — PyPI extras
(`socks`, `use-chardet-on-py3`) render with `(optional, extra)`
tails.
- `--json` envelope: `runtime.items[].version` resolves,
`transitive.dependencyGraph` and legacy `dag` both absent.
4 tasks
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.
Summary
Migrates
package_dependenciesoff the backend's deprecatedGenericJSONfields and onto their typed replacements. Defensive upgrade — the legacy fields are@deprecatedbut still served, so this is safe to land ahead of removal.Backend provides typed siblings for all four legacy fields:
GenericJSON)transitive.dagtransitive.dependencyGraph({formatVersion, nodes, edges}with indexed edges)transitive.conflictstransitive.dependencyConflicts(importer identity fixed — no morefrom: \"npm\"bug)transitive.circularDependenciestransitive.circularDependencyCycles(pre-joineddisplayChain)dependencyGroups.environmentConstraintsdependencyGroups.environmentMarkers({type, value, raw})Changes
Service layer (
package-intelligence-service.ts):DependencyGraph/DependencyGraphNode/DependencyGraphEdge,DependencyConflict/DependencyConflictEdge,CircularDependencyCycle,EnvironmentMarker.TransitiveDependencySummarydrops the fourGenericJSONfields, gains typed ones.DependencyGroupsInfo.environmentConstraints→environmentMarkers.PACKAGE_DEPENDENCIES_QUERYselects typed fields only.UntypedGenericJSONtype retained forChangelogEntryDetail.metadata(the one remaining passthrough).Envelope builder (
package-dependencies-response.ts):decodeDag/decodeNodes/decodeEdges/decodeObjectNode+ tuple-vs-object format tolerance. Replaced with straight field access on the typed graph.buildDirectVersionLookupandbuildTransitivePackageswalk the typed graph.fromIndex: null(synthetic-root edges) handled explicitly so version resolution still works on packages with synthetic-root DAGs.buildTypedConflicts/decodeConflictEntryForEnvelope/buildTypedCycles/decodeCycleEntryForEnvelope+ raw-fallback machinery collapse to straight.map()calls.Terminal formatter:
formatConflictsAndCycles: drops theisTypedConflictArray/isTypedCycleArrayguards and JSON-blob fallbacks. Conflict table + cycle chain rendering behaviour preserved.groups.environmentMarkersunder--verboseupgrades fromJSON.stringifyper marker totype: valueone-liners (e.g. `extra: async`, `python_version: >= 3.8`).Public envelope shape
Preserved where possible, one rename:
transitive.conflicts: {name, requiredVersions}[]— unchanged.transitive.circularDependencies: {cycle: string[]}[]— unchanged.groups.environmentConstraints: UntypedGenericJSON[]→ renamed togroups.environmentMarkers: {type?, value?, raw?}[]. Clean break — the rename matches the typed concept and the backend field name; consumers branch on the presence of the new field.The typed
dependencyGraphsits on the service-level result for a futurepkg deps-dagcommand; this tool's envelope deliberately doesn't surface it, consistent with the existing design commitment.Testing
bun test).Live-smoke (production pkgseer)
pkg deps npm:express --transitive --verbose— 28 direct + 67 transitive packages resolved; per-package importer provenance renders correctly (- ^2.0.0 required by express@5.2.1).pkg deps npm:jest --transitive --verbose— all 16 known conflicts render in the conflicts table (ansi-regex: ^5.0.1, ^6.2.2, …).pkg deps pypi:requests --groups --verbose— PyPI extras (socks,use-chardet-on-py3) render with(optional, extra)tails.--json:runtime.items[].versionresolves from the typed graph;transitive.dependencyGraphand legacydagboth absent from the envelope.Test plan
bun testpasses (1326 / 0)bun run typecheck/bun run build/bun run lintclean🤖 Generated with Claude Code