memorio 4.7.3 → 4.8.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/README.md CHANGED
@@ -67,6 +67,9 @@ npm i memorio
67
67
 
68
68
  # Optional - only if you use the React hook
69
69
  npm i react react-dom
70
+
71
+ # Optional - only if you want a local sql.js for `sqlite` (default uses CDN)
72
+ npm i sql.js
70
73
  ```
71
74
 
72
75
  ---
@@ -160,6 +163,88 @@ const user = await idb.data.get('my-db', 'users', 1)
160
163
 
161
164
  > IndexedDB is a browser-only primitive - see [Cross-Platform Behavior](#cross-platform-behavior) for what happens off the browser.
162
165
 
166
+ ### `sqlite` - in-memory SQLite, zero config
167
+
168
+ `memorio.sqlite` is an optional, lazily-loaded SQLite engine backed by [`sql.js`](https://sql.js.org) (the optional `sql.js` npm package is **not** a build-time dependency). It runs entirely in memory — ideal for ad-hoc SQL, structured queryable data, and local relational lookups. The engine is fetched on first use and never loaded when you don't touch `sqlite`.
169
+
170
+ ```javascript
171
+ import 'memorio' // or: import { sqlite } from 'memorio'
172
+
173
+ await sqlite.ready // waits until the sql.js engine is loaded (or rejected)
174
+ sqlite.db.support() // true when the current platform can run sql.js
175
+ ```
176
+
177
+ Bases (databases) are addressed by name, like `idb`:
178
+ const SQL = await sqlite.db.getSQL() // the underlying sql.js engine (loaded once, cached)
179
+ sqlite.db.version() // engine version string, or null before load
180
+ ```
181
+
182
+ Bases (databases) are addressed by name, like `idb`. `sqlite.db.create('app')` opens (or creates) an in-memory database, while `sqlite.db.get('app')` retrieves an already-open handle (`sqlite.db.list/delete/size` round out the lifecycle):
183
+
184
+ ```javascript
185
+ await sqlite.db.create('app') // in-memory database handle
186
+ await sqlite.query.run('app', 'CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)')
187
+ ``` `sqlite.data` gives a small shortcut layer, and `sqlite.query` runs SQL:
188
+
189
+ ```javascript
190
+ // Write (shortcut over raw prepare/bind)
191
+ await sqlite.data.set('app', 'users', { id: 1, name: 'Sara' })
192
+ await sqlite.data.get('app', 'users', 1) // { id: 1, name: 'Sara' }
193
+
194
+ // SQL
195
+ await sqlite.query.run('app', 'CREATE TABLE items (id INTEGER PRIMARY KEY, name TEXT)')
196
+ const rows = await sqlite.query.select('app', 'SELECT * FROM items', [1])
197
+
198
+ // Portability: export / import a whole base as a binary blob
199
+ const blob = await sqlite.db.export('app')
200
+ await sqlite.db.import('app', blob)
201
+
202
+ // Clean up
203
+ await sqlite.db.delete('app')
204
+ ```
205
+
206
+ #### Loading the engine
207
+
208
+ The default loader injects the sql.js UMD build (`sql-wasm-browser.js`) from the
209
+ jsDelivr CDN as a classic `<script>`; on load it exposes the `initSqlJs` factory
210
+ on `globalThis`, which memorio then calls with your `locateFile`. This avoids a
211
+ bare `import('sql.js')` so bundlers never resolve the optional `sql.js`
212
+ dependency at build time:
213
+
214
+ ```javascript
215
+ // Zero config: CDN <script> loader (default)
216
+ await sqlite.ready
217
+ ```
218
+
219
+ If you prefer a local/bundled `sql.js` (offline, private network, or to pin a
220
+ version), install it and supply your own loader:
221
+
222
+ ```bash
223
+ npm i sql.js # optional peer
224
+ ```
225
+
226
+ ```javascript
227
+ import { sqlite } from 'memorio'
228
+
229
+ sqlite.config({ loader: () => import('sql.js') }) // local/npm build of sql.js
230
+ await sqlite.ready
231
+ ```
232
+
233
+ You can also point the WASM `locateFile` step at your own CDN/base via
234
+ `sqliteWasmBase` on `globalThis`, or pass a custom `locateFile` through
235
+ `sqlite.config({ locateFile })`:
236
+
237
+ ```javascript
238
+ globalThis.sqliteWasmBase = 'https://your-cdn.example.com/sql.js/dist/'
239
+ sqlite.config({ locateFile: (file) => `https://your-cdn.example.com/sql.js/dist/${file}` })
240
+ ```
241
+
242
+ When the engine can't load (e.g. no WASM support), `sqlite.ready` rejects, `sqlite._disabled` becomes `true`, and `sqlite._warning` explains why — `db.support()` simply returns `false` so you can degrade gracefully.
243
+
244
+ - `sqlite.config({ persistence: true })` / `sqlite.db.create('app', { persistence: true })` snapshot a database to `store` (localStorage, namespaced) and restore it on reopen — sql.js databases are in-memory by default; see [SQLite docs – Persistence & dev download](markdown/SQLITE.md#example-5-persistence--dev-download).
245
+
246
+ See [SQLite docs](markdown/SQLITE.md) for the full reference.
247
+
163
248
  ### `observer` / `useObserver` - watch a path, react to it
164
249
 
165
250
  ```javascript
@@ -403,6 +488,126 @@ See [Memory docs](markdown/MEMORY.md) for full reference.
403
488
 
404
489
  ---
405
490
 
491
+ ## Synchronization & Cloud (optional)
492
+
493
+ `memorio.memory` is **local-first**. The data is created and served from the
494
+ device; the cloud is only ever a **transport/persistence provider**, never the
495
+ source of truth. Enabling sync does not replace local storage — it *mirrors* it.
496
+
497
+ ```
498
+ memorio
499
+
500
+ ┌────────┴────────┐
501
+ │ Memory Engine │
502
+ └────────┬────────┘
503
+ ┌────────────┼────────────┐
504
+ ▼ ▼ ▼
505
+ local SQLite cloud
506
+ memory durable sync
507
+ ```
508
+
509
+ ### Configuring a backend
510
+
511
+ Sync is **opt-in**. You supply an application-owned `provider` that knows how to
512
+ talk to your backend (REST, WebSocket, Supabase, a custom agent server, a PostgreSQL
513
+ database, etc.). **Memorio never handles credentials** — authentication and
514
+ authorization live in your backend/provider (OWASP A01: Broken Access Control):
515
+
516
+ ```ts
517
+ memorio.memory.configure({
518
+ namespace: 'user:123:device:abc', // tenant/user/device — partitions the journal
519
+ provider: { // application-owned transport
520
+ push(ops) { return fetch('/api/sync', { method: 'POST', body: JSON.stringify(ops), headers: auth }) }
521
+ pull(since) { return fetch(`/api/sync?since=${since}`).then(r => r.json()) }
522
+ },
523
+ auto: true // auto-replay pending ops on focus/online (default true)
524
+ })
525
+
526
+ await memorio.memory.ready // waits until the local journal substrate is chosen
527
+ ```
528
+
529
+ Once configured, `memorio.memory` records every operation locally and keeps it
530
+ until the provider acknowledges it.
531
+
532
+ ### The local journal
533
+
534
+ Every mutation (`remember`, `update`, `forget`, `expire`, `confirm`, `supersede`)
535
+ is written to a local, **namespaced** operation journal:
536
+
537
+ ```ts
538
+ await memory.remember('user.language', 'Italian', { scope: 'local' })
539
+
540
+ memory.journal.pending() // -> [{ id, key:'user.language', value:'Italian', operation:'remember', sync:'pending', version: 1, updatedAt: ... }]
541
+ memory.journal.markSynced([id])
542
+ memory.journal.replay() // pushes pending() to provider.provider, marks synced, optional pull+apply
543
+ memory.journal.status() // 'sqlite' | 'store' (substrate in use)
544
+ ```
545
+
546
+ The journal stores the full entry plus `operation`, `version`, `updatedAt` and
547
+ `sync` status, so conflicts can be resolved when connectivity returns.
548
+
549
+ ### Substrates
550
+
551
+ The journal is persisted on `store` (localStorage / in-memory Map fallback) — **not** on a sql.js database, because sql.js databases are volatile (in-memory) and would lose pending operations on reload:
552
+
553
+ | Substrate | Used for | Notes |
554
+ |---|---|---|
555
+ | `store` (localStorage) | sync journal | persistent across reloads; namespaced; Map fallback in Node |
556
+ | `sqlite` (sql.js) | ad-hoc SQL / value storage | in-memory by default — use `persistence: true` to snapshot to `store` |
557
+ | `idb` (IndexedDB) | `memorio.memory` durable scope values | persistent; persistent across reloads |
558
+
559
+ The `sqlite.db.download(name)` dev helper also triggers a browser `.sqlite` download of an in-memory database on demand.
560
+
561
+ ### Sync = operations, not database dumps
562
+
563
+ Memorio syncronizza **operazioni di memoria**, never a raw database dump. When
564
+ two devices diverge, conflict resolution is based on `confidence`,
565
+ `lastConfirmedAt`, `source`, `version` and `scope` — not just "last write wins":
566
+
567
+ ```ts
568
+ // Device A: user.language = Italian, confidence 0.92
569
+ // Device B: user.language = English, confidence 0.61
570
+ // → the higher-confidence entry wins locally; the provider decides for shared.
571
+ ```
572
+
573
+ The `namespace` (tenant / user / device) is the single partition key: local
574
+ journal reads and writes are scoped to it, and they can never cross namespaces —
575
+ a client holding a fake/forged namespace simply sees its own empty journal.
576
+
577
+ ### Scopes (isolation, not a security boundary)
578
+
579
+ ```
580
+ scope: 'device' // only this browser/device
581
+ scope: 'user' // follows the user across devices (via sync)
582
+ scope: 'shared' // shared across users/tenant
583
+ ```
584
+
585
+ `scope: 'user'`/`'shared'` require a provider + namespace. `scope: 'device'`
586
+ is local only. As with `memorio.createContext`, **scoping is a naming convention,
587
+ not a security boundary** — enforce real isolation in your backend.
588
+
589
+ ### Security posture
590
+
591
+ - **NIST SP 800-53 / OWASP**: no credentials, tokens, or secrets are read from or
592
+ stored by `memorio`; sensitive state you place in `state`/`store`/`session`/`idb`
593
+ is not encrypted by memorio (see [Security](#security)).
594
+ - **Namespace isolation**: journal reads/writes are keyed by
595
+ `namespace:id` at the storage layer; there is no API to enumerate or open
596
+ another namespace's journal (defense-in-depth).
597
+ - **No dynamic code**: journal entries are strictly JSON-round-tripped and
598
+ size-capped (10 MB/entry); no `eval`/template-injection of provider data.
599
+ - **Trust boundary**: the provider/backend owns authentication, authorization,
600
+ and remote-side conflict resolution. Memorio owns the local durable copy and
601
+ the operation log; it surfaces `conflict`/`error` rows via `journal.pending()`.
602
+ - **NSA/CISA advice (data-at-rest/secrets)**: if you persist `state`/`store`
603
+ server-side or ship user data through your backend, encrypt it server-side with
604
+ keys you manage; memorio treats the local journal as untrusted-from-the-browser
605
+ and does not attest its own integrity.
606
+
607
+ See [SQLite docs](markdown/SQLITE.md), [Memory docs](markdown/MEMORY.md), and the [Synchronization & Cloud guide](markdown/SYNC.md).
608
+
609
+ ---
610
+
406
611
  ## Cross-Platform Behavior
407
612
 
408
613
  memorio runs everywhere JavaScript does - but "everywhere" means different guarantees in different places, and we'd rather tell you now than have you find out at 2am:
@@ -415,6 +620,7 @@ memorio runs everywhere JavaScript does - but "everywhere" means different guara
415
620
  | `store` | `localStorage` | `Map` fallback - **not durable across restarts** | `Map` fallback - not durable | `localStorage` where available, else `Map` |
416
621
  | `session` | `sessionStorage` | `Map` fallback - not durable | `Map` fallback - not durable | `sessionStorage` where available, else `Map` |
417
622
  | `idb` | ✅ `IndexedDB` | ❌ not available | ❌ not available | ⚠️ check `getCapabilities()` |
623
+ | `sqlite` | ✅ `sql.js` (lazy, CDN by default) | ❌ not available | ❌ not available | ⚠️ check `sqlite.db.support()` |
418
624
  | `devtools` | ✅ | ❌ | ❌ | ⚠️ |
419
625
 
420
626
  Same API top to bottom - that's the promise. But if your server code leans on `store.get(...)` surviving a redeploy, know that on Node/Deno it won't; the fallback is an in-memory cache with the same shape, not durable storage.
package/SUMMARY.md CHANGED
@@ -12,6 +12,7 @@
12
12
  * [Store](markdown/STORE.md) - Persistent localStorage management
13
13
  * [Session](markdown/SESSION.md) - Temporary sessionStorage management
14
14
  * [IDB](markdown/IDB.md) - IndexedDB for large data storage
15
+ * [SQLite](markdown/SQLITE.md) - SQLite in the browser via sql.js (WASM)
15
16
 
16
17
  ## Typed & Validated
17
18
 
@@ -26,6 +27,12 @@
26
27
  ## Memory System
27
28
 
28
29
  * [Memory](markdown/MEMORY.md) - Semantic memory layer with TTL, confidence, scopes
30
+ * [Node Attachment](markdown/MEMORY-ATTACHMENT.md) - Dynamic node attachment system
31
+
32
+ ## Experimental
33
+
34
+ * [Memory Substrates Benchmark](experimental/memory-substrates/BENCHMARK.md) - Protocol for evaluating runtime substrates
35
+ * [Leak Detection](experimental/memory-substrates/LEAK-DETECTION.md) - Tooling & methods for memory leak analysis
29
36
 
30
37
  ## Platform & Compatibility
31
38