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

@redrixx/writ

v0.1.1

Published

A substrate-agnostic ownership and lifecycle contract for hot shared state, with separate writer and reader capabilities.

Downloads

242

Readme

@redrixx/writ

One ownership and lifecycle model across state systems.

writ is a small, adoptable ownership and lifecycle contract for hot shared application state. It targets high-churn state with many observers, recurring external events, and dynamic entity collections: realtime messaging, presence, permissions and roles, multiplayer or collaborative state, device telemetry, voice sessions, long-lived desktop clients, and offline or reconnected systems.

It grew out of repeatedly debugging state whose ownership and lifecycle had become implicit. writ is the pattern I now prefer for avoiding those footguns: make writers, readers, commands, and entity transitions recognizable without replacing the state system that already fits the application.

State libraries provide storage, reactivity, selectors, update propagation, and mature tooling. writ does not duplicate those benefits. It adds a shared reader/writer shape, scoped ownership model, and explicit create, update, upsert, and destroy vocabulary that can be used across different state substrates.

  • Separate capabilities: creating state returns one writer capability and a reader with no writ mutation method. Sharing the writer shares authority.
  • Asserted entity existence: spawn / destroy, strict by default. This is not a complete state-machine model of every domain transition.
  • Strict registries: publish intentional readers and commands without raw writers. Each registry is one named slot, not proof of one application root.

The core has zero runtime dependencies and is framework-agnostic. Its built-in cell is the batteries-included adoption path when another state substrate is not needed. Existing state libraries can retain responsibility for their own storage, reactivity, selectors, tooling, and performance characteristics.

What writ is not

writ does not replace Zustand, Redux, Jotai, Solid stores, or RxJS. It is not a server-state cache, data-fetching library, forms or local-component-state solution, complete state-machine runtime, or security boundary, and it is not intended for every piece of application state.

Capability limits

writ does not determine one human or module owner at runtime. Writers can be exported or passed to multiple callers, in which case every holder can write. It is an API and type boundary, not hostile-code isolation or protection from as any, unsafe casts, reflection, or mutable references.

Readers expose no writ mutation method, but writ does not deeply freeze values. Objects returned by get() can still be mutated when their references are mutable. Prefer immutable updates and readonly state types at public reader boundaries.

npm install @redrixx/writ

This package is ESM-only. CommonJS consumers must use dynamic import(). The browser-compatible runtime has no Node-specific runtime requirement.

One-writer cell

import { createCell } from "@redrixx/writ";

const count = createCell(0);
count.set((n) => n + 1); // owner writes
export const counter = count.reader; // everyone else: get() + subscribe(), no set

Entity store — strict lifecycle, explicit escape hatches

import { createEntityStore } from "@redrixx/writ";

const users = createEntityStore<{ name: string; online: boolean }>();

users.spawn("u1", { name: "Cody", online: true }); // birth — throws if already alive
users.update("u1", { online: false }); // patch — throws if absent
users.destroy("u1"); // death — throws if absent

users.upsert("u1", { name: "Cody", online: true }); // spawn-or-replace, never throws
users.destroyIfPresent("u1"); // no-throw death → boolean

Strict throws name their own way out: spawn("u1"): entity is already alive. Use upsert() to replace it.

Composition root

import { createEntityStore, createRegistry } from "@redrixx/writ";

export const app = createRegistry<{ users: EntityReader<User> }>("AppState");

export function bootstrap(events: EventSource) {
  const users = createEntityStore<User>();
  events.on("join", (u) => users.upsert(u.id, u)); // owner retains the writer
  events.on("leave", (u) => users.destroyIfPresent(u.id));
  app.register({ users: users.reader }); // only readers leave here
}

Optional persistence

Inject any synchronous key/value port; core imports no platform storage.

import { createEntityStore, createMemoryPersistence } from "@redrixx/writ";

const store = createEntityStore<User>({
  persistence: createMemoryPersistence(), // or localStorage / MMKV / Tauri adapters
  key: "users",
});
store.rehydrate(); // load on boot; writes persist through automatically

React

See @redrixx/writ-react for useReader / useEntities / useEntity. Core is fully usable without it.

Not yet supported (v0.1)

Other framework adapters · devtools/inspector · a sync story beyond the injectable port · SSR · a plugin system · per-entity ownership transfer.

Pre-1.0: 0.x releases may make breaking changes.

Contract and error behavior

Strict lifecycle failures throw synchronously before commit, notification, or persistence. Persistence failures are warned after the in-memory commit and do not roll memory back. Corrupt persisted input is removed without replacing the current in-memory collection.

clear() is one administrative reset that wipes persisted state; it is not a series of asserted entity deaths. Reader values are not deep-frozen, and sharing a writer deliberately shares authority.

See the complete package contract, including subscription semantics, scoped-owner disposal, and measured operating-range guidance.

For migration steps, mutation-placement guidance, a complete dynamic registry, testing practices, terminology, and a feature decision tree, see the adoption guides.

License

Apache-2.0 © Cody Magnuson (Redrixx)