MoonCache

A transport-independent, explainable HTTP cache policy and runtime toolkit.

http
cache
rfc9111
moon add Ag108/MoonCache@0.1.3
Download zip
Author
Version
0.1.3
License
Apache-2.0
Last updated
14 days ago
Downloads
9

Dependencies

README

#MoonCache

MoonCache is a transport-independent, explainable HTTP cache policy and runtime toolkit for MoonBit. It implements the 0.1.3 cache lifecycle:

Request -> normalized cache key -> Store lookup -> Vary selection -> freshness decision -> Fetch or conditional revalidation -> 304 merge or replacement -> Store update -> RuntimeResponse + CacheTrace

It is an HTTP caching semantics layer, not an HTTP protocol stack, generic LRU, cookie jar, or production reverse proxy.

#Release status

The 0.1.3 release includes:

  • normalized request, response, header, URI, and saturating time models;
  • private/shared storage rules and authenticated-request protection;
  • RFC 9111 corrected age and freshness calculations;
  • Vary variants with missing-versus-empty field semantics;
  • ETag and Last-Modified conditional requests;
  • 304 Not Modified metadata merging with cached-body retention;
  • an HTTP-specific MemoryStore with deterministic eviction and limits;
  • a complete transport-independent CachedRuntime;
  • deterministic FakeTransport and RecordingTransport;
  • redacted text/JSON trace, runtime, and Store statistics reports;
  • a native CLI with explain, validate, and replay commands;
  • optional f4ah6o/http11 and native moonbitlang/async/http adapters;
  • four runnable, self-checking examples;
  • 280 deterministic test blocks with no public-network or real-wait tests.

  • Source: yzy726/MoonCache
  • Package: Ag108/MoonCache
  • License: Apache-2.0

#Build and test

moon fmt --check moon check --deny-warn moon test --deny-warn

The portable packages are also checked explicitly:

moon check --target native --deny-warn moon check --target js --deny-warn moon check --target wasm-gc --deny-warn

Add the published release with:

moon add Ag108/MoonCache@0.1.3

#Minimal cached runtime

Add the root package to moon.pkg:

///|
import {
"Ag108/MoonCache" @mooncache,
}

Then inject a Store, a Transport, and time:

let store = @mooncache.MemoryStore::default()
let origin = @mooncache.FakeTransport::new([
@mooncache.TransportResponse::new(
@mooncache.ResponseMeta::complete(
200,
@mooncache.HeaderMap::from_pairs([
("Cache-Control", "max-age=60"),
]),
@mooncache.Timestamp::zero(),
@mooncache.Timestamp::zero(),
),
b"hello",
),
])
let runtime = @mooncache.CachedRuntime::new(
store,
origin,
@mooncache.CacheOptions::private_cache(),
)
let request = @mooncache.RuntimeRequest::get(
"https://example.test/greeting",
)
let first = try! runtime.execute(request, @mooncache.Timestamp::zero())
let second = try! runtime.execute(
request,
@mooncache.Timestamp::from_seconds(10L),
)
println(first.source.label()) // upstream
println(second.source.label()) // cache

CacheStore and Transport are open traits, so applications can replace both implementations without changing policy code.

#CLI

The CLI consumes deterministic JSON scenarios and never performs a network request:

moon run cmd/main -- explain examples/scenarios/stale-etag.json moon run cmd/main -- explain examples/scenarios/stale-etag.json --json moon run cmd/main -- validate examples/scenarios/basic-cache.json moon run cmd/main -- replay testdata/scenarios moon run cmd/main -- replay testdata/scenarios --json

replay sorts paths explicitly and continues after an unreadable or malformed individual file, returning a failing exit code with a complete summary.

#Runnable examples

moon run examples/basic_cache moon run examples/vary_language moon run examples/etag_revalidation moon run examples/shared_private

ExampleDemonstrates
basic_cachefirst request misses, second request hits
vary_languageEnglish and Chinese variants remain independent
etag_revalidationstale ETag request receives 304 and retains its body
shared_privateprivate cache reuses a private response; shared cache rejects it

Every example checks its own expected source, body, variant count, or origin call count and exits unsuccessfully on a regression.

#Optional adapters

#HTTP11

adapters/http11 directly converts f4ah6o/http11@0.1.1 request and response types. Origin-form request targets require the caller to supply the absolute URI:

///|
let cached_request = @mooncache_http11.request_to_runtime_at_uri(
wire_request, "https://example.test/data", request_time,
)

The core package does not import HTTP11.

#Native async HTTP

adapters/async_http provides AsyncTransport, MoonbitAsyncHttpTransport, ScriptedAsyncTransport, and AsyncCachedRuntime. The real transport currently supports GET, POST, and PUT through moonbitlang/async/http@0.20.2 and receives an injected response clock:

///|
let origin = @mooncache_async.MoonbitAsyncHttpTransport::new(clock)

///|
let client = @mooncache_async.AsyncCachedRuntime::new(
@mooncache.MemoryStore::default(),
origin,
@mooncache.CacheOptions::private_cache(),
)

///|
let response = client.execute(request, now)

The adapter is native-only; model and policy portability do not depend on it. See Async adapter.

