cache

package
v0.2.2 Latest Latest
Warning

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

Go to latest
Published: May 29, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package cache is the directory-agnostic tier-1 cache core (working-with-state.md §5b): a self-describing JSON envelope with atomic temp-file-rename writes, version-mismatch-as-miss, and freshness classification. Location is always injected via Locator; this package never resolves a directory and never imports a CLI's config. Tier 2 (resource registry / dependency DAG / fetchers / refresh wiring) is deliberately out of scope (§5b, rule-of-three, deferred).

Index

Constants

View Source
const Version = 1

Version is the on-disk envelope schema version. A mismatch is treated as a cache miss so schema bumps self-heal on the next write.

Variables

View Source
var (
	// ErrInvalidRoot is returned when Locator.Root is empty or not absolute. A
	// zero-value Locator would otherwise filepath.Join("", key, name) and
	// write relative to the process working directory.
	ErrInvalidRoot = errors.New("cache: locator root must be a non-empty absolute path")
	// ErrInvalidName is returned when an instance key or resource name is
	// unsafe as a path component.
	ErrInvalidName = errors.New("cache: unsafe path component")
)
View Source
var ErrCacheMiss = errors.New("cache: miss")

ErrCacheMiss reports an envelope that is absent, version-mismatched, or whose stored identity (resource/instance) disagrees with the key it was read under. It is not an error condition for callers — it is the "fetch and populate" signal.

View Source
var ErrInstanceMismatch = errors.New("cache: envelope instance does not match locator")

ErrInstanceMismatch reports that a WriteEnvelope call supplied an envelope whose Instance does not match the Locator's InstanceKey. Writing it would produce a file the next ReadResource immediately rejects as a miss, so the write is refused and nothing is created.

Functions

func Age

func Age(fetchedAt, now time.Time) string

Age returns a short human-readable age ("8h", "3d", "2m", "45s") for status output. A zero fetchedAt returns "-"; a negative delta is clamped to 0.

func WriteEnvelope

func WriteEnvelope[T any](loc Locator, env Envelope[T]) error

WriteEnvelope atomically writes a caller-supplied envelope verbatim: FetchedAt, TTL, and Version are preserved exactly as given (unlike WriteResource, which stamps a fresh FetchedAt/Version). This is the invalidation primitive — e.g. a "stale" marker writes an envelope whose FetchedAt is the zero time and expects that to survive the round-trip.

The resource name is taken from env.Resource (so the file the next ReadResource looks up is exactly the one written), and env.Instance MUST equal loc.InstanceKey. Because ReadResource treats a resource/instance mismatch as a miss, WriteEnvelope refuses to write an envelope that the next read would immediately reject: a mismatch returns ErrInstanceMismatch and writes nothing. env.Resource is validated by the shared path guard (ErrInvalidName on an unsafe name).

func WriteResource

func WriteResource[T any](loc Locator, name, ttl string, data T) error

WriteResource atomically writes an envelope for name at loc. Resource, Instance (= loc.InstanceKey), Version, and FetchedAt (UTC now) are set here; ttl comes from the caller (a hard-coded per-resource value — §4.4).

Types

type Envelope

type Envelope[T any] struct {
	Resource  string    `json:"resource"`
	Instance  string    `json:"instance"`
	FetchedAt time.Time `json:"fetched_at"`
	TTL       string    `json:"ttl"`
	Version   int       `json:"version"`
	Data      T         `json:"data"`
}

Envelope is the on-disk JSON shape for a single cached resource.

func ReadResource

func ReadResource[T any](loc Locator, name string) (Envelope[T], error)

ReadResource reads the envelope for name at loc.

  • (envelope, nil) on success.
  • (zero, ErrCacheMiss) if the file does not exist, the on-disk Version differs from the current schema, or the stored resource/instance does not match the requested name / loc.InstanceKey.
  • (zero, error) on path validation, I/O, or JSON decode failure.

ReadResource does NOT check freshness; callers use Classify.

type Locator

type Locator struct {
	// Root is the non-empty absolute cache root for one tool.
	Root string
	// InstanceKey is the per-instance subdir (jtk: hostname / cloud-id;
	// gro: a constant; a single-instance CLI: "default").
	InstanceKey string
}

Locator is the injected cache location. The cache library is directory-agnostic: Root is supplied by the caller (typically statedir.Cache.CacheDir / CacheDirEnsured), never derived here.

type Status

type Status int

Status is the coarse freshness classification.

Classify returns ONLY Fresh, Stale, or Manual. Uninitialized is caller-derived (a ReadResource ErrCacheMiss — no envelope on disk) and Unavailable is registry-derived (a tier-2 concern). Both are defined here for jtk parity and a clean tier-2 lift, and are exercised only via String(); Classify never returns them.

const (
	StatusUninitialized Status = iota // no envelope on disk (caller-derived from ErrCacheMiss)
	StatusFresh                       // on disk, FetchedAt + TTL still in the future
	StatusStale                       // on disk, FetchedAt + TTL elapsed (or zero FetchedAt)
	StatusManual                      // TTL == "manual"; never auto-expires
	StatusUnavailable                 // registry-derived; never returned by Classify
)

func Classify

func Classify(fetchedAt time.Time, ttl string, now time.Time) Status

Classify inspects an envelope's FetchedAt + TTL at now and returns one of StatusFresh, StatusStale, or StatusManual. A zero FetchedAt (the uninitialized / Touch-ed state) and an unparseable TTL both classify as StatusStale — callers that need to distinguish "never fetched" check FetchedAt.IsZero() themselves.

func (Status) String

func (s Status) String() string

String returns the status label.

Jump to

Keyboard shortcuts

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