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
@@ -0,0 +1,318 @@
1
+ > **Status:** Published
2
+ > **Date:** 2026-09-12
3
+ > **Scope**: API Reference
4
+ > **Standard**: Memorio API Specification v5
5
+ >
6
+ ---
7
+ # Synchronization & Cloud (optional)
8
+
9
+ `memorio.memory` is **local-first**. Data is created and served from the device;
10
+ the cloud is only ever a **transport/persistence provider**, never the source of
11
+ truth. Enabling sync does not replace local storage - it *mirrors* it.
12
+
13
+ ```
14
+ memorio
15
+ │
16
+ ┌────────┴────────┐
17
+ │ Memory Engine │
18
+ └────────┬────────┘
19
+ ┌────────────┼────────────┐
20
+ ▼ ▼ ▼
21
+ local SQLite cloud
22
+ memory durable sync
23
+ ```
24
+
25
+ ## 1. The rule: the data is born local
26
+
27
+ ```ts
28
+ memorio.memory.remember('user.language', 'Italian', { scope: 'local' })
29
+ // ↓ local first
30
+ // store / sessionStorage / IndexedDB / sql.js
31
+ // ↓ sync / push (when online)
32
+ // cloud provider
33
+ ```
34
+
35
+ The cloud therefore does not **replace** memory: it **replicates** it. This
36
+ gives you: offline-first, lowest latency, data available immediately,
37
+ synchronization when online, multi-device, multi-user, centralized persistence.
38
+
39
+ We deliberately do **not** provide:
40
+
41
+ ```ts
42
+ // ❌ two mental models
43
+ memory.cloud.save(...)
44
+ ```
45
+
46
+ Instead:
47
+
48
+ ```ts
49
+ memorio.memory.remember('user.language', 'Italian')
50
+ // and a single configuration point:
51
+ memorio.memory.configure({ sync: { provider: myCloudProvider, namespace: '…' } })
52
+ ```
53
+
54
+ ## 2. Scopes (isolation, not a security boundary)
55
+
56
+ | Scope | Lifetime | Syncs by default |
57
+ |---|---|---|
58
+ | `'device'` | this browser/device only | no (sticky) |
59
+ | `'user'` | follows the user across devices | yes (requires provider + namespace) |
60
+ | `'shared'` | shared across users / tenant | yes (requires provider + namespace) |
61
+
62
+ > As with `memorio.createContext`, **scoping is a naming convention, not a
63
+ > security boundary.** Enforce real isolation server-side.
64
+
65
+ ## 3. SQLite as the local durable store
66
+
67
+ SQLite (`sql.js`) is **in-memory by default** (volatie per page load). It becomes
68
+ the durable journal/value store when you opt in:
69
+
70
+ - `sqlite.config({ persistence: true })` / `sqlite.db.create('app', { persistence: true })`
71
+ snapshot the database to `store` (localStorage) and restore it on reopen.
72
+ - Writes are snapshotted via sql.js `updateHook` (debounced).
73
+ - `sqlite.db.persist(name)` forces an immediate save; `sqlite.db.close(name)`
74
+ flushes + closes; `sqlite.db.download(name, file?)` triggers a browser
75
+ `.sqlite` download (dev convenience).
76
+
77
+ See `docs/markdown/SQLITE.md` for the full SQLite reference.
78
+
79
+ ## 4. The local operation journal
80
+
81
+ The **sync journal** is the durable op log that drives cloud reconciliation.
82
+ It is persisted on `store` (localStorage) - **not** on an in-memory sql.js db,
83
+ because pending operations must survive a refresh for offline-first to work.
84
+
85
+ | Method | Returns | Notes |
86
+ |---|---|---|
87
+ | `memory.journal.append(entry, operation)` | `Promise<MemoryEntry>` | records `remember\|update\|forget\|expire\|confirm\|supersede` with `sync:'pending'` |
88
+ | `memory.journal.pending()` | `Promise<MemoryEntry[]>` | rows where `sync != 'synced'`, for the current namespace |
89
+ | `memory.journal.markSynced(ids)` | `Promise<number>` | advances rows to `synced` (namespace-scoped) |
90
+ | `memory.journal.get(id)` | `Promise<MemoryEntry \| null>` | single entry, namespace-scoped |
91
+ | `memory.journal.clear()` | `Promise<void>` | wipes the current namespace's journal |
92
+ | `memory.journal.replay()` | `Promise<SyncAck>` | pushes `pending()` to the provider, marks synced, optional `pull` |
93
+ | `memory.journal.status()` | `Promise<'store'>` | the substrate in use |
94
+
95
+ We sync **operations of memory**, never a raw database dump:
96
+
97
+ ```
98
+ user A device A
99
+ remember X ─────► local ─────► sync ─────► cloud
100
+ forget Z ──────► local ─────► sync ─────► cloud
101
+ ```
102
+
103
+ ## 5. Conflict resolution
104
+
105
+ The cloud must not simply say "last write wins." Memorio tags every entry with:
106
+
107
+ - `confidence` (0–1, user/system trust in the value)
108
+ - `lastConfirmedAt` / `updatedAt` (epoch ms)
109
+ - `version` (monotonic per-key counter)
110
+ - `source` / `scope`
111
+
112
+ Remote conflicts are surfaced as `sync:'conflict'` rows via
113
+ `journal.pending()`; the provider's `resolve(op)` hint decides locally. Example:
114
+
115
+ ```
116
+ Laptop: language=Italian, confidence=0.92
117
+ Phone: language=English, confidence=0.61
118
+ → higher-confidence entry wins locally; the provider decides for shared scope.
119
+ ```
120
+
121
+ ## 6. Configuring a backend
122
+
123
+ Sync is **opt-in**. You supply an application-owned `provider` that knows how to
124
+ talk to your backend (REST, WebSocket, Supabase, a custom agent server, …).
125
+
126
+ ```ts
127
+ memorio.memory.configure({
128
+ namespace: 'user:123:device:abc', // tenant/user/device - partitions the journal
129
+ provider: {
130
+ push(ops) { return fetch('/api/sync', { method: 'POST', body: JSON.stringify(ops), headers: authHeaders }) }
131
+ pull(since) { return fetch(`/api/sync?since=${since}`).then(r => r.json()) }
132
+ resolve(op) { return op.confidence >= 0.8 ? 'local' : 'remote' }
133
+ },
134
+ auto: true // auto-replay on focus/online (default true)
135
+ })
136
+ ```
137
+
138
+ ```ts
139
+ interface SyncProvider {
140
+ push(ops: MemoryEntry[]): Promise<{ synced: string[]; conflicts?: string[]; error?: string }>
141
+ pull?(since?: number): Promise<MemoryEntry[]>
142
+ resolve?(op: MemoryEntry): Promise<'local' | 'remote' | 'merge'>
143
+ }
144
+ ```
145
+
146
+ `memorio.memory.ready` resolves once the local journal substrate is chosen.
147
+
148
+ ## 7. Security (NIST / OWASP / NSA posture)
149
+
150
+ - **Memorio never handles credentials.** No passwords, tokens, or API keys are
151
+ read from or stored by memorio. Authentication/authorization live in your
152
+ `provider`/backend (OWASP A01: Broken Access Control).
153
+ - **Namespace isolation.** The journal is keyed by `namespace:id` at the storage
154
+ layer; there is **no API** to enumerate or open another namespace's journal. A
155
+ client holding a forged/fake namespace simply sees its own (empty) journal.
156
+ - **No dynamic code.** Journal entries are strictly JSON-round-tripped,
157
+ size-capped (10 MB/entry), and never `eval`'d. The sql.js loader never
158
+ `import()`s a bare specifier that could be hijacked at build time.
159
+ - **Trust boundary:** memorio owns the local durable copy + operation log; the
160
+ provider/backend owns remote-side auth and conflict resolution. Memorio
161
+ surfaces `conflict`/`error` rows; it does not fabricate a winner.
162
+ - **Data-at-rest (NSA/CISA).** memorio's `store`/`idb`/`sqlite` snapshots are
163
+ **not encrypted**. If you persist user data server-side or ship it through your
164
+ backend, encrypt it server-side with keys you manage - memorio treats the local
165
+ store as untrusted-from-the-browser and does not attest its own integrity.
166
+
167
+ ## 8. Where data lives
168
+
169
+ | Substrate | API | Volatile? | Persistent? |
170
+ |---|---|---|---|
171
+ | in-memory `Proxy` | `state` | yes (per tab) | no |
172
+ | `localStorage` / Map | `store` | no | yes (browser) |
173
+ | `sessionStorage` / Map | `session` | no | per-tab (browser) |
174
+ | IndexedDB | `idb`, `memory` durable | no | yes |
175
+ | sql.js (WASM heap) | `sqlite` | **yes** | only with `persistence: true` (snapshot → `store`) |
176
+ | sync journal | `memory.journal` | no | yes (`store`) |
177
+
178
+ ---
179
+
180
+ ## 9. Evolving the journal toward multi-device consistency
181
+
182
+ Moving from sequential, single-device sync to concurrent offline edits across
183
+ multiple devices is a classic local-first challenge. Below are six
184
+ architectural strategies, in increasing order of sophistication, that can be
185
+ layered onto the existing journal **without** adopting a full CRDT framework
186
+ (Yjs, Automerge, etc.).
187
+
188
+ > **Scope note.** These strategies target `memorio.memory` first - it already
189
+ > carries the metadata a journal needs (`confidence`, `source`, `tag`, `scope`,
190
+ > `createdAt`, `lastConfirmedAt`). If synchronization is ever extended to
191
+ > `state` or `store`, those layers must gain HLC timestamps and path-level
192
+ > fields explicitly - they cannot inherit them from `memory`.
193
+
194
+ ### 9.1 Field-level / path-level journaling
195
+
196
+ Recording an entire entity in the journal makes orthogonal edits un-mergeable:
197
+
198
+ ```json
199
+ { "op": "set", "path": "user.role", "value": "admin", "timestamp": 1710000000 }
200
+ ```
201
+
202
+ Device A writes `user.name`, device B writes `user.role` - both are patch
203
+ operations on the **same entity**. An entity-level journal would produce one
204
+ opaque `UPDATE user = {…}` and one write would clobber the other. Path-level
205
+ patches merge automatically because the paths are disjoint.
206
+
207
+ > **Not a panacea.** Path-level journaling merges edits to *different* fields.
208
+ > Two devices writing the **same** path concurrently still need explicit conflict
209
+ > resolution (Section 5). HLC tells you *when* the events happened; it does not
210
+ > tell you *which value wins* when events are truly concurrent on the same path.
211
+
212
+ ### 9.2 Causal ordering with Hybrid Logical Clocks (HLC)
213
+
214
+ Wall-clock timestamps alone fail under clock drift. Associate every journal
215
+ entry with an HLC that combines a physical component, a logical counter, and a
216
+ node identifier:
217
+
218
+ ```
219
+ hlc:1710000005:2:deviceB
220
+ ```
221
+
222
+ This gives constant-size causal ordering (vs. vector clocks, which grow with
223
+ the number of writers - problematic for a bounded journal). The sync engine can
224
+ then apply causally-dependent operations in order and invoke the conflict
225
+ resolver only for genuinely concurrent writes on the same path.
226
+
227
+ ### 9.3 Ordering collections without full OT - fractional indexing
228
+
229
+ Arrays and ordered lists are the hardest non-CRDT case. Numeric indices shift
230
+ when a peer inserts or deletes nearby. Two lightweight options:
231
+
232
+ 1. **Keyed collections** - treat list items as a `Map<id, value>` rather than a
233
+ positional array. No index renumbering needed.
234
+ 2. **Fractional indexing** - assign each element a sortable key between its
235
+ neighbours (e.g. `1.0`, `2.0` → insert at `1.5`). On repeated re-inserts
236
+ between the same pair, keys grow in length and should be rebalanced
237
+ periodically. Use a mature library (`fractional-indexing` on npm) rather than
238
+ reimplementing the arithmetic.
239
+
240
+ ### 9.4 Explicit deletions (tombstones)
241
+
242
+ A bare "remove" entry can be resurrected as a "zombie" when a concurrent
243
+ update is replayed after it. Instead, record every `forget` / `delete` as a
244
+ first-class journal event with its own HLC:
245
+
246
+ ```json
247
+ { "op": "delete", "path": "user.role", "timestamp": "hlc:1710000005:0:deviceA" }
248
+ ```
249
+
250
+ The tombstone participates in the same causal comparison as `set` events: if
251
+ the delete's HLC succeeds the update's, the field is gone; if it precedes, the
252
+ update is re-applied. Keep tombstones around until all known peers have
253
+ acknowledged them, then garbage-collect during a maintenance sweep.
254
+
255
+ ### 9.5 Hybrid sync - server-assisted consensus
256
+
257
+ Since memorio's cloud role is transport-and-acknowledgement (not source of
258
+ truth), the backend can resolve conflicts on the server and return the
259
+ canonical sequence:
260
+
261
+ | Pattern | Description |
262
+ |---|---|
263
+ | **Optimistic local apply** | Apply the local journal entry immediately and emit reactive events. |
264
+ | **Server ack** | Backend validates causal order against the central state and returns the official sequence. |
265
+ | **Client journal rebase** | Confirmed entries are purged from the local journal; unconfirmed local entries are replayed on top of the acknowledged state. |
266
+
267
+ ### 9.6 Validation - convergence simulation
268
+
269
+ Causal correctness is theoretical until you test it across replay orderings:
270
+
271
+ - Generate random concurrent `set` / `delete` operations across N simulated
272
+ devices (shared paths and disjoint paths).
273
+ - Replay the operation log in every plausible ordering on each simulated
274
+ device.
275
+ - Assert **state convergence**: every device arrives at the same final state
276
+ regardless of delivery order.
277
+ - Include hand-crafted pathological cases (`update` vs concurrent `delete` on
278
+ the same path, `insert` vs concurrent `delete` on the same array index,
279
+ interleaved reorders).
280
+
281
+ ### 9.7 Migration
282
+
283
+ Path-level journal entries with HLC and tombstones are a format change from
284
+ entity-level entries. Mitigate with:
285
+
286
+ - An explicit **version header** on every journal entry.
287
+ - A clear migration policy: either a one-time compaction pass that folds
288
+ legacy entity-level entries into the current state and starts a fresh
289
+ journal, or a dual-format reader that can replay both formats during the
290
+ transition window.
291
+
292
+ ### 9.8 Recommended architecture diagram
293
+
294
+ ```text
295
+ [ Local Mutation ]
296
+ │
297
+ ▼
298
+ [ Field-Level Patch (set/delete) + HLC Timestamp ]
299
+ │
300
+ ├───► Local State (immediate reactive update)
301
+ │
302
+ └───► Local Journal (incl. tombstones for deletes)
303
+ │
304
+ (Online Sync)
305
+ │
306
+ ▼
307
+ [ Backend Conflict Resolver ]
308
+ (concurrent path → resolveConflict;
309
+ otherwise apply in HLC order)
310
+ │
311
+ ▼
312
+ [ State Ack / Rebased Journal ]
313
+ ```
314
+
315
+ Applying granular path-level patches, HLC for causality, fractional indexing
316
+ for ordered collections, explicit tombstones for deletions, and a convergence
317
+ test suite lets memorio handle high-frequency concurrent offline edits across
318
+ devices without the overhead of a full CRDT stack.
@@ -0,0 +1,164 @@
1
+ > **Status:** Published
2
+ > **Date:** 2026-09-12
3
+ > **Scope**: API Reference
4
+ > **Standard**: Memorio API Specification v5
5
+ >
6
+ ---
7
+ # Typed Stores - Memorio
8
+
9
+ > ✅ **Universal**: Works in Browser, Node.js, Deno, and Edge Workers
10
+
11
+ `memorio.typed<T>()` returns the global `state` proxy cast to a TypeScript type `T`, giving you **compile-time** type safety on every access and mutation.
12
+
13
+ It's a **zero-runtime-cost** wrapper: the returned object is the *exact same* Proxy as `globalThis.state`, just with TypeScript types applied via a generic.
14
+
15
+ ---
16
+
17
+ ## Quick Start
18
+
19
+ ```typescript
20
+ import { memorio, state, typed, useObserver } from 'memorio'
21
+
22
+ interface AppState {
23
+ user: { name: string; age: number; email: string }
24
+ theme: 'light' | 'dark'
25
+ items: string[]
26
+ }
27
+
28
+ const app = memorio.typed<AppState>()
29
+
30
+ // Type-checked at compile time:
31
+ app.user = { name: 'Sara', age: 30, email: 'sara@test.com' }
32
+ app.theme = 'dark'
33
+
34
+ // TypeScript errors:
35
+ // app.user = { name: 42 } // age missing, name wrong type
36
+ // app.theme = 'purple' // not a valid literal
37
+ ```
38
+
39
+ ---
40
+
41
+ ## Why use typed stores?
42
+
43
+ | Without typed | With `memorio.typed<T>()` |
44
+ |---|---|
45
+ | `state.user = { name: 42 }` - runs silently, bug at runtime | `app.user = { name: 42 }` - TypeScript error at compile time |
46
+ | No autocomplete on `state.user.email` | Full IntelliSense: properties, types, method suggestions |
47
+ | Rename `user` to `profile` - no compiler warning anywhere | Every `app.user` access flagged as an error |
48
+ | AI-generated code lacks guardrails | AI gets autocomplete and type feedback inline |
49
+
50
+ ---
51
+
52
+ ## Combine with Schema Validation
53
+
54
+ Typed stores catch type errors at compile time; schema validation catches invalid values at runtime. Together they form a **defense-in-depth** strategy:
55
+
56
+ ```typescript
57
+ import { memorio, state } from 'memorio'
58
+
59
+ interface ProfileState {
60
+ profile: { bio: string; avatar?: string }
61
+ }
62
+
63
+ const app = memorio.typed<ProfileState>()
64
+
65
+ memorio.registerSchema('profile', {
66
+ type: 'object',
67
+ required: ['bio'],
68
+ properties: {
69
+ bio: { type: 'string', min: 1 },
70
+ avatar: { type: 'string' }
71
+ }
72
+ })
73
+
74
+ app.profile = { bio: 'Developer', avatar: 'pic.png' } // ✅ type + schema pass
75
+ app.profile = { avatar: 'pic.png' } // ❌ TypeScript: bio missing
76
+ // ❌ Runtime: bio required
77
+ ```
78
+
79
+ See [Schema Validation](SCHEMA.md) for runtime validation details.
80
+
81
+ ---
82
+
83
+ ## Named import variant
84
+
85
+ `typed` is also available as a named export if you prefer explicit dependencies:
86
+
87
+ ```typescript
88
+ import { typed } from 'memorio'
89
+
90
+ const app = typed<AppState>()
91
+ ```
92
+
93
+ The `memorio` namespace object is the same across both usage styles - named imports and the `memorio/global` entrypoint share the same runtime instances.
94
+
95
+ ---
96
+
97
+ ## Full API
98
+
99
+ | Method | Parameters | Returns | Description |
100
+ |--------|-----------|---------|-------------|
101
+ | `memorio.typed<T>()` | Generic type `T` | `T` | Returns the global `state` proxy cast to `T` |
102
+
103
+ The returned object shares the same identity as `globalThis.state`:
104
+
105
+ ```typescript
106
+ const app = memorio.typed<AppState>()
107
+ console.debug(app === state) // true - same Proxy instance
108
+ ```
109
+
110
+ ---
111
+
112
+ ## React + typed stores
113
+
114
+ Pair with the `useObserver` hook for type-safe, reactive React components:
115
+
116
+ ```tsx
117
+ import { memorio, state, useObserver } from 'memorio'
118
+ import { useReducer } from 'react'
119
+
120
+ interface AppState {
121
+ user: { name: string; age: number }
122
+ theme: 'light' | 'dark'
123
+ }
124
+
125
+ const app = memorio.typed<AppState>()
126
+
127
+ function UserProfile() {
128
+ const [, forceUpdate] = useReducer(x => x + 1, 0)
129
+
130
+ useObserver(forceUpdate, [state.user.name])
131
+
132
+ return (
133
+ <div>
134
+ <h1>{app.user.name}</h1>
135
+ <span>Theme: {app.theme}</span>
136
+ </div>
137
+ )
138
+ }
139
+ ```
140
+
141
+ ---
142
+
143
+ ## Best Practices
144
+
145
+ 1. **Define your AppState at the root** of your app and import it everywhere:
146
+
147
+ ```typescript
148
+ // types/app-state.ts
149
+ export interface AppState {
150
+ user: { name: string; email: string }
151
+ theme: 'light' | 'dark'
152
+ }
153
+ ```
154
+
155
+ ```typescript
156
+ // anywhere in your app
157
+ import { memorio } from 'memorio'
158
+ import type { AppState } from '../types/app-state'
159
+ const app = memorio.typed<AppState>()
160
+ ```
161
+
162
+ 2. **Layer schema validation on top** for runtime safety, especially for data coming from APIs or user input.
163
+
164
+ 3. **Use alongside `memorio.help()`** (via `import 'memorio/global'`) to list available globals during development.