# @wrnexus/reactive

> Tiny, type-safe reactive primitives (signals) with zero dependencies.

Part of the **WrNexus** framework — an SSR-first, Bun-native full-stack web framework.

## Overview

`@wrnexus/reactive` is the seed of WrNexus's reactivity layer: a minimal `signal`
primitive that holds a value, notifies subscribers when it changes, and hands back
an unsubscribe function. It is deliberately small and framework-agnostic — it powers
nothing on its own, but is shaped so client islands (and later the `.wrn` compiler's
`state` blocks) can build reactive bindings on top of it. Reach for it when you need
observable state without pulling in a full reactivity library.

## Installation

```bash
bun add @wrnexus/reactive
```

> Private package — the machine must be authenticated to the `wrnexus` npm org
> (a read token in `~/.npmrc`). Requires **Bun** (Node is not supported).

## API

The package has a single entry point (`.`) exporting one function and three types.

### `signal<T>(initial: T): Signal<T>`

Creates a reactive signal seeded with `initial`. Returns a `Signal<T>`:

| Member      | Signature                          | Description                                                                                              |
| ----------- | ---------------------------------- | -------------------------------------------------------------------------------------------------------- |
| `get`       | `(): T`                            | Read the current value.                                                                                  |
| `set`       | `(next: T): void`                  | Write a new value. Subscribers run **only when the value actually changes** (compared with `Object.is`). |
| `update`    | `(fn: (current: T) => T): void`    | Apply a function to the current value; equivalent to `set(fn(get()))`.                                   |
| `subscribe` | `(fn: Subscriber<T>): Unsubscribe` | Register a subscriber; returns a function that removes it.                                               |

### Types

```ts
type Subscriber<T> = (value: T) => void;
type Unsubscribe = () => void;

interface Signal<T> {
  get(): T;
  set(next: T): void;
  update(fn: (current: T) => T): void;
  subscribe(fn: Subscriber<T>): Unsubscribe;
}
```

Notes on semantics:

- **No-op updates are skipped.** `set` compares the incoming value to the current
  one with `Object.is`; identical values do not notify subscribers.
- **Safe unsubscribe during notification.** Subscribers are iterated over a copy of
  the subscriber set, so a subscriber may call its own (or another's) unsubscribe
  while a notification is in flight.

## Usage

```ts
import { signal } from "@wrnexus/reactive";

const count = signal(0);

count.get(); // 0

// Subscribe; the returned function unsubscribes.
const off = count.subscribe((value) => {
  console.log("count is now", value);
});

count.set(1); // logs: count is now 1
count.set(1); // no-op — value unchanged, no notification
count.update((n) => n + 1); // logs: count is now 2

off(); // stop listening
count.set(3); // nothing logged
```

Typed signals infer `T` from the initial value, or can be annotated explicitly:

```ts
import { signal, type Signal } from "@wrnexus/reactive";

const user: Signal<{ name: string } | null> = signal(null);
user.set({ name: "Ada" });
```

## Requirements / Notes

- **Bun-only.** Distributed as TypeScript source (`main`/`exports` point at
  `src/index.ts`); consume it under Bun, which runs `.ts` directly.
- **Zero dependencies.** The only runtime API used is the standard `Object.is`.
- Foundational primitive for WrNexus client islands and the forthcoming `.wrn`
  compiler `state` blocks.
