routeranalytics API

routeranalytics

package

API reference for the routeranalytics package.

S
struct
Implements: prefetcher

wasmPrefetcher

plugins/routeranalytics/prefetch_js.go:11-16
type wasmPrefetcher struct

Methods

request
Method

Parameters

resources []string
func (*wasmPrefetcher) request(resources []string)
{
	if len(resources) == 0 {
		return
	}
	p.mu.Lock()
	defer p.mu.Unlock()
	if p.requested == nil {
		p.requested = make(map[string]struct{})
	}
	filtered := make([]string, 0, len(resources))
	for _, r := range resources {
		if r == "" {
			continue
		}
		if _, ok := p.requested[r]; ok {
			continue
		}
		p.requested[r] = struct{}{}
		filtered = append(filtered, r)
	}
	if len(filtered) == 0 {
		return
	}
	p.tick++
	// Same wire shape a netcode client would produce: hints are one-way, so
	// the full client (snapshots, interpolation) is not needed here.
	hostclient.Send(p.channel, map[string]any{
		"tick":     p.tick,
		"commands": []any{map[string]any{"resources": filtered}},
	})
}
reset
Method
func (*wasmPrefetcher) reset()
{
	p.mu.Lock()
	p.requested = make(map[string]struct{})
	p.tick = 0
	p.mu.Unlock()
}

Fields

Name Type Description
mu sync.Mutex
tick int64
requested map[string]struct{}
channel string
F
function

newPrefetcher

Parameters

channel
string

Returns

plugins/routeranalytics/prefetch_js.go:18-23
func newPrefetcher(channel string) prefetcher

{
	return &wasmPrefetcher{
		channel:   channel,
		requested: make(map[string]struct{}),
	}
}
F
function

newPrefetcher

Parameters

string

Returns

plugins/routeranalytics/prefetch_stub.go:5-5
func newPrefetcher(string) prefetcher

{ return noopPrefetcher{} }
S
struct

Options

Options configures route normalization and prefetch prediction.

plugins/routeranalytics/plugin.go:14-26
type Options struct

Fields

Name Type Description
Normalize func(string) string
PrefetchLimit int
PrefetchThreshold float64
Channel string
S
struct

TransitionProbability

TransitionProbability describes one observed route transition.

plugins/routeranalytics/plugin.go:29-34
type TransitionProbability struct

Fields

Name Type Description
From string
To string
Count int
Probability float64
S
struct

Plugin

Plugin tracks route transitions and emits prefetch hints.

plugins/routeranalytics/plugin.go:37-41
type Plugin struct

Methods

Name
Method

Name returns the plugin name.

Returns

string
func (*Plugin) Name() string
{
	return "routeranalytics"
}
Build
Method

Build performs no build-time work.

Parameters

Returns

error
func (*Plugin) Build(json.RawMessage) error
{ return nil }
Install
Method

Install registers route tracking with the application.

Parameters

func (*Plugin) Install(a *core.App)
{
	if a == nil {
		return
	}
	setDefaultInstance(p)
	a.RegisterRouter(func(path string) {
		p.handleNavigation(path)
	})
}

TransitionProbabilities returns transitions observed after from.

Parameters

from string
func (*Plugin) TransitionProbabilities(from string) []TransitionProbability
{
	if p == nil {
		return nil
	}
	probs := p.tracker.probabilities(from)
	if len(probs) == 0 {
		return nil
	}
	out := make([]TransitionProbability, len(probs))
	copy(out, probs)
	return out
}

MostLikelyNext returns the highest probability transitions after from.

Parameters

from string
limit int
func (*Plugin) MostLikelyNext(from string, limit int) []TransitionProbability
{
	if p == nil || limit <= 0 {
		return nil
	}
	probs := p.tracker.mostLikelyNext(from, limit)
	if len(probs) == 0 {
		return nil
	}
	out := make([]TransitionProbability, len(probs))
	copy(out, probs)
	return out
}
Reset
Method

Reset clears recorded route transitions.

func (*Plugin) Reset()
{
	if p == nil {
		return
	}
	p.tracker.reset()
	if p.prefetch != nil {
		p.prefetch.reset()
	}
}

Parameters

path string
func (*Plugin) handleNavigation(path string)
{
	normalized := strings.TrimSpace(path)
	if p.opts.Normalize != nil {
		normalized = p.opts.Normalize(path)
	}
	if normalized == "" {
		return
	}
	p.tracker.visit(normalized)
	p.enqueuePrefetch(normalized)
}

Parameters

