pkgsite

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Jun 25, 2026 License: MIT Imports: 9 Imported by: 0

README

pkgsite

Go Reference

A small, idiomatic Go client for the pkg.go.dev API. It gives Go tooling a typed interface to module and package metadata — search, version history, package and symbol listings, importers, and vulnerability data — instead of scraping the website.

⚠️ API stability: v1beta

This client targets the pkg.go.dev API at https://pkg.go.dev/v1beta/. The API is explicitly labelled v1beta: a stable v1 is planned but not yet released, and response shapes may change before then. Pin a version of this module and expect to update when the upstream API moves to v1.

Install

go get github.com/pouya1364/pkgsite

Requires Go 1.23 or later (the iterator methods use iter.Seq2).

Zero runtime dependencies

The library uses only the Go standard library. Running go get on it pulls in no transitive dependencies.

Quick start

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/pouya1364/pkgsite"
)

func main() {
	client := pkgsite.NewClient()

	pkg, err := client.Package(context.Background(), "golang.org/x/time/rate")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(pkg.Name, pkg.Synopsis)
}

NewClient accepts options: WithHTTPClient to supply your own *http.Client (for custom timeouts or transport), and WithBaseURL to point at a different API host (useful in tests).

The eight endpoints

Each example assumes a client := pkgsite.NewClient() and ctx.

Package — metadata for one package
pkg, err := client.Package(ctx, "golang.org/x/time/rate")
// pkg.ModulePath, pkg.Version, pkg.Name, pkg.Synopsis, ...
Module — metadata for one module
mod, err := client.Module(ctx, "golang.org/x/time")
// mod.Path, mod.Version, mod.IsLatest, mod.RepoURL, ...
Versions — a module's version history (paginated)
page, err := client.Versions(ctx, "golang.org/x/time")
for _, v := range page.Items {
	fmt.Println(v.Version, "latest:", v.LatestVersion)
}
ModulePackages — the packages in a module (paginated)
page, err := client.ModulePackages(ctx, "golang.org/x/time")
for _, p := range page.Items {
	fmt.Println(p.Path, p.Synopsis)
}
Search — ranked search results (paginated)
page, err := client.Search(ctx, "rate limiter", pkgsite.SearchOptions{
	Symbol: "Limiter", // optional: narrow to packages exporting this symbol
})
for _, r := range page.Items {
	fmt.Println(r.PackagePath)
}
Symbols — exported symbols in a package (paginated)
page, err := client.Symbols(ctx, "golang.org/x/time/rate")
for _, s := range page.Items {
	fmt.Println(s.Kind, s.Name)
}
ImportedBy — packages that import a package (paginated)
page, err := client.ImportedBy(ctx, "golang.org/x/time/rate")
for _, importPath := range page.Items { // items are import path strings
	fmt.Println(importPath)
}
Vulnerabilities — known vulnerabilities for a module or package
page, err := client.Vulnerabilities(ctx, "github.com/dgrijalva/jwt-go")
for _, v := range page.Items {
	fmt.Println(v.ID, v.Details, "fixed in:", v.FixedVersion)
}

Pagination and iterators

Paginated endpoints return a Page[T]:

type Page[T any] struct {
	Items         []T
	Total         int
	NextPageToken string
}

func (p Page[T]) HasMore() bool // true when another page exists

You can page manually with the Token option:

page, _ := client.Search(ctx, "bloom filter")
for page.HasMore() {
	page, _ = client.Search(ctx, "bloom filter", pkgsite.SearchOptions{
		ListOptions: pkgsite.ListOptions{Token: page.NextPageToken},
	})
}

Or let the iterator handle paging for you. Every paginated endpoint has an …Iter variant returning a Go 1.23 iter.Seq2[T, error], so you can range over all results across all pages:

for result, err := range client.SearchIter(ctx, "bloom filter") {
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(result.PackagePath)
}

The iterators are VersionsIter, ModulePackagesIter, SearchIter, SymbolsIter, and ImportedByIter. Breaking out of the loop stops cleanly without fetching further pages.

Filters

The paginated list options accept a Filter — a Go expression the API evaluates against each item. The library URL-encodes it for you:

page, err := client.ModulePackages(ctx, "golang.org/x/tools", pkgsite.ListOptions{
	Filter: `contains(path,"internal")`,
})

Errors

Failures come back as typed errors you can inspect with errors.As:

_, err := client.Package(ctx, "encoding/json/v2")
switch {
case err == nil:
	// ok
default:
	var ambiguous *pkgsite.ErrAmbiguousPath
	var rateLimited *pkgsite.ErrRateLimit
	var apiErr *pkgsite.APIError
	switch {
	case errors.As(err, &ambiguous):
		// The path matches more than one module. Retry with the Module option
		// set to one of ambiguous.Candidates.
		_, _ = client.Package(ctx, "encoding/json/v2", pkgsite.PackageOptions{
			Module: ambiguous.Candidates[0],
		})
	case errors.As(err, &rateLimited):
		// HTTP 429. rateLimited.RetryAfter is the Unix timestamp when the
		// limit resets. The API allows 40 requests/sec per IP.
	case errors.As(err, &apiErr):
		// Any other non-2xx response. apiErr.Code, apiErr.Message, apiErr.Fixes.
	}
}

Testing with the mock package

pkgsite.Client is a concrete type. To test your own code without real HTTP calls, declare an interface in your package listing the methods you use, accept that interface, and substitute *mock.Client in tests. Both *pkgsite.Client and *mock.Client satisfy such an interface.

package myapp

import (
	"context"

	"github.com/pouya1364/pkgsite"
)

// The slice of the API this code depends on.
type PackageGetter interface {
	Package(ctx context.Context, path string, opts ...pkgsite.PackageOptions) (*pkgsite.PackageInfo, error)
}

func Synopsis(ctx context.Context, c PackageGetter, path string) (string, error) {
	pkg, err := c.Package(ctx, path)
	if err != nil {
		return "", err
	}
	return pkg.Synopsis, nil
}
package myapp_test

import (
	"context"
	"testing"

	"github.com/pouya1364/pkgsite"
	"github.com/pouya1364/pkgsite/mock"
)

func TestSynopsis(t *testing.T) {
	c := &mock.Client{
		PackageFn: func(ctx context.Context, path string, opts ...pkgsite.PackageOptions) (*pkgsite.PackageInfo, error) {
			return &pkgsite.PackageInfo{Synopsis: "Package rate provides a rate limiter."}, nil
		},
	}

	got, err := Synopsis(context.Background(), c, "golang.org/x/time/rate")
	if err != nil || got != "Package rate provides a rate limiter." {
		t.Fatalf("got %q, %v", got, err)
	}
}

Each method on mock.Client has a matching …Fn field. A method whose field is left nil returns the zero value and a nil error.

Integration tests

This repository's own tests use net/http/httptest and make no network calls. A separate set of integration tests exercises the live API; they are guarded by the integration build tag and the PKGSITE_INTEGRATION environment variable, so they never run by accident:

PKGSITE_INTEGRATION=1 go test -tags integration -run TestIntegration ./...

License

MIT

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type APIError

type APIError struct {
	Code    int
	Message string
	Fixes   []string
}

APIError is returned when the API responds with a non-2xx status code that is not a known structured error (rate limit, ambiguous path, etc.).

func (*APIError) Error

func (e *APIError) Error() string

type Client

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

Client is a pkg.go.dev API client. The zero value is not usable; use NewClient.

func NewClient

func NewClient(opts ...ClientOption) *Client

NewClient returns a Client ready to make requests.

func (*Client) ImportedBy

func (c *Client) ImportedBy(ctx context.Context, path string, opts ...ImportedByOptions) (*Page[string], error)

ImportedBy returns one page of the import paths of packages that import the given package. Use ImportedByIter to range over all of them without handling page tokens.

func (*Client) ImportedByIter

func (c *Client) ImportedByIter(ctx context.Context, path string, opts ...ImportedByOptions) iter.Seq2[string, error]

ImportedByIter ranges over every import path that imports the given package, fetching successive pages as needed. On the first error it yields the zero value with that error and stops. Breaking out of the range loop stops cleanly.

func (*Client) Module

func (c *Client) Module(ctx context.Context, path string, opts ...ModuleOptions) (*ModuleInfo, error)

Module returns metadata for the module at the given module path.

func (*Client) ModulePackages

func (c *Client) ModulePackages(ctx context.Context, path string, opts ...ListOptions) (*Page[ModulePackage], error)

ModulePackages returns one page of the packages within a module. Use ModulePackagesIter to range over all of them without handling page tokens.

func (*Client) ModulePackagesIter

func (c *Client) ModulePackagesIter(ctx context.Context, path string, opts ...ListOptions) iter.Seq2[ModulePackage, error]

