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

segment-state

v0.2.1

Published

Path-addressed state for Octane and React with O(observed) memory and transactional commits.

Readme

[!WARNING] Segment is experimental and currently follows 0.x versioning. Its core behavior is tested, but public APIs may still change before 1.0.

What is Segment?

Segment is a small state engine for Octane and React applications where data has a natural address: records, documents, caches, server payloads, and large keyed collections.

Many state APIs make a selector, atom object, or store snapshot the identity at the call site. Segment instead gives every declared value a structural path:

users/42/profile/name

That path can be read, written, observed, serialized, or passed to an external service without sharing an in-memory object reference. The same addressing model powers fine-grained subscriptions, transaction logs, server hydration, resources, and adapters.

| | What it means | | ------------------------ | ----------------------------------------------------------------------------- | | Structural paths | Any declared value can be reached through a typed ref or a plain path string. | | Targeted updates | A write wakes observers of the affected address, not the whole store. | | Atomic commits | Multiple writes land together; a thrown transaction is rolled back. | | O(observed) memory | Large segments materialize nodes only for addresses currently being watched. | | Async state built in | Resources support caching, cancellation, staleness, live data, and save-back. | | Runtime independent | The core imports neither a UI framework nor the DOM. |

Benchmark

The suite combines exact work counts with a 20,000-record workload, 2,000 targeted writes, and a 200-row mounted window. Callback, selector, and retained-entry counts are the primary results; elapsed time and heap measurements are machine-specific and should be read directionally.

Run it locally with pnpm benchmark. The full benchmark methodology documents the fixture, library versions, measurement caveats, and the commands used to compare a candidate change against an identical baseline. The comparison describes design trade-offs, not a universal ranking for every application shape.

Installation

npm install segment-state octane   # an Octane application
npm install segment-state react    # a React application (React 19+)
pnpm add segment-state octane
pnpm add segment-state react

Install the renderer you use: the package root serves the Octane hooks, and segment-state/react serves the React ones. Both peers are optional, so neither renderer is pulled into an application that uses the other. Segment is ESM-only, and Node.js 22+ is required for Node runtimes and development tooling. Renderer-free server, worker, or tooling modules can import the DOM-free engine from segment-state/core.

Quick start

Declare the shape of the state once. Segment turns it into a typed tree of addresses while keeping values inside the store.

import { createStore, segment } from 'segment-state';

export const store = createStore({
	todos: segment({ title: '', completed: false }),
});

export const s = store.state;

s.todos.replaceAll({
	docs: { title: 'Ship the documentation', completed: false },
	release: { title: 'Publish the package', completed: false },
});

const completed = s.todos.at('docs').completed;

const stop = store.observe(completed, () => {
	console.log('completed:', store.get(completed));
});

store.update(completed, (value) => !value, 'todo/toggle');

console.log(completed.path); // todos/docs/completed
stop();

There is no provider and no hidden global store:

  1. createStore() creates one isolated state container.
  2. store.state exposes typed refs; it does not expose mutable state objects.
  3. store.get() reads once, while store.observe() subscribes.
  4. Every write becomes a named commit that adapters and external services can see.

Atomic updates

Use store.act() when several writes must become visible together:

store.act((tx) => {
	tx.set(s.todos.at('docs').completed, true);
	tx.set(s.todos.at('release').completed, true);
}, 'release/complete');

Observers see one commit and never an intermediate state. If the callback throws, none of its writes are published.

Define the state model

Ordinary values are ordinary writable state. Markers are only needed when the value itself cannot describe the behavior you want.

| Declaration | Use it for | | ------------------ | -------------------------------------------------------------------- | | count: 0 | A writable value with an inferred type. | | profile: { … } | A branch whose fields each receive an address. | | cell<T>(initial) | A narrowed union or a plain object stored as one value. | | segment({ … }) | A large keyed collection with observation-scaled memory. | | list({ … }) | An addressable array whose items have addressable fields. | | derived<T>() | A cached synchronous value computed from other addresses. | | resource<T>() | Async state with load, save, cancellation, staleness, and live data. | | action() | One synchronous, atomic state transition. | | task() | An async flow made of several atomic transitions. |

Computed slots and callable actions receive their implementations through .with(). A store containing only plain data does not need this step.

