| title | cache | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| description | Memoize synchronous computations with typed arguments, optional result codecs and host-managed persistence. | ||||||||||||||||||||||||||||||||
| sourceReview |
|
||||||||||||||||||||||||||||||||
| sidebar |
|
||||||||||||||||||||||||||||||||
| head |
|
cache reuses deterministic computation results by function identity and encoded arguments. Use it for expensive ordinary data; custom geometry builders have their own ownership-aware cache.
import {cache} from '@code3d/core';
function buildProfile(radius: number, sides: number) {
return Array.from({length: sides}, (_, index) => {
const angle = (index * 2 * Math.PI) / sides;
return [radius * Math.cos(angle), radius * Math.sin(angle)];
});
}
const profile = cache(buildProfile);
const points = cache(buildProfile, [10, 6]);// Simplified overloads: Promise-like results are excluded.
cache<Args extends unknown[], Value>(
compute: (...args: Args) => Value,
args: Readonly<NoInfer<Args>>,
options?: CacheOptions<NoInfer<Value>>,
): Value;
cache<Args extends unknown[], Value>(
compute: (...args: Args) => Value,
args?: undefined,
options?: CacheOptions<NoInfer<Value>>,
): (...args: Args) => Value;
type CacheOptions<Value> = Readonly<{
encoder(value: Value): Uint8Array;
decoder(bytes: Uint8Array): Value;
}>;Import the functions and named types from @code3d/core.
cache(fn) returns a memoized function; cache(fn, args) immediately returns
its result for the supplied argument tuple. Both forms preserve synchronous
parameter/result types and use the same definition and argument cache keys.
In the example, profile(10, 6) reuses the same cached result as points. An empty tuple [] immediately invokes
a computation with no arguments. The argument tuple is not part of the
compiler's function fingerprint: changing inputs selects another cache entry.
The API caches
ordinary data; use definePrimitive() for Replicad geometry so Core also owns
native resources and creates fresh model metadata. Treat cached results as
immutable. A memory hit returns the retained computed or decoded value directly,
without decoding, copying or freezing it.
The default persistent codec supports plain objects, arrays, scalar values
(including undefined, nonfinite numbers and bigint), Date, Map, Set, ArrayBuffer,
standard TypedArrays and DataView. Shared references, cycles, sparse arrays and
shared buffer views survive restoration. Arguments use the same data encoding;
changing dynamic state must be supplied as arguments. Functions, native handles
and application class instances are not ordinary data arguments.
For custom result types, supply both functions as
cache(fn, undefined, {encoder: value => bytes, decoder: bytes => value}).
For immediate evaluation, use cache(fn, args, options) with the same codec options.
The encoder runs when saving to disk; the decoder runs once when restoring an
entry into memory. A subsequent memory hit never calls either codec. Async
computations are excluded: incomplete work is not admitted to the cache.
New results are written to disk only when their computation reaches the configured threshold, 1 ms by default. In the App, change Disk cache threshold (ms) under Settings → Cache; fractional values are supported and 0 removes the time threshold. Changes apply to new computations from the next model execution; existing entries retain their disk eligibility. Faster results still use the memory cache, and later memory hits do not promote them to disk. The computation timer excludes the surrounding cache lookup, argument hashing and persistence encoding. Batched snapshot queries use their local or Worker computation time, excluding input restoration and transport. Existing disk records remain readable; restoring a record preserves its disk eligibility.
The model engine fingerprints static function definitions, their referenced
local declarations, imported implementation graphs and codec definitions. Aliases
and re-exports of Core cache factories are supported. Editing an unrelated local
binding, moving a definition or adding/removing export preserves its identity;
changing a referenced helper or dependency invalidates it. Functions supplied as parameters, dynamic factory results and closures capturing
enclosing function/loop bindings use memory-only object identity.
Outside the model engine, ordinary Node calls also use function object identity
and share the process-wide memory LRU. Authors do not provide cache IDs or versions.
Public cached computations, primitives, Core geometry, font parsing, glyph contours and snapshot queries share one cache. Memory and browser OPFS disk budgets are managed by the host; consult App settings for the active disk budget. Cancellation and exceptions retain completed entries and editing history.
CacheOptions<Value> requires both encoder(value: Value): Uint8Array and
decoder(bytes: Uint8Array): Value. They are synchronous and must round-trip the
value without relying on hidden changing state. Custom result codecs do not
change how arguments are keyed; arguments still use the standard data encoding.
The computation must be synchronous and deterministic. Its thrown errors propagate and are not stored as results. Promise-like results are excluded by the TypeScript overloads and rejected at runtime. Unsupported arguments fail while creating their content key, before invoking the computation. Persistence encoding or restoration failures are recorded by the cache and degrade to memory use or recomputation; they do not make a broken codec reliable.
Read input and timeOffset outside the cached body, then pass their values as arguments. Avoid reading mutable module state, random numbers or the wall clock from inside a supposedly reusable computation.