#Security defaults

  • no-store, incomplete bodies, Vary: *, and unknown statuses without explicit freshness are not stored.
  • Shared caches reject private responses and authorized requests unless an explicit option/rule permits them.
  • Authorization, Proxy-Authorization, Cookie, and Set-Cookie values are redacted from reports.
  • MemoryStore strips hop-by-hop response fields, strips Set-Cookie by default, and removes request credentials unless a Vary field requires one.
  • Body size, total body bytes, and entry count are bounded.
  • Age arithmetic saturates and clamps backward clocks.
  • Core policy never reads a system clock or accesses the network.

Read the security model before integrating a shared cache.

#Documentation

The generated root interface is pkg.generated.mbti. Adapter packages have their own generated interfaces after moon info.

#License

Apache-2.0. See LICENSE.

#
CacheStore

pub(open) trait CacheStore {
fn find_variants(Self, PrimaryCacheKey) -> Array[StoredEntry]
fn put(Self, StoredEntry) -> StorePutResult
fn remove_variant(Self, PrimaryCacheKey, VariantKey) -> Bool
fn invalidate_uri(Self, String) -> Int
fn clear(Self) -> Unit
fn stats(Self) -> StoreStats
}

#
Transport

pub(open) trait Transport {
fn execute(Self, RuntimeRequest) -> TransportResponse raise TransportError
}

#
RuntimeError

pub suberror RuntimeError {
RuntimeTransportFailure(String)
} derive(Eq,
Debug
)

#
RuntimeError::message

fn RuntimeError::message(self : RuntimeError) -> String

#
ScenarioError

pub suberror ScenarioError {
ScenarioError(String)
} derive(Eq,
Debug
)

#
ScenarioError::message

fn ScenarioError::message(self : ScenarioError) -> String

#
TransportError

pub suberror TransportError {
ScriptExhausted
ScriptedTransportFailure(String)
} derive(Eq,
Debug
)

#
TransportError::message

fn TransportError::message(self : TransportError) -> String

#
ValidationError

pub suberror ValidationError {
ExpectedNotModified(Int)
} derive(Eq,
Debug
)

#
ValidationError::message

fn ValidationError::message(self : ValidationError) -> String

#
AgeCalculation

pub(all) struct AgeCalculation {
apparent_age : DeltaSeconds
response_delay : DeltaSeconds
age_value : DeltaSeconds
corrected_age_value : DeltaSeconds
corrected_initial_age : DeltaSeconds
resident_time : DeltaSeconds
current_age : DeltaSeconds
clock_clamped : Bool
overflow_clamped : Bool
} derive(Eq,
Debug
)

#
CacheActionKind

pub(all) enum CacheActionKind {
ServeFresh
Revalidate
Fetch
ServeStale
Bypass
OnlyIfCachedMiss
} derive(Compare, Eq,
Debug
)

#
CacheActionKind::label

fn CacheActionKind::label(self : CacheActionKind) -> String

#
CacheControl

pub(all) struct CacheControl {
no_cache : Bool
no_store : Bool
is_private : Bool
is_public : Bool
must_revalidate : Bool
proxy_revalidate : Bool
immutable : Bool
only_if_cached : Bool
max_age : DeltaSeconds?
shared_max_age : DeltaSeconds?
min_fresh : DeltaSeconds?
max_stale : DeltaSeconds?
max_stale_unbounded : Bool
stale_if_error : DeltaSeconds?
stale_while_revalidate : DeltaSeconds?
malformed_delta : Bool
} derive(Eq,
Debug
)

Cache directives used by both request and response policy. Unknown extensions are intentionally ignored; malformed delta-seconds never become permissive values.

#
CacheControl::new

#
CacheDecision

pub(all) struct CacheDecision {
action : CacheActionKind
trace : CacheTrace
storage : StorageDecision
} derive(Eq,
Debug
)

#
CacheMode

pub(all) enum CacheMode {
Private
Shared
} derive(Compare, Eq,
Debug
)

#
CacheMode::from_string

fn CacheMode::from_string(value : String) -> CacheMode?

#
CacheMode::label

fn CacheMode::label(self : CacheMode) -> String

#
CacheOptions

pub(all) struct CacheOptions {
mode : CacheMode
allow_heuristic_freshness : Bool
heuristic_fraction_per_mille : Int
max_heuristic_lifetime : DeltaSeconds
allow_shared_authorized : Bool
cache_head_responses : Bool
strip_sensitive_metadata : Bool
} derive(Eq,
Debug
)

Policy settings with privacy-preserving defaults.

#
CacheOptions::private_cache

fn CacheOptions::private_cache() -> CacheOptions

#
CacheOptions::shared_cache

fn CacheOptions::shared_cache() -> CacheOptions

#
CacheOptions::with_cache_head_responses

fn CacheOptions::with_cache_head_responses(self : CacheOptions, enabled : Bool) -> CacheOptions

#
CacheOptions::with_heuristic_freshness

fn CacheOptions::with_heuristic_freshness(self : CacheOptions, enabled : Bool) -> CacheOptions

#
CacheOptions::with_heuristic_settings

fn CacheOptions::with_heuristic_settings(self : CacheOptions, fraction_per_mille : Int, maximum : DeltaSeconds) -> CacheOptions

#
CacheOptions::with_shared_authorized

fn CacheOptions::with_shared_authorized(self : CacheOptions, enabled : Bool) -> CacheOptions