import { action, createStore, derived } from 'segment-state';

export const counter = createStore({
	count: 0,
	doubled: derived<number>(),
	increment: action<[by?: number]>(),
}).with((s) => ({
	doubled: (get) => get(s.count) * 2,
	increment: (tx, by = 1) => tx.update(s.count, (count) => count + by),
}));

counter.state.increment(2);
console.log(counter.get(counter.state.doubled)); // 4

.with() is optional

If you prefer an explicit module API, keep the schema data-only and export ordinary functions. Use the equivalent store-level composition methods for behavior that still needs a reactive address:

import { createStore } from 'segment-state';

interface User {
	name: string;
}

export const app = createStore({ count: 0, updatedAt: 0 });
export const s = app.state;

export function increment(by = 1): void {
	app.update(s.count, (count) => count + by, 'counter/increment');
}

export function reset(): void {
	app.act((tx) => {
		tx.set(s.count, 0);
		tx.set(s.updatedAt, Date.now());
	}, 'counter/reset');
}

export const doubled = app.derive((get) => get(s.count) * 2);

export const loadUser = app.resourceOf<User, [id: string]>(async ({ args: [id], signal }) => {
	const response = await fetch(`/api/users/${id}`, { signal });
	return (await response.json()) as User;
});

Both styles use the same store and commit protocol. Choose .with() when behavior should be discoverable on store.state, needs a stable schema address, or a task() should expose automatic status, result, and error refs. Choose exported functions for a smaller, conventional module API. A multi-write function should use store.act() to remain atomic; a standalone resource created with resourceOf() has a session-local address.

See the state model guide for collections, refs, derivations, actions, and tasks. Resources, SSR, ports, and persistence boundaries live in the advanced guide.

Read and write from anywhere

The core API is deliberately small:

| Operation | Purpose | | ------------------------ | ------------------------------------------------------ | | store.get(ref) | Read a value without subscribing. | | store.observe(ref, cb) | Subscribe to one address or subtree. | | store.set(ref, value) | Replace one writable value. | | store.update(ref, fn) | Apply one read-modify-write transition. | | store.patch(ref, data) | Update selected fields as one commit. | | store.act(fn) | Group multiple reads and writes atomically. | | store.ref(path) | Resolve an address from a structural path string. | | store.commits(cb) | Subscribe to the serializable stream of state changes. |

This makes the same store usable from UI code, tests, workers, sockets, persistence layers, and developer tools.

Server rendering: server → client

Segment uses the same store model on the server and in the browser. Each server request creates an isolated store, renders from it, and sends only a versioned data payload to the client. The browser creates its own store and hydrates that payload before the first client render.

// app-state.ts — imported by both server and client
import { cell, createStore } from 'segment-state';

export interface Viewer {
	id: string;
	name: string;
}

export function createAppStore() {
	return createStore({
		page: {
			title: '',
			viewer: cell<Viewer | null>(null),
		},
		ui: { theme: cell<'light' | 'dark'>('light') },
	});
}

On the server, create a fresh instance for every request and embed a safely escaped payload next to the rendered application:

import { dehydrate } from 'segment-state/ssr';

const store = createAppStore();

store.patch(store.state.page, {
	title: 'Dashboard',
	viewer: await loadViewer(request),
});

const appHtml = await renderApp(store);
const payload = dehydrate(store, { at: Date.now() });
const payloadJson = JSON.stringify(payload).replaceAll('<', '\\u003c');

return `
	<div id="app">${appHtml}</div>
	<script id="segment-state" type="application/json">${payloadJson}</script>
	<script type="module" src="/client.js"></script>
`;

In the browser, hydrate before mounting so the first client render sees exactly the state used for the server HTML:

import { hydrate, type Payload } from 'segment-state/ssr';

const element = document.querySelector<HTMLScriptElement>('#segment-state');
const root = document.querySelector('#app');
if (!element?.textContent || !root) throw new Error('Incomplete SSR document');

const payload = JSON.parse(element.textContent) as Payload;
const store = createAppStore();

hydrate(store, payload, { maxAge: 60_000 });
mountApp(store, root);

hydrate() publishes one atomic commit. Derived values and actions are recreated from code, not serialized. A resource resolved on the server arrives ready on the client and does not repeat its initial request; an older stamped value can still be used for first paint and refreshed in the background through maxAge.

