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
- Variables
- type Activity
- type Deps
- type DepsData
- type Group
- type Handler
- func (h *Handler) DepsURL(pkgPath string) string
- func (h *Handler) DepsView(r *http.Request, pkgPath, title string) (int, *components.View, error)
- func (h *Handler) HasIndexer() bool
- func (h *Handler) Importers(ctx context.Context, r *http.Request, pkgPath string) (*Importers, error)
- func (h *Handler) Map(ctx context.Context, l Listing) components.MapParts
- type Head
- type ImportReader
- type Importers
- type Indexer
- type Label
- type Limiter
- type Listing
- type MapData
- type Metric
- type Rect
- type ScaleStep
- type Subgroup
- type Tile
Constants ¶
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 )
const ( ActivityPending = "pending" // 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.
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 ¶
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) )
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 ¶
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 ¶
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 ¶
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 ¶
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.
type Head ¶
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 ¶
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 ¶
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 ¶
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.
type Metric ¶
type Metric string
Metric is what a map's tiles are shaded by, chosen with ?color= on $map.
func ParseMetric ¶
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 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 ¶
PathSegments is Path cut after each slash, for line breaks in lists.