provides

package module
v0.2.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 4, 2026 License: MIT Imports: 6 Imported by: 0

README

provides

A Go library for mapping package identities to the names used in source code. A Python distribution may provide a differently named module, a Go module may contain several package paths, and a Maven artifact may contain several Java packages. Project bindings retain dependency renames and aliases separately from the canonical package identity.

Installation

go get github.com/git-pkgs/provides

Model

Surface maps a versioned PURL to its provided source names. Binding connects a PURL to the imported and local names used by one project. An aliased binding may also retain the package-side target name. Both retain the evidence used to produce the mapping.

ProvidedName.Name is the exact source-visible spelling. Package-manager normalisation does not apply to it, and matching is case-sensitive by default. flask therefore does not match Flask. Set CaseInsensitive for languages whose module or namespace lookup folds case, for example PHP, where use GuzzleHttp\Client and use guzzlehttp\client resolve identically.

Matching names

Exact names match only themselves. Prefix names also match descendants separated by the configured boundary:

pythonModule := provides.ProvidedName{
	Language:  "python",
	Name:      "werkzeug",
	Kind:      "module",
	Match:     provides.MatchPrefix,
	Separator: ".",
}

pythonModule.Matches("werkzeug")      // true
pythonModule.Matches("werkzeug.http") // true
pythonModule.Matches("werkzeugx")     // false
pythonModule.Matches("Werkzeug.http") // false

Explicit export maps can use exact matching so an exported npm subpath does not imply that deeper paths are available:

export := provides.ProvidedName{
	Language: "javascript",
	Name:     "react/jsx-runtime",
	Kind:     "subpath",
	Match:    provides.MatchExact,
}

export.Matches("react/jsx-runtime")         // true
export.Matches("react/jsx-runtime/private") // false

Merging results

Resolvers can find the same mapping through manifests, installed metadata, artifacts, or curated data. MergeSurfaceResults and MergeBindingResults deduplicate mappings, combine their evidence, retain conflicting mappings, and return stable ordering.

const purl = "pkg:pypi/pyyaml@6.0.3"

manifest := provides.SurfaceResult{Surface: provides.Surface{
	PURL: purl,
	Provides: []provides.ProvidedName{{
		Language: "python",
		Name:     "yaml",
		Kind:     "module",
		Evidence: []provides.Evidence{{
			Method: provides.EvidenceManifest,
			Source: "METADATA",
		}},
	}},
}}

result := provides.MergeSurfaceResults(purl, manifest)

Non-fatal resolver problems are returned as Diagnostic values beside any successful mappings.

Usage

Resolve package surfaces once for a dependency snapshot, then reuse the result for every source import. This avoids repeating package resolution and keeps each lookup to a scan of the cached surfaces:

project, err := provides.ResolveProjectSurfaces(
	context.Background(),
	curated.Python(),
	packages,
	provides.SurfaceOptions{},
)
if err != nil {
	return err
}

for _, name := range imports {
	result := provides.MatchImport("python", name, project)
	// Use result.Matches and result.Diagnostics.
}

ResolveImport is a convenience for a single lookup. When checking several imports from the same project, use ResolveProjectSurfaces and MatchImport as above.

Curated Python surfaces

The curated package includes local mappings for PyYAML, brotlipy, Brotli, Pillow, and Beautiful Soup. ResolveProjectSurfaces joins caller-supplied dependency PURLs to those mappings:

packages := []provides.Package{
	{PURL: "pkg:pypi/PyYAML@6.0.3"},
	{PURL: "pkg:pypi/brotlipy@0.7.0"},
	{PURL: "pkg:pypi/Pillow@11.0.0"},
}

result, err := provides.ResolveProjectSurfaces(
	context.Background(),
	curated.Python(),
	packages,
	provides.SurfaceOptions{},
)

This path reads no files, runs no package-manager commands, and makes no network requests. PyPI distribution names are normalised for catalog lookup, while each returned Surface.PURL retains the caller's spelling and version. Unknown packages are omitted without producing a diagnostic.

Heuristic surfaces

The heuristic package derives conventional source names from a package's PURL type and name alone: an npm package ws provides module ws and any ws/... subpath, PyPI Engine-IO-Parser provides engine_io_parser, gem active_support provides both feature active_support and constant ActiveSupport, Cargo tokio-util provides crate tokio_util. It covers npm, pypi, golang, gem, cargo, composer, hex, and maven; other PURL types resolve to an empty surface. Every returned name carries EvidenceHeuristic so callers can distinguish a naming-convention guess from a verified mapping.

