memorio 4.9.31 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +3 -3
- package/README.md +327 -330
- package/SECURITY.md +17 -1
- package/SUMMARY.md +59 -45
- package/adr/001-state-proxy-model.md +96 -0
- package/adr/002-observer-semantics.md +180 -0
- package/adr/003-deep-mutation-semantics.md +129 -0
- package/adr/004-array-mutation-semantics.md +128 -0
- package/adr/005-scheduler-contract.md +149 -0
- package/adr/006-context-isolation.md +92 -0
- package/adr/007-mutation-records.md +118 -0
- package/adr/008-transactions.md +106 -0
- package/adr/009-history-model.md +110 -0
- package/adr/README.md +46 -0
- package/adr/template.md +49 -0
- package/examples/basic.ts +115 -115
- package/examples/browser-vanilla.html +358 -358
- package/examples/cache.ts +72 -72
- package/examples/cross-platform-guards.ts +57 -57
- package/examples/history.ts +104 -0
- package/examples/idb.ts +109 -109
- package/examples/multi-tenant-context.ts +44 -44
- package/examples/node-server.ts +308 -308
- package/examples/observer.ts +60 -60
- package/examples/platform.ts +115 -115
- package/examples/react-app.tsx +362 -362
- package/examples/react-observer.tsx +63 -63
- package/examples/semantic-memory.ts +60 -60
- package/examples/session-advanced.ts +91 -91
- package/examples/sqlite-batched-writes.ts +57 -57
- package/examples/state-advanced.ts +89 -89
- package/examples/store-advanced.ts +117 -117
- package/examples/sync.ts +90 -0
- package/examples/typed-and-schema.ts +102 -100
- package/examples/useObserver.tsx +140 -141
- package/global.cjs +4594 -0
- package/global.d.ts +8 -0
- package/global.js +4532 -0
- package/index.cjs +706 -649
- package/index.d.ts +1 -0
- package/index.js +686 -648
- package/llms.txt +72 -4
- package/markdown/AUDIT-REPORT.md +135 -0
- package/markdown/CACHE.md +100 -0
- package/markdown/CHANGELOG.md +243 -0
- package/markdown/DEVTOOLS.md +129 -0
- package/markdown/DISPATCH.md +177 -0
- package/markdown/HISTORY.md +199 -0
- package/markdown/IDB.md +178 -0
- package/markdown/IMPORT.md +153 -0
- package/markdown/INSPECT.md +123 -0
- package/markdown/LOGGER.md +154 -0
- package/markdown/MEMORY-ATTACHMENT.md +96 -0
- package/markdown/MEMORY.md +162 -0
- package/markdown/OBSERVER.md +209 -0
- package/markdown/PLATFORM.md +271 -0
- package/markdown/PROJECT.md +311 -0
- package/markdown/SCHEMA.md +176 -0
- package/markdown/SECURITY.md +330 -0
- package/markdown/SESSION.md +165 -0
- package/markdown/SQLITE.md +190 -0
- package/markdown/STATE.md +160 -0
- package/markdown/STORE.md +171 -0
- package/markdown/SYNC.md +319 -0
- package/markdown/TYPED.md +165 -0
- package/markdown/USEOBSERVER.md +257 -0
- package/modules/redux.cjs +561 -374
- package/modules/redux.cjs.map +1 -1
- package/modules/redux.js +561 -374
- package/modules/redux.js.map +1 -1
- package/package.json +13 -3
- package/types/env.d.ts +19 -9
- package/types/exports.d.ts +20 -0
- package/types/history.d.ts +13 -1
- package/types/memorio.d.ts +17 -5
- package/types/mutation.d.ts +75 -0
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
> **Status:** Published
|
|
2
|
+
> **Date:** 2026-09-12
|
|
3
|
+
> **Deciders:** Memorio 5.x Core Team, BigLogic
|
|
4
|
+
> **Scope**: Project Documentation
|
|
5
|
+
> **Standard**: Memorio Documentation Standard v5
|
|
6
|
+
>
|
|
7
|
+
---
|
|
8
|
+
# Memorio - NPM Package
|
|
9
|
+
|
|
10
|
+
## Versione
|
|
11
|
+
|
|
12
|
+
**4.9.5** - Current: `4.9.5` branch
|
|
13
|
+
|
|
14
|
+
## Chi Siamo
|
|
15
|
+
|
|
16
|
+
- **Jo**: AI assistant, amico di Dario, professionale e diretto
|
|
17
|
+
- **Dario Passariello**: CTO e fondatore di BigLogic
|
|
18
|
+
- **BigLogic**: Azienda specializzata in soluzioni SaaS artigianali, AI e design ad alta precisione
|
|
19
|
+
|
|
20
|
+
## Progetto Attuale
|
|
21
|
+
|
|
22
|
+
**Memorio** è un NPM package leggero e semplice per piccoli progetti dove servono soluzioni veloci senza complessità enterprise:
|
|
23
|
+
|
|
24
|
+
- **State** - memoria volatile reattiva (Proxy-based)
|
|
25
|
+
- **Store** - localStorage persistenza
|
|
26
|
+
- **Session** - sessionStorage
|
|
27
|
+
- **IDB** - IndexedDB storage strutturato
|
|
28
|
+
- **Cache** - in-memory cache
|
|
29
|
+
- **Observer** - pattern observer per vanilla JS
|
|
30
|
+
- **UseObserver** - React hook per observer
|
|
31
|
+
- **DevTools** - Browser console debugging tools
|
|
32
|
+
- **Logger** - utility di logging
|
|
33
|
+
- **Dispatch** - vanilla JS event system
|
|
34
|
+
- **Schema** - runtime validation per state paths
|
|
35
|
+
- **Typed** - compile-time type safety su state proxy
|
|
36
|
+
- **History** - time-travel (undo/redo/snapshot/diff/trace)
|
|
37
|
+
- **Inspect** - introspection utilities
|
|
38
|
+
- **Memory** - AI memory system con persistence e TTL
|
|
39
|
+
|
|
40
|
+
**Target**: Progetti piccoli/medi
|
|
41
|
+
**Confronto**: @Biglogic/rgs = enterprise, Memorio = piccoli progetti
|
|
42
|
+
|
|
43
|
+
## Stack Tecnologico
|
|
44
|
+
|
|
45
|
+
- TypeScript 6.0.3 (strict mode)
|
|
46
|
+
- tsup 8.5.1 per build (ESM + CJS)
|
|
47
|
+
- ES2022 target
|
|
48
|
+
- ESM (`"type": "module"`)
|
|
49
|
+
- vitest per test (browser + node)
|
|
50
|
+
- Playwright per test end-to-end
|
|
51
|
+
- Peer dependencies: React >=16.8.0 (opzionale)
|
|
52
|
+
- Nessuna UI (è una libreria)
|
|
53
|
+
- Production: dependency-free (zero runtime dependencies)
|
|
54
|
+
|
|
55
|
+
## Struttura File
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
memorio/
|
|
59
|
+
├── index.ts # Entry point (named exports + memorio namespace)
|
|
60
|
+
├── core/ # Core modules (bootstrap, platform, internal)
|
|
61
|
+
│ ├── global.ts # Bootstrap: publishes to globalThis
|
|
62
|
+
│ ├── env.ts # Runtime DEV/PROD detection
|
|
63
|
+
│ ├── internal.ts # Module-local internal state singleton
|
|
64
|
+
│ ├── platform.ts # Browser/Node/Deno/Edge detection
|
|
65
|
+
│ ├── dispatch.ts # Event dispatch system
|
|
66
|
+
│ ├── hlc.ts # Hybrid Logical Clock
|
|
67
|
+
│ └── fractional.ts # Fractional indexing
|
|
68
|
+
├── types/ # TypeScript declarations
|
|
69
|
+
│ └── memorio.d.ts
|
|
70
|
+
├── functions/ # Feature modules
|
|
71
|
+
│ ├── state/ # Reactive state (Proxy)
|
|
72
|
+
│ ├── store/ # LocalStorage
|
|
73
|
+
│ ├── session/ # SessionStorage
|
|
74
|
+
│ ├── cache/ # In-memory cache
|
|
75
|
+
│ ├── idb/ # IndexedDB (browser)
|
|
76
|
+
│ ├── sqlite/ # SQLite (sql.js, optional)
|
|
77
|
+
│ ├── observer/ # Observer pattern
|
|
78
|
+
│ ├── useObserver/ # React hook
|
|
79
|
+
│ ├── logger/ # Logging
|
|
80
|
+
│ ├── devtools/ # DevTools (dev-only)
|
|
81
|
+
│ ├── schema/ # Runtime validation
|
|
82
|
+
│ ├── typed/ # Type-safe state views
|
|
83
|
+
│ ├── history/ # Time-travel undo/redo
|
|
84
|
+
│ ├── inspect/ # Introspection
|
|
85
|
+
│ ├── memory/ # AI memory system
|
|
86
|
+
│ ├── message/ # User messages
|
|
87
|
+
│ └── dispatch/ # Event dispatch
|
|
88
|
+
├── tests/ # Vitest + Playwright tests
|
|
89
|
+
│ ├── vitest/ # Vitest config + test files
|
|
90
|
+
│ └── playwright/ # E2E test specs
|
|
91
|
+
├── .project/markdown/ # Project context & documentation
|
|
92
|
+
│ ├── CHANGELOG.md
|
|
93
|
+
│ ├── PROJECT.md
|
|
94
|
+
│ └── ... (module docs)
|
|
95
|
+
├── docs/ # Published documentation
|
|
96
|
+
├── package.json
|
|
97
|
+
├── tsconfig.json
|
|
98
|
+
├── tsup.config.ts
|
|
99
|
+
└── .kilo/ # Kilo AI agent memory
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## API Pubbliche
|
|
103
|
+
|
|
104
|
+
### Core Namespace
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
import memorio, { state, store, session, cache, idb, sqlite, observer, useObserver, dispatch, message } from 'memorio'
|
|
108
|
+
// or for global access:
|
|
109
|
+
// import 'memorio/global'
|
|
110
|
+
|
|
111
|
+
// Runtime environment detection
|
|
112
|
+
memorio.env.isDev // true in development, false in production
|
|
113
|
+
memorio.env.isProd // inverse of isDev
|
|
114
|
+
|
|
115
|
+
// Global API (opt-in via memorio/global entry)
|
|
116
|
+
memorio.global() // Force-expose dev globals on globalThis (idempotent)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### State
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
// Via named import (or global entrypoint if opted in)
|
|
123
|
+
state.key = 'value' // Set
|
|
124
|
+
state.key // Get (reactive)
|
|
125
|
+
state.remove('key') // Delete
|
|
126
|
+
state.removeAll() // Clear all
|
|
127
|
+
state.lock() // Lock all modifications
|
|
128
|
+
state.unlock() // Unlock
|
|
129
|
+
state.list // Deep clone of all state
|
|
130
|
+
state.typed<T>() // Type-safe view
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Store (localStorage)
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
store.set('key', { value: 42 })
|
|
137
|
+
store.get('key') // { value: 42 }
|
|
138
|
+
store.remove('key')
|
|
139
|
+
store.removeAll()
|
|
140
|
+
store.isPersistent // boolean
|
|
141
|
+
store.size() // character count
|
|
142
|
+
store.quota() // [used, quota]
|
|
143
|
+
store.list() // all memorio keys
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Session (sessionStorage)
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
session.set('key', 'value')
|
|
150
|
+
session.get('key')
|
|
151
|
+
session.remove('key')
|
|
152
|
+
session.removeAll()
|
|
153
|
+
session.isPersistent
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Cache (in-memory)
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
cache.set('key', 'value')
|
|
160
|
+
cache.get('key')
|
|
161
|
+
cache.remove('key')
|
|
162
|
+
cache.list() // all cached items
|
|
163
|
+
cache.clear() // alias for removeAll
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Schema Validation
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
memorio.registerSchema('user.name', {
|
|
170
|
+
type: 'string',
|
|
171
|
+
required: true,
|
|
172
|
+
min: 1,
|
|
173
|
+
pattern: /^[a-zA-Z]+$/
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
memorio.validate('user.name', 'Jo') // { valid: true, errors: [] }
|
|
177
|
+
memorio.listSchemas() // registered paths
|
|
178
|
+
memorio.unregisterSchema('user.name')
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### History / Time Travel
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
memorio.enableHistory() // Start tracking mutations
|
|
185
|
+
const snap = memorio.snapshot() // Deep clone of state
|
|
186
|
+
memorio.undo() // Revert last mutation
|
|
187
|
+
memorio.redo() // Re-apply
|
|
188
|
+
memorio.canUndo() // boolean
|
|
189
|
+
memorio.canRedo() // boolean
|
|
190
|
+
memorio.rollback(snap) // Restore to snapshot
|
|
191
|
+
memorio.trace() // List all mutations
|
|
192
|
+
memorio.clearHistory() // Clear undo/redo/trace
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Platform Detection
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
memorio.isBrowser() // boolean
|
|
199
|
+
memorio.isNode() // boolean
|
|
200
|
+
memorio.isDeno() // boolean
|
|
201
|
+
memorio.isEdge() // boolean
|
|
202
|
+
memorio.getCapabilities() // { platform, hasLocalStorage, hasIndexedDB, ... }
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Context (multi-tenant)
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
memorio.createContext('tenant-name') // Isolate state by context
|
|
209
|
+
memorio.listContexts() // List all contexts
|
|
210
|
+
memorio.deleteContext('context-id') // Delete a context
|
|
211
|
+
memorio.isolate('tenant-name') // Shorthand for createContext
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## NPM Scripts
|
|
215
|
+
|
|
216
|
+
| Comando | Descrizione |
|
|
217
|
+
|---------|-------------|
|
|
218
|
+
| `npm run build` | Build con tsup (ESM + CJS) |
|
|
219
|
+
| `npm run watch` | Watch mode con tsup |
|
|
220
|
+
| `npm test` | Esegui vitest test suite |
|
|
221
|
+
| `npm run lint` | Lint con oxlint |
|
|
222
|
+
| `npm run tsc` | TypeScript type check |
|
|
223
|
+
|
|
224
|
+
## Test Suite
|
|
225
|
+
|
|
226
|
+
| Suite | Tests | Stato |
|
|
227
|
+
|-------|-------|-------|
|
|
228
|
+
| State | 24 | ✅ |
|
|
229
|
+
| Store | 17 | ✅ |
|
|
230
|
+
| Session | 12 | ✅ |
|
|
231
|
+
| Cache | 5 | ✅ |
|
|
232
|
+
| Observer | 12 | ✅ |
|
|
233
|
+
| useObserver | 12 | ✅ |
|
|
234
|
+
| DevTools | 8 | ✅ |
|
|
235
|
+
| Schema | 10 | ✅ |
|
|
236
|
+
| History | 8 | ✅ |
|
|
237
|
+
| Inspect | 6 | ✅ |
|
|
238
|
+
| ID | 3 | ✅ |
|
|
239
|
+
| Dispatch | 5 | ✅ |
|
|
240
|
+
| Message | 4 | ✅ |
|
|
241
|
+
| Logger | 3 | ✅ |
|
|
242
|
+
| Memory | 5 | ✅ |
|
|
243
|
+
| **Totale** | **~120+** | ✅ |
|
|
244
|
+
|
|
245
|
+
## Regole Importanti
|
|
246
|
+
|
|
247
|
+
1. Documentazione in `.project/markdown/`, non nella root
|
|
248
|
+
2. Usare `console.debug()` per il debugging
|
|
249
|
+
- Nei blocchi `catch` e codice di gestione errori operativi è consentito `console.error()` e `console.warn`
|
|
250
|
+
3. Arrow functions obbligatorie
|
|
251
|
+
4. JSDoc obbligatorio su tutte le funzioni pubbliche
|
|
252
|
+
5. Peer dependency React >=16.8.0 opzionale
|
|
253
|
+
6. Nessuno stile CSS/SCSS (è una libreria)
|
|
254
|
+
7. Nessun database embedded
|
|
255
|
+
8. Tutti i moduli usano singleton keys su `globalThis` per prevenire module duplication
|
|
256
|
+
|
|
257
|
+
## Dipendenze
|
|
258
|
+
|
|
259
|
+
### Produzione
|
|
260
|
+
- Nessuna (dependency-free)
|
|
261
|
+
|
|
262
|
+
### Sviluppo
|
|
263
|
+
- `typescript: 6.0.3`
|
|
264
|
+
- `tsup: 8.5.1`
|
|
265
|
+
- `vitest: 5.0.0`
|
|
266
|
+
- `playwright: chromium`
|
|
267
|
+
- `@types/node: ^25.9.1`
|
|
268
|
+
- `react: ^19.2.6` (peer)
|
|
269
|
+
- `react-dom: ^19.2.6` (peer)
|
|
270
|
+
|
|
271
|
+
### Peer
|
|
272
|
+
- `react: >=16.8.0` (opzionale)
|
|
273
|
+
- `react-dom: >=16.8.0` (opzionale)
|
|
274
|
+
|
|
275
|
+
## Module Duplication Strategy
|
|
276
|
+
|
|
277
|
+
Memorio can be imported via multiple paths (ESM `dist/index.js`, CJS `dist/index.cjs`, package name `memorio`). Each path may resolve to a separate module instance in the bundler. To prevent duplicate proxies:
|
|
278
|
+
|
|
279
|
+
| Module | Singleton Key |
|
|
280
|
+
|--------|--------------|
|
|
281
|
+
| `state` | `__memorio_state_instance__` |
|
|
282
|
+
| `store` | `__memorio_store_instance__` |
|
|
283
|
+
| `session` | `__memorio_session_instance__` |
|
|
284
|
+
| `cache` | `__memorio_cache_instance__` |
|
|
285
|
+
| internal state | `__memorio_internal_state` |
|
|
286
|
+
|
|
287
|
+
The first import path to execute creates the instance and stores it on `globalThis`. Subsequent imports find the existing instance and reuse it - guaranteeing all named exports point to the **same** proxy object.
|
|
288
|
+
|
|
289
|
+
## Valutazione Attuale
|
|
290
|
+
|
|
291
|
+
### Punti di Forza
|
|
292
|
+
|
|
293
|
+
- Struttura NPM corretta
|
|
294
|
+
- Build con tsup moderno (ESM + CJS)
|
|
295
|
+
- Testing setup (vitest + Playwright)
|
|
296
|
+
- Auto-detection React per reattività
|
|
297
|
+
- Multi-storage support (state, store, session, idb, cache, sqlite, devtools, logger)
|
|
298
|
+
- Totalmente dependency-free in produzione
|
|
299
|
+
- Cross-platform: Browser, Node.js, Deno, Edge Workers
|
|
300
|
+
- Session isolation e Context system per multi-tenant
|
|
301
|
+
- Module duplication protection via singleton keys
|
|
302
|
+
- Schema validation e typed stores per type safety
|
|
303
|
+
|
|
304
|
+
### Critica
|
|
305
|
+
|
|
306
|
+
- Coverage test non verificato
|
|
307
|
+
- Socket.dev Supply Chain Security da monitorare
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
*Ultimo aggiornamento: 2026-09-06*
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
> **Status:** Published
|
|
2
|
+
> **Date:** 2026-09-12
|
|
3
|
+
> **Deciders:** Memorio 5.x Core Team
|
|
4
|
+
> **Scope**: API Reference
|
|
5
|
+
> **Standard**: Memorio API Specification v5
|
|
6
|
+
>
|
|
7
|
+
---
|
|
8
|
+
# Schema Validation - Memorio
|
|
9
|
+
|
|
10
|
+
> ✅ **Universal**: Works in Browser, Node.js, Deno, and Edge Workers
|
|
11
|
+
|
|
12
|
+
Schema validation guards your `state` against invalid writes. It runs inside the state proxy's `set` trap, so any `state.somePath = value` that violates a registered schema is rejected at runtime - before the value is ever stored.
|
|
13
|
+
|
|
14
|
+
Schema validation is **opt-in** and **zero-dependency**.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Quick Start
|
|
19
|
+
|
|
20
|
+
```javascript
|
|
21
|
+
import { memorio, state } from 'memorio'
|
|
22
|
+
|
|
23
|
+
// Register a validator for a top-level state key
|
|
24
|
+
memorio.registerSchema('user', {
|
|
25
|
+
type: 'object',
|
|
26
|
+
required: ['name', 'email'],
|
|
27
|
+
properties: {
|
|
28
|
+
name: { type: 'string', min: 1 },
|
|
29
|
+
email: { type: 'string', pattern: /^[^@]+@[^@]+$/ },
|
|
30
|
+
age: { type: 'number', min: 0, max: 150 }
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
// Valid write - accepted
|
|
35
|
+
state.user = { name: 'Sara', email: 'sara@test.com', age: 30 }
|
|
36
|
+
|
|
37
|
+
// Invalid write - rejected, returns false
|
|
38
|
+
state.user = { name: 'Sara' } // missing 'email'
|
|
39
|
+
state.user = { name: 42, email: 'x' } // wrong type for 'name'
|
|
40
|
+
state.user = { age: -5 } // out of range
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Schema Definition
|
|
46
|
+
|
|
47
|
+
A `Schema` object supports the following fields:
|
|
48
|
+
|
|
49
|
+
| Field | Type | Description |
|
|
50
|
+
|-------|------|-------------|
|
|
51
|
+
| `type` | `'string' \| 'number' \| 'boolean' \| 'object' \| 'array' \| 'any'` | Runtime type check |
|
|
52
|
+
| `required` | `string[]` | Property names that must exist (objects only) |
|
|
53
|
+
| `properties` | `Record<string, Schema>` | Nested property schemas (validated recursively) |
|
|
54
|
+
| `min` | `number` | Number: minimum value. String: minimum length |
|
|
55
|
+
| `max` | `number` | Number: maximum value. String: maximum length |
|
|
56
|
+
| `pattern` | `RegExp` | Regex the string value must match |
|
|
57
|
+
| `enum` | `any[]` | Whitelist of allowed values |
|
|
58
|
+
| `validator` | `(value) => boolean \| string` | Custom validator function |
|
|
59
|
+
|
|
60
|
+
### Custom validator functions
|
|
61
|
+
|
|
62
|
+
For logic that's hard to express declaratively, pass a function instead of a schema object:
|
|
63
|
+
|
|
64
|
+
```javascript
|
|
65
|
+
memorio.registerSchema('counter', (value) => {
|
|
66
|
+
if (typeof value !== 'number') return 'counter must be a number'
|
|
67
|
+
if (value < 0) return 'counter must be >= 0'
|
|
68
|
+
return true
|
|
69
|
+
})
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
A custom validator receives the raw value. Return `true` to accept, or a **string** describing the error to reject.
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Path-based registration
|
|
77
|
+
|
|
78
|
+
Schemas are keyed by their **state path**, relative to `state`:
|
|
79
|
+
|
|
80
|
+
| API call | Catches |
|
|
81
|
+
|----------|---------|
|
|
82
|
+
| `registerSchema('user', schema)` | `state.user = value` |
|
|
83
|
+
| `registerSchema('user.age', schema)` | `state.user.age = value` |
|
|
84
|
+
| `registerSchema('items', schema)` | `state.items = value` |
|
|
85
|
+
|
|
86
|
+
The full dotted path is constructed from the proxy's tree depth. Nested sets propagate the full path automatically.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Manual validation
|
|
91
|
+
|
|
92
|
+
You can validate a value without writing it to state:
|
|
93
|
+
|
|
94
|
+
```javascript
|
|
95
|
+
memorio.validate('user', { name: 'Sara', email: 'sara@test.com' })
|
|
96
|
+
// { valid: true }
|
|
97
|
+
|
|
98
|
+
memorio.validate('user', { name: 'Sara' })
|
|
99
|
+
// { valid: false, errors: ["user: missing required property 'email'"] }
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
When no schema is registered for a path, `validate` returns `{ valid: true }`.
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Schema management
|
|
107
|
+
|
|
108
|
+
```javascript
|
|
109
|
+
memorio.listSchemas() // ['user', 'theme', 'items', 'counter']
|
|
110
|
+
memorio.unregisterSchema('counter') // removes the schema
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Full API
|
|
116
|
+
|
|
117
|
+
| Method | Parameters | Returns | Description |
|
|
118
|
+
|--------|-----------|---------|-------------|
|
|
119
|
+
| `memorio.registerSchema(path, schema)` | `string`, `Schema \| fn` | `void` | Register a validator |
|
|
120
|
+
| `memorio.validate(path, value)` | `string`, `any` | `{ valid, errors? }` | Manually validate a value |
|
|
121
|
+
| `memorio.unregisterSchema(path)` | `string` | `boolean` | Remove a registered schema |
|
|
122
|
+
| `memorio.listSchemas()` | none | `string[]` | List all registered paths |
|
|
123
|
+
| `memorio.registerSchema()` is also importable | `registerSchema` | named export | same function |
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Combine with Typed Stores
|
|
128
|
+
|
|
129
|
+
Schema validation gives you **runtime** safety; typed stores give you **compile-time** safety. Use both for full coverage:
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
import { memorio, state } from 'memorio'
|
|
133
|
+
|
|
134
|
+
interface AppState {
|
|
135
|
+
user: { name: string; email: string; age: number }
|
|
136
|
+
theme: 'light' | 'dark'
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const app = memorio.typed<AppState>()
|
|
140
|
+
|
|
141
|
+
memorio.registerSchema('user', {
|
|
142
|
+
type: 'object',
|
|
143
|
+
required: ['name', 'email'],
|
|
144
|
+
properties: {
|
|
145
|
+
name: { type: 'string', min: 1 },
|
|
146
|
+
email: { type: 'string', pattern: /^[^@]+@[^@]+$/ },
|
|
147
|
+
age: { type: 'number', min: 0, max: 150 }
|
|
148
|
+
}
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
app.user = { name: '', email: 'bad' } // ❌ TypeScript: age missing
|
|
152
|
+
// ❌ Runtime: missing required fields
|
|
153
|
+
app.user = { name: 'Sara', email: 'ok', age: 30 } // ✅ both checks pass
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
See [Typed Stores](TYPED.md) for compile-time type safety.
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## How it works
|
|
161
|
+
|
|
162
|
+
1. When you call `registerSchema(path, schema)`, the schema is stored in an internal `Map`.
|
|
163
|
+
2. On every `state.set` operation, the proxy's `set` trap computes the full path (e.g. `'user.name'`).
|
|
164
|
+
3. If a schema is registered for that path, the value is validated.
|
|
165
|
+
4. If validation fails, the write is rejected (`return false`), and an error is logged via `console.error` (when `memorio.debug = true`) or `console.debug` (via the internal `message` helper).
|
|
166
|
+
5. If no schema is registered, the write proceeds normally.
|
|
167
|
+
|
|
168
|
+
The validation adds negligible overhead when no schemas are registered (a single `Map` lookup that returns `undefined`).
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Limitations
|
|
173
|
+
|
|
174
|
+
- Schema validation hooks into the `state` proxy only. `store`, `session`, and `cache` are not validated (they use separate storage). Use `validate()` before writing to other modules.
|
|
175
|
+
- Path matching is **exact**: `registerSchema('user')` guards `state.user = ...`, but does **not** recursively validate `state.user.name = 'new'`. Register schemas at each path you need to guard.
|
|
176
|
+
- The schema system is not a replacement for server-side validation. It protects against accidental misuse and provides defense-in-depth in the browser.
|