ModulePackagesIter ranges over every package in the module, fetching successive pages as needed. On the first error it yields the zero value with that error and stops. Breaking out of the range loop stops cleanly.

func (*Client) Package

func (c *Client) Package(ctx context.Context, path string, opts ...PackageOptions) (*PackageInfo, error)

Package returns metadata for the package at the given import path.

func (*Client) Search

func (c *Client) Search(ctx context.Context, query string, opts ...SearchOptions) (*Page[SearchResult], error)

Search returns one page of ranked results for the query. When SearchOptions.Symbol is set, the search is narrowed to packages exporting that symbol. Use SearchIter to range over all results without handling page tokens.

func (*Client) SearchIter

func (c *Client) SearchIter(ctx context.Context, query string, opts ...SearchOptions) iter.Seq2[SearchResult, error]

SearchIter ranges over every result for the query, fetching successive pages as needed. On the first error it yields the zero value with that error and stops. Breaking out of the range loop stops cleanly.

func (*Client) Symbols

func (c *Client) Symbols(ctx context.Context, path string, opts ...SymbolsOptions) (*Page[Symbol], error)

Symbols returns one page of the exported symbols in a package. Use SymbolsIter to range over all of them without handling page tokens.

func (*Client) SymbolsIter

func (c *Client) SymbolsIter(ctx context.Context, path string, opts ...SymbolsOptions) iter.Seq2[Symbol, error]

SymbolsIter ranges over every exported symbol in the package, fetching successive pages as needed. On the first error it yields the zero value with that error and stops. Breaking out of the range loop stops cleanly.

func (*Client) Versions

func (c *Client) Versions(ctx context.Context, path string, opts ...VersionsOptions) (*Page[ModuleVersion], error)

Versions returns one page of a module's version history. Use VersionsIter to range over every version without handling page tokens.

func (*Client) VersionsIter

func (c *Client) VersionsIter(ctx context.Context, path string, opts ...VersionsOptions) iter.Seq2[ModuleVersion, error]

VersionsIter ranges over every version of the module, fetching successive pages as needed. On the first error it yields the zero value with that error and stops. Breaking out of the range loop stops cleanly.

func (*Client) Vulnerabilities

func (c *Client) Vulnerabilities(ctx context.Context, path string, opts ...VulnsOptions) (*Page[Vulnerability], error)

Vulnerabilities returns the known vulnerabilities for the given module or package. The result is a single Page: the API does not paginate this endpoint because vulnerability lists are short, so there is no iterator variant. The common case is an empty list (no known vulnerabilities).

type ClientOption

type ClientOption func(*Client)

ClientOption configures a Client.

func WithBaseURL

func WithBaseURL(url string) ClientOption

WithBaseURL overrides the API base URL. Intended for testing against a local pkgsite instance.

func WithHTTPClient

func WithHTTPClient(c *http.Client) ClientOption

WithHTTPClient replaces the default HTTP client. Useful for setting custom timeouts or transport-level behavior.

type ErrAmbiguousPath

type ErrAmbiguousPath struct {
	Path       string
	Candidates []string
}

ErrAmbiguousPath is returned when a path could refer to more than one module. Retry the call with the Module option set to one of the Candidates.

func (*ErrAmbiguousPath) Error

func (e *ErrAmbiguousPath) Error() string

type ErrRateLimit

type ErrRateLimit struct {
	RetryAfter int64 // Unix timestamp when the limit resets
}

ErrRateLimit is returned when the API responds with HTTP 429.

func (*ErrRateLimit) Error

func (e *ErrRateLimit) Error() string

type ImportedByOptions

type ImportedByOptions struct {
	Module  string
	Version string
	ListOptions
}

ImportedByOptions controls an ImportedBy request.

type ListOptions

type ListOptions struct {
	Limit  int
	Token  string // page token from a previous response
	Filter string // Go expression; will be URL-encoded automatically
}

ListOptions are shared by paginated endpoints.

type ModuleInfo

type ModuleInfo struct {
	Path              string    `json:"path"`
	Version           string    `json:"version"`
	CommitTime        time.Time `json:"commitTime"`
	IsLatest          bool      `json:"isLatest"`
	IsRedistributable bool      `json:"isRedistributable"`
	IsStandardLibrary bool      `json:"isStandardLibrary"`
	HasGoMod          bool      `json:"hasGoMod"`
	RepoURL           string    `json:"repoUrl"`
}

ModuleInfo holds metadata about a single Go module.

type ModuleOptions

