Skip to main content

Local-First Store

The data/store.js module provides a local-first collection store backed by IndexedDB. Data is available offline instantly, with optional background sync to a server.

Creating a Collection

js
import { collection } from 'weblisk/data/store.js';

const todos = collection('todos');

With server sync:

js
const todos = collection('todos', {
  syncUrl: 'https://api.example.com/todos'
});

CRUD Operations

Create / Update

js
// Auto-generates an ID
await todos.put({ text: 'Buy milk', done: false });

// Provide your own ID
await todos.put({ id: 'abc123', text: 'Buy milk', done: false });

// Update existing
await todos.put({ id: 'abc123', done: true });

Records automatically get an _updated timestamp.

Read

js
// Single record
const todo = await todos.get('abc123');

// All records
const all = await todos.getAll();

// Filtered
const done = await todos.getAll(r => r.done === true);

// Count
const total = await todos.count();

Delete

js
// Soft-delete (marks _deleted, keeps for sync)
await todos.delete('abc123');

// Hard-delete all soft-deleted records
await todos.purgeDeleted();

Reactive Queries

Create a signal that live-updates when the collection changes:

js
import { effect } from 'weblisk';
import { collection } from 'weblisk/data/store.js';

const todos = collection('todos');
const allTodos = todos.live();

effect(() => {
  const items = allTodos();
  document.querySelector('.count').textContent = `${items.length} items`;
});

With a filter:

js
const activeTodos = todos.live(r => !r.done);

Change Events

js
const unsub = todos.onChange((records) => {
  console.log('Collection changed:', records.length, 'items');
});

Server Sync

When syncUrl is configured, mutations are queued for Background Sync:

js
const todos = collection('todos', {
  syncUrl: 'https://api.example.com/todos'
});

await todos.put({ text: 'Buy milk' });
// → Saved to IDB immediately
// → Queued for background sync to server

Merging Server Data

Pull changes from the server and merge (newer timestamps win):

js
const serverData = await fetch('/api/todos').then(r => r.json());
await todos.merge(serverData);

Incremental Sync

Get only records changed since a timestamp:

js
const changes = await todos.changesSince(lastSyncTimestamp);

Listening for Sync Results

js
import { onSyncResult } from 'weblisk/data/store.js';

onSyncResult((msg) => {
  if (msg.ok) console.log(`Synced ${msg.collection}`);
  else console.error(`Sync failed:`, msg.error);
});