memorio 5.0.0 โ 5.1.1
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/README.md +98 -435
- package/SECURITY.md +152 -42
- package/SUMMARY.md +1 -1
- package/adr/001-state-proxy-model.md +95 -96
- package/adr/002-observer-semantics.md +179 -180
- package/adr/003-deep-mutation-semantics.md +7 -8
- package/adr/004-array-mutation-semantics.md +127 -128
- package/adr/005-scheduler-contract.md +148 -149
- package/adr/006-context-isolation.md +91 -92
- package/adr/007-mutation-records.md +5 -6
- package/adr/008-transactions.md +6 -7
- package/adr/009-history-model.md +6 -7
- package/adr/README.md +46 -46
- package/adr/template.md +48 -49
- package/bin/cli.js +68 -0
- package/global.cjs +1462 -323
- package/global.js +1459 -324
- package/index.cjs +1462 -323
- package/index.d.ts +1 -0
- package/index.js +1459 -324
- package/llms.txt +42 -5
- package/markdown/AUDIT-REPORT.md +7 -8
- package/markdown/CACHE.md +190 -99
- package/markdown/DEVTOOLS.md +0 -1
- package/markdown/DISPATCH.md +0 -1
- package/markdown/HISTORY.md +0 -1
- package/markdown/IDB.md +0 -1
- package/markdown/IMPORT.md +0 -1
- package/markdown/INSPECT.md +0 -1
- package/markdown/LOGGER.md +0 -1
- package/markdown/MEMORY-ATTACHMENT.md +0 -1
- package/markdown/MEMORY.md +0 -1
- package/markdown/OBSERVER.md +0 -1
- package/markdown/PLATFORM.md +277 -271
- package/markdown/REDUX.md +54 -0
- package/markdown/SCHEMA.md +0 -1
- package/markdown/SESSION.md +0 -1
- package/markdown/SQLITE.md +0 -1
- package/markdown/STATE.md +0 -1
- package/markdown/STORE.md +0 -1
- package/markdown/SYNC.md +0 -1
- package/markdown/TYPED.md +0 -1
- package/markdown/USEOBSERVER.md +0 -1
- package/modules/redux.cjs +381 -10
- package/modules/redux.cjs.map +1 -1
- package/modules/redux.js +381 -10
- package/modules/redux.js.map +1 -1
- package/package.json +14 -2
- package/types/broadcast.d.ts +61 -0
- package/types/computed.d.ts +96 -0
- package/types/encryption.d.ts +129 -0
- package/types/exports.d.ts +9 -0
- package/types/memorio.d.ts +19 -12
- package/types/security.d.ts +67 -0
- package/types/session.d.ts +23 -5
- package/types/store.d.ts +19 -3
- package/vsix/memorio.vsix +0 -0
- package/markdown/CHANGELOG.md +0 -243
- package/markdown/PROJECT.md +0 -311
- package/markdown/SECURITY.md +0 -330
package/README.md
CHANGED
|
@@ -1,8 +1,26 @@
|
|
|
1
1
|
# ๐ง memorio
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+

