Files
goclaw/pkg/browser/refs.go
T
Viet Tran f3f4c67b36 Initial commit: GoClaw AI agent gateway
Multi-agent AI gateway with WebSocket RPC, HTTP API, and messaging channel integrations.
Go port of OpenClaw with multi-tenant PostgreSQL, per-user isolation, security hardening,
and production observability.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 14:58:07 +07:00

92 lines
2.0 KiB
Go

package browser
import (
"regexp"
"strings"
"sync"
)
const defaultMaxRefStoreSize = 50
var refPattern = regexp.MustCompile(`^e\d+$`)
// RefStore is a thread-safe, per-tab LRU cache for snapshot refs.
// Each entry maps a targetID to its ref→RoleRef mapping from the last snapshot.
type RefStore struct {
mu sync.RWMutex
entries map[string]map[string]RoleRef
order []string // LRU order (most recently used at end)
maxSize int
}
// NewRefStore creates a RefStore with default capacity.
func NewRefStore() *RefStore {
return &RefStore{
entries: make(map[string]map[string]RoleRef),
maxSize: defaultMaxRefStoreSize,
}
}
// Store saves refs for a target, evicting oldest entries if over capacity.
func (rs *RefStore) Store(targetID string, refs map[string]RoleRef) {
rs.mu.Lock()
defer rs.mu.Unlock()
// Remove from current position in LRU order
rs.removeFromOrder(targetID)
// Add to end (most recently used)
rs.order = append(rs.order, targetID)
rs.entries[targetID] = refs
// Evict oldest if over capacity
for len(rs.order) > rs.maxSize {
oldest := rs.order[0]
rs.order = rs.order[1:]
delete(rs.entries, oldest)
}
}
// Resolve looks up a ref for a given target.
func (rs *RefStore) Resolve(targetID, ref string) (*RoleRef, bool) {
rs.mu.RLock()
defer rs.mu.RUnlock()
normalized := NormalizeRef(ref)
refs, ok := rs.entries[targetID]
if !ok {
return nil, false
}
r, ok := refs[normalized]
if !ok {
return nil, false
}
return &r, true
}
// NormalizeRef normalizes ref formats: "@e5", "ref=e5", "e5" → "e5".
func NormalizeRef(raw string) string {
s := strings.TrimSpace(raw)
if s == "" {
return ""
}
if strings.HasPrefix(s, "@") {
s = s[1:]
} else if strings.HasPrefix(s, "ref=") {
s = s[4:]
}
if refPattern.MatchString(s) {
return s
}
return s
}
func (rs *RefStore) removeFromOrder(targetID string) {
for i, id := range rs.order {
if id == targetID {
rs.order = append(rs.order[:i], rs.order[i+1:]...)
return
}
}
}