Skip to content

rcheruti/comes-storage

Repository files navigation

comes-storage

A persistence plugin for comes — the lightweight address-based event system.

comes-storage automatically saves and restores event values to a storage backend (browser localStorage by default). When your app starts, the last saved value for each tracked event is replayed into the event system so all listeners receive it immediately upon registration — no manual rehydration needed.

Installation

npm install comes comes-storage

comes is a peer dependency and must be installed alongside this package.

Quick Start

import { es } from 'comes';
import { setupStorage } from 'comes-storage';

// Persist the 'theme' and 'language' events under a single storage key
setupStorage('app/settings', ['theme', 'language']);

// The last saved value is replayed immediately to any listener
es.listen('theme', (value) => {
  console.log('Theme:', value); // restored from localStorage on page reload
});

// Saving is automatic — just send events normally
await es.send('theme', 'dark');    // persisted
await es.send('language', 'en');   // persisted

After a page reload, both 'dark' and 'en' are automatically restored and delivered to listeners.

How It Works

setupStorage registers an interceptor on each tracked event address. Every time a value is sent to one of those addresses, the interceptor serializes and saves the updated state to storage under the storageName key.

All tracked events share a single storage entry structured as:

{
  "theme": "dark",
  "language": "en"
}

On setup, setupStorage reads this entry and calls es.send() for each event with its last known value, so every listener registered afterward receives the restored value immediately (via comes's built-in value caching).

Special case: if storageName is also listed in the events array, its value is stored directly at the root key (not nested inside the object).

API

setupStorage(storageName, events, storage?, es?)

import { setupStorage } from 'comes-storage';

setupStorage(
  storageName: string,
  events:      string[],
  storage?:    Storage,
  es?:         EventSystem
): void
Parameter Type Default Description
storageName string The key used to read/write all event data in storage
events string[] Event addresses to track and persist
storage Storage new LocalStorageStorage() Storage backend to use
es EventSystem global es from comes The event system instance to attach to

LocalStorageStorage

The default storage backend. Uses globalThis.localStorage to persist data as JSON. Falls back to an empty object {} if the stored value is missing or cannot be parsed.

import { LocalStorageStorage } from 'comes-storage';

const storage = new LocalStorageStorage();
storage.save('my-key', { count: 1 });
const data = storage.load('my-key'); // { count: 1 }

Storage interface

Implement this interface to use a custom storage backend (e.g. sessionStorage, IndexedDB, AsyncStorage for React Native, or a server-side store).

import type { Storage } from 'comes-storage';

const myStorage: Storage = {
  load(address: string) {
    const raw = sessionStorage.getItem(address);
    return raw ? JSON.parse(raw) : {};
  },
  save(address: string, data: any) {
    sessionStorage.setItem(address, JSON.stringify(data));
  },
};

Examples

Persisting a form across page reloads

import { es } from 'comes';
import { setupStorage } from 'comes-storage';

setupStorage('checkout/form', ['checkout/name', 'checkout/email', 'checkout/address']);

// Bind to inputs
es.listen('checkout/name',    (v) => (nameInput.value    = v ?? ''));
es.listen('checkout/email',   (v) => (emailInput.value   = v ?? ''));
es.listen('checkout/address', (v) => (addressInput.value = v ?? ''));

// Save on change
nameInput.addEventListener('input',    () => es.send('checkout/name',    nameInput.value));
emailInput.addEventListener('input',   () => es.send('checkout/email',   emailInput.value));
addressInput.addEventListener('input', () => es.send('checkout/address', addressInput.value));

Using a custom storage backend

import { EventSystem } from 'comes';
import { setupStorage } from 'comes-storage';
import type { Storage } from 'comes-storage';

const memoryStorage: Storage = {
  store: {} as Record<string, any>,
  load(address) { return this.store[address] ?? {}; },
  save(address, data) { this.store[address] = data; },
};

const es = new EventSystem();
setupStorage('app', ['app/user', 'app/prefs'], memoryStorage, es);

Using a custom EventSystem instance

import { EventSystem } from 'comes';
import { setupStorage } from 'comes-storage';

const es = new EventSystem();
setupStorage('ui/state', ['ui/sidebar', 'ui/theme'], undefined, es);

await es.send('ui/theme', 'light'); // persisted to localStorage under 'ui/state'

License

ISC

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors