chainmap

package
v0.0.0 Latest Latest
Warning

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

Go to latest
Published: Oct 30, 2026 License: UNKNOWN not legal advice Imports: 0 Imported by: 0

Documentation

Overview

Package chainmap draws what is on the chain as a map: every `$map` URL, a treemap of the packages under a path, and the indexer-backed answers the dependency graph needs.

The map shows exactly the listing the directory view shows. gnoweb lists the paths once and hands the same slice to either rendering, so the two can never disagree about what exists.

With no indexer the map is complete and says what it measures: one tile per package, grouped by the next path segment. An indexer adds call activity and the packages that reference a given one. Whatever it adds is tagged as indexer data with its freshness, and a value it could not compute is shown as unknown, never as zero.

Index

Constants

View Source
const (

	// LookupsPerMinute and LookupsBurst bound the importer lookups one
	// address may start: a reader browsing dependencies starts a few, not
	// dozens. A lookup only a cached answer serves, or one joining another
	// in flight, is not charged.
	LookupsPerMinute = 6
	LookupsBurst     = 3
)
View Source
const (
	ActivityPending     = "pending"
	ActivityUnavailable = "unavailable"
	// ActivityNotCalled is a map of pure packages, which nothing calls.
	ActivityNotCalled = "not-called"
)

Why a map shows no activity, which the key says. A map never shows a count it does not have, so "not counted yet" and "could not count" are said, not drawn.

View Source
const MinPackages = 10

MinPackages is the smallest listing worth a map: below it a map shows nothing the list does not, so the listing offers no map at all.

Variables

View Source
var (
	// ErrRateLimited is returned when a reader asks for more lookups than
	// the limiter allows. It is not remembered.
	ErrRateLimited = fmt.Errorf("%w: too many requests", errTransient)

	// ErrNotLive is what an ImportReader returns for a path with no live
	// package.
	ErrNotLive = errors.New("package is not live")

	// ErrBusy is returned when every lookup slot is taken. It is not
	// remembered: the next reader may find a free slot.
	ErrBusy = fmt.Errorf("%w: every lookup slot is taken", errTransient)
)
View Source
var ErrPending = errors.New("answer still pending")

ErrPending is returned when a reader's own wait ran out before the answer came. The fetch carries on; a reader can say "still working" for this error, and only for this one, since a fetch that failed on a deadline is a failure.

Functions

This section is empty.

Types

type Activity

type Activity struct {
	// Calls, Callers and Gas are keyed by fully qualified package path. A
	// transaction's gas is shared evenly between the calls it makes, the
	// indexer reporting gas per transaction only.
	Calls   map[string]int
	Callers map[string]int
	Gas     map[string]int64

	// From and To are the heights counted, both inclusive.
	From, To int
	// Since is when block From was produced.
	Since time.Time

	// Partial is set when part of the window could not be read. A count is
	// then a lower bound, and a zero says nothing.
	Partial bool
}

Activity is the call count of every realm over the window.

type Deps

type Deps struct {
	// Indexer answers activity and proposes importers. Optional: nil means
	// no indexer is configured.
	Indexer Indexer

	// Imports reads a package's imports from the chain: the graph's left
	// side, and the check on every importer the indexer proposes. Required.
	Imports ImportReader

	// Domain is the chain's domain (e.g. "gno.land"), which turns a gnoweb
	// path into the package path the indexer keys on.
	Domain string

	// Limiter bounds the per-IP rate of importer lookups. Optional.
	Limiter Limiter

	Logger *slog.Logger
}

Deps is a struct of interfaces so each field is independently mockable.

type DepsData

type DepsData struct {
	// Title names the package in the heading, "ufmt/v0" for a versioned one.
	Title string
	Graph components.DepGraph
	// Indexer is the provenance footer, set when the indexer answered.
	Indexer *components.IndexerStatus
}

DepsData is the render payload for templates/deps.html.

type Group