Never share a mutable module-level store between server requests. See the complete SSR and hydration guide for partial payloads, resource behavior, and authority boundaries.

Octane integration

The render-aware hooks are exported directly from segment-state. There is no provider, and subscriptions stay scoped to the address read by a component.

import { useValue } from 'segment-state';

export function TodoRow({ id }: { id: string }) @{
	const [completed, setCompleted] = useValue(s.todos.at(id).completed);

	<button onClick={() => setCompleted(!completed)}>
		{completed ? 'Done' : 'Mark complete'}
	</button>
}
  • useValue(ref) reads writable, derived, branch, and resource addresses.
  • useStatus(ref) exposes resource state without suspending.
  • useDraft(ref) keeps a local edit and publishes it on demand.

Store operations remain usable outside a component through get, observe, and commit streams. Import those APIs from segment-state/core in infrastructure that must not load a renderer.

React integration

The same three hooks ship for React (19 or newer) through segment-state/react, which also re-exports the whole application API, so a React app imports from one place and never loads Octane:

import { useValue } from 'segment-state/react';

export function TodoRow({ id }: { id: string }) {
	const [completed, setCompleted] = useValue(s.todos.at(id).completed);

	return (
		<button onClick={() => setCompleted(!completed)}>{completed ? 'Done' : 'Mark complete'}</button>
	);
}

The contract matches the Octane binding: no provider, subscriptions scoped to the address a component reads, resource reads waiting through <Suspense>, and the array form of useValue starting several loads before suspending once.

Where Segment fits

Segment is a strong fit when:

  • a large keyed dataset has a much smaller visible or observed window;
  • state must be addressed outside the component that created it;
  • several writes must be atomic and attributable;
  • server payloads, workers, sockets, or devtools need one serializable protocol;
  • async values should share the same address and lifecycle model as local state.

For a small amount of component-local UI state, the state primitive built into your renderer is usually simpler. Segment is also intentionally not a router, database, or request client—it coordinates application state around those systems.

Package entry points

| Import | Contents | | --------------------- | ---------------------------------------------------------------- | | segment-state | Store API, Octane hooks, and tree-shakeable adapter helpers. | | segment-state/core | Renderer-free state engine; imports neither a renderer nor DOM. | | segment-state/react | The same application API with React hooks in place of Octane's. | | segment-state/ports | Optional path-based external adapter lifecycle. | | segment-state/ssr | Optional dehydrate() and atomic hydrate() serialization API. |

Documentation

| Resource | What it covers | | ---------------------------------------------------------------------------- | ----------------------------------------------------------- | | Documentation site | Searchable guide and API concepts. | | Getting started | Installation, first store, subscriptions, and transactions. | | State model | Cells, branches, segments, lists, derivations, and actions. | | Advanced guide | Resources, Octane, SSR, hydration, ports, and guarantees. | | Playground | Embedded interactive store and a full-screen application. | | Core internals | Trie design, commit protocol, complexity, and measurements. | | Release guide | Maintainer release and trusted publishing workflow. |

Development

pnpm install
pnpm check

Run the interactive example with pnpm playground, or build it with pnpm playground:build. The playground consumes the package through workspace:*; pnpm pack:check additionally installs the real tarball in a clean offline consumer to catch missing files or broken exports.

Agent guidance is maintained once in .rulesync/ and generated for Codex, Claude Code, Cursor, and GitHub Copilot. After changing a rule or skill, run pnpm agents:generate; pnpm check verifies that all committed agent files remain in sync. The included skills cover architecture and API design, performance audits, and systematic regression hunting.

Every ordinary pull request must include release intent. Use a semantic changeset for published API or runtime behavior:

pnpm changeset

For documentation, tests, examples, benchmarks, CI, dependency maintenance, or other non-release work, add an empty changeset instead:

pnpm changeset --empty

After changesets reach main, GitHub Actions maintains a reviewable version PR that updates package.json and CHANGELOG.md. The generated changeset-release/* PR is the only exception to the CI rule because it consumes those files. Merging that PR automatically publishes the stable package under npm's latest dist-tag, then creates the matching v<version> tag and GitHub Release. See the release guide for version policy, dry runs, recovery, and trusted npm publishing.

License

MIT © Michal Makowski