Packages whose importable name is not a mechanical transform of their registry name (PyYAML → yaml, Pillow → PIL, most Composer PSR-4 roots) need curated data or an artifact resolver. Chain runs several resolvers over the same package and merges their results, so an authoritative source can be tried first and the naming convention fills whatever it does not cover:

resolver := provides.Chain(curated.Python(), heuristic.Resolver())

project, err := provides.ResolveProjectSurfaces(ctx, resolver, packages, provides.SurfaceOptions{})

For PyYAML this returns both yaml (curated) and pyyaml (heuristic) with their respective evidence; MatchImport("python", "yaml", project) matches on the curated entry while a package the catalog does not list still resolves via the heuristic.

Resolving an import

ResolveImport combines project-surface resolution with a reverse lookup. Every matching dependency is returned when an import is ambiguous:

result, err := provides.ResolveImport(
	context.Background(),
	curated.Python(),
	provides.ImportRequest{
		Language: "python",
		Name:     "brotli",
		Packages: []provides.Package{
			{PURL: "pkg:pypi/brotlipy@0.7.0"},
			{PURL: "pkg:pypi/brotli@1.1.0"},
		},
	},
)

result.Matches contains both PURLs and the curated evidence for the brotli module. Supplying only the dependency declared by a project narrows the result without applying a package-name heuristic.

Rust and Go bindings

The bindings package parses package-manager output supplied by the caller. It does not read files, run commands, or use the network.

ParseCargoManifest reads dependency, development-dependency, build-dependency, and target-specific tables from Cargo.toml. A renamed dependency keeps its canonical package in the PURL and exposes its source-visible crate name through Binding.Imported:

result, err := bindings.ParseCargoManifest("Cargo.toml", cargoToml)

Manifest bindings use versionless PURLs because Cargo manifest versions are constraints. Path and Git dependencies are omitted when the manifest alone cannot establish a registry identity. ParseCargoMetadata reads the resolved root package or default workspace members and returns versioned PURLs. Its resolve.nodes[].deps[].name value preserves Cargo renames:

result, err := bindings.ParseCargoMetadata(cargoMetadataJSON)

ParseGoList accepts the concatenated JSON stream written by go list -deps -json. Each non-standard dependency package becomes a binding to its module PURL. Binding.Imported contains the full import path and Binding.Local contains the declared Go package name, which may differ from the last path component:

result, err := bindings.ParseGoList(goListJSON)

The caller controls how those byte slices are obtained. Cargo metadata is more precise than a manifest when both are available because it contains resolved versions and exact crate names.

JavaScript and TypeScript bindings

ParseNPMManifest reads registry dependencies and npm: aliases from the dependency sections in package.json. Manifest ranges produce versionless PURLs. Local paths, Git sources, and URL dependencies are omitted when their npm identity cannot be established.

result, err := bindings.ParseNPMManifest("package.json", packageJSON)

For "my-react": "npm:react@18", Binding.Imported is my-react, Binding.Target is react, and the PURL is pkg:npm/react.

ParseNPMPackage reads a package root and explicit exports subpaths. It reports JavaScript and TypeScript names separately, with exact matching for each declared export:

result, err := bindings.ParseNPMPackage(
	"pkg:npm/react@19.0.0",
	"package.json",
	packageJSON,
)

Export patterns are returned as diagnostics by the manifest parser. A later artifact resolver can expand them against the files shipped by the package.

ParseDenoConfig reads npm targets from the top-level imports map in deno.json. Keys ending in / become literal prefix bindings, while other keys remain exact. Binding.Target retains the package-side root or subpath:

result, err := bindings.ParseDenoConfig("deno.json", denoJSON)

Artifact surfaces

The artifacts package accepts an archives.Reader opened by the caller. It does not download artifacts or add another archive abstraction.

reader, err := archives.OpenBytes("demo.whl", wheelBytes)
if err != nil {
	return err
}
defer reader.Close()

result, err := artifacts.ResolvePythonWheel(
	context.Background(),
	provides.Package{PURL: "pkg:pypi/demo@1.0.0"},
	reader,
)

The package contains five artifact inspectors:

  • ResolvePythonWheel reads Import-Name and Import-Namespace, then falls back to top_level.txt and wheel paths when those fields are absent.
  • ResolveJavaArchive enumerates Java packages and reads explicit or automatic module names.
  • ResolveNPMTarball reads package roots and exports, expanding export patterns against the tarball file list.
  • ResolveCargoCrate reads the published library target, with src/lib.rs as a fallback.
  • ResolveGoModule enumerates directories containing non-test Go source files.

