# [memorio](https://npmjs.com/package/memorio)

![image](https://raw.githubusercontent.com/passariello/container/refs/heads/main/memorio/banner.svg)

![version](https://img.shields.io/npm/v/memorio.svg)
![downloads](https://img.shields.io/npm/dm/memorio.svg)

![Snyk](https://img.shields.io/badge/Snyk-gray?logo=Snyk)
![Socket](https://img.shields.io/badge/Socket-gray?logo=Socket)

![Node.js](https://img.shields.io/badge/Node.js-gray?logo=node.js)
![Browser](https://img.shields.io/badge/Browser-gray?logo=google-chrome)
![Deno](https://img.shields.io/badge/Deno-gray?logo=deno)
![tsup](https://img.shields.io/badge/tsup-gray?logo=esbuild)

![React](https://img.shields.io/badge/React-gray?logo=React)
![TypeScript](https://img.shields.io/badge/TypeScript-gray?logo=typescript)
![Javascript](https://img.shields.io/badge/Javascript-gray?logo=Javascript)
![Jquery](https://img.shields.io/badge/jquery-gray?logo=jquery)

![Vitest](https://img.shields.io/badge/Vitest-gray?logo=vitest)
![OXlint](https://img.shields.io/badge/Oxlint-gray?logo=oxc)
![Playwright](https://img.shields.io/badge/Playwright-gray?logo=playwright)

## Why memorio?

| | memorio | Redux | Zustand |
|---|---|---|---|
| **Setup** | 1 import | Boilerplate hell | Moderate |
| **Dependencies** | **Zero** | Many | Few |
| **TypeScript** | ✅ Native | ✅ | ✅ |
| **Binary storage** | ✅ Built-in IDB | ❌ Add-on | ❌ |
| **Observer** | ✅ Built-in | ❌ Add-on | ❌ |
| **DevTools** | ✅ Built-in + dphelper-manager extension | ❌ Extension | ❌ |
| **Running on the edge** | ✅ Workers, Deno | ⚠️ Limited | ⚠️ Limited |

If you need a `store` on the server. A `cache` in the edge worker.
Session isolation in Next.js. IndexedDB in a Service Worker.
All from one import, zero configuration.

---

## Features

| | |
|---|---|
| **`state`** | Reactive, volatile state — listen with `useObserver` |
| **`store`** | localStorage persistence — survives refresh |
| **`session`** | sessionStorage — dies with the tab |
| **`cache`** | In-memory cache — fastest read possible |
| **`idb`** | IndexedDB with typed tables — structured, persistent, async |
| **`observer`** | Object watcher — legacy, still works |
| **`useObserver`** | React hook with auto-discovery — drops in |
| **`devtools`** | `memorio.devtools.inspect()` — see everything in console |
| **`logger`** | Auto-log every state change with timestamps |
| **Session Isolation** | Every browser tab, every request: isolated namespace |
| **Platform Detection** | `isBrowser`, `isNode`, `isDeno`, `isEdge` |

No Zustand. No Redux. No provider boilerplate.
Just import it and start storing.

---

## Installation options

```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
```

## **Setup**

```typescript
// IMPORT ONCE AT YOUR APP ENTRY POINT

import 'memorio'
```

```typescript
// Memory
state.user = { name: 'Sara', role: 'admin' }
state.counter++
state.settings = { theme: 'dark', lang: 'it' }

// Call it "a store" but make it observe automatically
useObserver(
  () => { console.debug('user changed:', state.user) },
  [state.user]
)
```

That is it.
No context providers. No `<Store>` wrappers. No action creators.
Import → assign → done.

---

## API Reference

### `state` — Volatile reactive 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 — lock/unlock available for frozen objects
state.removeAll()
state.lock()       // freeze everything
state.unlock()     // unfreeze
```

### `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 (DEPRECATED)

```javascript
globalThis.observer('state.user', (newVal, oldVal) => {
  console.debug('user changed:', newVal, oldVal)
})
```

### `useObserver` — React observer hook

Available globally after `import 'memorio'`.

```jsx
import 'memorio'

function Counter() {
  const [, forceUpdate] = useReducer(x => x + 1, 0)

  useObserver(forceUpdate, [state.counter])
  // State path auto-discovered during render — no manual deps needed

  return <div>Count: {state.counter}</div>
}
```

### `devtools` — inspect everything in one call

```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 — same as globalThis.state
```

> **Browser Extension Integration:** When used with dphelper-manager browser extension, memorio's global state namespace is automatically detected and visualized through a dedicated DevTools panel, providing time-travel debugging and structural guardrails.

### `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

Access via `memorio.*`:

```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, ... }
```

### Context (multi-tenant)

```javascript
memorio.createContext('tenant-name')
memorio.listContexts()
memorio.deleteContext('context-id')
memorio.isolate('tenant-name')
```

---

## Browser Extension (Chrome/Edge)

![dphelper Banner](https://raw.githubusercontent.com/passariello/container/refs/heads/main/dphelper/assets/images/screenshot.png)

Manage your `Memorio` on `dphelper Manager Browser Extension*` environment, monitor memory usage, and access documentation directly from your browser.

*Need to install dphelper npm

- [Download for Chrome](https://chrome.google.com/webstore/detail/dphelper-manager-dev-tool/oppppldaoknfddeikfloonnialijngbk)
- [Download for Edge](https://microsoftedge.microsoft.com/addons/detail/dphelper-manager-dev-to/kphabkbdpaljlfagldhojilhfammepnk)

---

## 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)
