Space state
The hardest thing to convey about this store in prose is that effects are
returned, not performed. Below, notify-chat and typing-timer land in the
right-hand pane as plain data — nothing plays a sound, nothing asks for
notification permission, nothing starts a timer. What any of it means is your
call.
What to try
Section titled “What to try”- Press member-joined. A log line lands in
chatLinesand two effects come back:soundandnotify-join. The log text is<log.joined>because this demo passes atthat just marks the key — the store never picks wording itself. - Press typing, then press it again. The second one returns the same state
reference, so a subscriber would not re-render. The
typing-timereffect still comes back both times, because the timer has to be re-armed even when the display did not change. - Press chat while someone is typing. You get
typing-timer-clearalongsidesoundandnotify-chat— removing the indicator and cancelling the pending 3-second timer are one atomic step.
The code behind it
Section titled “The code behind it”import { createSpaceStore } from '@insession/space-state';
const store = createSpaceStore({ selfName: 'you', t: (key) => key, // any resolver; the identity function is fine in tests getPresence: () => 'active',});
// You decide what an effect means.store.onEffect((effect) => { if (effect.type === 'sound') playSound(effect.sound); if (effect.type === 'notify-chat') notify(`${effect.name}: ${effect.text}`);});
// Inbound messages fold through a pure reducer.store.receive({ type: 'chat', name: 'ren', text: 'hello' });
store.getState().chatLines.at(-1);Because receive takes plain objects and effects are plain descriptors, a full
state transition is assertable under node --test — no server, no browser, no
sockets. That is the same property this page relies on to run entirely in your
browser.
See the package reference for the full effect union, the store methods, and the plugin contract.