@mocanvas/store · class
declare class Store<R extends UnknownRecord = UnknownRecord, Props = unknown>A reactive, transactional collection of records.
one atom per record, so consumers subscribe to exactly what they read
per-type id sets maintained incrementally
writes are batched; listeners receive one squashed diff per outermost operation
records are frozen on write
Methods · 18
allRecords(): R[]All records (reactive over every record and every type's membership).
applyDiff(diff: RecordsDiff<R>, options?: {Apply a diff (e.g. from extractingChanges or reverseRecordsDiff).
With ignoreEphemeralKeys, ephemeral keys of updated records keep their
current store values instead of the diff's.
atomic<T>(fn: () => T, options?: {Run fn as one operation: side effects' operationComplete handlers run
once at the end, and listeners get a single squashed history entry.
clear(): voidRemove every record.
constructor(options: StoreOptions<R, Props>)dispose(): voidextractingChanges(fn: () => void): RecordsDiff<R>Run fn and return the squashed diff of everything it changed. Listeners are still notified.
getScope(typeName: string): RecordScopeScope of a record type; unknown types are document.
getStoreSnapshot(scope?: RecordScope | "all"): StoreSnapshot<R>getTypeIndex(typeName: string): TypeIndex<R>@internal
isDisposed(): booleanlisten(onHistory: StoreListener<R>, filters?: Partial<StoreListenerFilters>): () => voidSubscribe to history entries. Called after each outermost operation with the squashed changes, filtered by source and record scope.
loadStoreSnapshot(snapshot: StoreSnapshot<R>): voidReplace the store's contents with a snapshot (migrating it first).
Existing records in document scope and in every scope present in the
snapshot are removed unless the snapshot contains them; other scopes are
left alone. Side effects do not run; listeners are notified.
mergeRemoteChanges(fn: () => void): voidChanges made inside fn are reported to listeners with source remote.
migrateSnapshot(snapshot: StoreSnapshot<R>): StoreSnapshot<R>Bring a snapshot saved by an older document up to this store's schema, without loading it.
Every migration sequence the schema knows is run — including the ones
createStore derives from the shape and binding utils, so a board saved
before a prop existed is backfilled here rather than failing validation on
load. The input is not mutated: the result is a new snapshot carrying this
schema's serialized version, ready for Store.loadStoreSnapshot (or
for a caller that wants to inspect the migrated records first).
A snapshot that cannot be migrated — an unknown schema version, a sequence from a NEWER build than this one, a migration that throws — raises rather than returning half-migrated data, so a caller can fail closed on it.
put(records: readonly R[], phaseOverride?: StoreValidationPhase): voidInsert or update records. Records are validated, passed through before*
side effects, frozen, and written. after* side effects run once every
record in the call has been written.
remove(ids: readonly IdOf<R>[]): voidRemove records by id. Missing ids are ignored. beforeDelete handlers may veto.
serialize(scope?: RecordScope | "all"): SerializedStore<R>Plain-object snapshot of the records in scope (default document).
Properties · 12
createComputedCache<T, K extends IdOf<R> = IdOf<R>>(name: string, derive: (record: RecordFromId<K>) => T, options?: {A per-record derived value, recomputed only when that record changes. Entries are dropped automatically when records are removed.
get<K extends IdOf<R>>(id: K): RecordFromId<K> | undefinedGet a record (reactive: subscribes to the record, or to its type's membership when absent).
has<K extends IdOf<R>>(id: K): booleanreadonly history: Atom<number, RecordsDiff<R>>Bumped once per completed operation that changed something.
The counter itself carries no information; the diffs do. The atom keeps a
bounded history of the squashed RecordsDiff of each operation, so a
derived collection can ask history.getDiffSince(epoch) and patch itself
instead of rebuilding. store.query.filterHistory(typeName) is the same
thing narrowed to one record type.
readonly id: stringreadonly props: Propsreadonly query: StoreQueries<R>readonly schema: StoreSchema<R, Props>readonly scopedTypes: {readonly sideEffects: StoreSideEffects<R>unsafeGetWithoutCapture<K extends IdOf<R>>(id: K): RecordFromId<K> | undefinedGet a record without registering a reactive dependency.
update<K extends IdOf<R>>(id: K, updater: (record: RecordFromId<K>) => RecordFromId<K>): voidUpdate one record with a function. No-op when the record does not exist.
mocanvas is built by Symbio. Free for non-commercial, non-profit and development use; commercial use needs an agreement. Not affiliated with or endorsed by tldraw.