#
CacheReason

pub(all) struct CacheReason {
code : CacheReasonCode
detail : String?
rfc : String?
} derive(Eq,
Debug
)

#
CacheReason::new

#
CacheReason::with_detail

fn CacheReason::with_detail(code : CacheReasonCode, detail : String) -> CacheReason

#
CacheReason::with_rfc

fn CacheReason::with_rfc(code : CacheReasonCode, rfc : String) -> CacheReason

#
CacheReasonCode

pub(all) enum CacheReasonCode {
StoreAllowed
StoreMethodUnsupported
StoreStatusDefault
StoreStatusExplicit
StoreStatusUnknown
StoreNoStore
StorePrivateShared
StoreAuthorizationShared
StoreIncomplete
StoreVaryStar
StoreOversized
StoreCapacityZero
FreshMaxAge
FreshSharedMaxAge
FreshExpires
FreshHeuristic
FreshNone
FreshStillFresh
FreshStale
AgeApparent
AgeCorrected
AgeClockClamped
AgeOverflowClamped
RequestNoStore
RequestNoCache
RequestOnlyIfCached
RequestMaxAge
RequestMinFresh
RequestMaxStale
VaryDefault
VaryMatched
VaryMismatch
VaryMissing
VaryStar
RevalidateEtag
RevalidateLastModified
RevalidateBoth
RevalidateCallerCondition
RevalidateNotModified
RevalidateModified
RuntimeHit
RuntimeMiss
RuntimeBypass
RuntimeFetch
RuntimeStored
RuntimeNotStored
RuntimeOnlyIfCachedMiss
StoreReplaced
StoreEvictedEntries
StoreEvictedBytes
StoreInvalidated
InvalidateUnsafeMethod
InvalidateRelatedUri
InvalidateSkippedSafeMethod
InvalidateSkippedErrorStatus
} derive(Compare, Eq,
Debug
)

Stable machine-readable reasons used by policy, store, runtime, and CLI traces. Existing code strings are not repurposed.

#
CacheReasonCode::code

fn CacheReasonCode::code(self : CacheReasonCode) -> String

#
CacheReasonCode::message

fn CacheReasonCode::message(self : CacheReasonCode) -> String

#
CacheScenario

pub(all) struct CacheScenario {
name : String
mode : String
now : Int
request : ScenarioRequest
stored_response : ScenarioResponse?
upstream_response : ScenarioResponse?
expected : ScenarioExpected?
} derive(Eq, ToJson,
Debug
,
FromJson
)

Stable, transport-free scenario format used by unit tests and the CLI.

#
CacheTrace

pub(all) struct CacheTrace {
action : CacheActionKind
reasons : Array[CacheReason]
age : AgeCalculation?
freshness_lifetime : DeltaSeconds?
stale_by : DeltaSeconds?
primary_key : String?
selected_variant : String?
validator : String?
generated_headers : HeaderMap
} derive(Eq,
Debug
)

#
CacheTrace::add_reason

fn CacheTrace::add_reason(self : CacheTrace, reason : CacheReason) -> Unit

#
CacheTrace::has_reason

fn CacheTrace::has_reason(self : CacheTrace, code : CacheReasonCode) -> Bool

#
CacheTrace::json_report

fn CacheTrace::json_report(self : CacheTrace) -> String

#
CacheTrace::new

#
CacheTrace::text_report

fn CacheTrace::text_report(self : CacheTrace) -> String

#
CacheTrace::to_json

fn CacheTrace::to_json(self : CacheTrace) -> Json

#
CachedRuntime

pub struct CachedRuntime[S, T] {
// private fields
}

#
CachedRuntime::execute

fn[S : CacheStore, T : Transport] CachedRuntime::execute(self : CachedRuntime[S, T], request : RuntimeRequest, now : Timestamp) -> RuntimeResponse raise RuntimeError

#
CachedRuntime::new

fn[S, T] CachedRuntime::new(store : S, transport : T, options : CacheOptions) -> CachedRuntime[S, T]

#
CachedRuntime::store_stats

fn[S : CacheStore, T] CachedRuntime::store_stats(self : CachedRuntime[S, T]) -> StoreStats

#
DeltaSeconds

A non-negative number of seconds. Construction and arithmetic saturate instead of wrapping into negative values.

#
DeltaSeconds::from_seconds

fn DeltaSeconds::from_seconds(value : Int64) -> DeltaSeconds

#
DeltaSeconds::max

#
DeltaSeconds::max_value

fn DeltaSeconds::max_value() -> DeltaSeconds

#
DeltaSeconds::min

#
DeltaSeconds::saturating_add

fn DeltaSeconds::saturating_add(self : DeltaSeconds, other : DeltaSeconds) -> DeltaSeconds

#
DeltaSeconds::saturating_scale

fn DeltaSeconds::saturating_scale(self : DeltaSeconds, numerator : Int64, denominator : Int64) -> DeltaSeconds

Scale a duration with integer arithmetic. Invalid or non-positive factors produce zero; multiplication is rearranged to avoid overflow.

#
DeltaSeconds::saturating_sub

fn DeltaSeconds::saturating_sub(self : DeltaSeconds, other : DeltaSeconds) -> DeltaSeconds

#
DeltaSeconds::seconds