current string
func (*Plugin) enqueuePrefetch(current string)
{
	if p.opts.PrefetchLimit <= 0 || p.prefetch == nil {
		return
	}
	predictions := p.tracker.mostLikelyNext(current, p.opts.PrefetchLimit)
	if len(predictions) == 0 {
		return
	}
	threshold := p.opts.PrefetchThreshold
	if threshold < 0 {
		threshold = 0
	}
	resources := make([]string, 0, len(predictions))
	for _, prob := range predictions {
		if prob.Probability < threshold {
			continue
		}
		resources = append(resources, prob.To)
	}
	if len(resources) == 0 {
		return
	}
	p.prefetch.request(resources)
}

Fields

Name Type Description
opts Options
tracker *transitionTracker
prefetch prefetcher
F
function

New

New creates a route analytics plugin.

Parameters

opts

Returns

plugins/routeranalytics/plugin.go:49-74
func New(opts Options) *Plugin

{
	if opts.Normalize == nil {
		opts.Normalize = defaultNormalize
	}
	if opts.PrefetchLimit < 0 {
		opts.PrefetchLimit = 0
	} else if opts.PrefetchLimit == 0 {
		opts.PrefetchLimit = 3
	}
	if opts.PrefetchThreshold <= 0 {
		opts.PrefetchThreshold = 0.2
	}
	if opts.Channel == "" {
		opts.Channel = "RouterPrefetch"
	}
	plugin := &Plugin{
		opts:    opts,
		tracker: newTransitionTracker(),
	}
	if opts.PrefetchLimit > 0 {
		plugin.prefetch = newPrefetcher(opts.Channel)
	} else {
		plugin.prefetch = noopPrefetcher{}
	}
	return plugin
}
F
function

TransitionProbabilities

TransitionProbabilities queries the default plugin instance.

Parameters

from
string
plugins/routeranalytics/plugin.go:135-140
func TransitionProbabilities(from string) []TransitionProbability

{
	if inst := getDefaultInstance(); inst != nil {
		return inst.TransitionProbabilities(from)
	}
	return nil
}
F
function

MostLikelyNext

MostLikelyNext queries the default plugin instance.

Parameters

from
string
limit
int
plugins/routeranalytics/plugin.go:143-148
func MostLikelyNext(from string, limit int) []TransitionProbability

{
	if inst := getDefaultInstance(); inst != nil {
		return inst.MostLikelyNext(from, limit)
	}
	return nil
}
F
function

Reset

Reset clears the default plugin instance.

plugins/routeranalytics/plugin.go:151-155
func Reset()

{
	if inst := getDefaultInstance(); inst != nil {
		inst.Reset()
	}
}
S
struct

transitionTracker

plugins/routeranalytics/plugin.go:157-162
type transitionTracker struct

Methods

visit
Method

Parameters

routeID string
func (*transitionTracker) visit(routeID string)
{
	if routeID == "" {
		return
	}
	t.mu.Lock()
	if t.last != "" {
		t.recordLocked(t.last, routeID)
	}
	t.last = routeID
	t.mu.Unlock()
}
recordLocked
Method

Parameters

from string
to string
func (*transitionTracker) recordLocked(from, to string)
{
	if from == "" || to == "" {
		return
	}
	if t.transitions[from] == nil {
		t.transitions[from] = make(map[string]int)
	}
	t.transitions[from][to]++
	t.totals[from]++
}
reset
Method
func (*transitionTracker) reset()
{
	t.mu.Lock()
	t.transitions = make(map[string]map[string]int)
	t.totals = make(map[string]int)
	t.last = ""
	t.mu.Unlock()
}
probabilities
Method

Parameters

from string
func (*transitionTracker) probabilities(from string) []TransitionProbability
{
	t.mu.RLock()
	total := t.totals[from]
	if total == 0 {
		t.mu.RUnlock()
		return nil
	}
	counts := t.transitions[from]
	result := make([]TransitionProbability, 0, len(counts))
	for to, count := range counts {
		result = append(result, TransitionProbability{
			From:        from,
			To:          to,
			Count:       count,
			Probability: float64(count) / float64(total),
		})
	}
	t.mu.RUnlock()
	sort.Slice(result, func(i, j int) bool {
		if result[i].Probability == result[j].Probability {
			return result[i].To < result[j].To
		}
		return result[i].Probability > result[j].Probability
	})
	return result
}

Parameters

from string
limit int
func (*transitionTracker) mostLikelyNext(from string, limit int) []TransitionProbability
{
	if limit <= 0 {
		return nil
	}
	probs := t.probabilities(from)
	if len(probs) == 0 {
		return nil
	}
	if limit >= len(probs) {
		out := make([]TransitionProbability, len(probs))
		copy(out, probs)
		return out
	}
	out := make([]TransitionProbability, limit)
	copy(out, probs[:limit])
	return out
}

Fields

Name Type Description
mu sync.RWMutex
transitions map[string]map[string]int
totals map[string]int
last string
F
function

newTransitionTracker

plugins/routeranalytics/plugin.go:164-169
func newTransitionTracker() *transitionTracker

