npm package: @ripetchor/ssm
Description: Simple State Manager
Author: ripetchor
Latest version: 1.0.5

# SSM

## Features

- **Type-Safe**: Fully typed with generics for state and slices.
- **Batched Updates**: Queues state updates and flushes them in batches to optimize performance.
- **Selectors**: Extract specific slices of state without re-rendering the entire store.
- **Subscriptions**: Subscribe to state changes with optional equality checks and immediate callbacks.
- **Flush Strategies**: Control when updates are applied (`sync`, `pre` via microtask, or `post` via idle callback).

## Usage

### Basic Example

Create a store, set state, and subscribe to changes.

```typescript
import { createStore } from "@ripetchor/ssm";

interface AppState {
  count: number;
  user: string;
}

const initialState: AppState = { count: 0, user: "guest" };

const store = createStore(initialState);

// Get current state
console.log(store.getState()); // { count: 0, user: 'guest' }

// Set state
store.setState({ count: 1 });

// Subscribe to a slice
const unsubscribe = store.subscribe(
  (state) => state.count,
  (newCount) => console.log(`Count updated: ${newCount}`),
);

// Update state again
store.setState((prev) => ({ count: prev.count + 1 }));

// Unsubscribe
unsubscribe();
```

### Advanced Usage

#### Selectors

Use selectors to derive computed values from the state.

```typescript
import { createSelector } from "@ripetchor/ssm";

const countSelector = createSelector<AppState, number>((state) => state.count);

const selectedCount = store.select(countSelector);
console.log(selectedCount); // e.g., 2
```

#### Subscription Options

Customize subscriptions with equality functions and immediate callbacks.

```typescript
store.subscribe(
  (state) => state.user,
  (newUser) => console.log(`User: ${newUser}`),
  {
    immediate: false, // Don't call callback immediately
    equalityFn: (a, b) => a === b, // Custom equality check
  },
);
```

#### Flush Options

Control when batched updates are flushed.

- **`sync`**: Flush immediately (synchronous).
- **`pre`**: Flush in the next microtask
- **`post`**: Flush during idle time

```typescript
store.setState({ user: "admin" }, { flush: "sync" });
```

## API Reference

### `createStore<TState>(initialState: TState): Store<TState>`

Creates a new instance of the `Store` class.

- **Parameters**:
  - `initialState`: The initial state object (generic `TState`).

- **Returns**: A `Store` instance.

### `Store<TState>` Class

The core class for managing state.

#### Constructor

`new Store(initialState: TState)`

Initializes the store with a deep clone of the initial state.

#### Methods

- **`getInitialState(): TState`**

  Returns the original initial state (read-only).

- **`getState(): TState`**

  Returns the current state.

- **`select<TSlice>(selector: SelectorFunction<TState, TSlice>): TSlice`**

  Applies a selector function to the current state and returns the selected slice.
  - **Parameters**:
    - `selector`: A function that takes the state and returns a slice.

- **`setState(partial: ((prevState: TState) => Partial<TState>) | Partial<TState>, options?: SetStateOptions): void`**

  Queues a state update. The update can be a partial object or a function that returns a partial based on the previous state.
  - **Parameters**:
    - `partial`: Partial state or updater function.
    - `options`: Optional object with `flush` strategy (`'sync' | 'pre' | 'post'`). Defaults to `{ flush: 'pre' }`.

  Updates are batched and applied during flush. Multiple updates in the queue are merged sequentially.

- **`subscribe<TSlice>(selector: SelectorFunction<TState, TSlice>, callback: (nextState: TSlice) => void, options?: SubscribeOptions<TSlice>): () => void`**

  Subscribes to changes in a selected slice of state.
  - **Parameters**:
    - `selector`: Function to select the slice.
    - `callback`: Function called with the new slice when it changes.
    - `options`: Optional object:
      - `immediate`: Boolean (default `true`): Call callback immediately with current slice.
      - `equalityFn`: Function to compare previous and next slices (default `Object.is`).

  - **Returns**: An unsubscribe function.

  Subscribers are notified only if the selected slice changes according to the equality function.

- **`destroy(): void`**

  Clears all subscribers, empties the update queue, and resets the current state to the initial state.

### `createSelector<TState, TSlice>(selector: SelectorFunction<TState, TSlice>): (state: TState) => TSlice`

Creates a reusable selector function.

- **Parameters**:
  - `selector`: The selector logic.

- **Returns**: A function that can be passed to `select` or `subscribe`.

### Types

The library exports several types from `./types`:

- **`SelectorFunction<TState, TSlice>`**: `(state: TState) => TSlice`
- **`SetStateOptions`**: `{ flush: 'sync' | 'pre' | 'post' }`
- **`SubscribeOptions<TSlice>`**: `{ immediate?: boolean; equalityFn?: (a: TSlice, b: TSlice) => boolean }`
- **`Subscriber<TState, TSlice>`**: Internal type for subscribers.
- **`UpdateFunction<TState>`**: `(prev: TState) => Partial<TState>`

## Examples

### Counter App

```typescript
const store = createStore({ count: 0 });

const increment = () => store.setState((prev) => ({ count: prev.count + 1 }));

store.subscribe(
  (state) => state.count,
  (count) => console.log(`New count: ${count}`),
);

increment(); // Logs: New count: 1
```

### With Custom Equality

For arrays or objects, provide a deep equality function.

```typescript
import { deepEqual } from "some-deep-equal-lib"; // Assume imported

store.subscribe(
  (state) => state.items,
  (newItems) => console.log("Items changed"),
  { equalityFn: deepEqual },
);
```

## License

MIT