fn DeltaSeconds::seconds(self : DeltaSeconds) -> Int64

#
DeltaSeconds::zero

#
FakeTransport

pub struct FakeTransport {
// private fields
}

#
FakeTransport::call_count

fn FakeTransport::call_count(self : FakeTransport) -> Int

#
FakeTransport::execute

#
FakeTransport::new

#
FakeTransport::with_outcomes

fn FakeTransport::with_outcomes(outcomes : Array[TransportOutcome]) -> FakeTransport

#
FreshnessCalculation

pub(all) struct FreshnessCalculation {
lifetime : DeltaSeconds
source : FreshnessSource
date_value : Timestamp
expires_value : Timestamp?
last_modified_value : Timestamp?
} derive(Eq,
Debug
)

#
FreshnessSource

pub(all) enum FreshnessSource {
SharedMaxAge
MaxAge
Expires
HeuristicLastModified
NoFreshness
} derive(Compare, Eq,
Debug
)

#
FreshnessSource::label

fn FreshnessSource::label(self : FreshnessSource) -> String

#
FreshnessSource::reason

#
HeaderMap

pub struct HeaderMap {
values : Map[String, Array[String]]
} derive(Eq,
Debug
)

A case-insensitive, insertion-ordered collection of HTTP fields.

Repeated fields are retained as separate values. contains distinguishes a missing field from a present field whose value is empty, which is required for correct Vary matching.

#
HeaderMap::append

fn HeaderMap::append(self : HeaderMap, name : String, value : String) -> Bool

Add one value for name, retaining previous values.

#
HeaderMap::contains

fn HeaderMap::contains(self : HeaderMap, name : String) -> Bool

#
HeaderMap::copy

fn HeaderMap::copy(self : HeaderMap) -> HeaderMap

Create a deep copy so callers cannot mutate arrays held by the source map.

#
HeaderMap::from_pairs

fn HeaderMap::from_pairs(pairs : Array[(String, String)]) -> HeaderMap

#
HeaderMap::get

fn HeaderMap::get(self : HeaderMap, name : String) -> String?

Combine repeated field values using the comma convention used by the cache directives handled by MoonCache.

#
HeaderMap::get_all

fn HeaderMap::get_all(self : HeaderMap, name : String) -> Array[String]

Return a defensive copy of every value for name.

#
HeaderMap::get_first

fn HeaderMap::get_first(self : HeaderMap, name : String) -> String?

#
HeaderMap::is_empty

fn HeaderMap::is_empty(self : HeaderMap) -> Bool

#
HeaderMap::length

fn HeaderMap::length(self : HeaderMap) -> Int

#
HeaderMap::names

fn HeaderMap::names(self : HeaderMap) -> Array[String]

Return canonical field names in deterministic lexical order.

#
HeaderMap::new

fn HeaderMap::new() -> HeaderMap

#
HeaderMap::overlay

fn HeaderMap::overlay(self : HeaderMap, updates : HeaderMap) -> HeaderMap

Overlay every field from updates, replacing existing repeated values.

#
HeaderMap::pairs

fn HeaderMap::pairs(self : HeaderMap) -> Array[(String, String)]

Return normalized field/value pairs in deterministic name order while preserving value order within each repeated field.

#
HeaderMap::redacted

fn HeaderMap::redacted(self : HeaderMap) -> HeaderMap

Return a copy suitable for diagnostics. Credential and cookie values are replaced while their presence remains visible.

#
HeaderMap::remove

fn HeaderMap::remove(self : HeaderMap, name : String) -> Bool

#
HeaderMap::set

fn HeaderMap::set(self : HeaderMap, name : String, value : String) -> Bool

Replace all existing values for name. Invalid field names are rejected and leave the map unchanged.

#
InvalidationPlan

pub(all) struct InvalidationPlan {
uris : Array[String]
reasons : Array[CacheReason]
} derive(Eq,
Debug
)

#
MemoryStore

pub struct MemoryStore {
// private fields
}

HTTP-specific in-memory store with deterministic logical access ordering.

#
MemoryStore::cached_uris

fn MemoryStore::cached_uris(self : MemoryStore) -> Array[String]

Return each normalized cached URI once in deterministic lexical order.

#
MemoryStore::clear

fn MemoryStore::clear(self : MemoryStore) -> Unit

#
MemoryStore::contains_variant

fn MemoryStore::contains_variant(self : MemoryStore, key : PrimaryCacheKey, variant : VariantKey) -> Bool

#
MemoryStore::default

fn MemoryStore::default() -> MemoryStore

#
MemoryStore::find_variants

fn MemoryStore::find_variants(self : MemoryStore, key : PrimaryCacheKey) -> Array[StoredEntry]

#
MemoryStore::invalidate_uri

fn MemoryStore::invalidate_uri(self : MemoryStore, uri : String) -> Int

#
MemoryStore::new

#
MemoryStore::options

Return the immutable configuration used by this Store.

#
MemoryStore::peek_variants

fn MemoryStore::peek_variants(self : MemoryStore, uri : String) -> Array[StoredEntry]

Inspect variants for a URI without updating logical access order or lookup counters. Returned entries are defensive copies.

#
MemoryStore::put

fn MemoryStore::put(self : MemoryStore, source_entry : StoredEntry) -> StorePutResult