Entry extraction failures become diagnostics when other archive evidence can still produce names. Listing failures and invalid PURLs remain errors. The caller owns and closes the archive reader.

Resolver interfaces

SurfaceResolver resolves the names provided by one package. BindingResolver resolves dependency bindings for a project directory. The core package defines these interfaces without running package managers or making network requests.

Further acquisition adapters are planned. The current package contains the shared types, matching rules, merge helpers, resolver interfaces, project-surface join, local binding parsers, artifact inspectors, and the built-in Python catalog used by Hyrum.

License

This project is licensed under the MIT License.

Documentation

Overview

Package provides maps package identities to source-level names and project bindings.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Binding

type Binding struct {
	PURL     string
	Imported string
	Target   string
	Local    string
	Match    MatchMode
	Evidence []Evidence
}

Binding connects a package to the name used by one project.

func (Binding) Matches

func (binding Binding) Matches(imported string) bool

Matches reports whether imported is covered by the project binding. Prefix bindings retain their complete literal prefix, including any trailing slash.

type BindingResolver

type BindingResolver interface {
	ResolveBindings(ctx context.Context, projectDir string) (BindingResult, error)
}

BindingResolver resolves dependency bindings for a project directory.

type BindingResult

type BindingResult struct {
	Bindings    []Binding
	Diagnostics []Diagnostic
}

BindingResult contains project bindings and any non-fatal diagnostics.

func MergeBindingResults

func MergeBindingResults(results ...BindingResult) BindingResult

MergeBindingResults combines project bindings, deduplicates mappings and evidence, and returns deterministic output.

type Catalog

type Catalog struct {
	// contains filtered or unexported fields
}

Catalog is an immutable collection of package surfaces indexed by PURL. Versioned entries apply only to that version. Entries without a version are used as fallbacks for every version of the package.

func NewCatalog

func NewCatalog(surfaces ...Surface) (*Catalog, error)

NewCatalog creates a curated surface catalog.

func (*Catalog) ResolveSurface

func (c *Catalog) ResolveSurface(
	ctx context.Context,
	pkg Package,
	_ SurfaceOptions,
) (SurfaceResult, error)

ResolveSurface implements SurfaceResolver using curated local data.

type Diagnostic

type Diagnostic struct {
	Source  string
	Message string
}

Diagnostic records a non-fatal resolver problem.

type Evidence

type Evidence struct {
	Method EvidenceMethod
	Source string
}

Evidence describes where a mapping came from.

type EvidenceMethod

type EvidenceMethod string

EvidenceMethod identifies how a package or binding mapping was obtained.

const (
	// EvidenceResolver comes from package-manager resolution output.
	EvidenceResolver EvidenceMethod = "resolver"
	// EvidenceManifest comes from a manifest, lockfile, or import map.
	EvidenceManifest EvidenceMethod = "manifest"
	// EvidenceInstalled comes from installed package metadata or contents.
	EvidenceInstalled EvidenceMethod = "installed"
	// EvidenceArtifact comes from a package artifact.
	EvidenceArtifact EvidenceMethod = "artifact"
	// EvidenceCurated comes from an explicit maintained mapping.
	EvidenceCurated EvidenceMethod = "curated"
	// EvidenceHeuristic comes from an opt-in naming guess.
	EvidenceHeuristic EvidenceMethod = "heuristic"
)

type ImportMatch

type ImportMatch struct {
	PURL     string
	Provided ProvidedName
}

ImportMatch connects an import to one matching package surface.

type ImportRequest

type ImportRequest struct {
	Language string
	Name     string
	Packages []Package
	Options  SurfaceOptions
}

ImportRequest describes a source import and the project dependencies that may provide it.

type ImportResult

type ImportResult struct {
	Language    string
	Name        string
	Matches     []ImportMatch
	Diagnostics []Diagnostic
}

ImportResult contains every package surface matching an import and any non-fatal diagnostics collected while resolving project surfaces.

func MatchImport

func MatchImport(language, name string, project ProjectSurfaceResult) ImportResult

MatchImport returns every matching package from an already resolved project.

func ResolveImport

func ResolveImport(
	ctx context.Context,
	resolver SurfaceResolver,
	request ImportRequest,
) (ImportResult, error)

ResolveImport resolves the requested project package surfaces and returns every package that provides the import.

