Store<EM extends EventMapBase, R extends string, S extends Record<R, any>>

class
store/Store.ts:162

implements StoreInstance<R, S, EM>

Public Store surface.

The concrete Store implements this as StoreInstance<R, DeepReadonly<S>, EM>.

Type parameters

NameTypeDescription
EMextends EventMapBaseReducer name union.
Rextends stringState record (already readonly at the call site).
Sextends Record<R, any>Event map.

Constructor

constructor

Creates a store from a StoreSpec.

signature
new<EM, R, S>(spec): Store<EM, R, S>

Creates a store from a StoreSpec.

Type parameters

NameTypeDescription
EMextends EventMapBase
Rextends string
Sextends Record<R, any>

Parameters

NameTypeDescription
specStoreSpec<R, S, EM>Store configuration (name, reducers, middleware, optional effects).

Returns

Store<EM, R, S>

Properties

NameTypeDescription
namestringStore name (used by DevTools & diagnostics).

Methods

__devtoolsIntrospect

Returns a structured introspection snapshot for DevTools UIs.

signature
__devtoolsIntrospect(): { atomic: { property: string; reducer: string }[]; coarse: number; dedupHits: number; effects: { channel: string; description?: string; name?: string; type: string }[]; event: { channel: string; phase: string; type: string }[]; middleware: { description?: string; name?: string; when?: unknown }[]; queueDepth: number; reducers: { name: string; when: undefined | When<EM> }[] }

Returns a structured introspection snapshot for DevTools UIs.

Returns

{ atomic: { property: string; reducer: string }[]; coarse: number; dedupHits: number; effects: { channel: string; description?: string; name?: string; type: string }[]; event: { channel: string; phase: string; type: string }[]; middleware: { description?: string; name?: string; when?: unknown }[]; queueDepth: number; reducers: { name: string; when: undefined | When<EM> }[] }

static buildAncestorPaths

Builds ancestor paths for a dotted path. For "a.b.c", returns ["a", "a.b", "a.b.c"]. Leading dots are trimmed.

signature
buildAncestorPaths(path): string[]

Builds ancestor paths for a dotted path. For "a.b.c", returns ["a", "a.b", "a.b.c"]. Leading dots are trimmed.

Parameters

NameTypeDescription
pathstringDotted path string.

Returns

string[]Array of ancestor paths.

Example

example
Store.buildAncestorPaths('x.y.z'); // ['x','x.y','x.y.z']

call

Sends a request and waits for the reply, correlating the two automatically.

signature
call<C, T>(channel, type, payload, opts): CallHandle<EventUnion<EM>, EventUnion<EM>>

Sends a request and waits for the reply, correlating the two automatically.

Type parameters

NameTypeDescription
Cextends stringRequest channel.
Textends stringRequest type within C.

Parameters

NameTypeDescription
channelCChannel to send on.
NameTypeDescription
typeTEvent type to send.
NameTypeDescription
payloadEM[C][T]The **request** payload. This is what you are sending; what comes back is described by CallOptions.reply, not by this.
NameTypeDescription
optsCallOptions<EM>Which replies end the call, and how long to wait. See CallOptions.

Returns

CallHandle<EventUnion<EM>, EventUnion<EM>>A CallHandle: await it for the terminal reply, or for await it for progress events as they arrive.

Example

example
// Survives a job that streams for minutes; fails a responder that goes quiet for 5s.
await store.call("job", "start", { id }, { reply: ["job", "done"], timeoutMs: 5_000 });

Example

example
const call = store.call("rpc", "ask", { q }, { reply: ["rpc", "answer"] });
useEffect(() => () => call.cancel("unmounted"), [call]);

connect

Connects a **fine-grained** listener to a dotted path under a slice.

signature
connect(spec, h, options?): () => void

Connects a **fine-grained** listener to a dotted path under a slice.

Parameters

spec — object with:

NameTypeDescription
propertystring
reducerR
NameTypeDescription
h(chg: Change) => voidHandler receiving a Change with { oldValue, newValue, path }.
NameTypeDescription
options?ConnectOptions

Returns

() => voidUnsubscribe function.

Example

example
const off = store.connect(
  { reducer: 'todos', property: 'items.0.title' },
  (chg) => console.log('title changed:', chg.newValue)
);
off();

Example

example
// Listen to any item title change
const off = store.connect(
  { reducer: 'todos', property: 'items.*.title' },
  (chg) => console.log('some title changed')
);

dispose

Cleanup resources (timers, etc.) when disposing the store. Call this if you're dynamically creating/destroying stores.

signature
dispose(): void

Cleanup resources (timers, etc.) when disposing the store. Call this if you're dynamically creating/destroying stores.

Returns

void

Example

example
const store = createStore({ ... });
// later
store.dispose();

emit

Emit a typed event (channel, type, payload). Returns a promise that resolves when the event has been processed.

signature
emit<C, T>(channel, type, payload, opts?): Promise<EmitResult>

Emits a typed event (channel, type, payload). Events are queued and processed **sequentially** (FIFO). **Pipeline per event:** the *reduce phase* (steps 1-4) runs **synchronously**, so getState() reflects the change as soon as emit() returns; the *effect phase* (step 5) runs afterwards, asynchronously. 1. **Deduplication** (opt-in) - Skip when content-dedup is enabled (dedupWindowMs > 0) or a matching dedupKey recurs; off by default 2. **Middleware** (sync) - Pre-reducer hooks; may cancel by returning false 3. **Reducers** (sync) - every matching slice is *staged*; nothing is written yet, so a refusal from the last reducer still stops the first one's write 4. **Commit + subscribers** (sync) - all staged slices are assigned under one new root, then event subscribers (committed, then written when state actually changed), then coarse listeners 5. **Effects** (async) - side-effects keyed by (channel, type); the returned promise resolves once they complete **Change Detection**: Uses reference equality (===) on this.state to determine if any slice changed. Works because the commit builds a new state reference via shallow spread when any slice changes.

Type parameters

NameTypeDescription
Cextends stringChannel key in EM.
Textends stringType key within channel C.

Parameters

NameTypeDescription
channelCChannel name.
NameTypeDescription
typeTEvent type name.
NameTypeDescription
payloadEM[C][T]Payload typed as EM[C][T].
NameTypeDescription
opts?EmitOptionsOptional per-emit options (e.g. dedupKey for identity-based dedup).

Returns

Promise<EmitResult>A promise that resolves once this event's effects have finished. State is already updated synchronously before emit() returns.

Example

example
await store.emit('ui', 'increment', 1);

Example

example
store.registerMiddleware((state, event) => {
  if (event.type === 'dangerous') return false; // cancel
  return true; // allow
});

await store.emit('ui', 'dangerous', null); // cancelled, no state change

getState

Returns the current immutable state snapshot.

signature
getState(): DeepReadonly<S>

Returns the current immutable state snapshot.

Returns

DeepReadonly<S>Deep-readonly state object.

Example

example
const state = store.getState();
console.log(state.counter.value);

hotReplace

Convenience API to replace **any subset** of store parts (HMR patterns).

signature
hotReplace(partial): void

Convenience API to replace **any subset** of store parts (HMR patterns).

Parameters

partial — object with:

NameTypeDescription
effects?EffectSpec<DeepReadonly<S>, EM>[]
middleware?MiddlewareInput<DeepReadonly<S>, EM>[]
preserveState?boolean
reducer?Record<R, ReducerSpec<S[R], EM>>

Returns

void

Example

example
store.hotReplace({
  reducer: newReducers,
  middleware: newMiddleware,
  effects: newEffects,
  preserveState: true
});

instrument

Registers an instrumentation observer. See StoreInstance.instrument.

signature
instrument(observer): Unsubscribe

Registers an instrumentation observer. See StoreInstance.instrument.

Parameters

NameTypeDescription
observerInstrumentationObserver<EM>

Returns

Unsubscribe

onEffect

Convenience helper to register an **effect** filtered by a single (channel, type) pair.

signature
onEffect<C, T>(channel, type, handler): () => void

Convenience helper to register an **effect** filtered by a single (channel, type) pair.

Type parameters

NameTypeDescription
Cextends stringChannel key within EM.
Textends stringEvent type key within channel C.

Parameters

NameTypeDescription
channelCChannel to filter.
NameTypeDescription
typeTEvent type to filter.
NameTypeDescription
handler(payload: EM[C][T], getState: () => DeepReadonly<S>, emit: Emit<EM>, event: Event<EM, C, T>) => void | Promise<void>Effect handler (payload, getState, emit, event).

Returns

() => voidUnsubscribe/teardown function.

Example

example
const off = store.onEffect('ui', 'increment', async (n, get, emit) => {
  if (n > 10) await emit('ui', 'increment', -10);
});
// later
off();

onEvent

Subscribe to events by channel and type. Event subscriptions are intended for the View layer (e.g., React components) to react to events without affecting the event flow. They are fire-and-forget and cannot cancel event propagation. **Phases:** - 'committed' (default): Events that passed middleware and reached reducers. Notified after reducers, before effects. - 'uncommitted': Events rejected by middleware. Notified immediately after rejection. - 'all': Both committed and uncommitted events. Handler receives the phase parameter to distinguish between the two.

signature
onEvent<C, T>(channel, type, handler, phase): Unsubscribe

Subscribe to events by channel and type. Event subscriptions are intended for the View layer (e.g., React components) to react to events without affecting the event flow. They are fire-and-forget and cannot cancel event propagation. **Phases:** - 'committed' (default): Events that passed middleware and reached reducers. Notified after reducers, before effects. - 'uncommitted': Events rejected by middleware. Notified immediately after rejection. - 'all': Both committed and uncommitted events. Handler receives the phase parameter to distinguish between the two.

Type parameters

NameTypeDescription
Cextends stringChannel key within EM.
Textends stringEvent type key within channel C.

Parameters

NameTypeDescription
channelCChannel to subscribe to.
NameTypeDescription
typeTEvent type to subscribe to.
NameTypeDescription
handlerNarrowedEventHandler<DeepReadonly<S>, EM, C, T>Handler function (event, getState, emit, phase).
NameTypeDescription
phaseEventPhaseEvent phase to subscribe to (default: 'committed').

Returns

UnsubscribeUnsubscribe function.

Example

example
const off = store.onEvent('ui', 'save', (event, getState, emit, phase) => {
  console.log('Save committed:', event.payload);
});
off();

Example

example
store.onEvent('ui', 'delete', (event, getState, emit, phase) => {
  console.log('Delete was rejected by middleware');
}, 'uncommitted');

Example

example
store.onEvent('ui', 'action', (event, getState, emit, phase) => {
  console.log('Action:', phase); // 'committed' or 'uncommitted'
}, 'all');

registerEffect

Register a post-reducer effect (sees final state). Returns an unsubscribe.

signature
registerEffect(spec): () => void

Register a post-reducer effect (sees final state). Returns an unsubscribe.

Parameters

NameTypeDescription
specEffectSpec<DeepReadonly<S>, EM>

Returns

() => void

registerMiddleware

Registers a middleware (runs **before** reducers).

signature
registerMiddleware(mw): Unsubscribe

Registers a middleware (runs **before** reducers).

Parameters

NameTypeDescription
mwMiddlewareInput<DeepReadonly<S>, EM>Middleware (state, event, emit) => boolean. Return false to cancel event propagation.

Returns

UnsubscribeUnsubscribe function that removes this middleware.

Example

example
const off = store.registerMiddleware((state, event) => {
  console.log('Event:', event.channel, event.type, event.payload);
  return true; // allow
});
off();

Example

example
store.registerMiddleware((state, event) => {
  if (event.type === 'forbidden') return false; // cancel
  return true;
});

registerReducer

Dynamically **adds** a named slice reducer at runtime.

signature
registerReducer(name, spec): () => void

Dynamically **adds** a named slice reducer at runtime.

Parameters

NameTypeDescription
namestringNew slice name (must not already exist).
NameTypeDescription
specReducerSpec<any, EM>Reducer spec (state, when, reducer).

Returns

() => voidDisposer function that **removes** the slice (and its state).

Example

example
const dispose = store.registerReducer('filters', {
  state: { q: '' },
  events: [['ui', 'setQuery']],
  reducer(s, evt) {
    return evt.type === 'setQuery' ? { q: evt.payload } : s;
  }
});
// Later:
dispose();

replaceEffects

Replaces all registered **effects** (HMR-friendly).

signature
replaceEffects(next): void

Replaces all registered **effects** (HMR-friendly).

Parameters

NameTypeDescription
nextEffectSpec<DeepReadonly<S>, EM>[]New effects array (as EffectSpecs).

Returns

void

Example

example
if (import.meta.hot) {
  import.meta.hot.accept('./effects', (newModule) => {
    store.replaceEffects(newModule.effects);
  });
}

replaceMiddleware

Replaces the **entire** middleware pipeline (HMR-friendly).

signature
replaceMiddleware(next): void

Replaces the **entire** middleware pipeline (HMR-friendly).

Parameters

NameTypeDescription
nextMiddlewareInput<DeepReadonly<S>, EM>[]New middleware array.

Returns

void

Example

example
if (import.meta.hot) {
  import.meta.hot.accept('./middleware', (newModule) => {
    store.replaceMiddleware(newModule.middleware);
  });
}

replaceReducers

Replaces the entire **reducer set** (HMR-friendly).

signature
replaceReducers(next, opts): void

Replaces the entire **reducer set** (HMR-friendly).

Parameters

NameTypeDescription
nextRecord<R, ReducerSpec<S[R], EM>>Map of slice specs keyed by slice name.

opts — object with:

NameTypeDescription
preserveState?boolean

Returns

void

Example

example
if (import.meta.hot) {
  import.meta.hot.accept('./reducers', (newModule) => {
    store.replaceReducers(newModule.reducers, { preserveState: true });
  });
}

subscribe

Subscribes to **coarse-grained** commits (called once per successful event, only if state changed). **Use Case**: React's useSyncExternalStore or similar external store integrations.

signature
subscribe(fn): () => void

Subscribes to **coarse-grained** commits (called once per successful event, only if state changed). **Use Case**: React's useSyncExternalStore or similar external store integrations.

Parameters

NameTypeDescription
fn() => voidListener invoked after reducers/effects have run and state has changed.

Returns

() => voidUnsubscribe function.

Example

example
const off = store.subscribe(() => console.log('state committed'));
// Later:
off();