npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@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-persist

Peer 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 to state.get(...) (async).
  • Auto-save: hooks into $subscribe. Each mutation resets a debounce timer; when it fires, the filtered state is written via state.set(...).
  • Flush triggers: pending writes are flushed on @visibility:false (panel hidden) and window unload. A flush bypasses the debounce.
  • Errors: subscribes once to @state-error and logs save failures via console.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 on
  • hostConfig.stateCache — host-side LRU config for the state cache (per-page page-size + entry-count limits)