npm package: @minamorl/root-core
Description: An event-sourcing kernel with algebraic normalization for TypeScript. State is derived from a normalized event log — never mutated directly.
Latest version: 0.1.0

# @minamorl/root-core

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

## Install

```bash
npm install @minamorl/root-core
```

## Core Concept

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

```typescript
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

```typescript
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 `Update`s 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.

```typescript
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.

```typescript
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.

```typescript
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