Appearance
Store Sync
WARNING
This page documents the old Node.js API (StoreSyncAdapter, ISyncableStore). The v2 architecture (RFC 012) has not yet ported the store sync layer. The NAPI-RS bindings need updating before this API is available again.
Truffle provides cross-device state synchronization through the StoreSyncAdapter and ISyncableStore interface.
How It Works
- Each device maintains its own slice of data
- When local data changes, the store emits
localChanged StoreSyncAdapterbroadcasts the slice to all peers- Remote slices are applied to the store via
applyRemoteSlice() - When a device goes offline, its slice is removed
Implementing a Store
typescript
import { EventEmitter } from 'node:events';
import type { DeviceSlice } from '@vibecook/truffle';
import type { ISyncableStore } from '@vibecook/truffle';
interface TodoState {
items: string[];
}
class TodoStore extends EventEmitter implements ISyncableStore<TodoState> {
readonly storeId = 'todos';
private localSlice: DeviceSlice<TodoState> | null = null;
private remoteSlices = new Map<string, DeviceSlice<TodoState>>();
init(deviceId: string) {
this.localSlice = {
deviceId,
data: { items: [] },
version: 0,
updatedAt: Date.now(),
};
}
addItem(text: string) {
if (!this.localSlice) return;
this.localSlice = {
...this.localSlice,
data: { items: [...this.localSlice.data.items, text] },
version: this.localSlice.version + 1,
updatedAt: Date.now(),
};
this.emit('localChanged', this.localSlice);
}
getLocalSlice(): DeviceSlice<TodoState> | null {
return this.localSlice;
}
applyRemoteSlice(slice: DeviceSlice<TodoState>) {
this.remoteSlices.set(slice.deviceId, slice);
}
removeRemoteSlice(deviceId: string) {
this.remoteSlices.delete(deviceId);
}
clearRemoteSlices() {
this.remoteSlices.clear();
}
}Wiring Up
typescript
import { createMeshNode, StoreSyncAdapter } from '@vibecook/truffle';
const node = createMeshNode({ /* config */ });
const store = new TodoStore();
await node.start();
store.init(node.getLocalDevice().id);
const sync = new StoreSyncAdapter({
localDeviceId: node.getLocalDevice().id,
messageBus: node.getMessageBus(),
stores: [store],
});
sync.start();
// Now any changes to store will sync across devices
store.addItem('Buy groceries');React Integration
tsx
import { useSyncedStore } from '@vibecook/truffle-react';
function TodoList({ store }: { store: TodoStore }) {
const { localData, version } = useSyncedStore(store);
return (
<div>
<h2>Todos (v{version})</h2>
<ul>
{localData?.items.map((item, i) => (
<li key={i}>{item}</li>
))}
</ul>
</div>
);
}