Optional ReadonlySharesWhether this provider stores and returns live object references rather than serialized copies.
true — no serialization boundary. SetItem(k, v) retains v itself and
GetItem(k) hands the very same object back, so the store and every caller
share memory (e.g. the Map-based in-memory providers).false — a serialization / structured-clone boundary isolates stored data from
live objects in both directions (IndexedDB, localStorage, Redis, MMKV).Why callers care: ILocalStorageProvider implementations are
interchangeable, but this one property changes the ownership semantics of
everything stored. LocalCacheManager reads it to decide whether it must
deep-freeze row data at write time — without a freeze, a reference-sharing
provider lets any consumer that mutates a returned row silently corrupt the
process-wide cache. (That is a bug that actually shipped: a GraphQL resolver
renamed __mj_CreatedAt to its transport alias in place, rewriting the live
cache for every later reader.) Serializing providers were never exposed to this,
so they opt out and keep handing back freely-mutable copies.
Optional, but always declare it. Omitting it is not a way to opt out of the
contract: when it is undefined, LocalCacheManager MEASURES the provider at
initialization instead — it stores a sentinel object, reads it back, and compares
identity. Declaring the value skips that probe and documents intent at the
implementation site, which is why every in-repo provider declares it. It is
optional purely so that adding this contract does not break existing external
implementations at compile time.
OptionalClearClears all items in a specific category. If no category is specified, clears the default category only.
The category to clear
OptionalGetGets all keys in a specific category.
The category to list keys from
Retrieves a value from storage. The implementation is responsible for any deserialization required by the underlying medium:
Returns null for missing keys or corrupt entries.
Expected type of the stored value. Caller-controlled — the provider does
not validate the runtime shape against this type. Falls back to unknown.
The key to retrieve
Optionalcategory: stringOptional category for key isolation (e.g., 'RunViewCache', 'Metadata')
Batched retrieval — reads N values for N keys in one logical operation.
Returns a Map keyed by the input key strings. Missing keys map to null.
The map preserves the original key set so callers can index by key without
relying on array-position alignment.
Why batch? IndexedDB serializes transactions on the same object store —
Promise.all([...N GetItem calls]) looks parallel but pays per-transaction
setup cost (~3–10ms each) for every key. A single transaction with N
get() calls amortizes that overhead. Redis can use MGET/pipelines.
In-memory implementations have no real win but implement consistently for
a uniform API.
Implementations are free to fall back to per-key reads internally if the
underlying medium doesn't support batching — the contract is just "read
all of these as efficiently as you can". An empty keys array returns
an empty map without touching the storage backend.
Expected type of all stored values. Caller-controlled.
The keys to retrieve. Duplicates are deduplicated; the returned map has one entry per unique key.
Optionalcategory: stringOptional category for key isolation (applies to all keys)
Removes an item from storage.
The key to remove
Optionalcategory: stringOptional category for key isolation
Stores a value. Callers should pass plain data (objects/arrays/primitives/Date/etc). Implementations handle any serialization required by the medium:
Class instances lose their prototype on retrieval — store the underlying data
(e.g. via entity.GetAll()) and reconstruct on read if needed.
Type of the value being stored. Caller-controlled.
The key to store under
The value to store
Optionalcategory: stringOptional category for key isolation
Interface for local storage providers. Abstracts storage operations to support different storage backends (e.g., browser localStorage, IndexedDB, file system).
Implementations should handle the optional category parameter as follows:
mj:RunViewCache)[mj]:[category]:[key]When category is not provided, use a default category (e.g., 'default' or 'general').