Documentation

Storage & Hydration

Persistent signals, SSR data hydration, and zero-flash boot.

Persistence in Alwatr is layered: a low-level provider for versioned JSON storage, a reactive signal that wraps it, and a separate SSR bridge for hydrating state that the server already knows on first paint.

Storage providers

@alwatr/local-storage and @alwatr/session-storage provide versioned JSON persistence behind an identical provider API:

ts
import {createLocalStorageProvider} from '@alwatr/flux';

const storage = createLocalStorageProvider<{name: string}>({
  name: 'user-data',
  schemaVersion: 1,
});

storage.write({name: 'Ali'});
const data = storage.read(); // {name: string} | null
storage.remove();
Tip

Bump schemaVersion whenever the stored shape changes — the provider auto-clears older-versioned data on the next read, which is the whole migration story. Custom parse/stringify functions let you persist non-JSON-native types like Map, Set, or Date.

Reactive persistence

PersistentStateSignal and SessionStateSignal (see Signals) wrap these providers: every .set() auto-persists, debounced (saveDebounceDelay, roughly 1 second by default) so rapid updates don't thrash storage, with full BFCache lifecycle integration so state survives a page freeze/resume cycle correctly.

SSR hydration

Initial state should never be fetched via an async call during boot — it should already be on the page. EmbeddedDataCollector<T> extracts, parses, and validates JSON from a <script type="application/json"> tag the server rendered, so the client can synchronously seed state with zero extra round-trips and no flash of empty content.

html
<script type="application/json" id="app-config">{"theme":"dark"}</script>
ts
import {EmbeddedDataCollector} from '@alwatr/flux';

interface AppConfig { theme: 'dark' | 'light'; }
const isAppConfig = (value: unknown): value is AppConfig =>
  typeof value === 'object' && value !== null && 'theme' in value;

const collector = new EmbeddedDataCollector<AppConfig>('app-config', isAppConfig);
const config = collector.collect(); // AppConfig | null — SSR-safe

The pipeline is: locate the script tag by attribute → read textContent then clear it (a GC hint) → JSON.parse → run the validator → return T | null on any failure. Wrap it in Lazy so extraction only happens on first access:

ts
import {lazy} from '@alwatr/core';

export const appConfig = lazy(
  () => new EmbeddedDataCollector<AppConfig>('app-config', isAppConfig).collect(),
);

Page-ready

@alwatr/page-ready is a lightweight page-identity signal for multi-page apps — it reads the page-id attribute on <body>.

ts
import {onPageReady, subscribePageReady, dispatchPageReady} from '@alwatr/flux';

onPageReady('home', () => initHomePage());
subscribePageReady((pageId) => analytics.trackPageView(pageId));

dispatchPageReady(); // call once, at the end of bootstrap