#
MemoryStore::remove_variant

fn MemoryStore::remove_variant(self : MemoryStore, key : PrimaryCacheKey, variant : VariantKey) -> Bool

#
MemoryStore::stats

fn MemoryStore::stats(self : MemoryStore) -> StoreStats

#
MemoryStore::variant_count

fn MemoryStore::variant_count(self : MemoryStore, uri : String) -> Int

#
MemoryStoreOptions

pub(all) struct MemoryStoreOptions {
max_entries : Int
max_body_bytes : Int64
max_body_size : Int64
strip_set_cookie : Bool
strip_request_credentials : Bool
} derive(Eq,
Debug
)

#
MemoryStoreOptions::default

#
MemoryStoreOptions::with_entry_limit

fn MemoryStoreOptions::with_entry_limit(self : MemoryStoreOptions, max_entries : Int) -> MemoryStoreOptions

#
MemoryStoreOptions::with_limits

fn MemoryStoreOptions::with_limits(max_entries : Int, max_body_bytes : Int64, max_body_size : Int64) -> MemoryStoreOptions

#
MemoryStoreOptions::with_request_credential_stripping

fn MemoryStoreOptions::with_request_credential_stripping(self : MemoryStoreOptions, strip_request_credentials : Bool) -> MemoryStoreOptions

fn MemoryStoreOptions::with_set_cookie_stripping(self : MemoryStoreOptions, strip_set_cookie : Bool) -> MemoryStoreOptions

#
MemoryStoreOptions::with_single_body_limit

fn MemoryStoreOptions::with_single_body_limit(self : MemoryStoreOptions, max_body_size : Int64) -> MemoryStoreOptions

#
MemoryStoreOptions::with_total_body_limit

fn MemoryStoreOptions::with_total_body_limit(self : MemoryStoreOptions, max_body_bytes : Int64) -> MemoryStoreOptions

#
NotModifiedResult

pub(all) struct NotModifiedResult {
entry : StoredEntry
storage : StorageDecision
reasons : Array[CacheReason]
} derive(Eq,
Debug
)

#
PrimaryCacheKey

pub(all) struct PrimaryCacheKey {
http_method : String
uri : String
} derive(Compare, Eq, Hash,
Debug
)

The primary lookup key. GET and HEAD compatibility is applied by the variant module when constructing this value.

#
PrimaryCacheKey::from_request

fn PrimaryCacheKey::from_request(request : RequestMeta) -> PrimaryCacheKey?

#
PrimaryCacheKey::label

fn PrimaryCacheKey::label(self : PrimaryCacheKey) -> String

#
PrimaryCacheKey::new

fn PrimaryCacheKey::new(http_method : String, uri : String) -> PrimaryCacheKey

#
RecordingTransport

pub struct RecordingTransport {
// private fields
}

#
RecordingTransport::call_count

fn RecordingTransport::call_count(self : RecordingTransport) -> Int

#
RecordingTransport::calls

#
RecordingTransport::execute

#
RecordingTransport::new

#
RecordingTransport::with_outcomes

#
RequestMeta

pub(all) struct RequestMeta {
http_method : String
uri : String
headers : HeaderMap
request_time : Timestamp
} derive(Eq,
Debug
)

Metadata supplied to the cache policy. The request time is injected by the caller and never read from a system clock.

#
RequestMeta::new

fn RequestMeta::new(http_method : String, uri : String, headers : HeaderMap, request_time : Timestamp) -> RequestMeta

#
RequestMeta::simple

fn RequestMeta::simple(http_method : String, uri : String) -> RequestMeta

#
RequestMeta::with_headers

fn RequestMeta::with_headers(self : RequestMeta, headers : HeaderMap) -> RequestMeta

#
RequestMeta::with_request_time

fn RequestMeta::with_request_time(self : RequestMeta, request_time : Timestamp) -> RequestMeta

#
RequestPolicy

pub(all) struct RequestPolicy {
bypass : Bool
force_revalidation : Bool
only_if_cached : Bool
max_age : DeltaSeconds?
min_fresh : DeltaSeconds
max_stale : DeltaSeconds?
max_stale_unbounded : Bool
} derive(Eq,
Debug
)

#
RequestPolicy::allows_stale

fn RequestPolicy::allows_stale(self : RequestPolicy, stale_by : DeltaSeconds) -> Bool

#
ResponseMeta

pub(all) struct ResponseMeta {
status : Int
headers : HeaderMap
request_time : Timestamp
response_time : Timestamp
body_complete : Bool
} derive(Eq,
Debug
)

Normalized response metadata. body_complete lets adapters conservatively reject interrupted or size-limited bodies.

#
ResponseMeta::complete

fn ResponseMeta::complete(status : Int, headers : HeaderMap, request_time : Timestamp, response_time : Timestamp) -> ResponseMeta

#
ResponseMeta::new

fn ResponseMeta::new(status : Int, headers : HeaderMap, request_time : Timestamp, response_time : Timestamp, body_complete : Bool) -> ResponseMeta

#
ResponseSource

pub(all) enum ResponseSource {
CacheSource
UpstreamSource
RevalidatedSource
StaleSource
OnlyIfCachedSource
} derive(Compare, Eq,
Debug
)

#
ResponseSource::label

fn ResponseSource::label(self : ResponseSource) -> String

#
RevalidationPlan

pub(all) struct RevalidationPlan {
request : RequestMeta
generated_headers : HeaderMap
validator_kind : ValidatorKind?
reasons : Array[CacheReason]
} derive(Eq,
Debug
)

#
RevalidationPlan::can_revalidate

fn RevalidationPlan::can_revalidate(self : RevalidationPlan) -> Bool

#
RuntimeRequest

pub(all) struct RuntimeRequest {
meta : RequestMeta
body : Bytes
} derive(Eq,
Debug
)

#
RuntimeRequest::copy

#
RuntimeRequest::get

fn RuntimeRequest::get(uri : String) -> RuntimeRequest

#
RuntimeRequest::new

fn RuntimeRequest::new(meta : RequestMeta, body : Bytes) -> RuntimeRequest

#
RuntimeResponse

pub(all) struct RuntimeResponse {
meta : ResponseMeta
body : Bytes
source : ResponseSource
trace : CacheTrace
} derive(Eq,
Debug
)

#
RuntimeResponse::is_cache_reuse

fn RuntimeResponse::is_cache_reuse(self : RuntimeResponse) -> Bool

#
RuntimeResponse::json_report

fn RuntimeResponse::json_report(self : RuntimeResponse) -> String

#
RuntimeResponse::text_report

fn RuntimeResponse::text_report(self : RuntimeResponse) -> String

#
RuntimeResponse::to_json

fn RuntimeResponse::to_json(self : RuntimeResponse) -> Json

Structured runtime report. Response bodies are deliberately excluded; their size is enough for diagnostics and cannot leak cached content.

#
ScenarioAnalysis

pub(all) struct ScenarioAnalysis {
name : String
trace : CacheTrace
valid : Bool
errors : Array[String]
} derive(Eq,
Debug
)

#
ScenarioExpected

pub(all) struct ScenarioExpected {
action : String
reason : String?
} derive(Eq, ToJson,
Debug
,
FromJson
)

#
ScenarioRequest

pub(all) struct ScenarioRequest {
http_method : String
uri : String
headers : Map[String, String]
request_time : Int?
} derive(Eq, ToJson,
Debug
,
FromJson
)

#
ScenarioRequest::to_meta

#
ScenarioResponse

pub(all) struct ScenarioResponse {
status : Int
headers : Map[String, String]
request_time : Int?
response_time : Int?
body : String?
complete : Bool?
} derive(Eq, ToJson,
Debug
,
FromJson
)

#
ScenarioResponse::body_bytes

fn ScenarioResponse::body_bytes(self : ScenarioResponse) -> Bytes

#
ScenarioResponse::to_meta

fn ScenarioResponse::to_meta(self : ScenarioResponse, fallback_request_time : Timestamp) -> ResponseMeta

#
StorageDecision

pub(all) struct StorageDecision {
storable : Bool
policy : StoredPolicy
reasons : Array[CacheReason]
} derive(Eq,
Debug
)

#
StorePutResult

pub(all) enum StorePutResult {
StoreInserted
StoreReplacedResult
StoreRejectedNoStore
StoreRejectedOversized
StoreRejectedCapacity
} derive(Compare, Eq,
Debug
)

#
StorePutResult::accepted

fn StorePutResult::accepted(self : StorePutResult) -> Bool

#
StorePutResult::label

fn StorePutResult::label(self : StorePutResult) -> String

#
StoreStats

pub(all) struct StoreStats {
entries : Int
body_bytes : Int64
puts : Int64
replacements : Int64
evictions : Int64
lookups : Int64
hits : Int64
misses : Int64
invalidated_entries : Int64
rejected_entries : Int64
} derive(Eq,
Debug
)

#
StoreStats::hit_rate_per_mille

fn StoreStats::hit_rate_per_mille(self : StoreStats) -> Int

Integer hit ratio in thousandths, suitable for deterministic reports.

#
StoreStats::is_empty

fn StoreStats::is_empty(self : StoreStats) -> Bool

#
StoreStats::json_report

fn StoreStats::json_report(self : StoreStats) -> String

#
StoreStats::requests

fn StoreStats::requests(self : StoreStats) -> Int64

#
StoreStats::saved_origin_requests

fn StoreStats::saved_origin_requests(self : StoreStats) -> Int64

#
StoreStats::text_report

fn StoreStats::text_report(self : StoreStats) -> String

#
StoreStats::to_json

fn StoreStats::to_json(self : StoreStats) -> Json

#
StoredEntry

pub(all) struct StoredEntry {
primary_key : PrimaryCacheKey
variant_key : VariantKey
request : RequestMeta
response : ResponseMeta
body : Bytes
policy : StoredPolicy
stored_at : Timestamp
last_accessed_at : Timestamp
} derive(Eq,
Debug
)

#
StoredEntry::body_size

fn StoredEntry::body_size(self : StoredEntry) -> Int64

#
StoredEntry::copy

fn StoredEntry::copy(self : StoredEntry) -> StoredEntry

Deep-copy mutable metadata while sharing the immutable body bytes.

#
StoredEntry::new

fn StoredEntry::new(primary_key : PrimaryCacheKey, variant_key : VariantKey, request : RequestMeta, response : ResponseMeta, body : Bytes, policy : StoredPolicy, stored_at : Timestamp) -> StoredEntry

