@sqlrooms/room-store
Low-level state management primitives for SQLRooms, built on Zustand.
Use this package when you want to build custom room state from scratch.
If you want DuckDB + layout + room shell out of the box, use @sqlrooms/room-shell.
Installation
npm install @sqlrooms/room-storeWhat this package provides
createRoomStore()andcreateRoomStoreCreator()- base lifecycle slice:
createBaseRoomSlice() - generic slice helper:
createSlice() - React context/hooks:
RoomStateProvider,useBaseRoomStore,useRoomStoreApi - persistence helpers:
persistSliceConfigs(),createPersistHelpers() - room-store persistence glue:
createRoomStorePersistence() - persistence controller:
createPersistenceController()
Quick start
import {
BaseRoomStoreState,
createBaseRoomSlice,
createRoomStore,
createSlice,
type StateCreator,
} from '@sqlrooms/room-store';
type CounterSliceState = {
counter: {
value: number;
increment: () => void;
};
};
function createCounterSlice(): StateCreator<CounterSliceState> {
return createSlice<CounterSliceState>((set, get) => ({
counter: {
value: 0,
increment: () =>
set((state) => ({
counter: {
...state.counter,
value: get().counter.value + 1,
},
})),
},
}));
}
type RoomState = BaseRoomStoreState & CounterSliceState;
export const {roomStore, useRoomStore} = createRoomStore<RoomState>(
(set, get, store) => ({
...createBaseRoomSlice()(set, get, store),
...createCounterSlice()(set, get, store),
}),
);React integration
import {RoomStateProvider} from '@sqlrooms/room-store';
import {roomStore} from './store';
export function App() {
return (
<RoomStateProvider roomStore={roomStore}>
<Dashboard />
</RoomStateProvider>
);
}import {useRoomStore} from './store';
import {Button} from '@sqlrooms/ui';
function Dashboard() {
const value = useRoomStore((state) => state.counter.value);
const increment = useRoomStore((state) => state.counter.increment);
return <Button onClick={increment}>Count: {value}</Button>;
}Imperative access
Use roomStore.getState() for non-reactive code (events, timers, async jobs).
import {roomStore} from './store';
export function incrementLater() {
setTimeout(() => {
roomStore.getState().counter.increment();
}, 500);
}Persistence
For a Zustand room store with host-owned storage, prefer createRoomStorePersistence(). It composes createPersistHelpers() with a controller-backed PersistStorage, rehydrate saved-snapshot marking, optional room-store subscription, autosave, and final flush helpers. This is the default entry point for SQLRooms apps that persist room state to DuckDB, files, or another project-owned store. See the Persistence developer guide for the full integration model, data flow, and examples.
Use the lower-level createPersistenceController() only when you need the same persistence policy outside a room store or Zustand persist. The controller is storage-agnostic: hosts provide load() and save() adapter functions, while SQLRooms handles hydration state, dirty tracking, scheduled saves, final flush, in-flight save coalescing, and observable save status.
createPersistHelpers() still only handles schema-based partialization and rehydrate merging. Let createRoomStorePersistence() combine those helpers with save policy unless you have a custom integration that does not fit the room-store helper.
import {createRoomStorePersistence} from '@sqlrooms/room-store';
const persistence = createRoomStorePersistence({
partialize: (state) => ({room: state.room.config}),
autosaveDelayMs: 300,
load: async () => loadProjectSnapshot(),
save: async (snapshot, metadata) => {
await saveProjectSnapshot(snapshot, metadata?.reason);
},
});
await persistence.hydrate();
await persistence.flush('final-flush');Inside components, useRoomStoreApi() gives you the raw store API:
import {useRoomStoreApi} from '@sqlrooms/room-store';
import {Button} from '@sqlrooms/ui';
function ResetButton() {
const store = useRoomStoreApi();
return (
<Button
onClick={() => {
// Example: imperative read from store
const current = store.getState().room.initialized;
console.log('initialized', current);
}}
>
Inspect store
</Button>
);
}Interfaces
Type Aliases
- LayoutNodeKey
- LayoutPanelNode
- LayoutSplitNode
- LayoutTabsNode
- LayoutNode
- LayoutConfig
- LayoutDirection
- LayoutDirection
- BaseRoomConfig
- DataSourceTypes
- BaseDataSource
- FileDataSource
- UrlDataSource
- SqlQueryDataSource
- DataSource
- LoadFile
- StandardLoadOptions
- SpatialLoadOptions
- SpatialLoadFileOptions
- StandardLoadFileOptions
- LoadFileOptions
- BaseRoomStoreState
- BaseRoomStore
- UseRoomStore
- CreateBaseRoomSliceProps
- CommandCliAdapterOptions
- CommandCliAdapter
- CommandMcpToolDescriptor
- CommandMcpAdapterOptions
- CommandMcpAdapter
- RoomCommandSurface
- RoomCommandInvocation
- RoomCommandInvocationOptions
- RoomCommandExecutionContext
- RoomCommandPredicate
- RoomCommandInputComponentProps
- RoomCommandInputComponent
- RoomCommandRiskLevel
- RoomCommandKeystrokes
- RoomCommandPolicyMetadata
- RoomCommandUiMetadata
- RoomCommandResult
- RoomCommandExecuteOutput
- RoomCommandMiddlewareNext
- RoomCommandMiddleware
- RoomCommandInvokeStartEvent
- RoomCommandInvokeSuccessEvent
- RoomCommandInvokeFailureEvent
- RoomCommandInvokeErrorEvent
- CreateCommandSliceProps
- RoomCommand
- RegisteredRoomCommand
- RoomCommandDescriptor
- RoomCommandListOptions
- CommandSliceState
- PersistenceSaveReason
- PersistenceSaveMetadata
- PersistenceAdapter
- PersistenceControllerState
- PersistenceControllerListener
- PersistenceController
- CreatePersistenceControllerOptions
- RoomCommandPortableSchema
- RoomStateProviderProps
- RoomStorePersistenceSnapshotCodec
- RoomStorePersistenceSnapshotEquivalence
- RoomStorePersistenceChangePredicate
- CreateRoomStorePersistenceOptions
- RoomStorePersistence
Variables
- MAIN_VIEW
- LayoutNodeKey
- LayoutPanelNode
- LayoutSplitNode
- LayoutTabsNode
- LayoutNode
- LayoutConfig
- DEFAULT_ROOM_TITLE
- BaseRoomConfig
- DataSourceTypes
- BaseDataSource
- FileDataSource
- UrlDataSource
- SqlQueryDataSource
- DataSource
- LoadFile
- StandardLoadOptions
- SpatialLoadOptions
- SpatialLoadFileOptions
- isSpatialLoadFileOptions
- StandardLoadFileOptions
- LoadFileOptions
createRoomSlicecreateBaseSlice- RoomStateContext
Functions
- isLayoutPanelNode
- isLayoutSplitNode
- isLayoutTabsNode
- createDefaultLayout
- createDefaultBaseRoomConfig
- isFileDataSource
- isUrlDataSource
- isSqlQueryDataSource
- createBaseRoomSlice
- createSlice
- createRoomStore
- createRoomStoreCreator
- isRoomSliceWithInitialize
- isRoomSliceWithDestroy
- createCommandCliAdapter
- createCommandMcpAdapter
- createCommandSlice
- createRoomCommandExecutionContext
- hasCommandSliceState
- registerCommandsForOwner
- unregisterCommandsForOwner
- listCommandsFromStore
- invokeCommandFromStore
- validateCommandInput
- doesCommandRequireInput
- getCommandShortcut
- getCommandKeystrokes
- getCommandInputComponent
- resolveCommandPolicyMetadata
- exportCommandInputSchema
- createPersistenceController
- RoomStateProvider
- useRoomStoreApi
- useBaseRoomStore
- createPersistHelpers
- persistSliceConfigs
- createRoomStorePersistence
