web

package
v0.40.0 Latest Latest
Warning

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

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

Documentation

Overview

Package web composes the one-glance web-analytics view — live visitors, top pages, referrers, UTM sources, device split — from $pageview events. This is the Plausible-shaped report indie devs otherwise run a SECOND tool for; here it's the same engine, same events, one binary. Deterministic like every other report.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Recorded added in v0.29.0

type Recorded struct {
	TopPages     int `json:"top_pages"`
	Referrers    int `json:"referrers"`
	UTMSources   int `json:"utm_sources"`
	DeviceSplit  int `json:"device_split"`
	Browsers     int `json:"browsers"`
	OSes         int `json:"oses"`
	Countries    int `json:"countries"`
	UTMMediums   int `json:"utm_mediums"`
	UTMCampaigns int `json:"utm_campaigns"`
	EntryPages   int `json:"entry_pages"`
	ExitPages    int `json:"exit_pages"`
	TopTitles    int `json:"top_titles"`
	AIReferrers  int `json:"ai_referrers"`
}

Recorded is the untruncated total behind each ranked dimension: how many pageviews (for per-pageview dimensions) or first-touch visitors (for the per-visitor ones) actually carried a value, before rank() cut the list down to what fits on screen.

type Result

type Result struct {
	PeriodDays  int   `json:"period_days"`
	Visitors    int   `json:"visitors"`  // unique visitors in period
	Pageviews   int   `json:"pageviews"` // total $pageview in period
	LiveNow     int   `json:"live_now"`  // unique visitors in the last 5 minutes
	TopPages    []Row `json:"top_pages"`
	Referrers   []Row `json:"referrers"`    // grouped by host, "" → "direct"
	UTMSources  []Row `json:"utm_sources"`  // only when utm_source is present
	DeviceSplit []Row `json:"device_split"` // mobile / desktop
	// the dimensions people open first — browsers/os come from the ingest-time UA
	// parse, countries from ingest-time geo (when enabled), entry pages from the
	// SDK's session_id, titles from the SDK's title prop
	Browsers     []Row   `json:"browsers"`
	OSes         []Row   `json:"oses"`
	Countries    []Row   `json:"countries"`
	UTMMediums   []Row   `json:"utm_mediums"`
	UTMCampaigns []Row   `json:"utm_campaigns"`
	EntryPages   []Row   `json:"entry_pages"`
	ExitPages    []Row   `json:"exit_pages"` // last pageview of each session — where visits END
	TopTitles    []Row   `json:"top_titles"`
	Hours        [24]int `json:"hours"` // pageviews by UTC hour of day — the activity rhythm
	// engagement — from $engagement events (SDK measures visible+focused time).
	// Omitted (zero) when the SDK predates engagement tracking.
	HasEngagement  bool `json:"has_engagement"`
	AvgEngagedSecs int  `json:"avg_engaged_secs"` // mean engaged time per engaged visitor
	BounceRatePct  int  `json:"bounce_rate_pct"`  // 1 pageview AND <10s engaged
	// the AI channel — humans arriving FROM AI assistants (chatgpt/claude/perplexity...).
	// distinct from AI crawlers, which the bot filter drops before storage.
	AIVisitors  int   `json:"ai_visitors"`
	AIReferrers []Row `json:"ai_referrers"`
	// Every ranked list above is TRUNCATED to its top N. A reader that sums the rows it can
	// see and calls that the total gets a denominator that only covers the visible head: a
	// site with 40 countries, 20 US visitors out of 100, rendered "US 20 · 52%" because the
	// ten visible rows summed to 38. The visible shares then also add to exactly 100%,
	// asserting there is no tail at all. These are the pre-truncation recorded totals — the
	// only honest denominator for "share of recorded" — so no surface has to re-derive one
	// from rows it was already handed in truncated form.
	Recorded Recorded `json:"recorded"`
}

Result is the web overview for a period.

func Compute

func Compute(evs []event.Event, days int, asof time.Time) Result

Compute builds the overview over the trailing `days` (default 30) as of `asof`.

func ComputeRange added in v0.29.0

func ComputeRange(evs []event.Event, from, to time.Time) Result

ComputeRange is ComputeWindow over an EXPLICIT [from, to) — the form the dashboard needs.

ComputeWindow derives its bounds from a duration and an as-of, which produces a ROLLING window: "last 10 days" means now−240h. The trends path next to it aligns the same preset to calendar days (day0−9d .. now) precisely so the tile, /v1/trends and the MCP tool agree. Two tiles sat side by side on the same row, both labelled "· 10d", measuring windows that differ by up to a day at each edge — and on a young instance that is the difference between a prior window with data in it and one without, so one tile showed "71x vs prior" and the one beside it showed no delta at all. Taking the bounds as arguments is what makes ONE window definition possible for the whole row.

func ComputeWindow added in v0.15.0

func ComputeWindow(evs []event.Event, window time.Duration, asof time.Time) Result

ComputeWindow is Compute over an arbitrary window rather than a whole number of days, so the dashboard's sub-day presets (6h, 12h) can report the SAME window in the tiles as the chart. Without it a 6h range moved the chart only and left every tile on 24h, which is a range control that lies about what it changed.

type Row

type Row struct {
	Value    string `json:"value"`
	Count    int    `json:"count"` // pageviews
	Visitors int    `json:"visitors"`
}

Row is one ranked value (a page, a referrer, a source...).

Jump to

Keyboard shortcuts

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