|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/memorio)
|
|
6
|
+
[](./LICENSE)
|
|
7
|
+
[](https://bundlephobia.com/package/memorio)
|
|
8
|
+
[](https://socket.dev/npm/package/memorio)
|
|
9
|
+
[](https://snyk.io/test/npm/memorio)
|
|
10
|
+
[](#security)
|
|
11
|
+

|
|
12
|
+

|
|
13
|
+

|
|
14
|
+

|
|
15
|
+

|
|
16
|
+

|
|
17
|
+
[](#license)
|
|
18
|
+
|
|
19
|
+
<!--
|
|
20
|
+
[](https://github.com/GITHUB_ORG/GITHUB_REPO/actions)
|
|
21
|
+
-->
|
|
22
|
+
|
|
23
|
+
**The memory layer for AI agents and apps โ owned by the user, not the vendor.**
|
|
6
24
|
|
|
7
25
|
```ts
|
|
8
26
|
import 'memorio/global'
|
|
@@ -19,18 +37,20 @@ Just data, available where your application needs it.
|
|
|
19
37
|
|
|
20
38
|
---
|
|
21
39
|
|
|
22
|
-
## Why memorio
|
|
23
|
-
|
|
24
|
-
Most applications eventually need more than one kind of data: reactive state, persistent values, session data, caches, structured browser storage, local SQL, application memory, history, and optional sync.
|
|
40
|
+
## Why memorio
|
|
25
41
|
|
|
26
|
-
|
|
42
|
+
That's the whole API for the simple case. No provider tree, no boilerplate.
|
|
27
43
|
|
|
28
|
-
|
|
44
|
+
But real apps grow. Most AI-powered apps (and most apps in general) eventually need more than "just state" โ persistence, session data, caches, structured browser storage, local SQL, application memory, history, optional sync. Usually that means pulling in a different library โ and a different mental model โ for each. Memorio gives them one consistent runtime, without making the simple case complicated:
|
|
29
45
|
|
|
30
46
|
```ts
|
|
31
47
|
state.value = 42
|
|
32
48
|
```
|
|
33
49
|
|
|
50
|
+
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.
|
|
51
|
+
|
|
52
|
+
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).
|
|
53
|
+
|
|
34
54
|
Add persistence, memory, history, or sync only when your application actually needs them. The common case stays tiny; the runtime grows with you.
|
|
35
55
|
|
|
36
56
|
---
|
|
@@ -45,428 +65,113 @@ Zero production dependencies in core. Optional integrations (`react`, `sql.js`,
|
|
|
45
65
|
|
|
46
66
|
---
|
|
47
67
|
|
|
48
|
-
##
|
|
49
|
-
|
|
50
|
-
```ts
|
|
51
|
-
// Opt-in global access
|
|
52
|
-
import 'memorio/global'
|
|
53
|
-
state.user = { name: 'Sara' }
|
|
54
|
-
|
|
55
|
-
// Or explicit imports
|
|
56
|
-
import { state, store, memory } from 'memorio'
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
Memorio never puts APIs on `globalThis` automatically and never infers dev mode from `NODE_ENV` or your bundler - global access is always something you choose.
|
|
60
|
-
|
|
61
|
-
---
|
|
62
|
-
|
|
63
|
-
## The layers
|
|
64
|
-
|
|
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` | relational local SQL | Optional | No |
|
|
73
|
-
| `memory` | structured application memory | Yes | Optional |
|
|
74
|
-
| `journal` | local-first operation history | Yes | No |
|
|
75
|
-
|
|
76
|
-
You don't have to use all of them - start with `state`.
|
|
77
|
-
|
|
78
|
-
**Which one do I need?**
|
|
79
|
-
|
|
80
|
-
```text
|
|
81
|
-
Does the UI need to react automatically to changes?
|
|
82
|
-
yes โ state
|
|
83
|
-
no โ cache (runtime only) ยท session (this tab) ยท store (small persistent kv)
|
|
84
|
-
idb (structured browser data) ยท sqlite (relations/SQL) ยท memory (what the app should remember)
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
---
|
|
88
|
-
|
|
89
|
-
## Before / after
|
|
68
|
+
## Start here
|
|
90
69
|
|
|
91
|
-
|
|
92
|
-
// Without memorio
|
|
93
|
-
const [user, setUser] = useState(null)
|
|
94
|
-
useEffect(() => {
|
|
95
|
-
const saved = localStorage.getItem('user')
|
|
96
|
-
if (saved) setUser(JSON.parse(saved))
|
|
97
|
-
}, [])
|
|
98
|
-
useEffect(() => {
|
|
99
|
-
localStorage.setItem('user', JSON.stringify(user))
|
|
100
|
-
}, [user])
|
|
101
|
-
|
|
102
|
-
// With memorio
|
|
103
|
-
import { state, persist } from 'memorio'
|
|
104
|
-
state.user = { name: 'Sara' }
|
|
105
|
-
const off = persist('state.user') // restores + keeps in sync
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
---
|
|
109
|
-
|
|
110
|
-
## Persistent state
|
|
70
|
+
Three things cover most apps - reactive state, persistence, and observation:
|
|
111
71
|
|
|
112
72
|
```ts
|
|
113
|
-
import { state, persist } from 'memorio'
|
|
73
|
+
import { state, persist, observer } from 'memorio'
|
|
114
74
|
|
|
115
75
|
state.user = { name: 'Sara' }
|
|
116
|
-
const off = persist('state.user')
|
|
117
76
|
|
|
118
|
-
|
|
119
|
-
off()
|
|
120
|
-
```
|
|
77
|
+
persist('state.user')
|
|
121
78
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
---
|
|
126
|
-
|
|
127
|
-
## Observing changes
|
|
128
|
-
|
|
129
|
-
```ts
|
|
130
|
-
import { observer } from 'memorio'
|
|
131
|
-
|
|
132
|
-
const off = observer('state.user', (next, previous) => {
|
|
133
|
-
console.log('user changed:', next, previous)
|
|
79
|
+
observer('state.user', user => {
|
|
80
|
+
console.log(user)
|
|
134
81
|
})
|
|
135
|
-
|
|
136
|
-
off() // unsubscribe
|
|
137
82
|
```
|
|
138
83
|
|
|
139
|
-
|
|
140
|
-
The alternatives remove listeners by path name:
|
|
84
|
+
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:
|
|
141
85
|
|
|
142
86
|
```ts
|
|
143
|
-
|
|
144
|
-
observer.removeAll() // removes every observer at once
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
---
|
|
148
|
-
|
|
149
|
-
## Application memory
|
|
150
|
-
|
|
151
|
-
State stores what the application **has**. Memory stores what it **remembers**.
|
|
87
|
+
import { memory } from 'memorio'
|
|
152
88
|
|
|
153
|
-
|
|
154
|
-
import { memorio } from 'memorio'
|
|
155
|
-
|
|
156
|
-
await memorio.memory.remember('user.language', 'Italian', {
|
|
89
|
+
await memory.remember('user.language', 'Italian', {
|
|
157
90
|
type: 'preference',
|
|
158
91
|
confidence: 0.92,
|
|
159
|
-
scope: 'local',
|
|
160
|
-
tags: ['user', 'ui'],
|
|
161
92
|
source: 'conversation'
|
|
162
93
|
})
|
|
163
94
|
|
|
164
|
-
const language = await
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
Updating an entry doesn't overwrite it - the previous entry becomes `superseded`, the new one `active`:
|
|
168
|
-
|
|
169
|
-
```ts
|
|
170
|
-
await memorio.memory.update('user.language', 'English', { confidence: 0.95 })
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
Retrieve relevant memory instead of loading everything:
|
|
174
|
-
|
|
175
|
-
```ts
|
|
176
|
-
const context = await memorio.memory.context({
|
|
177
|
-
tags: 'user',
|
|
178
|
-
types: ['preference', 'decision'],
|
|
179
|
-
minConfidence: 0.7,
|
|
180
|
-
maxEntries: 10
|
|
181
|
-
})
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
> **Note:** "structured memory" here means metadata-based ranking (tags, type, confidence, recency) - not embedding similarity search. Pair `memorio.memory` with a dedicated vector store if you need that.
|
|
185
|
-
|
|
186
|
-
```text
|
|
187
|
-
sqlite โ what data do I have?
|
|
188
|
-
memory โ what does my application remember?
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
---
|
|
192
|
-
|
|
193
|
-
## The other layers, briefly
|
|
194
|
-
|
|
195
|
-
**`store`** - persistent key/value, backed by `localStorage` in browsers, memory elsewhere.
|
|
196
|
-
```ts
|
|
197
|
-
store.set('preferences', { theme: 'dark' })
|
|
198
|
-
const preferences = store.get('preferences')
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
**`session`** - scoped to the current browser session (`sessionStorage`). Good for wizard steps and tab-local state.
|
|
202
|
-
|
|
203
|
-
**`cache`** - volatile, in-memory, disappears when the runtime disappears.
|
|
204
|
-
|
|
205
|
-
**`idb`** - durable structured browser data:
|
|
206
|
-
```ts
|
|
207
|
-
await idb.db.create('app')
|
|
208
|
-
await idb.table.create('app', 'users')
|
|
209
|
-
await idb.data.set('app', 'users', { id: 1, name: 'Sara' })
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
**`sqlite`** - relational local SQL, in-memory by default via `sql.js`:
|
|
213
|
-
```ts
|
|
214
|
-
await sqlite.ready
|
|
215
|
-
await sqlite.db.create('app', { persistence: true })
|
|
216
|
-
await sqlite.query.run('app', `CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, role TEXT)`)
|
|
217
|
-
await sqlite.query.select('app', `SELECT * FROM users WHERE role = ?`, ['admin'])
|
|
218
|
-
```
|
|
219
|
-
> โ ๏ธ Persistence serializes the **entire database** on flush - fine for small/medium datasets, but large ones need deliberate batching.
|
|
220
|
-
|
|
221
|
-
Check what's actually available at runtime before depending on a backend: `memorio.getCapabilities()`.
|
|
222
|
-
|
|
223
|
-
---
|
|
224
|
-
|
|
225
|
-
## State Intelligence
|
|
226
|
-
|
|
227
|
-
With history tracking enabled, mutations are recorded with a unique ID, HLC timestamp, source, and causal context - the foundation for undo/redo, diff, trace, and (planned) replay.
|
|
228
|
-
|
|
229
|
-
```ts
|
|
230
|
-
import { memorio } from 'memorio'
|
|
231
|
-
|
|
232
|
-
memorio.enableHistory(true)
|
|
233
|
-
|
|
234
|
-
state.user = { name: 'John' }
|
|
235
|
-
state.user.name = 'Maria'
|
|
236
|
-
state.user.name = 'Pedro'
|
|
237
|
-
|
|
238
|
-
const history = memorio.trace()
|
|
239
|
-
memorio.undo()
|
|
240
|
-
memorio.redo()
|
|
241
|
-
|
|
242
|
-
memorio.canUndo()
|
|
243
|
-
memorio.canRedo()
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
**Explicit mutations & transactions:**
|
|
247
|
-
|
|
248
|
-
```ts
|
|
249
|
-
const mutation = memorio.mutate('state.user.role', 'admin', { source: 'permissions.enableAdmin' })
|
|
250
|
-
// mutation.id ยท mutation.hlc ยท mutation.source
|
|
251
|
-
|
|
252
|
-
const tx = memorio.transaction('user.migration', 'Migrate to v2 schema')
|
|
253
|
-
state.user.role = 'admin'
|
|
254
|
-
state.user.permissions = ['read', 'write']
|
|
255
|
-
memorio.commitTransaction()
|
|
256
|
-
|
|
257
|
-
// or abort and roll back
|
|
258
|
-
memorio.transaction('failed-op')
|
|
259
|
-
state.user.name = 'Maria'
|
|
260
|
-
memorio.abortTransaction()
|
|
261
|
-
```
|
|
262
|
-
|
|
263
|
-
**Roadmap:**
|
|
264
|
-
|
|
265
|
-
| Phase | Capability | Status |
|
|
266
|
-
| ----- | ---------------------------- | --------------------------------------------------- |
|
|
267
|
-
| 1 | Mutation Engine | โ
Active |
|
|
268
|
-
| 2 | Undo / Redo / Diff / Revert | โ
Partial (basic trace + undo/redo shipped above) |
|
|
269
|
-
| 3 | History Engine (persisted, queryable history) | Planned |
|
|
270
|
-
| 4 | Replay / Time Machine | Planned |
|
|
271
|
-
| 5 | Causal Graph | Planned |
|
|
272
|
-
| 6 | Explain / Trace / Impact | Planned |
|
|
273
|
-
| 7 | What-if Simulation | Planned |
|
|
274
|
-
|
|
275
|
-
---
|
|
276
|
-
|
|
277
|
-
## Typed state & validation
|
|
278
|
-
|
|
279
|
-
```ts
|
|
280
|
-
import { memorio } from 'memorio'
|
|
281
|
-
|
|
282
|
-
interface AppState {
|
|
283
|
-
user: { name: string; age: number; email: string }
|
|
284
|
-
theme: 'light' | 'dark'
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
const app = memorio.typed<AppState>()
|
|
288
|
-
app.theme = 'dark' // โ
|
|
289
|
-
app.theme = 'purple' // โ TypeScript error
|
|
290
|
-
|
|
291
|
-
app === state // same proxy, typed
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
Runtime schemas validate values crossing trust boundaries:
|
|
295
|
-
|
|
296
|
-
```ts
|
|
297
|
-
memorio.registerSchema('user', {
|
|
298
|
-
type: 'object',
|
|
299
|
-
required: ['name', 'email'],
|
|
300
|
-
properties: {
|
|
301
|
-
name: { type: 'string', min: 1 },
|
|
302
|
-
email: { type: 'string', pattern: /^[^@]+@[^@]+$/ }
|
|
303
|
-
}
|
|
304
|
-
})
|
|
305
|
-
|
|
306
|
-
memorio.registerSchema('tags', { type: 'array', items: { type: 'string' } })
|
|
307
|
-
|
|
308
|
-
state.tags = ['a', 'b'] // accepted
|
|
309
|
-
state.tags = [1, 2, 3] // rejected
|
|
95
|
+
const language = await memory.recall('user.language')
|
|
96
|
+
// { value: 'Italian', confidence: 0.92, source: 'conversation', ... }
|
|
310
97
|
```
|
|
311
98
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
## React integration
|
|
315
|
-
|
|
316
|
-
`useObserver` is a thin bridge over the observer engine. For the common React case,
|
|
317
|
-
wrap it in a tiny helper so components read state like any other value:
|
|
318
|
-
|
|
319
|
-
```tsx
|
|
320
|
-
import { useReducer } from 'react'
|
|
321
|
-
import { useObserver, state } from 'memorio'
|
|
322
|
-
|
|
323
|
-
// Subscribe to one or more state paths and re-render on change.
|
|
324
|
-
function useMemorioValue(path: any) {
|
|
325
|
-
const [, update] = useReducer((n: number) => n + 1, 0)
|
|
326
|
-
useObserver(update, path)
|
|
327
|
-
return path
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
function Counter() {
|
|
331
|
-
const counter = useMemorioValue(state.counter)
|
|
332
|
-
return <div>{counter}</div>
|
|
333
|
-
}
|
|
334
|
-
```
|
|
335
|
-
|
|
336
|
-
`useObserver` can also auto-discover dependencies โ pass a callback with no explicit
|
|
337
|
-
deps and it tracks every state path read inside it โ but the explicit-deps form above
|
|
338
|
-
is cheaper and easier to reason about at scale.
|
|
339
|
-
|
|
340
|
-
Memorio itself stays framework-independent - React is an integration, not a requirement.
|
|
99
|
+
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.
|
|
341
100
|
|
|
342
101
|
---
|
|
343
102
|
|
|
344
|
-
##
|
|
103
|
+
## Where to go next
|
|
345
104
|
|
|
346
|
-
|
|
105
|
+
### Core
|
|
106
|
+
| Topic | Reference |
|
|
107
|
+
| --- | --- |
|
|
108
|
+
| Reactive state | [`markdown/STATE.md`](./markdown/STATE.md) |
|
|
109
|
+
| Persistent key/value store | [`markdown/STORE.md`](./markdown/STORE.md) |
|
|
110
|
+
| Observing changes | [`markdown/OBSERVER.md`](./markdown/OBSERVER.md) |
|
|
111
|
+
| Structured application memory | [`markdown/MEMORY.md`](./markdown/MEMORY.md) |
|
|
347
112
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
type: 'preference',
|
|
356
|
-
scope: 'local',
|
|
357
|
-
tags: ['app', 'user']
|
|
358
|
-
},
|
|
359
|
-
'user.name': state => state.user.name
|
|
360
|
-
},
|
|
361
|
-
whitelist: ['user/preferencesChanged', 'user/nameChanged'],
|
|
362
|
-
debug: true
|
|
363
|
-
})
|
|
113
|
+
### Storage
|
|
114
|
+
| Topic | Reference |
|
|
115
|
+
| --- | --- |
|
|
116
|
+
| Session storage (tab-scoped) | [`markdown/SESSION.md`](./markdown/SESSION.md) |
|
|
117
|
+
| In-memory cache | [`markdown/CACHE.md`](./markdown/CACHE.md) |
|
|
118
|
+
| IndexedDB | [`markdown/IDB.md`](./markdown/IDB.md) |
|
|
119
|
+
| SQLite (`sql.js` / `bun:sqlite`) | [`markdown/SQLITE.md`](./markdown/SQLITE.md) |
|
|
364
120
|
|
|
365
|
-
|
|
121
|
+
### Sync & history
|
|
122
|
+
| Topic | Reference |
|
|
123
|
+
| --- | --- |
|
|
124
|
+
| Local-first sync & cloud | [`markdown/SYNC.md`](./markdown/SYNC.md) |
|
|
125
|
+
| Memory attachments (linking memory entries) | [`markdown/MEMORY-ATTACHMENT.md`](./markdown/MEMORY-ATTACHMENT.md) |
|
|
126
|
+
| History, undo/redo, snapshot, diff, trace | [`markdown/HISTORY.md`](./markdown/HISTORY.md) |
|
|
366
127
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
The bridge persists only mapped selectors, never mirrors the full tree, coalesces bursts, and never breaks Redux dispatch if Memorio fails.
|
|
128
|
+
### Integrations
|
|
129
|
+
| Topic | Reference |
|
|
130
|
+
| --- | --- |
|
|
131
|
+
| React (`useObserver`) | [`markdown/USEOBSERVER.md`](./markdown/USEOBSERVER.md) |
|
|
132
|
+
| Redux bridge | [`markdown/REDUX.md`](./markdown/REDUX.md) |
|
|
133
|
+
| Typed state | [`markdown/TYPED.md`](./markdown/TYPED.md) |
|
|
134
|
+
| Schema validation | [`markdown/SCHEMA.md`](./markdown/SCHEMA.md) |
|
|
135
|
+
| Pub/sub events outside React | [`markdown/DISPATCH.md`](./markdown/DISPATCH.md) |
|
|
136
|
+
| Console logging middleware | [`markdown/LOGGER.md`](./markdown/LOGGER.md) |
|
|
378
137
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
|
382
|
-
|
|
|
383
|
-
|
|
|
384
|
-
|
|
|
385
|
-
|
|
|
386
|
-
|
|
|
387
|
-
|
|
|
138
|
+
### Ops & security
|
|
139
|
+
| Topic | Reference |
|
|
140
|
+
| --- | --- |
|
|
141
|
+
| Platform detection & multi-tenant context isolation | [`markdown/PLATFORM.md`](./markdown/PLATFORM.md) |
|
|
142
|
+
| Classic `import` vs `memorio/global` | [`markdown/IMPORT.md`](./markdown/IMPORT.md) |
|
|
143
|
+
| Runtime introspection | [`markdown/INSPECT.md`](./markdown/INSPECT.md) |
|
|
144
|
+
| Browser DevTools | [`markdown/DEVTOOLS.md`](./markdown/DEVTOOLS.md) |
|
|
145
|
+
| Security, encryption, threat model | [`SECURITY.md`](./SECURITY.md) |
|
|
146
|
+
| Internal self-assessment *(not a third-party audit)* | [`markdown/SELF-ASSESSMENT.md`](./markdown/SELF-ASSESSMENT.md) |
|
|
388
147
|
|
|
389
|
-
|
|
148
|
+
### Other
|
|
149
|
+
| Topic | Reference |
|
|
150
|
+
| --- | --- |
|
|
151
|
+
| Version history | [`CHANGELOG.md`](./CHANGELOG.md) |
|
|
152
|
+
| 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/) |
|
|
153
|
+
| Architecture decisions | [`adr/`](./adr/) |
|
|
390
154
|
|
|
391
155
|
---
|
|
392
156
|
|
|
393
|
-
##
|
|
157
|
+
## Honest limitations
|
|
394
158
|
|
|
395
|
-
Memorio
|
|
159
|
+
Memorio is upfront about what it doesn't do, so you don't find out the hard way:
|
|
396
160
|
|
|
397
|
-
|
|
398
|
-
memorio.
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
pull: since => fetch(`/api/sync?since=${since}`).then(r => r.json())
|
|
403
|
-
},
|
|
404
|
-
auto: true
|
|
405
|
-
})
|
|
406
|
-
```
|
|
407
|
-
|
|
408
|
-
Concurrent operations use HLC ordering by default; override with a custom resolver:
|
|
409
|
-
|
|
410
|
-
```ts
|
|
411
|
-
memorio.memory.configure({
|
|
412
|
-
resolveConflict(local, remote) {
|
|
413
|
-
if (remote.source === 'user-correction') return remote
|
|
414
|
-
return local.confidence >= remote.confidence ? local : remote
|
|
415
|
-
}
|
|
416
|
-
})
|
|
417
|
-
```
|
|
418
|
-
|
|
419
|
-
The sync provider is still responsible for the final server-side conflict policy.
|
|
420
|
-
|
|
421
|
-
---
|
|
422
|
-
|
|
423
|
-
## Cross-platform support
|
|
424
|
-
|
|
425
|
-
| API | Browser | Node.js | Deno | Edge / Workers |
|
|
426
|
-
| -------------------------------- | :--------: | :---------------: | :----------------: | :---------------------: |
|
|
427
|
-
| `state` / `observer` / `cache` | โ
| โ
| โ
| โ
|
|
|
428
|
-
| `store` / `session` | persistent | memory fallback | memory fallback | capability-dependent |
|
|
429
|
-
| `idb` | โ
| โ | โ | capability-dependent |
|
|
430
|
-
| `sqlite` | โ
| โ | โ | capability-dependent |
|
|
431
|
-
| `memory` / `journal` | โ
| โ
| โ
| โ
|
|
|
432
|
-
| `devtools` | dev-only | โ | โ | capability-dependent |
|
|
433
|
-
|
|
434
|
-
```ts
|
|
435
|
-
memorio.getCapabilities()
|
|
436
|
-
memorio.isBrowser() // .isNode() ยท .isDeno() ยท .isEdge()
|
|
437
|
-
```
|
|
438
|
-
|
|
439
|
-
Don't assume every persistence backend exists in every runtime - check first.
|
|
440
|
-
|
|
441
|
-
> **SQLite on Node.js / Deno:** Memorio's `sqlite` is backed by `sql.js` (SQLite compiled to WebAssembly), which requires browser APIs (`window`, `document`, `WebAssembly`). It is not available in Node.js or Deno. For server-side SQL, pair Memorio with a native SQLite package directly; use `store` or `idb` for in-process persistence from Memorio.
|
|
442
|
-
|
|
443
|
-
---
|
|
444
|
-
|
|
445
|
-
## Security
|
|
446
|
-
|
|
447
|
-
Core has zero production dependencies. No `eval`, no dynamic code execution, no bundled telemetry, sanitized keys, validated inputs, bounded journal entries, UUID-based session identifiers. Independently checkable via Socket.dev / Snyk.
|
|
448
|
-
|
|
449
|
-
> โ ๏ธ **Memorio does not encrypt data by default** - this applies to every layer (`state`, `store`, `session`, `idb`, `memory`, `sqlite`). Treat browser storage as client-controlled. Use proper encryption and backend authorization for credentials or regulated data.
|
|
450
|
-
|
|
451
|
-
**Contexts are not authorization:**
|
|
452
|
-
|
|
453
|
-
```ts
|
|
454
|
-
memorio.createContext('tenant-123')
|
|
455
|
-
```
|
|
456
|
-
|
|
457
|
-
Contexts organize and isolate application concerns - they're not a security boundary. Code in the same JS runtime can potentially reach other contexts. Real tenant isolation belongs at the auth/backend layer.
|
|
458
|
-
|
|
459
|
-
DevTools expose data for development only, and only when you opt into `memorio/global`.
|
|
161
|
+
- **No encryption by default.** Data is stored in plain text unless you explicitly turn on `memorio.encryption` or pass an `encryptionKey`. See [`SECURITY.md`](./SECURITY.md).
|
|
162
|
+
- **Namespaces/contexts aren't a security boundary.** `memorio.isolate()` gives you logical separation between tenants or sessions, not authorization or access control โ enforce that at your application layer.
|
|
163
|
+
- **SQLite persistence (`sql.js` backend) isn't incremental.** Each write exports and re-saves the whole database, which gets costly as data grows. The `bun:sqlite` backend avoids this cost but doesn't support `export()` at all โ see [`markdown/SQLITE.md`](./markdown/SQLITE.md).
|
|
164
|
+
- **`memory.context()` is rule-based, not embedding-based.** It ranks structured entries by recency, confidence, and type โ it does not do semantic similarity search. Pair it with a vector store if your agent needs "find things like this."
|
|
165
|
+
- **Key management is your responsibility.** Memorio's encryption is client-side; it doesn't manage, rotate, or store keys for you.
|
|
460
166
|
|
|
461
167
|
---
|
|
462
168
|
|
|
463
169
|
## When to use something else
|
|
464
170
|
|
|
465
|
-
- **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.
|
|
171
|
+
- **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).
|
|
466
172
|
- **A server database** - for authoritative persistence, multi-user authorization, or server-side transactions.
|
|
467
|
-
- **A dedicated
|
|
468
|
-
|
|
469
|
-
Memorio occupies a distinct space: **application state + history + causality + replay + impact analysis + simulation**. If you need to know *why* state changed, *who* changed it, or *what would happen* if it changed, Memorio is the tool - not just a state container.
|
|
173
|
+
- **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).
|
|
174
|
+
- **A dedicated vector database** - for true embedding similarity search. Memorio manages structured memory (preferences, decisions, facts), not embeddings.
|
|
470
175
|
|
|
471
176
|
---
|
|
472
177
|
|
|
@@ -477,52 +182,10 @@ Memorio occupies a distinct space: **application state + history + causality + r
|
|
|
477
182
|
3. **The cloud is optional** - sync extends the local app; it doesn't define it.
|
|
478
183
|
4. **Use the right primitive** - relational data belongs in SQL, not a kv store; memory isn't the same as ordinary state.
|
|
479
184
|
5. **Memory has meaning** - confidence, source, type, scope, TTL, tags, history, status: more than "just a value."
|
|
480
|
-
6. **
|
|
481
|
-
7. **
|
|
482
|
-
8. **
|
|
483
|
-
|
|
484
|
-
---
|
|
485
|
-
|
|
486
|
-
## Quick recipes
|
|
487
|
-
|
|
488
|
-
```ts
|
|
489
|
-
// Global state
|
|
490
|
-
import 'memorio/global'
|
|
491
|
-
state.user = { name: 'Sara', role: 'admin' }
|
|
492
|
-
|
|
493
|
-
// Explicit state
|
|
494
|
-
import { state } from 'memorio'
|
|
495
|
-
state.counter++
|
|
496
|
-
|
|
497
|
-
// Persistent kv
|
|
498
|
-
import { store } from 'memorio'
|
|
499
|
-
store.set('preferences', { theme: 'dark' })
|
|
500
|
-
|
|
501
|
-
// Persistent reactive state
|
|
502
|
-
import { state, persist } from 'memorio'
|
|
503
|
-
state.user = { name: 'Sara' }
|
|
504
|
-
const off = persist('state.user')
|
|
505
|
-
|
|
506
|
-
// Application memory
|
|
507
|
-
import { memorio } from 'memorio'
|
|
508
|
-
await memorio.memory.remember('user.language', 'Italian', {
|
|
509
|
-
type: 'preference', confidence: 0.92, source: 'conversation'
|
|
510
|
-
})
|
|
511
|
-
|
|
512
|
-
// Observe a value
|
|
513
|
-
import { observer } from 'memorio'
|
|
514
|
-
const off = observer('state.user', (next, previous) => console.log(next, previous))
|
|
515
|
-
|
|
516
|
-
// Track mutations, undo, and trace history
|
|
517
|
-
import { memorio } from 'memorio'
|
|
518
|
-
memorio.enableHistory(true)
|
|
519
|
-
memorio.mutate('state.user.role', 'admin', { source: 'permissions.save' })
|
|
520
|
-
const tx = memorio.transaction('migration', 'v2 schema migration')
|
|
521
|
-
state.user.v2 = true
|
|
522
|
-
memorio.commitTransaction()
|
|
523
|
-
memorio.undo()
|
|
524
|
-
console.debug(memorio.trace())
|
|
525
|
-
```
|
|
185
|
+
6. **Memory belongs to whoever it's about** - structured and inspectable by default, encryptable with a key the vendor doesn't have to hold.
|
|
186
|
+
7. **Global access is explicit** - opt in with `import 'memorio/global'`, or keep it explicit with named imports.
|
|
187
|
+
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. (See [Honest limitations](#honest-limitations) above.)
|
|
188
|
+
9. **Keep the common case tiny** - `state.value = 42` is a complete, valid program.
|
|
526
189
|
|
|
527
190
|
---
|
|
528
191
|
|