type ModuleOptions struct {
	Version  string
	Licenses bool
	Readme   bool
}

ModuleOptions controls optional fields in a Module request.

type ModulePackage

type ModulePackage struct {
	Path              string `json:"path"`
	Name              string `json:"name"`
	Synopsis          string `json:"synopsis"`
	IsRedistributable bool   `json:"isRedistributable"`
}

ModulePackage is one package contained in a module.

type ModuleVersion

type ModuleVersion struct {
	ModulePath        string    `json:"modulePath"`
	Version           string    `json:"version"`
	CommitTime        time.Time `json:"commitTime"`
	IsRedistributable bool      `json:"isRedistributable"`
	HasGoMod          bool      `json:"hasGoMod"`
	LatestVersion     string    `json:"latestVersion"`
	Deprecated        bool      `json:"deprecated"`
	DeprecationReason string    `json:"deprecationReason"`
	Retracted         bool      `json:"retracted"`
	RetractionReason  string    `json:"retractionReason"`
}

ModuleVersion is one released version of a module. LatestVersion reports the latest version of the module, so a version is current when Version equals LatestVersion; the API does not send a per-item "is latest" flag.

type PackageInfo

type PackageInfo struct {
	ModulePath        string    `json:"modulePath"`
	Version           string    `json:"version"`
	CommitTime        time.Time `json:"commitTime"`
	IsLatest          bool      `json:"isLatest"`
	IsStandardLibrary bool      `json:"isStandardLibrary"`
	GOOS              string    `json:"goos"`
	GOARCH            string    `json:"goarch"`
	Path              string    `json:"path"`
	Name              string    `json:"name"`
	Synopsis          string    `json:"synopsis"`
	IsRedistributable bool      `json:"isRedistributable"`
}

PackageInfo holds metadata about a single Go package.

type PackageOptions

type PackageOptions struct {
	Module   string // disambiguate when the path matches multiple modules
	Version  string // specific version; defaults to latest
	GOOS     string
	GOARCH   string
	Doc      string // documentation format: text, html, md, markdown
	Examples bool   // include examples in documentation
	Imports  bool   // include the packages this one imports
	Licenses bool   // include license information
}

PackageOptions controls optional fields in a Package request.

type Page

type Page[T any] struct {
	Items         []T
	Total         int
	NextPageToken string
}

Page holds one page of results from a paginated endpoint.

func (Page[T]) HasMore

func (p Page[T]) HasMore() bool

HasMore reports whether there is at least one more page after this one.

type RateLimit

type RateLimit struct {
	Limit     int
	Remaining int
	Reset     int64 // Unix timestamp
}

RateLimit holds the rate limit state read from the API response headers. It is zero-valued when the response did not include rate limit headers.

type SearchOptions

type SearchOptions struct {
	Symbol string // if set, search for this symbol within matching packages
	ListOptions
}

SearchOptions controls a Search request.

type SearchResult

type SearchResult struct {
	PackagePath string `json:"packagePath"`
	ModulePath  string `json:"modulePath"`
	Version     string `json:"version"`
	Name        string `json:"name"`
	Synopsis    string `json:"synopsis"`
}

SearchResult is one ranked hit from a search.

type Symbol

type Symbol struct {
	Name     string `json:"name"`
	Kind     string `json:"kind"` // const, var, type, func, method
	Synopsis string `json:"synopsis"`
	Parent   string `json:"parent"` // enclosing type for methods and fields
}

Symbol is one exported symbol in a package.

type SymbolsOptions

type SymbolsOptions struct {
	Module  string
	Version string
	GOOS    string
	GOARCH  string
	ListOptions
}

SymbolsOptions controls a Symbols request.

type VersionsOptions

type VersionsOptions struct {
	ListOptions
}

VersionsOptions controls a Versions request.

type Vulnerability

type Vulnerability struct {
	ID           string `json:"id"`
	Summary      string `json:"summary"`
	Details      string `json:"details"`
	FixedVersion string `json:"fixedVersion"`
}

Vulnerability is one entry from the Go vulnerability database affecting the queried module or package.

type VulnsOptions

type VulnsOptions struct {
	Module  string
	Version string
	ListOptions
}

VulnsOptions controls a Vulnerabilities request.

Directories

Path Synopsis
internal
Package mock provides a test double for pkgsite.Client so consumers of the library can test their own code without making real HTTP calls.
Package mock provides a test double for pkgsite.Client so consumers of the library can test their own code without making real HTTP calls.

Jump to

Keyboard shortcuts

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