# 🧠 memorio

![image](https://raw.githubusercontent.com/passariello/container/refs/heads/main/memorio/banner.svg)

[![npm version](https://img.shields.io/npm/v/memorio.svg)](https://npmjs.com/package/memorio)
[![npm downloads](https://img.shields.io/npm/dm/memorio.svg)](https://npmjs.com/package/memorio)
[![Node.js](https://img.shields.io/badge/Node.js-%3E%3D18-gray?logo=node.js)](https://nodejs.org)
![Browser](https://img.shields.io/badge/Browser-Chrome%20/%20Firefox%20/%20Safari-gray?logo=google-chrome)
![Deno](https://img.shields.io/badge/Deno-compatible-gray?logo=deno)
![Edge Workers](https://img.shields.io/badge/Edge%20Workers-compatible-gray)
![TypeScript](https://img.shields.io/badge/TypeScript-native-gray?logo=typescript)
![React](https://img.shields.io/badge/React-compatible-gray?logo=react)
![Tests](https://img.shields.io/badge/tests-101%20passed-green)
![License](https://img.shields.io/badge/License-MIT-gray)

**State + Observer + Store + IDB. One import. Zero config.**

Memorio is a universal, cross-platform state management library for JavaScript and TypeScript. Reactive state, persistent store, session cache, IndexedDB, observer system, React hook, devtools, and logger — all from one import, zero dependencies.

---

## ✨ Why memorio?

| Feature | 🔥 memorio | Redux | Zustand |
|---|---|---|---|
| **Setup** | ✅ **1 import** | ❌ Boilerplate hell | ⚠️ Moderate |
| **Dependencies** | ✅ **Zero** | ❌ Many | ⚠️ Few |
| **TypeScript** | ✅ Native | ✅ Yes | ✅ Yes |
| **Binary storage** | ✅ Built-in IDB | ❌ Add-on | ❌ Add-on |
| **Observer** | ✅ Built-in | ❌ Add-on | ❌ Add-on |
| **DevTools** | ✅ Built-in + dphelper-manager | ⚠️ Extension | ⚠️ Extension |
| **Edge runtime** | ✅ Workers, Deno | ❌ Limited | ❌ Limited |
| **Learning curve** | ✅ **5 minutes** | ❌ Hours | ⚠️ 30 min |
| **Boilerplate** | ✅ **None** | ❌ Tons | ⚠️ Some |
| **React support** | ✅ `useObserver` hook | ✅ `connect` | ✅ `useSyncExternalStore` |
| **Context isolation** | ✅ Multi-tenant | ⚠️ Manual | ⚠️ Manual |

Zero dependencies. Lightweight. One import.

---

## 🚀 Features

| | |
|---|---|
| **`state`** | Reactive, Proxy-based volatile state |
| **`store`** | localStorage persistence (survives refresh) |
| **`session`** | sessionStorage (dies with tab) |
| **`cache`** | In-memory fastest cache |
| **`idb`** | IndexedDB with typed tables, structured and async |
| **`observer`** | Legacy object watcher for vanilla JS |
| **`useObserver`** | React hook with auto-discovery |
| **`dispatch`** | Event system: listen, emit, subscribe |
| **`devtools`** | Inspect everything in console |
| **`logger`** | Auto-log every state change with timestamps |
| **Context isolation** | Per-request / multi-tenant namespace |
| **Platform detection** | `isBrowser`, `isNode`, `isDeno`, `isEdge` |

No Zustand. No Redux. No provider boilerplate.
**Import → assign → done.**

---

## 📦 Installation

```bash
# npm
npm i memorio

# pnpm
pnpm add memorio

# yarn
yarn add memorio

# React peer dep (optional, React >= 16.8)
npm i react react-dom
```

---

## 🎯 Quick Start

### Global style (original)

```typescript
// import 'memorio' once at your app entry point
import 'memorio'

// state is now available everywhere
state.user = { name: 'Sara', role: 'admin' }
state.counter++
state.settings = { theme: 'dark', lang: 'it' }

// React - automatic dependency discovery
useObserver(
  () => { console.debug('user changed:', state.user) },
  [state.user]
)

// Vanilla JS - event system
memorio.dispatch.listen('state.user', (event) => {
  console.debug('user changed:', event.detail)
})
```

### Classic `import` style (new)

Every module is also a named export. Same instances, explicit dependencies.

```typescript
// ESM
import {
  state,
  store,
  session,
  cache,
  idb,
  observer,
  useObserver,
  dispatch,
  memorio
} from 'memorio'

state.user = { name: 'Sara' }
store.set('theme', 'dark')

// CJS
const { state, store, memorio } = require('memorio')
```

```tsx
// React with named imports
import { useObserver, state } from 'memorio'

function Counter() {
  const [, forceUpdate] = useReducer(x => x + 1, 0)

  useObserver(forceUpdate, [state.counter])

  return <div>Count: {state.counter}</div>
}
```

Both styles share the exact same instances. Pick whichever fits your project.

---

## 📚 API Reference

### `state` — Reactive volatile state

Global, Proxy-based, reactive. Access anywhere.

```javascript
// Set
state.user = { name: 'Sara', role: 'admin' }
state.items = [1, 2, 3]

// Get
const name = state.user.name    // 'Sara'

// List all keys
console.debug(state.list)         // ['user', 'items']

// Remove one key
state.remove('items')

// Clear all
state.removeAll()

// Lock/unlock (prevents modifications)
state.config = { maxUsers: 100 }
state.config.lock()
state.config.maxUsers = 200 // Error: state 'config' is locked
state.config.unlock()
```

### `store` — Survives refresh

```javascript
store.set('preferences', { theme: 'dark' })
const prefs = store.get('preferences')     // { theme: 'dark' } or null
store.remove('preferences')
store.removeAll()
console.debug(store.size(), 'chars stored')
console.debug(store.isPersistent)         // true -> real localStorage
```

### `session` — Dies with tab

```javascript
session.set('token', 'user-abc-123')
const token = session.get('token')         // 'user-abc-123' or null
session.removeAll()
```

### `cache` — In-memory, disappears on refresh

```javascript
cache.set('temp', computeExpensiveResult())
const result = cache.get('temp')           // undefined or the value
cache.clear()                              // empty it all
```

### `idb` — Structured & typed

```javascript
await idb.db.create('my-db')
await idb.table.create('my-db', 'users')
await idb.data.set('my-db', 'users', { id: 1, name: 'Sara' })
const user = await idb.data.get('my-db', 'users', 1)
```

### `observer` — Object watcher (legacy)

```javascript
observer('state.user', (newVal, oldVal) => {
  console.debug('user changed:', newVal, oldVal)
})
```

### `useObserver` — React observer hook

```jsx
import { useObserver, state } from 'memorio'

function Counter() {
  const [, forceUpdate] = useReducer(x => x + 1, 0)

  useObserver(forceUpdate, [state.counter])

  return <div>Count: {state.counter}</div>
}
```

### `dispatch` — Event system

```javascript
// Listen
memorio.dispatch.listen('state.user', (event) => {
  console.debug('user changed:', event.detail)
})

// Emit
memorio.dispatch.set('state.user', { detail: { name: 'state.user' } })

// Remove
memorio.dispatch.remove('state.user')
```

### `devtools` — Inspect everything

```javascript
memorio.devtools.inspect()   // pretty-prints state, store, session, cache
memorio.devtools.stats()     // { stateKeys, storeKeys, sessionKeys, ... }
memorio.devtools.clear('state')
memorio.devtools.exportData() // JSON snapshot
$state  // console shortcut -> globalThis.state
```

> 💡 **Browser Extension**: When used with [dphelper-manager](https://chrome.google.com/webstore/detail/dphelper-manager-dev-tool/oppppldaoknfddeikfloonnialijngbk), Memorio's global state is automatically detected and visualized with time-travel debugging.

### `logger` — Track every change

```javascript
memorio.logger.configure({ enabled: true, logToConsole: true })
memorio.logger.getHistory()   // [{ timestamp, module, action, path, value }, ...]
memorio.logger.getStats()     // { total, state, set, get, ... }
memorio.logger.exportLogs()   // JSON string of all history
```

---

## 🌍 Platform detection

```javascript
memorio.isBrowser()       // true in Chrome, Firefox, Safari
memorio.isNode()          // true in Node.js
memorio.isDeno()          // true in Deno
memorio.isEdge()          // true in Cloudflare Workers, Vercel Edge

const caps = memorio.getCapabilities()
// { platform: 'browser', hasLocalStorage: true, hasIndexedDB: true, ... }
```

Named exports work too:

```typescript
import { isBrowser, isNode, getCapabilities } from 'memorio'
```

---

## 🏢 Context isolation (multi-tenant)

```javascript
// Create isolated context
const ctx = memorio.createContext('tenant-123')

// Use context storage (prefix: 'tenant-123-key')
ctx.state.user = { name: 'Isolated' }
ctx.store.set('settings', { theme: 'dark' })
ctx.session.set('token', 'abc123')

// Context is completely isolated from global state
console.debug(state.user) // undefined

// Manage contexts
memorio.listContexts()    // ['tenant-123']
memorio.deleteContext('tenant-123')
```

Named exports:

```typescript
import { createContext, listContexts, deleteContext, isolate } from 'memorio'
```

---

## 🖥️ Cross-Platform

Memorio runs in every JavaScript environment, with automatic fallbacks.

| Tool | Browser | Node.js | Deno | Edge / Workers |
|---|---|---|---|---|
| `state` | ✅ | ✅ | ✅ | ✅ |
| `observer` / `useObserver` | ✅ | ✅ | ✅ | ✅ |
| `cache` | ✅ | ✅ | ✅ | ✅ |
| `store` | localStorage | memory | memory | localStorage |
| `session` | sessionStorage | memory | memory | sessionStorage |
| `idb` | IndexedDB | ❌ | ❌ | ⚠️ |
| `devtools` | ✅ | ❌ | ❌ | ⚠️ |

> **Why memory fallbacks on the server?** There is no browser. `store` and `session` gracefully fall back to `Map`. You still get the same API. Same `state`, same `cache`, same `useObserver`. No extra config required.

---

## 🔒 Security

- Zero production dependencies — no supply chain surprises
- NIST & NSA aligned — enterprise-grade security standards
- No `eval`, no obfuscation, no hardcoded secrets
- All inputs validated, keys sanitized, errors caught
- Secure random session IDs via `crypto.randomUUID`

---

## 📄 License

MIT © [Dario Passariello](https://dario.passariello.ca)