type Group struct {
	Key string

	Count    int
	Rect     Rect
	HueClass string
	// Head is the name band; nil when the group is too small to carry one.
	Head *Head

	Subgroups []*Subgroup
	// contains filtered or unexported fields
}

Group is the packages sharing the first path segment below the map's root.

type Handler

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

Handler renders maps and answers the indexer-backed queries behind them.

func New

func New(deps Deps) *Handler

New validates required deps and returns a Handler. The caches exist only with an indexer, so none can be reached without one.

func (*Handler) DepsURL

func (h *Handler) DepsURL(pkgPath string) string

DepsURL is the dependencies page of pkgPath, a gnoweb-relative path, or "" on a deployment that cannot list importers: without an indexer the overview's graph already shows all there is.

func (*Handler) DepsView

func (h *Handler) DepsView(r *http.Request, pkgPath, title string) (int, *components.View, error)

DepsView renders the dependency graph of pkgPath, a gnoweb-relative path: its imports from the chain and, with an indexer, its importers. It is a page of its own because the importers cost a whole-chain scan plus a node read per candidate; the overview only links to it.

The error is the chain's, from reading pkgPath's imports, for the caller to render as it renders any other read failure.

func (*Handler) HasIndexer

func (h *Handler) HasIndexer() bool

HasIndexer reports whether the indexer-backed answers exist on this deployment.

func (*Handler) Importers

func (h *Handler) Importers(ctx context.Context, r *http.Request, pkgPath string) (*Importers, error)

Importers answers which live packages import pkgPath, a fully qualified package path.

Only the reader who starts a lookup pays for it: the slot and the limiter are taken inside the shared fetch, which runs once however many readers ask. A held answer, or joining a lookup already running, costs nothing. A lookup refused for want of a slot is refused before the limiter is asked, so it costs the reader nothing either.

func (*Handler) Map

Map draws a listing: the figure for the listing's body and its key for the listing's rail. The page around them is the directory view's own.

type Head struct {
	// Class is the band's block class: one partial draws both kinds.
	Class string
	Band  Rect
	Label Label
	// Zoom is the path the box maps, ending with a slash; empty when the box
	// has no map of its own.
	Zoom string
	// Query keeps the map's metric on the zoomed map; see MapData.Query.
	Query string
}

Head is the name band over the top of a group or subgroup: a link to that box's own map when it has one.

type ImportReader

type ImportReader interface {
	Imports(ctx context.Context, pkgPath string) ([]string, error)
}

ImportReader reads a live package's non-test imports from the chain. It returns ErrNotLive when no live package sits at the path.

type Importers

type Importers struct {
	// Paths are fully qualified and sorted.
	Paths []string
	// Capped is set when the search stopped at a limit: the indexer capped a
	// band, or there were more than maxCandidates. A retry cannot lift it.
	Capped bool
	// Unread is set when a candidate read failed or ran out of time, which a
	// retry can complete. The answer is then kept only briefly.
	Unread bool
	// AsOf is the indexer's last block when the lookup ran.
	AsOf int
}

Importers are the live packages whose non-test source imports a package.

The indexer only proposes candidates: it can say which deploys quote the import path, not which package in a batched deploy did, nor whether the quote is an import, a test import or a string. Each candidate is then checked against the chain's own import list, so every path here is an import as of the chain's current state.

type Indexer

type Indexer interface {
	LatestBlockHeight(ctx context.Context) (int, error)
	Block(ctx context.Context, height int) (*indexer.Block, error)
	CallsBetween(ctx context.Context, lower, upper int) ([]indexer.Tx, error)
	// DeploysQuoting returns the deploys in the heights (lower, upper] whose
	// source holds pkgPath as a Go string literal, delimiters included, so
	// that a path never matches inside a longer one.
	DeploysQuoting(ctx context.Context, pkgPath string, lower, upper int) ([]indexer.Tx, error)
	URL() string
}

