Cross-device sync for Chrome extensions. No backend required.
npm install levityimport { createStore } from 'levity';
const store = createStore({
initial: {
notes: [] as Note[],
activeId: null as string | null,
},
});
await store.init();
// read
store.get('notes');
store.getAll();
// write
await store.set('notes', [...]);
await store.setAll({ notes: [...], activeId: '1' });
// subscribe
store.subscribe('notes', (value, source) => {
// source: 'local' | 'remote'
});const store = createStore({
initial: { ... }, // required
prefix: 'levity', // storage key prefix
debounce: 300, // write delay in ms
onConflict: (ctx) => {}, // conflict resolver
onQuotaWarning: (q) => {},
quotaWarningThreshold: 80,
});Returns the value for key.
Returns the entire state object.
Sets a single key. Returns a promise.
Sets multiple keys. Returns a promise.
Subscribes to changes on key. Callback receives (value, source) where source is 'local' or 'remote'.
Returns an unsubscribe function.
Subscribes to all changes. Callback receives (state, changedKeys, source).
Loads state from storage and starts sync. Call this before reading/writing.
Returns true if init has completed.
Returns storage usage:
{ used: number, total: number, percent: number }Default behavior is last-write-wins. To customize:
createStore({
initial: { ... },
onConflict: ({ local, remote, key, localUpdatedAt, remoteUpdatedAt }) => {
return remoteUpdatedAt > localUpdatedAt ? remote : local;
},
});Chrome Storage Sync limits:
- Total: ~100KB
- Per key: ~8KB
- Writes: ~120/min
Levity requires Chrome extension environment. It won't work in regular web pages.
- Check you're logged into Chrome with same account on both devices
- Verify Chrome sync is enabled:
chrome://settings/syncSetup - Check quota:
await store.getQuota()
Chrome Storage Sync has ~100KB limit. Options:
- Store less data
- Use
chrome.storage.localfor large data (no sync) - Implement data pruning with
onQuotaWarning
createStore({
initial: { ... },
onQuotaWarning: (quota) => {
if (quota.percent > 90) {
// Prune old data
}
},
});Default is last-write-wins. For important data, implement merge logic:
createStore({
initial: { ... },
onConflict: ({ local, remote, key }) => {
if (key === 'notes') {
// Merge arrays
return [...new Set([...local, ...remote])];
}
return remote;
},
});Full type inference from initial:
const store = createStore({
initial: {
count: 0,
items: [] as string[],
},
});
store.get('count'); // number
store.get('items'); // string[]
store.set('count', 'wrong'); // Type errorMIT