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

zenon

v1.2.0

Published

A minimalist Zustand-inspired state manager for Vue 3 with TypeScript support

Readme


✨ Features

  • 🍃 Vue 3 Composition API - Modern reactive state management
  • ⚡️ Zustand-like DX - Familiar API with set, get, and selectors
  • 🧑‍💻 Full TypeScript Support - Complete type safety and inference
  • 🚀 Lightweight - Minimal bundle size, zero dependencies (except Vue)
  • 🎯 Selector-based Subscriptions - Optimize rendering with partial updates
  • 🔌 Pluggable Middleware - Logger, Persist, ErrorBoundary, and more
  • 🧩 Composable - Chain or compose multiple middlewares
  • 📦 Tree-shakeable - Import only what you need
  • Production Ready - Fully tested with 30+ test cases

💾 Installation

pnpm add zenon
# or
npm install zenon
# or
yarn add zenon

📝 Quick Start

Basic Usage

// stores/counter.ts
import { createStore } from "zenon";

export const useCounter = () =>
  createStore((set, get) => ({
    count: 0,
    increment: () => set({ count: get().count + 1 }, "increment"),
    decrement: () => set({ count: get().count - 1 }, "decrement"),
    reset: () => set({ count: 0 }, "reset"),
  }));
<!-- Counter.vue -->
<template>
  <div>
    <h2>Count: {{ count }}</h2>
    <button @click="increment">+1</button>
    <button @click="decrement">-1</button>
    <button @click="reset">Reset</button>
  </div>
</template>

<script setup lang="ts">
import { useCounter } from "./stores/counter";

const store = useCounter();
const count = store.useSelector((s) => s.count); // Partial subscription
const { increment, decrement, reset } = store;
</script>

With Middleware

import { createStore, compose } from "zenon";
import { withLogger, withPersist, withErrorBoundary } from "zenon/plugins";

export const useCounter = () =>
  createStore(
    compose(
      withLogger({ store: "counter", timestamp: true }),
      withPersist("zenon-counter", {
        storage: window.localStorage,
        version: 1,
      }),
      withErrorBoundary({
        onError: (error, actionName) => {
          console.error(`Error in ${actionName}:`, error);
        },
      })
    )((set, get) => ({
      count: 0,
      increment: () => set({ count: get().count + 1 }, "increment"),
      reset: () => set({ count: 0 }, "reset"),
    }))
  );

🧩 Middleware Plugins

Zenon provides three powerful middleware plugins that can be composed together:

withLogger

Logs all state changes to the console with timestamps.

import { withLogger } from "zenon/plugins";

withLogger({
  store: "counter", // Store name for logging
  timestamp: true, // Include timestamps (default: false)
  expanded: false, // Auto-expand logs (default: false)
});

withPersist

Persists store state to localStorage or sessionStorage.

import { withPersist } from "zenon/plugins";

withPersist("zenon-counter", {
  storage: window.localStorage, // default: localStorage
  version: 1, // Version for migration control
  partialKeys: ["count"], // Persist only specific keys
  serialize: JSON.stringify, // Custom serializer
  deserialize: JSON.parse, // Custom deserializer
  onError: (error) => console.error(error), // Error handler
  merge: (persisted, current) => ({ ...current, ...persisted }), // Merge strategy
});

withErrorBoundary

Catches and handles errors in store actions.

import { withErrorBoundary } from "zenon/plugins";

withErrorBoundary({
  onError: (error, actionName) => {
    // Handle error (e.g., send to error tracking service)
    console.error(`Error in ${actionName}:`, error);
  },
  preventRollback: false, // Prevent state rollback on error (default: false)
  rethrow: false, // Rethrow error after handling (default: false)
});

Composing Multiple Plugins

import { createStore, compose } from "zenon";
import { withLogger, withPersist, withErrorBoundary } from "zenon/plugins";

// Compose style (recommended)
export const useStore = () =>
  createStore(
    compose(
      withLogger({ store: "app" }),
      withPersist("app-state"),
      withErrorBoundary({ onError: console.error })
    )((set, get) => ({
      // Your store implementation
    }))
  );

// Or chain style
export const useStore = () =>
  createStore(
    withLogger({ store: "app" })(
      withPersist("app-state")(
        withErrorBoundary({ onError: console.error })((set, get) => ({
          // Your store implementation
        }))
      )
    )
  );

📚 API Reference

createStore

Creates a new store with reactive state management.

function createStore<T>(
  initializer: (set: SetFunction<T>, get: GetFunction<T>) => T
): StoreApi<T>;

Returns: StoreApi<T> with the following methods:

  • useSelector<U>(selector: (state: T) => U): ComputedRef<U> - Subscribe to partial state
  • subscribe(listener: Listener<T>): () => void - Subscribe to all state changes
  • setSilent(patch: Partial<T>) - Update state without triggering subscribers
  • getState(): T - Get current state snapshot
  • Plus all store actions defined in your initializer

useSelector

Subscribe to a specific part of the store state. Only triggers re-renders when the selected value changes.

const store = useCounter();
const count = store.useSelector((s) => s.count); // Subscribe only to count
const double = store.useSelector((s) => s.count * 2); // Derived values work too

subscribe

Subscribe to all state changes with a callback function.

const unsubscribe = store.subscribe((state, prevState) => {
  console.log("State changed:", { state, prevState });
});

// Cleanup
unsubscribe();

setSilent

Update state without notifying subscribers. Useful for batch updates or internal state changes.

store.setSilent({ count: 10 }); // Updates state without triggering listeners

� TypeScript Support

Zenon is written in TypeScript and provides full type safety out of the box.

import { createStore, StoreApi, SetFunction, GetFunction } from "zenon";

// Type your store
type CounterState = {
  count: number;
  increment: () => void;
  decrement: () => void;
};

export const useCounter = (): StoreApi<CounterState> =>
  createStore<CounterState>((set, get) => ({
    count: 0,
    increment: () => set({ count: get().count + 1 }, "increment"),
    decrement: () => set({ count: get().count - 1 }, "decrement"),
  }));

Exported Types:

  • StoreApi<T> - Store instance type
  • SetFunction<T> - State setter function type
  • GetFunction<T> - State getter function type
  • Listener<T> - Subscribe callback type
  • StoreMiddleware<T> - Middleware function type

🧪 Testing

Zenon is thoroughly tested with 30+ test cases covering all features:

  • ✅ Core store functionality (reactivity, subscriptions, selectors)
  • ✅ All middleware plugins (logger, persist, error boundary)
  • ✅ Error handling and edge cases
  • ✅ TypeScript type safety

Run tests:

pnpm test

�📚 License

MIT

⭐️ Star & Contribute

Ideas, PRs, and feedback are all welcome!