ActionStore
The persistence contract behind inline JSX handlers — action snapshots, cold-path rehydration, the in-memory default, and durability tiers.
Overview
ActionStore is the persistence contract behind inline interaction handlers. When you post JSX with an onClick, the engine mints a content-stable, opaque id for the control — mintId(componentName, path, props), a "ck:" + sha1(…) prefix — and stores an ActionSnapshot describing how to re-derive the handler. What's stamped on the native message is the opaque id plus the control's value prop (serialized — up to 2000 chars on Slack); handler code, other props, and bind() args never leave the process. Don't put secrets in a control's value.
On a click, the ActionRegistry resolves the handler from a hot in-memory cache; on a miss it rehydrates from the store — load the snapshot, re-render the named component with the frozen props, re-walk to the handler's path.
Contract
interface ActionStore {
put(id: string, snap: ActionSnapshot, ttlMs?: number): Promise<void>;
get(id: string): Promise<ActionSnapshot | undefined>;
delete(id: string): Promise<void>;
}Prop
Type
Prop
Type
Prop
Type
ActionSnapshot
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Prop
Type
Durability tiers
- Inline handlers — re-derivable from the component's serializable props alone; nothing extra to persist. This is why components must be pure functions of their props (see renderToIR).
bind(handler, args)— attaches a handler-specificargspayload, snapshotted asboundArgs. v1 caveat: the cold path does not yet injectboundArgson rehydration — after a restart the handler is re-derived from props, soargsmust currently be derivable from props to survive one (see bind()).- A durable store — required for actions to survive a process restart at all.
The in-memory default
The default is InMemoryActionStore — a Map with optional TTL. It is lost on restart: clicking a button posted before a restart raises ActionExpiredError internally, which createBot swallows rather than crashing the bot — the click is acked but nothing happens, and no message is posted.
A durable store (Redis, a database) is not shipped in v1 — implement the three-method contract against your own backend and pass it in:
const bot = createBot({
adapters: [slack({ botToken, appToken })],
actionStore: new RedisActionStore(redisClient), // your implementation
});