#
StoredEntry::touch

fn StoredEntry::touch(self : StoredEntry, now : Timestamp) -> StoredEntry

#
StoredPolicy

pub(all) struct StoredPolicy {
cacheable : Bool
freshness_lifetime : DeltaSeconds
requires_revalidation : Bool
no_store : Bool
vary_star : Bool
must_revalidate : Bool
proxy_revalidate : Bool
is_private : Bool
is_public : Bool
} derive(Eq,
Debug
)

#
StoredPolicy::cacheable

fn StoredPolicy::cacheable(lifetime : DeltaSeconds, requires_revalidation : Bool) -> StoredPolicy

#
StoredPolicy::from_directives

fn StoredPolicy::from_directives(lifetime : DeltaSeconds, directives : CacheControl, vary_star : Bool) -> StoredPolicy

#
StoredPolicy::uncacheable

fn StoredPolicy::uncacheable() -> StoredPolicy

#
Timestamp

A non-negative instant represented as caller-supplied epoch seconds.

#
Timestamp::elapsed_since

fn Timestamp::elapsed_since(self : Timestamp, earlier : Timestamp) -> DeltaSeconds

Return a non-negative duration even when the supplied clock moved backward.

#
Timestamp::from_seconds

fn Timestamp::from_seconds(value : Int64) -> Timestamp

#
Timestamp::saturating_add

fn Timestamp::saturating_add(self : Timestamp, delta : DeltaSeconds) -> Timestamp

#
Timestamp::seconds

fn Timestamp::seconds(self : Timestamp) -> Int64

#
Timestamp::zero

fn Timestamp::zero() -> Timestamp

#
TransportOutcome

pub(all) enum TransportOutcome {
TransportSuccess(TransportResponse)
TransportFailure(String)
} derive(Eq,
Debug
)

#
TransportResponse

pub(all) struct TransportResponse {
meta : ResponseMeta
body : Bytes
} derive(Eq,
Debug
)

#
TransportResponse::copy

#
TransportResponse::new

fn TransportResponse::new(meta : ResponseMeta, body : Bytes) -> TransportResponse

#
ValidatorKind

pub(all) enum ValidatorKind {
StrongEtag
WeakEtag
LastModified
StrongEtagAndLastModified
WeakEtagAndLastModified
CallerConditions
} derive(Compare, Eq,
Debug
)

#
ValidatorKind::label

fn ValidatorKind::label(self : ValidatorKind) -> String

#
VariantKey

pub(all) struct VariantKey {
canonical : String
label : String
} derive(Compare, Eq, Hash,
Debug
)

An opaque deterministic encoding of the request fields selected by Vary.

#
VariantKey::empty

fn VariantKey::empty() -> VariantKey

#
VariantKey::from_canonical

fn VariantKey::from_canonical(canonical : String, label : String) -> VariantKey

#
VariantSelection

pub(all) struct VariantSelection {
entry : StoredEntry?
inspected : Int
variant_key : VariantKey?
reasons : Array[CacheReason]
} derive(Eq,
Debug
)

#
VaryMatch

pub(all) struct VaryMatch {
kind : VaryMatchKind
differing_field : String?
stored_key : VariantKey?
candidate_key : VariantKey?
reason : CacheReason
} derive(Eq,
Debug
)

#
VaryMatchKind

pub(all) enum VaryMatchKind {
VaryMatchedResult
VaryMismatchResult
VaryStarResult
} derive(Compare, Eq,
Debug
)

#
VarySpec

pub(all) struct VarySpec {
fields : Array[String]
star : Bool
} derive(Eq,
Debug
)

#
VarySpec::default

fn VarySpec::default() -> VarySpec

#
MAX_DELTA_SECONDS

let MAX_DELTA_SECONDS : Int64

The greatest non-negative duration accepted by MoonCache.

#
analyze_scenario

fn analyze_scenario(scenario : CacheScenario) -> ScenarioAnalysis raise ScenarioError

#
analyze_scenario_json

fn analyze_scenario_json(text : String) -> ScenarioAnalysis raise ScenarioError

#
build_variant_key

fn build_variant_key(headers : HeaderMap, vary : VarySpec) -> VariantKey?

Build an opaque length-prefixed key so missing, empty, repeated, and delimiter-containing values cannot collide.

#
cache_control_from_headers

fn cache_control_from_headers(headers : HeaderMap) -> CacheControl

#
cache_origin

fn cache_origin(uri : String) -> String?

#
calculate_current_age

fn calculate_current_age(response : ResponseMeta, now : Timestamp) -> AgeCalculation

Calculate corrected current age according to RFC 9111 section 4.2.3.

#
calculate_freshness_lifetime

fn calculate_freshness_lifetime(response : ResponseMeta, options : CacheOptions) -> FreshnessCalculation

Calculate freshness lifetime using RFC directive precedence. Shared caches prefer s-maxage; explicit expiration precedes the conservative Last-Modified heuristic.

#
create_revalidation_plan

fn create_revalidation_plan(request : RequestMeta, stored_response : ResponseMeta) -> RevalidationPlan

Generate conditional fields without replacing conditions supplied by the caller. Strong and weak ETags are both valid for If-None-Match.

