@wippy-fe/pinia-persist
v0.0.39
Published
Pinia plugin for auto-persisting stores via the Wippy state preservation API.
Downloads
3,376
Readme
@wippy-fe/pinia-persist
Pinia plugin for auto-persisting store state via the Wippy state preservation API. State survives iframe destruction (close + reopen, navigation away + back, breakpoint-driven panel remount) and is restored on next mount.
Installation
npm install @wippy-fe/pinia-persistPeer dependencies: @wippy-fe/proxy, pinia
Quick Start
import { createPinia } from 'pinia'
import { createWippyPersist, preloadWippyState } from '@wippy-fe/pinia-persist'
// Preload state BEFORE creating any store so hydration is synchronous.
// Without preload, stores hydrate asynchronously on first access — fine
// for most cases but causes a one-frame flash of initial state.
const preloadedState = await preloadWippyState()
const pinia = createPinia()
pinia.use(createWippyPersist({ preloadedState }))In your store, opt in via the wippyPersist option:
import { defineStore } from 'pinia'
export const useUiStore = defineStore('ui', {
state: () => ({ sidebarOpen: true, theme: 'dark', drafts: {} }),
wippyPersist: true, // persist all state with defaults
})
export const useSettings = defineStore('settings', {
state: () => ({ name: '', token: '', preferences: {} }),
wippyPersist: {
pick: ['name', 'preferences'], // only these survive
omit: ['token'], // OR exclude these
debounce: 500, // ms before auto-save (default 300)
},
})API
createWippyPersist(options?)
The Pinia plugin. Pass to pinia.use(...).
| Option | Type | Description |
|--------|------|-------------|
| preloadedState | Record<string, unknown> | State pre-fetched via preloadWippyState(). When provided, stores hydrate synchronously on creation; without it, hydration is async. |
| scope | string | Default scope for all stores using this plugin instance. Per-store scope option overrides. See Scoping below. |
preloadWippyState(stateApi?, scope?)
Fetches all persisted state from the host in one call. Returns Promise<Record<string, unknown>>. Call this BEFORE createPinia() so the plugin can hydrate synchronously.
wippyPersist store option
Per-store config. Set to true to persist all state with defaults, or to a WippyPersistOptions object:
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| pick | string[] | — | Only persist these state keys. Mutually compatible with omit. |
| omit | string[] | — | Exclude these state keys from persistence. |
| debounce | number | 300 | Debounce window (ms) for auto-save on store mutation. Lower = more responsive saves but more host roundtrips. |
| scope | string | — | Override the per-instance scope. See Scoping below. |
How it works
- Hydration: on store creation, the plugin reads
preloadedState[pinia:${storeId}](sync) or falls back tostate.get(...)(async). - Auto-save: hooks into
$subscribe. Each mutation resets adebouncetimer; when it fires, the filtered state is written viastate.set(...). - Flush triggers: pending writes are flushed on
@visibility:false(panel hidden) andwindowunload. A flush bypasses the debounce. - Errors: subscribes once to
@state-errorand logs save failures viaconsole.warn(e.g. host-side quota exceeded).
Scoping
State is keyed by pinia:<storeId> and scoped at the host by the page/artifact UUID by default — every iframe instance of the same page sees the same scope, every separate artifact sees its own.
When you need to isolate state PER INSTANCE (e.g. two <my-widget persist-key="a"> and <my-widget persist-key="b"> on the same page should have independent state), pass scope:
defineStore('widget', {
state: () => ({ ... }),
wippyPersist: { scope: `my-widget:${this.persistKey}` },
})Custom scopes are automatically prefixed with @custom: to prevent collisions with system scopes (which use raw UUIDs).
⚠️ Custom scope values MUST be globally unique across your application. Two unrelated components using scope: 'sidebar' will collide. Use namespaced keys (my-app:sidebar, dashboard:filters).
Pairs with
instance.state(@wippy-fe/proxy) — the raw key/value API this plugin builds onhostConfig.stateCache— host-side LRU config for the state cache (per-page page-size + entry-count limits)
