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

@supercat1337/store2-deep

v0.1.0

Published

Deep reactivity for @supercat1337/store2 — mutable nested state with granular updates

Readme

@supercat1337/store2-deep

Deep reactivity for @supercat1337/store2 — mutable nested state with granular updates.

npm version License: MIT Bundle Size


Why store2-deep?

  • Mutable nested state – update deeply nested objects directly, no immutable boilerplate.
  • Granular reactivity – only the exact properties used in autorun/computed trigger updates.
  • Seamless integration – works with batch(), Store, ReactiveList, and all other store2 APIs.
  • Lightweight – built on Proxy and WeakMap, no heavy dependencies.
  • Full TypeScript support – includes type definitions.
  • Change tracking – optional onChange callback with path, old and new values.

Installation

npm install @supercat1337/store2-deep @supercat1337/store2

Quick Start

import { deepReactive } from '@supercat1337/store2-deep';
import { autorun, batch } from '@supercat1337/store2';

const state = deepReactive({
    user: {
        name: 'Alex',
        tags: ['admin', 'dev'],
    },
    settings: {
        theme: 'dark',
    },
});

autorun(() => {
    console.log(state.user.name, state.settings.theme);
});
// Logs: "Alex dark"

// Direct mutations — no immutable updates needed!
state.user.name = 'Alexander'; // triggers autorun
state.user.tags.push('lead'); // triggers autorun
state.settings.theme = 'light'; // triggers autorun

// Batch multiple changes
batch(() => {
    state.user.name = 'Sasha';
    state.settings.theme = 'light';
}); // single autorun execution

API

deepReactive<T>(target: T, options?: DeepReactiveOptions): T

Wraps a plain object or array into a deeply reactive proxy.

const state = deepReactive({ nested: { value: 42 } });

Options:

| Option | Type | Description | | ---------- | ------------------------------------------------------------------------ | ----------------------------------- | | onChange | (path: string[], oldValue: any, newValue: any, target: object) => void | Callback invoked on every mutation. |

toRaw(proxy): any

Returns the raw (unproxied) object. Handles circular references safely.

const raw = toRaw(state);

isDeepReactive(value): boolean

Checks if a value is a deep reactive proxy.

if (isDeepReactive(state)) {
    /* ... */
}

markRaw(target): target

Marks an object as raw – it will not be proxied even if used inside a deep reactive object.

const obj = { value: 42 };
markRaw(obj);
const state = deepReactive({ data: obj });
// state.data is the raw object, not a proxy

Advanced: Change Tracking with onChange

You can pass an onChange callback to deepReactive to track every mutation:

const state = deepReactive(
    { user: { name: 'Alex', age: 25 } },
    {
        onChange: (path, oldValue, newValue, target) => {
            console.log(`${path.join('.')} changed from ${oldValue} to ${newValue}`);
        },
    }
);

state.user.name = 'Alexander';
// Logs: user.name changed from Alex to Alexander

state.user.age = 26;
// Logs: user.age changed from 25 to 26

delete state.user.age;
// Logs: user.age changed from 26 to undefined

// Adding a new property
state.user.tags = ['admin'];
// Logs: user.tags changed from undefined to ['admin']

The onChange callback receives:

  • path – The path to the changed property as an array of strings (e.g., ['user', 'name'] or ['items', '0'] for array indices).
  • oldValue – The previous value (or undefined if the property is new).
  • newValue – The new value (or undefined if deleted).
  • target – The raw object that contains the property.

This is useful for:

  • Debugging and logging
  • Persistence (saving changes to localStorage, server, etc.)
  • Time-travel debugging
  • Integration with external systems

Integration with store2 APIs

All store2 primitives work seamlessly with deep-reactive objects.

autorun

autorun(() => {
    console.log(state.user.name);
});

computed

const fullName = computed(() => `${state.user.firstName} ${state.user.lastName}`);

reaction

reaction(
    () => state.user.name,
    name => console.log('Name changed to', name)
);

batch

batch(() => {
    state.user.name = 'Bob';
    state.user.age = 30;
});

when / waitUntil

when(
    () => state.ready === true,
    () => console.log('Ready!')
);

await waitUntil(() => state.data !== null);

TypeScript

The package ships with its own type definitions. Import types if needed:

import type { DeepReactive, DeepReactiveOptions } from '@supercat1337/store2-deep';

const state: DeepReactive<{ user: { name: string } }> = deepReactive({ user: { name: 'Alex' } });

How It Works

  • Each property of the reactive object has its own Atom from store2.
  • Reads track dependencies via Engine (same as atom.value).
  • Writes update the corresponding atom and notify dependents.
  • Arrays and their mutating methods (push, pop, splice, etc.) are intercepted for batch updates.
  • Structure changes (delete, new keys) are tracked via a special ITERATE atom.

For a deep dive into the internals, see ARCHITECTURE.md. For LLM prompts, guidelines, and detailed pitfall prevention, see AI_DOCS.md.


License

MIT © 2025–2026 Albert Bazaleev