{
	return &transitionTracker{
		transitions: make(map[string]map[string]int),
		totals:      make(map[string]int),
	}
}
F
function

defaultNormalize

Parameters

path
string

Returns

string
plugins/routeranalytics/plugin.go:284-299
func defaultNormalize(path string) string

{
	trimmed := strings.TrimSpace(path)
	if trimmed == "" {
		return ""
	}
	if idx := strings.IndexAny(trimmed, "?#"); idx >= 0 {
		trimmed = trimmed[:idx]
	}
	if trimmed == "" {
		return ""
	}
	if !strings.HasPrefix(trimmed, "/") {
		trimmed = "/" + trimmed
	}
	return trimmed
}
F
function

NormalizePath

NormalizePath applica la normalizzazione predefinita (trim, rimozione di query/hash e slash iniziale).

Parameters

path
string

Returns

string
plugins/routeranalytics/plugin.go:302-304
func NormalizePath(path string) string

{
	return defaultNormalize(path)
}
I
interface

prefetcher

plugins/routeranalytics/plugin.go:306-309
type prefetcher interface

Methods

request
Method

Parameters

resources []string
func request(...)
reset
Method
func reset(...)
S
struct

noopPrefetcher

plugins/routeranalytics/plugin.go:311-311
type noopPrefetcher struct

Methods

request
Method

Parameters

[]string
func (noopPrefetcher) request([]string)
{}
reset
Method
func (noopPrefetcher) reset()
{}
F
function

setDefaultInstance

Parameters

p
plugins/routeranalytics/plugin.go:317-321
func setDefaultInstance(p *Plugin)

{
	defaultMu.Lock()
	defaultInstance = p
	defaultMu.Unlock()
}
F
function

getDefaultInstance

Returns

plugins/routeranalytics/plugin.go:323-327
func getDefaultInstance() *Plugin

{
	defaultMu.RLock()
	defer defaultMu.RUnlock()
	return defaultInstance
}
F
function

TestTransitionProbabilities

Parameters

plugins/routeranalytics/plugin_test.go:5-30
func TestTransitionProbabilities(t *testing.T)

{
	p := New(Options{})
	p.Reset()
	p.handleNavigation("/home")
	p.handleNavigation("/posts")
	p.handleNavigation("/home")
	p.handleNavigation("/posts")
	p.handleNavigation("/settings")
	p.handleNavigation("/posts")
	p.handleNavigation("/home")

	probs := p.TransitionProbabilities("/posts")
	if len(probs) != 2 {
		t.Fatalf("expected two transitions, got %d", len(probs))
	}
	if probs[0].To != "/home" {
		t.Fatalf("expected /home to be most probable, got %s", probs[0].To)
	}
	if probs[0].Count != 2 || probs[1].Count != 1 {
		t.Fatalf("expected counts to reflect visits, got %v", probs)
	}

	if got := p.TransitionProbabilities("/missing"); got != nil {
		t.Fatalf("expected nil for unknown route, got %v", got)
	}
}
F
function

TestMostLikelyNextOrdering

Parameters

plugins/routeranalytics/plugin_test.go:32-62
func TestMostLikelyNextOrdering(t *testing.T)

{
	p := New(Options{})
	p.Reset()
	p.handleNavigation("/a")
	p.handleNavigation("/b")
	p.handleNavigation("/c")
	p.handleNavigation("/b")
	p.handleNavigation("/c")
	p.handleNavigation("/d")
	p.handleNavigation("/b")
	p.handleNavigation("/d")

	probs := p.MostLikelyNext("/b", 2)
	if len(probs) != 2 {
		t.Fatalf("expected two results, got %d", len(probs))
	}
	if probs[0].To != "/c" {
		t.Fatalf("expected /c to be first, got %s", probs[0].To)
	}
	if probs[0].Probability <= probs[1].Probability {
		t.Fatalf("expected probability ordering")
	}

	if limited := p.MostLikelyNext("/b", 1); len(limited) != 1 || limited[0].To != "/c" {
		t.Fatalf("expected limit to return most probable route")
	}

	if none := p.MostLikelyNext("/z", 3); none != nil {
		t.Fatalf("expected nil for unknown transitions, got %v", none)
	}
}
F
function

TestDefaultNormalize

Parameters

plugins/routeranalytics/plugin_test.go:64-74
func TestDefaultNormalize(t *testing.T)

{
	p := New(Options{})
	normalized := p.opts.Normalize(" docs/view?id=7#section ")
	if normalized != "/docs/view" {
		t.Fatalf("expected normalized path, got %q", normalized)
	}

	if global := NormalizePath(" docs/view?id=7#section "); global != normalized {
		t.Fatalf("expected NormalizePath to match default normalize, got %q", global)
	}
}