StoreManager (reactive document state)
StoreManager is a thin wrapper around Svelte stores that binds them to Foundry documents (Actor, Item). It gives you reactive, path-based access to document data and keeps both sides in sync via Foundry hooks, without sprinkling update
calls around your user interface code.
What it does
- One manager per document:
StoreManager.Subscribe(document)
returns a cached manager keyed by document identifier;Unsubscribe(document)
cleans up hooks when the last subscriber leaves. - Path-based stores:
GetRWStore("path.to.field")
returns a writable store that updates the document and re-renders consumers;GetROStore
mirrors document changes without writing. System is omitted from the path! - Derived totals:
GetSumROStore("attributes.reaction")
returns{ value, mod, sum }
as a derived store pattern used across attributes/pools. Sum returns value + mod. This pattern is used because Active Effects must be separated to not cause infinite recursion. - Flags and ephemeral state:
GetFlagStore(flagKey)
binds toflags.sr3e.<flagKey>
;GetShallowStore(documentId, name, initial)
is for view-only ephemeral state. - Syncs with Foundry: Managers listen to
update<DocType>
andactorSystemRecalculated
to keep stores fresh when other parts of the app change the document.
Scope & boundaries
- Provides reactive access to document fields and writes back via
update
. - Validation and rules belong to Procedures/Controllers and the spec for that component.
- User interface components consume stores and emit intent; they do not implement business rules here.
Quick usage
<script>
import { onDestroy } from "svelte";
import { StoreManager } from "@sveltehelpers/StoreManager.svelte.js";
let { actor } = $props();
const storeManager = StoreManager.Subscribe(actor);
onDestroy(() => StoreManager.Unsubscribe(actor));
// Read/write document data (anchors to `system.` by default)
const stun = storeManager.GetRWStore("health.stun.value"); // number (0..10)
const overflow = storeManager.GetRWStore("health.overflow.value");
// Read-only derived (penalty is computed elsewhere and exposed as a number)
const penalty = storeManager.GetROStore("health.penalty"); // number (0..3)
// Attribute pattern: value + mod => sum
const reaction = storeManager.GetSumROStore("attributes.reaction"); // { value, mod, sum }
</script>
API surface (most used)
StoreManager.Subscribe(document)
->manager
StoreManager.Unsubscribe(document)
-> voidmanager.GetRWStore(path: string, isRoot = false)
->Writable<T>
manager.GetROStore(path: string)
->Writable<T>
(do not set to write)manager.GetSumROStore(path: string)
->Derived<{ value:number, mod:number, sum:number }>
manager.GetShallowStore(documentId: string, name: string, initial?: T)
->Writable<T>
manager.GetFlagStore(key: string)
->Writable<any>
(backsflags.sr3e.<key>
)
Notes
GetRWStore("health.stun.value")
targetssystem.health.stun.value
. PassisRoot=true
to target a root property, for examplename
.- Updates call
document.update({ [fullPath]: value }, { render: false })
and rely on Svelte for a reactive user interface. - Always unsubscribe in
onDestroy
to release hooks when a sheet or widget is torn down.