Skip to content

GraphNode has no package-URL accessor, so every consumer reimplements the projection #43

Description

@bomly-guy

Raised while fixing a TUI defect in bomly-cli#423: the interactive scan rendered a module's NodeID() under a column labelled "PURL", handing the viewer module:package.json#pkg:npm/app@1.0.0 — a string no consumer can parse as a package URL.

What is missing

"What package URL does this node publish" is a property of the node, and the three kinds answer differently:

  • a dependency node's ID is its canonical package URL;
  • a module's ID is the structural module:<path>#<purl> grammar, and its package URL is a separate PURL() field;
  • a manifest has no package URL at all — it is a file.

GraphNode exposes NodeID(), Kind(), NodeLocations(), NodeWarnings() and CloneNode(), but nothing for this. v0.9.0 added NodeCoordinates, NodeDisplayName and NodeVersion for exactly this class of question; this one was not included.

Why it matters

Every consumer that publishes a purl writes the three-case switch itself, and they disagree the moment one is missed. In the CLI today the same projection exists in internal/output, in internal/sbom, and — until this PR — wrongly in two TUI paths. The SBOM and JSON copies were right; the TUI copy shipped an unparseable value to users. That is the drift this accessor removes.

The two remaining CLI copies stay separate on purpose: an SBOM codec importing the CLI's output layer would be a layering inversion, so they converge on this accessor rather than on each other. Both are commented with this issue.

Suggested shape

// NodePURL returns the package URL a node publishes, or "" when it has none.
func NodePURL(node GraphNode) string

Additive, so a minor bump.

What still needs checking

  • Whether a module whose coordinates cannot mint a package URL should return "" or its generic fallback — NewModuleNode derives the PURL when it can and warns otherwise, so the empty case is reachable.
  • Whether Package.ID/PackageRef consumers want the same accessor, since they key on the dependency case only today.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions