Store
import { Store } from '@hurum/core'Store({ state })
Section titled “Store({ state })”Creates a store builder from an initial state object. Returns a chainable builder that is also a StoreDefinition — you can call .create() at any point or continue chaining.
Signature
Section titled “Signature”function Store<TState extends Record<string, unknown>>( config: { state: TState },): StoreBuilder<Record<string, never>, TState, Record<string, never>>Parameters
Section titled “Parameters”| Parameter | Type | Description |
|---|---|---|
config.state | Record<string, unknown> | The initial raw state. Each key becomes a state field. |
Returns
Section titled “Returns”A StoreBuilder with chainable methods.
Example
Section titled “Example”const CounterStore = Store({ state: { count: 0, multiplier: 2 } }) .on(CounterEvent.incremented, (state, { amount }) => ({ ...state, count: state.count + amount, })) .computed({ doubled: (state) => state.count * 2, }) .intents(CounterIntents) .executors(IncrExec, DecrExec)
const store = CounterStore.create()Builder Methods
Section titled “Builder Methods”.on(event, handler) — per-event handler
Section titled “.on(event, handler) — per-event handler”Registers a state reducer for a single event type.
.on( event: EventCreator<TType, TPayload>, handler: (state: State, payload: TPayload) => RawState,): StoreBuilderThe state parameter includes raw state, computed values, and resolved nested state. The return value must be the raw state shape only (no computed fields).
.on(namespace, handlers) — namespace handler
Section titled “.on(namespace, handlers) — namespace handler”Registers multiple handlers from an event namespace at once.
.on( events: EventNamespace, handlers: { [K in keyof EventNamespace]?: (state: State, payload: Payload) => RawState },): StoreBuilderExample
Section titled “Example”// Per-event.on(CartEvent.itemAdded, (state, { item }) => ({ ...state, items: [...state.items, item],}))
// Namespace.on(CartEvent, { itemAdded: (state, { item }) => ({ ...state, items: [...state.items, item] }), cleared: () => ({ items: [] }),}).computed(definitions)
Section titled “.computed(definitions)”Defines derived state fields that recalculate when their dependencies change.
.computed<C extends Record<string, (state: ResolvedState) => unknown>>( def: C,): StoreBuilderSee the Computed API reference for details on dependency tracking and behavior.
Example
Section titled “Example”.computed({ total: (state) => state.items.reduce((sum, item) => sum + item.price, 0), isEmpty: (state) => state.items.length === 0,}).intents(container)
Section titled “.intents(container)”Registers an intents container. This enables store.send.intentName() shorthand.
.intents(container: IntentsContainer): StoreBuilder.executors(...executors)
Section titled “.executors(...executors)”Registers one or more executors. Each executor’s command is matched at dispatch time.
.executors(...execs: Executor[]): StoreBuilderExample
Section titled “Example”.intents(CartIntents).executors(AddItemExec, RemoveItemExec, CheckoutExec).deps<T>()
Section titled “.deps<T>()”Declares the dependency types for this store. Dependencies are injected at Store.create({ deps }).
.deps<D extends Record<string, unknown>>(): StoreBuilderExample
Section titled “Example”.deps<{ repo: CartRepo; analytics: Analytics }>().childDeps(key, mapper)
Section titled “.childDeps(key, mapper)”Maps parent dependencies to a nested child store’s dependencies.
.childDeps<K extends keyof State & string>( key: K, mapper: (deps: TDeps) => ChildDeps,): StoreBuilderExample
Section titled “Example”.childDeps('transaction', (deps) => ({ transactionRepo: deps.repo.transactions,})).relay(event, handler)
Section titled “.relay(event, handler)”Registers a relay handler that transforms one event into zero or more other events. Used for cross-store event coordination.
.relay( event: EventCreator<TType, TPayload>, handler: (event: EventInstance, state: State) => EventInstance[],): StoreBuilderExample
Section titled “Example”.relay(ChildEvent.completed, (event, state) => { if (state.items.length === 0) { return [ParentEvent.allCompleted()] } return []}).middleware(...middlewares)
Section titled “.middleware(...middlewares)”Registers one or more middleware instances or factories.
.middleware(...mws: (Middleware | MiddlewareFactory)[]): StoreBuilder.create(options?)
Section titled “.create(options?)”Instantiates the store. Returns a live StoreInstance.
.create(options?: StoreCreateOptions): StoreInstanceStoreCreateOptions
Section titled “StoreCreateOptions”| Property | Type | Description |
|---|---|---|
initialState | Partial<ResolvedState> | Override initial state values. Deep merged with the default state. |
deps | Partial<TDeps> | Inject dependencies. Shallow merged. |
Example
Section titled “Example”const store = MyStore.create({ initialState: { count: 10 }, deps: { repo: new CartRepo(apiClient) },})StoreInstance
Section titled “StoreInstance”A live store instance returned by .create(). This is the primary runtime API.
Methods
Section titled “Methods”| Method | Signature | Description |
|---|---|---|
send | (prepared: PreparedIntent) => IntentRef | Dispatch a prepared intent. Returns an IntentRef for cancellation. |
send | (intent: IntentDescriptor, payload) => IntentRef | Dispatch with explicit intent descriptor and payload. |
send.intentName | (payload) => IntentRef | Named shorthand. Available when intents are registered via .intents(). |
cancel | (ref: IntentRef) => void | Cancel a specific running intent by its ref. |
cancelAll | () => void | Cancel all running intents. |
getState | () => State | Get the current combined state (raw + computed + nested). |
subscribe | (cb: (state) => void) => () => void | Subscribe to state changes. Returns an unsubscribe function. |
subscribe | ('events', cb: (event) => void) => () => void | Subscribe to raw event emissions. |
selector | (fn: (state) => T) => Selector<T> | Create a memoized derived state selector. |
dispose | () => void | Cleanup: cancels all intents, disposes nested children, clears listeners. |
Properties
Section titled “Properties”| Property | Type | Description |
|---|---|---|
scope | ScopeOf<TRawState> | Access nested child store instances. Keys match the nested field names. |
Send API
Section titled “Send API”There are three ways to dispatch an intent:
// 1. PreparedIntent (recommended)store.send(CartIntents.addItem({ item }))
// 2. Named shorthandstore.send.addItem({ item })
// 3. Descriptor + payload (legacy)store.send(CartIntents.addItem, { item })All three return an IntentRef.
Lifecycle
Section titled “Lifecycle”Disposal
Section titled “Disposal”After store.dispose():
- All running intents are cancelled.
- All nested child stores are disposed.
- All listeners are cleared.
- Calling
store.send()throws an error. - Calling
emit()from an already-running executor is silently ignored.
State Subscriptions
Section titled “State Subscriptions”subscribe(cb) fires synchronously whenever state changes. The callback receives the full combined state (raw + computed + nested). Returns an unsubscribe function.
subscribe('events', cb) fires for every event applied to the store, including events bubbled from nested children.
- The store builder is immutable. Each method returns a new builder instance.
- The builder itself is a
StoreDefinition— you can pass it directly to testing APIs likeTestStore(). initialStateuses deep merge. Nested objects are merged recursively.depsuses shallow merge.- Sending an intent after disposal throws. This is intentional — it surfaces lifecycle bugs early.