type MatchMode

type MatchMode string

MatchMode controls how a source name matches an import name.

const (
	// MatchExact matches only the complete source name.
	MatchExact MatchMode = "exact"
	// MatchPrefix matches a source-name prefix.
	MatchPrefix MatchMode = "prefix"
)

type Package

type Package struct {
	PURL string
}

Package identifies a package whose source-level surface should be resolved.

type ProjectSurfaceResult

type ProjectSurfaceResult struct {
	Surfaces    []Surface
	Diagnostics []Diagnostic
}

ProjectSurfaceResult contains the package surfaces resolved for one set of project dependencies and any non-fatal diagnostics.

func ResolveProjectSurfaces

func ResolveProjectSurfaces(
	ctx context.Context,
	resolver SurfaceResolver,
	packages []Package,
	options SurfaceOptions,
) (ProjectSurfaceResult, error)

ResolveProjectSurfaces resolves surfaces for caller-supplied project dependencies. It continues after resolver errors and returns successful surfaces alongside the joined error.

type ProvidedName

type ProvidedName struct {
	Language string
	Name     string
	Kind     string
	Match    MatchMode
	// Separator is the boundary between a prefix name and a matched
	// descendant, for example "." for a Python module or "/" for an npm
	// subpath. It is unused for MatchExact.
	Separator string
	// CaseInsensitive folds ASCII case when matching. Set it for languages
	// whose module or namespace lookup is itself case-insensitive, for
	// example PHP, where `use GuzzleHttp\Client` and `use guzzlehttp\client`
	// resolve to the same class. Name still retains its canonical spelling.
	CaseInsensitive bool
	Evidence        []Evidence
}

ProvidedName is a source-level name supplied by a package.

func (ProvidedName) Matches

func (name ProvidedName) Matches(imported string) bool

Matches reports whether imported is covered by the provided name. Matching is case-sensitive by default because Name retains its exact source-visible spelling; CaseInsensitive folds ASCII case for languages whose lookup does.

type Surface

type Surface struct {
	PURL     string
	Provides []ProvidedName
}

Surface contains the source-level names provided by a package.

type SurfaceOptions

type SurfaceOptions struct {
	IncludeHeuristics bool
}

SurfaceOptions controls package-surface resolution.

type SurfaceResolver

type SurfaceResolver interface {
	ResolveSurface(ctx context.Context, pkg Package, options SurfaceOptions) (SurfaceResult, error)
}

SurfaceResolver resolves the source-level surface provided by a package.

type SurfaceResolverFunc added in v0.2.0

type SurfaceResolverFunc func(ctx context.Context, pkg Package, options SurfaceOptions) (SurfaceResult, error)

SurfaceResolverFunc adapts a function to a SurfaceResolver.

func Chain added in v0.2.0

func Chain(resolvers ...SurfaceResolver) SurfaceResolverFunc

Chain returns a SurfaceResolver that queries each resolver in order and merges every non-empty result for a package. Later resolvers still run after an earlier one produces a result so callers can combine, for example, a curated catalog with a heuristic fallback and receive both mappings with their distinct evidence. A resolver that returns an error contributes a diagnostic and the chain continues.

func (SurfaceResolverFunc) ResolveSurface added in v0.2.0

func (f SurfaceResolverFunc) ResolveSurface(ctx context.Context, pkg Package, options SurfaceOptions) (SurfaceResult, error)

ResolveSurface calls f.

type SurfaceResult

type SurfaceResult struct {
	Surface     Surface
	Diagnostics []Diagnostic
}

SurfaceResult contains a package surface and any non-fatal diagnostics.

func MergeSurfaceResults

func MergeSurfaceResults(purl string, results ...SurfaceResult) SurfaceResult

MergeSurfaceResults combines results for purl, deduplicates mappings and evidence, and returns deterministic output. A result for a different PURL is omitted and reported as a diagnostic.

Directories

Path Synopsis
Package artifacts inspects package archive contents for source-level names.
Package artifacts inspects package archive contents for source-level names.
Package bindings parses project-local package-manager metadata into source import bindings.
Package bindings parses project-local package-manager metadata into source import bindings.
Package curated contains local package-surface catalogs.
Package curated contains local package-surface catalogs.
Package heuristic provides a SurfaceResolver that maps a package identity to its conventional source-level name using per-ecosystem naming rules alone.
Package heuristic provides a SurfaceResolver that maps a package identity to its conventional source-level name using per-ecosystem naming rules alone.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL