memorio 4.9.35 → 5.1.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.
Files changed (82) hide show
  1. package/AGENTS.md +3 -3
  2. package/README.md +95 -516
  3. package/SECURITY.md +159 -33
  4. package/SUMMARY.md +59 -45
  5. package/adr/001-state-proxy-model.md +95 -0
  6. package/adr/002-observer-semantics.md +179 -0
  7. package/adr/003-deep-mutation-semantics.md +128 -0
  8. package/adr/004-array-mutation-semantics.md +127 -0
  9. package/adr/005-scheduler-contract.md +148 -0
  10. package/adr/006-context-isolation.md +91 -0
  11. package/adr/007-mutation-records.md +117 -0
  12. package/adr/008-transactions.md +105 -0
  13. package/adr/009-history-model.md +109 -0
  14. package/adr/README.md +46 -0
  15. package/adr/template.md +48 -0
  16. package/bin/cli.js +68 -0
  17. package/examples/basic.ts +115 -115
  18. package/examples/browser-vanilla.html +358 -358
  19. package/examples/cache.ts +72 -72
  20. package/examples/cross-platform-guards.ts +57 -57
  21. package/examples/history.ts +104 -0
  22. package/examples/idb.ts +109 -109
  23. package/examples/multi-tenant-context.ts +44 -44
  24. package/examples/node-server.ts +308 -308
  25. package/examples/observer.ts +60 -60
  26. package/examples/platform.ts +115 -115
  27. package/examples/react-app.tsx +362 -362
  28. package/examples/react-observer.tsx +63 -63
  29. package/examples/semantic-memory.ts +60 -60
  30. package/examples/session-advanced.ts +91 -91
  31. package/examples/sqlite-batched-writes.ts +57 -57
  32. package/examples/state-advanced.ts +89 -89
  33. package/examples/store-advanced.ts +117 -117
  34. package/examples/sync.ts +90 -0
  35. package/examples/typed-and-schema.ts +102 -100
  36. package/examples/useObserver.tsx +140 -141
  37. package/global.cjs +5733 -0
  38. package/global.d.ts +8 -0
  39. package/global.js +5667 -0
  40. package/index.cjs +2202 -1041
  41. package/index.d.ts +2 -0
  42. package/index.js +2178 -1040
  43. package/llms.txt +113 -8
  44. package/markdown/AUDIT-REPORT.md +134 -0
  45. package/markdown/CACHE.md +191 -0
  46. package/markdown/DEVTOOLS.md +128 -0
  47. package/markdown/DISPATCH.md +176 -0
  48. package/markdown/HISTORY.md +198 -0
  49. package/markdown/IDB.md +177 -0
  50. package/markdown/IMPORT.md +152 -0
  51. package/markdown/INSPECT.md +122 -0
  52. package/markdown/LOGGER.md +153 -0
  53. package/markdown/MEMORY-ATTACHMENT.md +95 -0
  54. package/markdown/MEMORY.md +161 -0
  55. package/markdown/OBSERVER.md +208 -0
  56. package/markdown/PLATFORM.md +277 -0
  57. package/markdown/REDUX.md +54 -0
  58. package/markdown/SCHEMA.md +175 -0
  59. package/markdown/SESSION.md +164 -0
  60. package/markdown/SQLITE.md +189 -0
  61. package/markdown/STATE.md +159 -0
  62. package/markdown/STORE.md +170 -0
  63. package/markdown/SYNC.md +318 -0
  64. package/markdown/TYPED.md +164 -0
  65. package/markdown/USEOBSERVER.md +256 -0
  66. package/modules/redux.cjs +701 -177
  67. package/modules/redux.cjs.map +1 -1
  68. package/modules/redux.js +701 -177
  69. package/modules/redux.js.map +1 -1
  70. package/package.json +26 -4
  71. package/types/broadcast.d.ts +61 -0
  72. package/types/computed.d.ts +96 -0
  73. package/types/encryption.d.ts +129 -0
  74. package/types/env.d.ts +19 -9
  75. package/types/exports.d.ts +29 -0
  76. package/types/history.d.ts +13 -1
  77. package/types/memorio.d.ts +25 -6
  78. package/types/mutation.d.ts +75 -0
  79. package/types/security.d.ts +67 -0
  80. package/types/session.d.ts +23 -5
  81. package/types/store.d.ts +19 -3
  82. package/vsix/memorio.vsix +0 -0
package/README.md CHANGED
@@ -1,115 +1,39 @@
1
1
  # 🧠 memorio
2
2
 
3
- **Local-first memory for JavaScript.**
4
- One import. Global state, local persistence, SQLite, semantic memory, optional sync.
5
-
6
- [![npm version](https://img.shields.io/npm/v/memorio.svg)](https://www.npmjs.com/package/memorio)
7
- [![npm downloads](https://img.shields.io/npm/dm/memorio.svg)](https://www.npmjs.com/package/memorio)
8
- [![Socket Badge](https://socket.dev/api/badge/npm/package/memorio)](https://socket.dev/npm/package/memorio)
9
- [![Known Vulnerabilities](https://snyk.io/test/npm/memorio/badge.svg)](https://snyk.io/test/npm/memorio)
10
- [![zero deps](https://img.shields.io/badge/dependencies-0-brightgreen)](#security)
11
- [![license](https://img.shields.io/npm/l/memorio.svg)](#license)
3
+ **The memory layer for AI agents and apps — owned by the user, not the vendor.**
12
4
 
13
5
  ```ts
14
- import 'memorio'
6
+ import 'memorio/global'
15
7
 
16
8
  state.user = { name: 'Sara', role: 'admin' }
17
9
  state.counter++
10
+
11
+ console.log(state.user.name)
12
+ console.log(state.counter)
18
13
  ```
19
14
 
20
15
  No provider tree. No reducers. No actions. No boilerplate.
21
16
  Just data, available where your application needs it.
22
17
 
23
- **And if you don't want a global API, don't use one:**
24
-
25
- ```ts
26
- import { state, store, session, cache, idb, sqlite, memorio } from 'memorio'
27
- ```
28
-
29
- The global API is a **choice**, not an architectural requirement.
30
-
31
- ---
32
-
33
- ## Table of contents
34
-
35
- - [Why memorio](#why-memorio)
36
- - [Global is optional](#global-is-optional)
37
- - [Before / After](#before--after)
38
- - [Which layer should I use](#which-layer-should-i-use)
39
- - [Install](#install)
40
- - [Quick start](#quick-start)
41
- - [Observing changes](#observing-changes)
42
- - [The layers](#the-layers) — store · session · cache · idb · sqlite · memory
43
- - [Redux and other state managers](#redux-and-other-state-managers)
44
- - [memorio vs Redux, at a glance](#memorio-vs-redux-at-a-glance)
45
- - [React integration](#react-integration)
46
- - [Typed state & schema validation](#typed-state--schema-validation)
47
- - [Local-first sync](#local-first-sync)
48
- - [Cross-platform support](#cross-platform-support)
49
- - [Security](#security)
50
- - [Honest limitations](#honest-limitations)
51
- - [When to use something else](#when-to-use-something-else)
52
- - [Design philosophy](#design-philosophy)
53
- - [A mental model](#a-mental-model)
54
- - [Recipes](#recipes)
55
- - [License](#license)
56
-
57
18
  ---
58
19
 
59
20
  ## Why memorio
60
21
 
61
- Modern JavaScript applications often split their data across systems that don't talk to each other: a state manager, `localStorage`, `sessionStorage`, IndexedDB, a cache, SQLite, a server database, maybe an AI memory layer, maybe a sync layer on top. Each with its own API, its own lifecycle, its own mental model.
62
-
63
- memorio brings these concerns into **one runtime and one consistent model**, while keeping each layer independent.
22
+ Most AI-powered apps (and most apps in general) eventually need more than "just state": reactive values, persistent data, session data, caches, structured browser storage, local SQL, application memory, history, and optional sync.
64
23
 
65
- | Layer | Purpose | Persistence | Reactive |
66
- |---|---|---:|---:|
67
- | `state` | application state | No | Yes |
68
- | `cache` | transient runtime data | No | No |
69
- | `session` | tab/session data | Yes | No |
70
- | `store` | persistent key/value data | Yes | No |
71
- | `idb` | durable structured browser data | Yes | No |
72
- | `sqlite` *(beta)* | relational local SQL | Optional | No |
73
- | `memory` | structured application memory | Yes | Optional |
74
- | `journal` *(beta)* | local-first operation history | Yes | No |
75
-
76
- Start with `state`. Add persistence or memory only when your application actually needs it.
77
-
78
- ## Global is optional
24
+ Usually that means a different library — and a different mental model — for each. Memorio gives them one consistent runtime, without making the simple case complicated:
79
25
 
80
26
  ```ts
81
- import 'memorio'
82
- state.user = { name: 'Sara' }
27
+ state.value = 42
83
28
  ```
84
29
 
85
- is useful when an application benefits from a shared runtime and a simple API. But memorio does **not** require applications to expose or use global state — explicit imports work against the exact same runtime:
30
+ But the reason memorio exists isn't just "fewer dependencies." It's this: **the memory an AI agent builds about a user shouldn't be locked inside a vendor's server, opaque, and impossible to inspect or move.** Memorio's memory layer is structured, inspectable, editable, and — when you turn on encryption — readable only with a key the vendor doesn't hold. It runs client-side, in the browser, on the edge, or in Node.
86
31
 
87
- ```ts
88
- import { state } from 'memorio'
89
- state.user = { name: 'Sara' }
90
- ```
32
+ That said, this covers *structured* memory - preferences, decisions, facts with confidence and provenance - not semantic similarity search. If your agent needs "find things like this conversation," pair memorio with a dedicated vector store; see [`markdown/MEMORY.md`](./markdown/MEMORY.md).
91
33
 
92
- This makes memorio suitable for small apps, modular apps, libraries, component systems, tests, or applications that already use another state manager and prefer explicit dependencies.
93
-
94
- **Global when convenient. Explicit when appropriate.**
95
-
96
- ## Which layer should I use
97
-
98
- ```text
99
- Does the UI need to react automatically to changes?
100
- │
101
- ├─ Yes → state
102
- │
103
- └─ No
104
- │
105
- ├─ Only while the runtime exists? → cache
106
- ├─ Only for this browser tab/session? → session
107
- ├─ Small persistent key/value data? → store
108
- ├─ Larger or structured browser data? → idb
109
- ├─ Relations, joins, or SQL? → sqlite
110
- └─ Knowledge the application should
111
- remember (confidence, source, expiry)? → memory
112
- ```
34
+ Add persistence, memory, history, or sync only when your application actually needs them. The common case stays tiny; the runtime grows with you.
35
+
36
+ ---
113
37
 
114
38
  ## Install
115
39
 
@@ -117,466 +41,121 @@ Does the UI need to react automatically to changes?
117
41
  npm install memorio
118
42
  ```
119
43
 
120
- Optional peers, only loaded when used:
121
-
122
- ```bash
123
- npm install react react-dom # React integration
124
- npm install sql.js # sqlite engine
125
- ```
126
-
127
- **Zero production dependencies** in the core package. See [Security](#security).
128
-
129
- ## Before / After
130
-
131
- ```ts
132
- // Without memorio
133
- const [user, setUser] = useState(null)
134
- useEffect(() => {
135
- const saved = localStorage.getItem('user')
136
- if (saved) setUser(JSON.parse(saved))
137
- }, [])
138
- useEffect(() => {
139
- localStorage.setItem('user', JSON.stringify(user))
140
- }, [user])
141
- ```
142
-
143
- ```ts
144
- // With memorio
145
- import 'memorio'
146
- store.set('user', { name: 'Sara' }) // persistent immediately, no boilerplate
147
-
148
- const user = store.get('user')
149
- ```
150
-
151
- Same result, one reactive line instead of two effects and manual JSON serialization.
152
-
153
- ## Quick start
154
-
155
- ```ts
156
- import 'memorio'
44
+ Zero production dependencies in core. Optional integrations (`react`, `sql.js`, etc.) are installed only when you use them.
157
45
 
158
- state.user = { name: 'Sara', role: 'admin' }
159
- state.counter++
160
-
161
- console.log(state.user.name)
162
- console.log(state.counter)
163
- ```
46
+ ---
164
47
 
165
- Reactive, Proxy-based state. No provider tree to configure, no reducer to maintain.
48
+ ## Start here
166
49
 
167
- Lock a slice you don't want mutated by accident:
50
+ Three things cover most apps - reactive state, persistence, and observation:
168
51
 
169
52
  ```ts
170
- state.config = { maxUsers: 100 }
171
- state.config.lock()
172
-
173
- state.config.maxUsers = 200 // throws
174
-
175
- state.config.unlock()
176
- state.config.maxUsers = 200 // ok
177
- ```
178
-
179
- Prefer explicit imports over the global? Same runtime either way:
53
+ import { state, persist, observer } from 'memorio'
180
54
 
181
- ```ts
182
- import { state, store, session, cache, idb, sqlite, memorio } from 'memorio'
183
- ```
184
-
185
- ## Observing changes
55
+ state.user = { name: 'Sara' }
186
56
 
187
- Reactivity is part of the runtime — it is not tied to React.
57
+ persist('state.user')
188
58
 
189
- ```ts
190
- observer('state.user', (next, previous) => {
191
- console.log('user changed:', next, previous)
59
+ observer('state.user', user => {
60
+ console.log(user)
192
61
  })
193
-
194
- // nested paths work too
195
- observer('state.user.name', callback)
196
62
  ```
197
63
 
198
- Lower-level event access is also available:
199
-
200
- ```ts
201
- const off = memorio.dispatch.listen('state.user', event => console.debug(event.detail))
202
- memorio.dispatch.set('state.user', { detail: { name: 'Sara' } })
203
-
204
- off() // remove only this subscription
205
- ```
206
-
207
- Each subscription owns its own unsubscribe function. Prefer the returned `off()` over `memorio.dispatch.remove('state.user')`, which removes **all** listeners registered under that name.
208
-
209
- React apps get a dedicated hook, `useObserver` — see [React integration](#react-integration).
210
-
211
- ## The layers
212
-
213
- ### `store` — persistent key/value data
64
+ And this is what makes memorio a *memory* layer, not just a state manager - structured, inspectable facts about a user or agent, not just ephemeral UI state:
214
65
 
215
66
  ```ts
216
- store.set('preferences', { theme: 'dark' })
217
- const preferences = store.get('preferences')
218
- ```
219
-
220
- Backed by `localStorage` in the browser. Falls back to memory in environments without persistent storage — check `store.isPersistent` when portability matters.
221
-
222
- ### `session` — data for the current session
223
-
224
- ```ts
225
- session.set('wizard-step', 3)
226
- const step = session.get('wizard-step')
227
- ```
228
-
229
- Backed by `sessionStorage`. Good for multi-step workflows, temporary preferences, tab-scoped data.
230
-
231
- ### `cache` — volatile runtime data
232
-
233
- ```ts
234
- cache.set('expensive-result', computeExpensiveResult())
235
- ```
236
-
237
- Disappears when the runtime disappears. No persistence guarantee, ever.
67
+ import { memorio } from 'memorio'
238
68
 
239
- ### `idb` — durable structured browser data
240
-
241
- ```ts
242
- await idb.db.create('app')
243
- await idb.table.create('app', 'users')
244
- await idb.data.set('app', 'users', { id: 1, name: 'Sara' })
245
-
246
- const user = await idb.data.get('app', 'users', 1)
247
- ```
248
-
249
- Check runtime support before depending on it: `memorio.getCapabilities()`.
250
-
251
- ### `sqlite` — local SQL
252
-
253
- ```ts
254
- await sqlite.ready
255
- await sqlite.db.create('app')
256
-
257
- await sqlite.query.run('app', `
258
- CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL, role TEXT)
259
- `)
260
- await sqlite.query.run('app', `INSERT INTO users (name, role) VALUES (?, ?)`, ['Sara', 'admin'])
261
-
262
- const admins = await sqlite.query.select('app', `SELECT * FROM users WHERE role = ?`, ['admin'])
263
- ```
264
-
265
- Runs **in memory by default**, using `sql.js`. Persistence is explicit:
266
-
267
- ```ts
268
- await sqlite.db.create('app', { persistence: true })
269
- ```
270
-
271
- > ⚠️ **Persistence is not incremental.** It serializes the **entire** database on flush — fine for small/medium local datasets, but large databases should not be persisted after every mutation. Batch writes and flush deliberately. memorio's SQLite layer is an embedded local SQL runtime, not a replacement for a server database.
272
-
273
- ### `memory` — application memory
274
-
275
- The layer that makes memorio more than a state manager. `memory` represents information the application wants to **remember**, not just store — it can carry type, confidence, source, scope, tags, lifetime, status, and history.
276
-
277
- ```ts
278
69
  await memorio.memory.remember('user.language', 'Italian', {
279
70
  type: 'preference',
280
71
  confidence: 0.92,
281
- scope: 'local',
282
- tags: ['user', 'ui'],
283
- source: 'conversation',
72
+ source: 'conversation'
284
73
  })
285
74
 
286
75
  const language = await memorio.memory.recall('user.language')
76
+ // { value: 'Italian', confidence: 0.92, source: 'conversation', ... }
287
77
  ```
288
78
 
289
- Updates don't overwrite — they supersede, preserving history:
290
-
291
- ```ts
292
- await memorio.memory.update('user.language', 'English', { confidence: 0.95 })
293
- // previous entry → status: 'superseded' | new entry → status: 'active'
294
- ```
295
-
296
- Retrieve what's relevant to the current operation, not everything:
79
+ For most apps, that's enough. Everything below is an optional capability you can add when your application needs it - each one documented in its own reference file.
297
80
 
298
- ```ts
299
- const context = await memorio.memory.context({
300
- tags: 'user',
301
- types: ['preference', 'decision'],
302
- minConfidence: 0.7,
303
- maxEntries: 10,
304
- })
305
- ```
306
-
307
- The context system considers tags, type, confidence, and recency.
308
-
309
- > ℹ️ **"Semantic" here means structured, not embedding-based.** `memory.context()` ranks by tags, type, confidence and recency — there is no hidden vector database and no claim that free text has been understood through embeddings. If you need true similarity search, pair a dedicated embedding/vector store with `memorio.memory` for the lifecycle on top (confidence, source, TTL, scope, history, supersession).
310
-
311
- In short: `sqlite` answers "what data do I have?" — `memory` answers "what does my app remember, and how sure is it?" They're not competing for the same job.
312
-
313
- ## Redux and other state managers
314
-
315
- memorio does not need to replace your existing state manager. Its Redux integration ships as a **separate, optional entry point**:
316
-
317
- ```ts
318
- import { createMemorioReduxBridge } from 'memorio/redux'
319
- ```
320
-
321
- Redux is not a dependency of memorio — the adapter works against a small structural interface (`getState` / `subscribe` / `dispatch`), so it can work with Redux-style stores without coupling memorio's core to Redux.
322
-
323
- > **Ownership rule:** your state manager owns application state. memorio owns memory.
324
-
325
- The bridge does not mirror the entire store — only explicitly mapped values are persisted:
326
-
327
- ```ts
328
- const memorioRedux = createMemorioReduxBridge<AppState>({
329
- mappings: {
330
- 'user.preferences': {
331
- selector: state => state.user.preferences,
332
- type: 'preference',
333
- scope: 'local',
334
- tags: ['app', 'user'],
335
- },
336
- 'user.name': state => state.user.name,
337
- },
338
- whitelist: ['user/preferencesChanged', 'user/nameChanged'],
339
- debug: true,
340
- })
341
-
342
- const store = createStore(reducer, applyMiddleware(memorioRedux.middleware))
343
- ```
344
-
345
- Hydration is explicit and one-shot:
346
-
347
- ```ts
348
- await memorioRedux.hydrate(store, {
349
- onHydration(values) {
350
- store.dispatch(restorePreferences(values))
351
- },
352
- })
353
- ```
354
-
355
- Direction of ownership — there is intentionally no permanent two-way mirror:
356
-
357
- ```text
358
- Redux ─────────────→ memorio memorio ────────────→ Redux
359
- application state durable memory bootstrap hydration
360
- ```
361
-
362
- - only mapped selectors are persisted — the entire Redux tree is never mirrored
363
- - hydration is one-shot; restore dispatches do not create synchronization loops
364
- - persistence happens outside the reducer path; bursts of actions are coalesced
365
- - memorio failures do not break Redux dispatch — errors can be reported through `onError`
366
- - the adapter does not expose the memorio database through `globalThis` or DevTools
367
-
368
- ### memorio vs Redux, at a glance
369
-
370
- Not a replacement — a different scope. Useful mainly for deciding which one (or both) fits a given piece of state.
371
-
372
- | | Redux | memorio |
373
- |---|---|---|
374
- | Core model | single store, plain object | multiple independent layers (`state`, `store`, `session`, `cache`, `idb`, `sqlite`, `memory`) |
375
- | State changes | via dispatched actions + reducers | direct mutation on a reactive proxy |
376
- | Persistence | not built in (needs `redux-persist` or similar) | built into `store`/`session`/`idb`/`sqlite` |
377
- | Structured "memory" (confidence, TTL, source, supersession) | not a concept in Redux | native, via the `memory` layer |
378
- | Offline sync / conflict resolution | not built in | built into `memory.configure()` (operations + journal + HLC ordering) |
379
- | Time-travel debugging, strict middleware pipelines | yes, mature ecosystem | not a goal — see [When to use something else](#when-to-use-something-else) |
380
- | Dependencies | small core, large plugin ecosystem | zero production dependencies |
381
-
382
- Pick Redux when you need its action-pipeline discipline and tooling. Pick memorio when you want state, persistence, and structured memory under one runtime without wiring several libraries together. Nothing stops you from using both — that's exactly what the [Redux bridge](#redux-and-other-state-managers) is for.
383
-
384
- ## React integration
385
-
386
- React is an integration, not a requirement — `useObserver` connects to the same observer system described above.
387
-
388
- ```tsx
389
- function Counter() {
390
- const [, forceUpdate] = useReducer(x => x + 1, 0)
391
- useObserver(forceUpdate, [state.counter])
392
- return <div>{state.counter}</div>
393
- }
394
- ```
395
-
396
- The runtime itself remains independent of React.
397
-
398
- ## Typed state & schema validation
399
-
400
- TypeScript types give compile-time guarantees without creating another state container:
401
-
402
- ```ts
403
- interface AppState {
404
- user: { name: string; age: number; email: string }
405
- theme: 'light' | 'dark'
406
- }
407
-
408
- const app = memorio.typed<AppState>()
409
- app.theme = 'dark'
410
- app.theme = 'purple' // TypeScript error
411
- ```
412
-
413
- `app === state` — same proxy, no duplicated store.
414
-
415
- Runtime validation protects values crossing trust boundaries:
416
-
417
- ```ts
418
- memorio.registerSchema('user', {
419
- type: 'object',
420
- required: ['name', 'email'],
421
- properties: {
422
- name: { type: 'string', min: 1 },
423
- email: { type: 'string', pattern: /^[^@]+@[^@]+$/ },
424
- },
425
- })
426
-
427
- state.user = { name: 'Sara' } // rejected: missing required "email"
428
- ```
429
-
430
- Arrays validate each element via `items`:
431
-
432
- ```ts
433
- memorio.registerSchema('tags', { type: 'array', items: { type: 'string' } })
434
-
435
- state.tags = ['a', 'b'] // accepted
436
- state.tags = [1, 2, 3] // rejected
437
- ```
438
-
439
- ## Local-first sync
440
-
441
- The local application owns its data — the cloud is optional transport. memorio synchronizes **operations** (`remember`, `update`, `forget`, `expire`, `confirm`, `supersede`), not database dumps. A local journal records operations so temporary network failures don't stop the application from working.
442
-
443
- ```ts
444
- memorio.memory.configure({
445
- namespace: 'user:123:device:abc',
446
- provider: {
447
- push: ops => fetch('/api/sync', { method: 'POST', body: JSON.stringify(ops) }),
448
- pull: since => fetch(`/api/sync?since=${since}`).then(r => r.json()),
449
- },
450
- auto: true,
451
- })
452
- ```
453
-
454
- ```text
455
- local application → memorio memory → local journal → offline / cloud
456
- ```
457
-
458
- The network is an extension of the local application, not its prerequisite.
459
-
460
- Remote operations are ordered causally using HLC timestamps. For concurrent writes to the same key, memorio provides a deterministic default — a custom resolver can override it:
461
-
462
- ```ts
463
- memorio.memory.configure({
464
- resolveConflict(local, remote) {
465
- if (remote.source === 'user-correction') return remote
466
- return local.confidence >= remote.confidence ? local : remote
467
- },
468
- })
469
- ```
470
-
471
- Without a resolver, the default policy applies the remote entry on a tie. The resolver operates client-side — the sync provider is still responsible for any final server-side conflict policy.
472
-
473
- ## Cross-platform support
474
-
475
- | API | Browser | Node.js | Deno | Edge / Workers |
476
- |---|:---:|:---:|:---:|:---:|
477
- | `state` / `observer` / `cache` | ✅ | ✅ | ✅ | ✅ |
478
- | `store` / `session` | persistent | memory fallback | memory fallback | capability-dependent |
479
- | `idb` | ✅ | ❌ | ❌ | capability-dependent |
480
- | `sqlite` | ✅ | ❌ | ❌ | capability-dependent |
481
- | `devtools` | dev-only | ❌ | ❌ | capability-dependent |
482
-
483
- ```ts
484
- memorio.getCapabilities()
485
- memorio.isBrowser() / memorio.isNode() / memorio.isDeno() / memorio.isEdge()
486
- ```
487
-
488
- Do not assume every persistence backend exists in every runtime.
489
-
490
- ## Security
491
-
492
- - Zero production dependencies — [checked by Socket.dev](https://socket.dev/npm/package/memorio) and [scanned by Snyk](https://snyk.io/test/npm/memorio)
493
- - No `eval`, no dynamic code execution, no bundled telemetry
494
- - Sanitized keys, validated inputs, caught module-boundary errors
495
- - Bounded journal entries, UUID-based session identifiers
496
-
497
- > ⚠️ **memorio does not encrypt data by default.** This applies to `state`, `store`, `session`, `idb`, `memory`, and `sqlite`. Treat browser storage as client-controlled data — if your application handles auth secrets, credentials, or regulated data, bring your own encryption, backend authorization, and server-side security controls.
498
-
499
- **Contexts are not security boundaries.** `memorio.createContext('tenant-123')` is useful for organizing and isolating application concerns — it is **not** an authorization mechanism. Code running inside the same JS runtime can potentially access other memorio contexts. Real tenant isolation belongs at the backend/authentication layer.
500
-
501
- memorio's DevTools are intended for development only, disabled via runtime detection (`process.env.NODE_ENV`) — not build-time removal. Make sure your bundler actually defines this correctly in production; don't treat DevTools absence as a security boundary.
502
-
503
- ## Honest limitations
81
+ ---
504
82
 
505
- memorio deliberately documents its edges.
83
+ ## Where to go next
84
+
85
+ ### Core
86
+ | Topic | Reference |
87
+ | --- | --- |
88
+ | Reactive state | [`markdown/STATE.md`](./markdown/STATE.md) |
89
+ | Persistent key/value store | [`markdown/STORE.md`](./markdown/STORE.md) |
90
+ | Observing changes | [`markdown/OBSERVER.md`](./markdown/OBSERVER.md) |
91
+ | Structured application memory | [`markdown/MEMORY.md`](./markdown/MEMORY.md) |
92
+
93
+ ### Storage
94
+ | Topic | Reference |
95
+ | --- | --- |
96
+ | Session storage (tab-scoped) | [`markdown/SESSION.md`](./markdown/SESSION.md) |
97
+ | In-memory cache | [`markdown/CACHE.md`](./markdown/CACHE.md) |
98
+ | IndexedDB | [`markdown/IDB.md`](./markdown/IDB.md) |
99
+ | SQLite (`sql.js` / `bun:sqlite`) | [`markdown/SQLITE.md`](./markdown/SQLITE.md) |
100
+
101
+ ### Sync & history
102
+ | Topic | Reference |
103
+ | --- | --- |
104
+ | Local-first sync & cloud | [`markdown/SYNC.md`](./markdown/SYNC.md) |
105
+ | Memory attachments (linking memory entries) | [`markdown/MEMORY-ATTACHMENT.md`](./markdown/MEMORY-ATTACHMENT.md) |
106
+ | History, undo/redo, snapshot, diff, trace | [`markdown/HISTORY.md`](./markdown/HISTORY.md) |
107
+
108
+ ### Integrations
109
+ | Topic | Reference |
110
+ | --- | --- |
111
+ | React (`useObserver`) | [`markdown/USEOBSERVER.md`](./markdown/USEOBSERVER.md) |
112
+ | Redux bridge | [`markdown/REDUX.md`](./markdown/REDUX.md) |
113
+ | Typed state | [`markdown/TYPED.md`](./markdown/TYPED.md) |
114
+ | Schema validation | [`markdown/SCHEMA.md`](./markdown/SCHEMA.md) |
115
+ | Pub/sub events outside React | [`markdown/DISPATCH.md`](./markdown/DISPATCH.md) |
116
+ | Console logging middleware | [`markdown/LOGGER.md`](./markdown/LOGGER.md) |
117
+
118
+ ### Ops & security
119
+ | Topic | Reference |
120
+ | --- | --- |
121
+ | Platform detection & multi-tenant context isolation | [`markdown/PLATFORM.md`](./markdown/PLATFORM.md) |
122
+ | Classic `import` vs `memorio/global` | [`markdown/IMPORT.md`](./markdown/IMPORT.md) |
123
+ | Runtime introspection | [`markdown/INSPECT.md`](./markdown/INSPECT.md) |
124
+ | Browser DevTools | [`markdown/DEVTOOLS.md`](./markdown/DEVTOOLS.md) |
125
+ | Security, encryption, threat model | [`SECURITY.md`](./SECURITY.md) |
126
+ | Internal self-assessment *(not a third-party audit)* | [`markdown/SELF-ASSESSMENT.md`](./markdown/SELF-ASSESSMENT.md) |
127
+
128
+ ### Other
129
+ | Topic | Reference |
130
+ | --- | --- |
131
+ | Version history | [`CHANGELOG.md`](./CHANGELOG.md) |
132
+ | Working examples - each file has a `Run:` comment at the top (typically `npx ts-node examples/<name>.ts`, or `npx ts-node --esm examples/<name>.tsx` for React examples) | [`examples/`](./examples/) |
133
+ | Architecture decisions | [`adr/`](./adr/) |
506
134
 
507
- - **Semantic memory is structured, not vector-based.** `memory.context()` uses structured ranking, not embedding similarity search.
508
- - **SQLite persistence is not incremental** — the current strategy serializes the complete database on flush; large datasets need deliberate batching.
509
- - **Contexts and namespaces are not authorization** — they organize application data, they don't replace authentication or backend authorization.
510
- - **Data is not encrypted automatically** — memorio doesn't pretend local persistence is secure storage.
511
- - **DevTools are runtime-controlled, not build-removed** — production builds should still configure `NODE_ENV` correctly.
135
+ ---
512
136
 
513
137
  ## When to use something else
514
138
 
515
- memorio is intentionally not everything.
139
+ - **A strict Redux-style architecture** - when you need action pipelines, extensive middleware, event-sourcing, or mature time-travel tooling. Memorio can integrate rather than replace - see [`markdown/REDUX.md`](./markdown/REDUX.md).
140
+ - **A server database** - for authoritative persistence, multi-user authorization, or server-side transactions.
141
+ - **A dedicated secrets manager** - for high-privilege credentials, encryption key management, or regulated data at rest. Memorio's encryption is client-side and key-management is your responsibility - see [`SECURITY.md`](./SECURITY.md).
142
+ - **A dedicated vector database** - for true embedding similarity search. Memorio manages structured memory (preferences, decisions, facts), not embeddings.
516
143
 
517
- - **Need strict Redux-style architecture** (action pipelines, reducer-based transitions, extensive middleware, time-travel debugging, event-sourcing)? memorio can integrate with such systems rather than replace them.
518
- - **Need a server database** (authoritative persistence, multi-user authorization, server-side transactions, backend-controlled access)? Use one — memorio doesn't provide it.
519
- - **Need true embedding similarity / vector search**? Pair a dedicated vector database with `memorio.memory`, which can still manage the lifecycle and metadata of the retrieved knowledge.
144
+ ---
520
145
 
521
146
  ## Design philosophy
522
147
 
523
- 1. **Local first** — the application stays useful when the network disappears.
524
- 2. **Persistence is optional** — start in memory, persist only when it provides value.
525
- 3. **The cloud is optional** — remote sync extends the local application; it does not define it.
526
- 4. **Use the right primitive** — don't put relational data in a key/value store, or semantic memory in ordinary state.
527
- 5. **Memory has meaning** — confidence, source, type, scope, TTL, tags, history, status. That's different from simply storing a value.
528
- 6. **Global access is a convenience, not a requirement** — both `import 'memorio'` and `import { state } from 'memorio'` are valid; memorio does not impose one architecture.
529
- 7. **Tell the truth about boundaries** — local data is not automatically secure, persistence is not authorization, structured memory is not automatically AI semantic search.
530
- 8. **Keep the common case tiny:**
531
- ```ts
532
- import 'memorio'
533
- state.value = 42
534
- ```
535
- Everything else is there when you need it.
536
-
537
- ## A mental model
538
-
539
- ```text
540
- memorio
541
- │
542
- ┌──────────────┼──────────────┐
543
- │ │ │
544
- runtime persistence memory
545
- │ │ │
546
- ┌─────┼─────┐ ┌────┼────┐ structured
547
- │ │ │ │ │ │ knowledge
548
- state cache session store idb sqlite
549
- │
550
- observer
551
- │
552
- ▼
553
- application → journal → optional sync → cloud
554
- ```
555
-
556
- The important part is not that memorio has many layers — it's that **you do not have to use them all**.
557
-
558
- ## Sneak Peek
148
+ 1. **Local first** - the app stays useful when the network disappears.
149
+ 2. **Persistence is optional** - start in memory, persist only when it adds value.
150
+ 3. **The cloud is optional** - sync extends the local app; it doesn't define it.
151
+ 4. **Use the right primitive** - relational data belongs in SQL, not a kv store; memory isn't the same as ordinary state.
152
+ 5. **Memory has meaning** - confidence, source, type, scope, TTL, tags, history, status: more than "just a value."
153
+ 6. **Memory belongs to whoever it's about** - structured and inspectable by default, encryptable with a key the vendor doesn't have to hold.
154
+ 7. **Global access is explicit** - opt in with `import 'memorio/global'`, or keep it explicit with named imports.
155
+ 8. **Tell the truth about boundaries** - encryption is opt-in (not default), contexts aren't authorization, and there's no embedding search unless paired with one.
156
+ 9. **Keep the common case tiny** - `state.value = 42` is a complete, valid program.
559
157
 
560
- ### Making a `state` slice persistent
561
-
562
- `state` is reactive but not persistent; `store` is persistent but not reactive. `persist()` bridges the two — it restores the value from `store` and then keeps the selected state path synchronized with it:
563
-
564
- ```ts
565
- import 'memorio'
566
-
567
- state.user = { name: 'Sara' }
568
-
569
- const off = persist('state.user') // restore from store, then persist changes
570
-
571
- // later, e.g. on component unmount
572
- off()
573
- ```
574
-
575
- `persist()` uses the same path syntax as `observer()`.
576
-
577
- Nested paths such as `state.user.preferences.theme` are supported, provided the intermediate objects already exist in `state`.
578
-
579
- In environments without `localStorage` (Node/Deno/edge), `store` falls back to memory. `persist()` still works there, but persistence does not survive process or runtime restarts.
158
+ ---
580
159
 
581
160
  ## License
582
161