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 ¶
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 ¶
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") )
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.
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 ¶
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 ¶
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 ¶
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 ¶
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.
func Classify ¶
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.