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

@minamorl/root-core

v0.1.0

Published

An event-sourcing kernel with algebraic normalization for TypeScript. State is derived from a normalized event log — never mutated directly.

Readme

@minamorl/root-core

An event-sourcing kernel with algebraic normalization for TypeScript. State is derived from a normalized event log — never mutated directly.

Install

npm install @minamorl/root-core

Core Concept

All state changes are expressed as a sequence of three event types:

type Event =
  | { type: "Create"; id: string; value: unknown }
  | { type: "Update"; id: string; value: unknown }
  | { type: "Delete"; id: string };

Events are normalized by a rewrite system that collapses redundant operations, then replayed to produce state. The fundamental invariant:

state(events) === state(rewrite(events))   // for all event sequences

Quick Start

import { Root, Patch, rewrite } from "@minamorl/root-core";

const root = new Root(rewrite, { enforce: (p) => p });

root.commit({ type: "Create", id: "1", value: { name: "Alice" } });
root.commit({ type: "Update", id: "1", value: { name: "Bob" } });

root.state();   // { "1": { name: "Bob" } }
root.history(); // normalized: [Create("1", {name:"Bob"})]

Architecture

types.ts           Event ADT: Create | Update | Delete
    ↓
state.ts           Pure replay: Event[] → Record<string, unknown>
    ↓
rewrite.ts         Normalization via 4 rewrite laws
    ↓
invert.ts          Inverse computation: events + base → undo events
    ↓
patch.ts           Patch: immutable event bundle with compose/invert
    ↓
root.ts            Root: facade with commit/undo/redo/subscribe/compact
    ↓
schema-registry.ts Schema metadata layer (Zod + SQL column meta)

API

rewrite(events: readonly Event[]): Event[]

Normalizes an event sequence by applying four rewrite laws:

| Law | Rule | Effect | |-----|------|--------| | L1 | Update without prior Create | Discarded | | L2 | Delete removes all history for that id | Cleared | | L3 | Create → Update* → Delete | Collapses to nothing | | L4 | Sequential Updates on same id | Collapses to last |

Returns only Create (plus at most one Update) per surviving id. Idempotent: rewrite(rewrite(x)) equals rewrite(x).

state(events: readonly Event[]): Record<string, unknown>

Replays events sequentially to produce a { id: value } state map.

invert(events: Event[], base: Record<string, unknown>): Event[]

Computes inverse events relative to a base state:

  • Create(id)Delete(id) if id was absent in base, else Update(id, oldValue)
  • Update(id)Update(id, oldValue) if existed in base
  • Delete(id)Create(id, oldValue) if existed in base

Patch

Immutable event bundle.

class Patch {
  static from(events: readonly Event[], rewrite: RewriteFn): Patch;
  compose(other: Patch): Patch;
  toNormalForm(): Event[];
  toEvents(): readonly Event[];
  invert(base: Record<string, unknown>): Patch;
}

Root

Main facade. Manages event history, normalization, undo/redo, and push-based subscriptions.

class Root {
  constructor(
    rewrite: (es: readonly Event[]) => Event[],
    law: { enforce(p: Patch): Patch }
  );
  commit(input: Patch | Event | readonly Event[]): void;
  state(): Record<string, unknown>;
  history(): readonly Event[];
  undo(p: Patch): void;
  redo(p: Patch): void;
  subscribe(fn: Subscriber): () => void;
  compact(): void;
}

| Method | Description | |--------|-------------| | commit(input) | Accepts a Patch, single Event, or Event array. Normalizes and appends to history. Notifies subscribers. | | state() | Returns current state by replaying normalized history. | | history() | Returns the normalized event log. | | undo(patch) | Reverts a previously committed patch using its base-state snapshot. | | redo(patch) | Re-commits a patch. | | subscribe(fn) | Registers a push callback. Immediately emits { type: "Snapshot" }. Returns unsubscribe. | | compact() | Collapses entire history to Create-only events from current state. |

Law enforcement is pluggable — the law parameter can transform or reject patches before they are committed.

SchemaRegistry

Type-safe registry mapping entity ids to Zod schemas and SQL table metadata.

class SchemaRegistry {
  register(entry: RootSchemaEntry): void;
  get(id: string): RootSchemaEntry | undefined;
  list(): RootSchemaEntry[];
}

interface RootSchemaEntry {
  id: string;
  schema: ZodTypeAny;
  meta: TableMeta;
}

interface TableMeta {
  table: string;
  version: string;
  columns: Record<string, ColumnMeta>;
}

Used by adapters (e.g., @minamorl/root-adapters) for database projection.

Design Decisions

  • Normalization is a rewrite system — history is always stored in normal form
  • Law enforcement is pluggable — transform or reject patches at commit time
  • Undo uses base-state snapshots stored in a WeakMap keyed by Patch instance
  • Values are unknown — deliberately untyped at the core level; Zod validation lives in SchemaRegistry
  • Push-based reactivity — subscribers receive individual events or Snapshot markers

Dependencies

  • zod — type-only import for SchemaRegistry

License

MIT