Indexer is the subset of *indexer.Client this feature consumes. A nil Indexer is the feature switch: the map then shows no activity and the dependency graph no importers, and neither says anything about them.

type Label

type Label struct {
	Text       string
	X, Y, Size float64
}

Label is a name fitted to the box it sits in, where to draw it, and the font size it was fitted to.

type Limiter

type Limiter interface {
	AllowRequest(r *http.Request) bool
}

Limiter is the admission check applied before indexer-backed work a reader asked for. Nil disables the check.

type Listing

type Listing struct {
	// Path is the listing's root, gnoweb-relative, e.g. "/r/gnoland".
	Path string
	// Paths are the gnoweb-relative package paths below Path.
	Paths []string
	// Up is the listing one level up, ending with a slash: the way back out
	// of a zoom. Empty when there is none, at a kind's root such as /r/ or
	// where the path above is a package, which has no listing to map.
	Up string
	// Metric is what the tiles are shaded by; the zero value is calls.
	Metric Metric
}

Listing is a directory listing: the one the list view renders too.

type MapData

type MapData struct {
	// Root is the listing's root, ending with a slash.
	Root string
	// Up is the listing one level up; see Listing.Up.
	Up     string
	Groups []*Group

	// Window is the activity the tiles are coloured with, nil when there is
	// none; Activity then says why, or is empty when no indexer is
	// configured and the key says nothing about activity at all.
	Window   *Activity
	Activity string
	// Scale is the colour steps the tiles use, for the key.
	Scale []ScaleStep

	// Indexer is the provenance footer, set whenever the indexer answered.
	Indexer *components.IndexerStatus

	// Busiest are the realms ranking highest on the metric, set with
	// activity.
	Busiest []Tile

	// Metric is what the tiles are shaded by, and Query what a map link
	// appends after $map to keep it.
	Metric Metric
	Query  string
}

MapData is the render payload for templates/map.html.

func (*MapData) Gas

func (d *MapData) Gas() bool

Gas reports whether the tiles are shaded by gas, for the template.

func (*MapData) SwitchURL

func (d *MapData) SwitchURL() string

SwitchURL is this map under the other metric, for the key's switch.

func (*MapData) ViewBox

func (*MapData) ViewBox() string

ViewBox is the SVG's coordinate space, the one the layout fitted to.

type Metric

type Metric string

Metric is what a map's tiles are shaded by, chosen with ?color= on $map.

const (
	MetricCalls Metric = "calls"
	MetricGas   Metric = "gas"
)

func ParseMetric

func ParseMetric(s string) Metric

ParseMetric reads the color= value of a map URL. Anything but "gas" is calls, so an unknown value draws the default map rather than an error.

type Rect

type Rect struct{ X, Y, W, H float64 }

Rect is a box in map units.

type ScaleStep

type ScaleStep struct {
	Class, Range string
	// contains filtered or unexported fields
}

ScaleStep is one colour of the key: a shade and the range it stands for.

type Subgroup

type Subgroup struct {
	Key string

	Rect Rect
	Head *Head

	Tiles []Tile
	// contains filtered or unexported fields
}

Subgroup is the packages of a group sharing its next path segment. A group too small to split holds one subgroup with no key.

type Tile

type Tile struct {
	Path  string // gnoweb-relative, the package's page
	Rect  Rect
	Label *Label
	// Title is the tooltip and accessible name: the full path, which the
	// label shortens, and the activity when there is any.
	Title string

	Calls, Callers int
	// Gas is the gas the realm's calls used over the window.
	Gas int64
	// Amount is the metric's value written for the key's busiest list.
	Amount string
	// Unknown marks a tile whose count the indexer could not settle.
	Unknown bool
	// ShadeClass is the activity colour, empty when the map shows none.
	ShadeClass string
}

Tile is one package.

func (Tile) PathSegments

func (t Tile) PathSegments() []string

PathSegments is Path cut after each slash, for line breaks in lists.

Jump to

Keyboard shortcuts

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