#
evaluate_cached_response

fn evaluate_cached_response(request : RequestMeta, response : ResponseMeta, options : CacheOptions, now : Timestamp) -> CacheDecision

Evaluate one matching cached response at the injected time. Variant lookup and conditional header generation are layered on this deterministic policy function.

#
evaluate_request_policy

fn evaluate_request_policy(request : RequestMeta) -> RequestPolicy

#
evaluate_storage

fn evaluate_storage(request : RequestMeta, response : ResponseMeta, options : CacheOptions) -> StorageDecision

Decide whether a complete response may enter the selected cache mode.

#
execute_cached

fn[S : CacheStore, T : Transport] execute_cached(store : S, transport : T, options : CacheOptions, request : RuntimeRequest, now : Timestamp) -> RuntimeResponse raise RuntimeError

Execute the complete initial-acceptance cache lifecycle.

#
explain_scenario_json

fn explain_scenario_json(text : String) -> String raise ScenarioError

#
explain_scenario_text

fn explain_scenario_text(text : String) -> String raise ScenarioError

#
has_usable_validator

fn has_usable_validator(response : ResponseMeta) -> Bool

#
is_default_cacheable_status

fn is_default_cacheable_status(status : Int) -> Bool

Statuses defined as heuristically cacheable by RFC 9111. Partial-content status 206 is excluded because range caching is outside the 0.1.3 scope.

#
is_safe_http_method

fn is_safe_http_method(http_method : String) -> Bool

#
is_valid_etag

fn is_valid_etag(value : String) -> Bool

#
is_valid_header_name

fn is_valid_header_name(name : String) -> Bool

Check the RFC 9110 token grammar used by HTTP field names.

#
match_vary

fn match_vary(stored_request : RequestMeta, candidate_request : RequestMeta, stored_response : ResponseMeta) -> VaryMatch

#
merge_not_modified

fn merge_not_modified(entry : StoredEntry, not_modified : ResponseMeta, options : CacheOptions, now : Timestamp) -> NotModifiedResult raise ValidationError

Merge a 304 response into a stored entry while retaining the cached status and body, then recompute cacheability, freshness, and variant identity.

#
normalize_cache_uri

fn normalize_cache_uri(uri : String) -> String?

Normalize an absolute HTTP(S) URI for primary cache lookup.

Scheme and host are lower-cased, default ports and fragments are removed, an empty path becomes /, and literal dot segments are resolved. Query text and percent-encoded octets are otherwise preserved.

#
normalize_header_name

fn normalize_header_name(name : String) -> String

Return the canonical representation used for case-insensitive HTTP field names. Leading and trailing whitespace is ignored so adapter input can be normalized at the boundary.

#
normalize_header_value

fn normalize_header_value(value : String) -> String

Remove optional whitespace around an HTTP field value without changing meaningful whitespace inside the value.

#
normalize_method

fn normalize_method(value : String) -> String

#
parse_cache_control

fn parse_cache_control(value : String) -> CacheControl

Parse the directive subset needed by MoonCache. Directive names are case-insensitive and repeated numeric directives resolve to the smallest valid value, a conservative deterministic choice.

#
parse_delta_seconds

fn parse_delta_seconds(value : String) -> DeltaSeconds?

Parse an RFC delta-seconds value. Negative, signed, fractional, and malformed values are rejected. Values that fit Int64 are represented without wrapping.

#
parse_http_date

fn parse_http_date(value : String) -> Timestamp?

Parse the IMF-fixdate form emitted by modern HTTP senders: Sun, 06 Nov 1994 08:49:37 GMT.

Obsolete HTTP-date forms belong in optional protocol adapters; the cache core intentionally implements only this narrow interoperable representation.

#
parse_scenario

fn parse_scenario(text : String) -> CacheScenario raise ScenarioError

#
parse_vary

fn parse_vary(headers : HeaderMap) -> VarySpec

#
plan_invalidation

fn plan_invalidation(request : RequestMeta, response : ResponseMeta) -> InvalidationPlan

RFC 9111 invalidation plan for a non-error response to an unsafe method. Related targets are included only when their resolved URI has the same origin as the request target.

#
primary_cache_key

fn primary_cache_key(request : RequestMeta) -> PrimaryCacheKey?

Construct the primary key. HEAD shares the GET lookup namespace while retaining its request method in RequestMeta.

#
request_has_legacy_no_cache

fn request_has_legacy_no_cache(headers : HeaderMap) -> Bool

RFC 9111 retains the older Pragma: no-cache request behavior when Cache-Control is absent. This helper keeps that compatibility isolated.
fn resolve_related_uri(base : String, reference : String) -> String?

Resolve the absolute and relative URI forms used by Location and Content-Location for invalidation.

#
response_validator_kind

fn response_validator_kind(response : ResponseMeta) -> ValidatorKind?

#
same_cache_origin

fn same_cache_origin(left : String, right : String) -> Bool

#
scenario_to_json

fn scenario_to_json(scenario : CacheScenario) -> String

#
select_variant

fn select_variant(entries : Array[StoredEntry], request : RequestMeta) -> VariantSelection

Select the newest matching entry. Equal timestamps keep the store's deterministic iteration order.

#
validate_scenario_text

fn validate_scenario_text(text : String) -> String raise ScenarioError