Storage: withStorage hydration
Functions
Section titled “Functions”createLocalStorageAdapter()
Section titled “createLocalStorageAdapter()”Creates a synchronous IStorageAdapter over localStorage.
The underlying Storage is resolved lazily per call via
getStorageInstance('localStorage'), so the adapter tracks the
environment (native storage when available, in-memory fallback otherwise).
Values are stringified on write.
export function createLocalStorageAdapter(): IStorageAdapter;Defined in: src/storage/adapters.ts
Example
Section titled “Example”import { createLocalStorageAdapter } from '@sandlada/document-context'
const adapter = createLocalStorageAdapter()adapter.setItem('k', JSON.stringify({ count: 1 }))createSessionStorageAdapter()
Section titled “createSessionStorageAdapter()”Creates a synchronous IStorageAdapter over sessionStorage.
Same lazy-resolution and stringification contract as
createLocalStorageAdapter(), scoped to the tab lifetime instead of
origin persistence.
export function createSessionStorageAdapter(): IStorageAdapter;Defined in: src/storage/adapters.ts
Example
Section titled “Example”import { createSessionStorageAdapter } from '@sandlada/document-context'
const adapter = createSessionStorageAdapter()adapter.setItem('k', 'v')deserializeWithMigration()
Section titled “deserializeWithMigration()”Deserializes a versioned payload, migrating stale schemas when asked.
null / undefined input yields null (no data). Enveloped payloads
({__v, data}) return data directly when __v >= targetVersion, or
migrate(data, __v) when older and a migrate function is supplied (no
migrate function means the stale data is returned as-is). Legacy bare JSON
without an envelope returns parsed as-is. Malformed JSON throws and is
converted upstream to InvalidStorageDataError.
export function deserializeWithMigration( raw: string | null, targetVersion = 1, migrate?: (oldData: any, oldVersion: number) => any): any;Defined in: src/storage/adapters.ts
Throws
Section titled “Throws”When raw is not valid JSON.
Example
Section titled “Example”import { deserializeWithMigration } from '@sandlada/document-context'
const data = deserializeWithMigration(raw, 2, (old, v) => ({ count: Number(old.count) || 0 }))extractDomProperties()
Section titled “extractDomProperties()”Extracts live DOM values for every bridge binding into a partial state.
Iterates all blueprint.bridges in order, reads each bound path via
readDomProperty(), applies the rule-level parse codec when present, and
keeps the value unless it is null, undefined, or '' (empty attributes
count as absent). Later bridges overwrite earlier ones on key collision.
export function extractDomProperties<S extends Record<PropertyKey, any>>( blueprint: IContextBlueprint<S, any>, element: HTMLElement): Partial<S>;Defined in: src/storage/hydration.ts
Example
Section titled “Example”import { extractDomProperties } from '@sandlada/document-context'
const domState = extractDomProperties(blueprint, element)getStorageAdapter()
Section titled “getStorageAdapter()”Normalizes a withStorage adapter option to a concrete adapter object.
The 'localStorage' / 'sessionStorage' shorthands construct the matching
built-in adapter; any other value (custom sync or async object) passes
through untouched. No validation is performed here; failures surface later
as InvalidStorageDataError on the session error stream.
export function getStorageAdapter( adapter: 'localStorage' | 'sessionStorage' | IStorageAdapter<any> | IAsyncStorageAdapter<any>): IStorageAdapter<any> | IAsyncStorageAdapter<any>;Defined in: src/storage/adapters.ts
Example
Section titled “Example”import { getStorageAdapter } from '@sandlada/document-context'
const adapter = getStorageAdapter('localStorage')getStorageInstance()
Section titled “getStorageInstance()”Resolves a usable Storage for 'localStorage' or 'sessionStorage'.
Probes window[type] first, then globalThis[type], verifying each with a
__test_probe__ write-remove round trip (guards private-mode throws and
Node 22 without --localstorage-file). When every probe fails, returns a
process-local in-memory fallback cached per type, so SSR and happy-dom
tests keep working without persistence.
export function getStorageInstance(type: 'localStorage' | 'sessionStorage'): Storage;Defined in: src/storage/adapters.ts
Example
Section titled “Example”import { getStorageInstance } from '@sandlada/document-context'
getStorageInstance('localStorage').setItem('k', 'v')resolveHydratedState()
Section titled “resolveHydratedState()”Merges blueprint seeds, live DOM values, and persisted storage into the initial session state according to a precedence strategy.
Pure function: reads DOM synchronously, allocates a fresh object, and never
touches the session. Non-object storageData is treated as {}. Spread
order (later wins) per strategy: 'storageFirst' (default)
{...blueprint, ...dom, ...storage}; 'domFirst' and 'merge' are
currently identical, both {...blueprint, ...storage, ...dom};
'blueprintFirst' is {...dom, ...storage, ...blueprint}.
export function resolveHydratedState<S extends Record<PropertyKey, any>>( blueprint: IContextBlueprint<S, any>, element: HTMLElement, storageData: Partial<S> | null | undefined, strategy: HydrationStrategy = 'storageFirst'): S;Defined in: src/storage/hydration.ts
Example
Section titled “Example”import { resolveHydratedState } from '@sandlada/document-context'
const state = resolveHydratedState(blueprint, element, { count: 5 }, 'storageFirst')serializeWithVersion()
Section titled “serializeWithVersion()”Serializes state into the versioned {"__v", "data"} envelope.
Every persistence write goes through this function, so readers can detect
stale schemas via __v and migrate. data is embedded as-is (no schema
filtering); version defaults to 1.
export function serializeWithVersion(data: any, version = 1): string;Defined in: src/storage/adapters.ts
Example
Section titled “Example”import { serializeWithVersion } from '@sandlada/document-context'
localStorage.setItem('counter', serializeWithVersion({ count: 1 }, 2))setupStorage()
Section titled “setupStorage()”Activates persistence, hydration, and cross-tab sync for a mounted session.
Normally invoked by the withStorage mount hook (and redundantly by the
storage global mount plugin when blueprint.storage exists), not by hand.
The pipeline is: synchronous read + resolveHydratedState() seeding (async
adapters hydrate late via update() instead) → BroadcastChannel
sandlada-sync-{key} subscription (unless crossTabSync === false) →
state subscription persisting every emission through
serializeWithVersion() (coordinated by navigator.locks when available,
rebroadcasting to the channel) → window storage-event listener. Every
parse or write failure is routed to the session error stream as
InvalidStorageDataError; disposed sessions stop persisting and applying
remote updates.
export function setupStorage<S extends Record<PropertyKey, any>>( session: ISession<S, any>, storageOptions: IStorageOptions<S>): () => void;Defined in: src/storage/sync.ts
Example
Section titled “Example”import { setupStorage } from '@sandlada/document-context'
const cleanup = setupStorage(session, { adapter: 'localStorage', key: 'counter' })cleanup()withStorage()
Section titled “withStorage()”Pure Phase 1 operator that registers persistence and hydration options.
Stores options on blueprint.storage (replacing any previous storage
config) and adds a mount hook invoking
setupStorage(session, options). The operator itself performs no I/O; all
reads, writes, and channel subscriptions start at mount.
export function withStorage<S extends Record<PropertyKey, any> = Record<PropertyKey, any>, Services = {}>( options: IStorageOptions<S>): <ActualState extends S, ActualServices extends Services>( blueprint: IContextBlueprint<ActualState, ActualServices>) => IContextBlueprint<ActualState, ActualServices>;Defined in: src/storage/with-storage.ts
Example
Section titled “Example”import { withStorage } from '@sandlada/document-context'
const blueprint = pipe( createContext({ count: 0 }), withStorage({ adapter: 'localStorage', key: 'counter-app', version: 1 }))References
Section titled “References”HydrationStrategy
Section titled “HydrationStrategy”Re-exports HydrationStrategy
IAsyncStorageAdapter
Section titled “IAsyncStorageAdapter”Re-exports IAsyncStorageAdapter
IStorageAdapter
Section titled “IStorageAdapter”Re-exports IStorageAdapter
IStorageOptions
Section titled “IStorageOptions”Re-exports IStorageOptions