memorio 4.9.35 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +3 -3
- package/README.md +307 -359
- package/SECURITY.md +17 -1
- package/SUMMARY.md +59 -45
- package/adr/001-state-proxy-model.md +96 -0
- package/adr/002-observer-semantics.md +180 -0
- package/adr/003-deep-mutation-semantics.md +129 -0
- package/adr/004-array-mutation-semantics.md +128 -0
- package/adr/005-scheduler-contract.md +149 -0
- package/adr/006-context-isolation.md +92 -0
- package/adr/007-mutation-records.md +118 -0
- package/adr/008-transactions.md +106 -0
- package/adr/009-history-model.md +110 -0
- package/adr/README.md +46 -0
- package/adr/template.md +49 -0
- package/examples/basic.ts +115 -115
- package/examples/browser-vanilla.html +358 -358
- package/examples/cache.ts +72 -72
- package/examples/cross-platform-guards.ts +57 -57
- package/examples/history.ts +104 -0
- package/examples/idb.ts +109 -109
- package/examples/multi-tenant-context.ts +44 -44
- package/examples/node-server.ts +308 -308
- package/examples/observer.ts +60 -60
- package/examples/platform.ts +115 -115
- package/examples/react-app.tsx +362 -362
- package/examples/react-observer.tsx +63 -63
- package/examples/semantic-memory.ts +60 -60
- package/examples/session-advanced.ts +91 -91
- package/examples/sqlite-batched-writes.ts +57 -57
- package/examples/state-advanced.ts +89 -89
- package/examples/store-advanced.ts +117 -117
- package/examples/sync.ts +90 -0
- package/examples/typed-and-schema.ts +102 -100
- package/examples/useObserver.tsx +140 -141
- package/global.cjs +4594 -0
- package/global.d.ts +8 -0
- package/global.js +4532 -0
- package/index.cjs +700 -678
- package/index.d.ts +1 -0
- package/index.js +680 -677
- package/llms.txt +72 -4
- package/markdown/AUDIT-REPORT.md +135 -0
- package/markdown/CACHE.md +100 -0
- package/markdown/CHANGELOG.md +243 -0
- package/markdown/DEVTOOLS.md +129 -0
- package/markdown/DISPATCH.md +177 -0
- package/markdown/HISTORY.md +199 -0
- package/markdown/IDB.md +178 -0
- package/markdown/IMPORT.md +153 -0
- package/markdown/INSPECT.md +123 -0
- package/markdown/LOGGER.md +154 -0
- package/markdown/MEMORY-ATTACHMENT.md +96 -0
- package/markdown/MEMORY.md +162 -0
- package/markdown/OBSERVER.md +209 -0
- package/markdown/PLATFORM.md +271 -0
- package/markdown/PROJECT.md +311 -0
- package/markdown/SCHEMA.md +176 -0
- package/markdown/SECURITY.md +330 -0
- package/markdown/SESSION.md +165 -0
- package/markdown/SQLITE.md +190 -0
- package/markdown/STATE.md +160 -0
- package/markdown/STORE.md +171 -0
- package/markdown/SYNC.md +319 -0
- package/markdown/TYPED.md +165 -0
- package/markdown/USEOBSERVER.md +257 -0
- package/modules/redux.cjs +320 -167
- package/modules/redux.cjs.map +1 -1
- package/modules/redux.js +320 -167
- package/modules/redux.js.map +1 -1
- package/package.json +13 -3
- package/types/env.d.ts +19 -9
- package/types/exports.d.ts +20 -0
- package/types/history.d.ts +13 -1
- package/types/memorio.d.ts +17 -5
- package/types/mutation.d.ts +75 -0
|
@@ -1,100 +1,102 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MEMORIO TYPED STORES + SCHEMA VALIDATION - LIVE EXAMPLES
|
|
3
|
-
*
|
|
4
|
-
* This file demonstrates every code snippet used in docs/markdown/SCHEMA.md
|
|
5
|
-
* and docs/markdown/TYPED.md. It is real, compiling TypeScript.
|
|
6
|
-
*
|
|
7
|
-
* Primary usage:
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* @module memorio/examples/typed-and-schema
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import 'memorio'
|
|
14
|
-
|
|
15
|
-
// =============================================================================
|
|
16
|
-
// TYPED STORES - compile-time type safety on the
|
|
17
|
-
// =============================================================================
|
|
18
|
-
|
|
19
|
-
interface AppState {
|
|
20
|
-
user: { name: string; age: number; email: string }
|
|
21
|
-
theme: 'light' | 'dark'
|
|
22
|
-
items: string[]
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const app = memorio.typed<AppState>()
|
|
26
|
-
|
|
27
|
-
app.user = { name: 'Sara', age: 30, email: 'sara@test.com' }
|
|
28
|
-
app.theme = 'dark'
|
|
29
|
-
app.items = ['apple', 'banana']
|
|
30
|
-
|
|
31
|
-
state.user.name = 'Luigi'
|
|
32
|
-
state.counter = 100
|
|
33
|
-
const currentTheme: 'light' | 'dark' = app.theme
|
|
34
|
-
void currentTheme
|
|
35
|
-
|
|
36
|
-
// =============================================================================
|
|
37
|
-
// SCHEMA VALIDATION - runtime guards that reject invalid state writes
|
|
38
|
-
// =============================================================================
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
memorio.registerSchema('
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
return
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
state.
|
|
69
|
-
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
state.
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
memorio.
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
// =============================================================================
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
1
|
+
/**
|
|
2
|
+
* MEMORIO TYPED STORES + SCHEMA VALIDATION - LIVE EXAMPLES
|
|
3
|
+
*
|
|
4
|
+
* This file demonstrates every code snippet used in docs/markdown/SCHEMA.md
|
|
5
|
+
* and docs/markdown/TYPED.md. It is real, compiling TypeScript.
|
|
6
|
+
*
|
|
7
|
+
* Primary usage: explicit named imports give you both the APIs and the
|
|
8
|
+
* `memorio` namespace object without polluting `globalThis`.
|
|
9
|
+
*
|
|
10
|
+
* @module memorio/examples/typed-and-schema
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { memorio, state } from 'memorio'
|
|
14
|
+
|
|
15
|
+
// =============================================================================
|
|
16
|
+
// TYPED STORES - compile-time type safety on the state proxy
|
|
17
|
+
// =============================================================================
|
|
18
|
+
|
|
19
|
+
interface AppState {
|
|
20
|
+
user: { name: string; age: number; email: string }
|
|
21
|
+
theme: 'light' | 'dark'
|
|
22
|
+
items: string[]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const app = memorio.typed<AppState>()
|
|
26
|
+
|
|
27
|
+
app.user = { name: 'Sara', age: 30, email: 'sara@test.com' }
|
|
28
|
+
app.theme = 'dark'
|
|
29
|
+
app.items = ['apple', 'banana']
|
|
30
|
+
|
|
31
|
+
state.user.name = 'Luigi'
|
|
32
|
+
state.counter = 100
|
|
33
|
+
const currentTheme: 'light' | 'dark' = app.theme
|
|
34
|
+
void currentTheme
|
|
35
|
+
|
|
36
|
+
// =============================================================================
|
|
37
|
+
// SCHEMA VALIDATION - runtime guards that reject invalid state writes
|
|
38
|
+
// =============================================================================
|
|
39
|
+
|
|
40
|
+
// `app` shares the same identity as `state` - same Proxy instance
|
|
41
|
+
|
|
42
|
+
memorio.registerSchema('user', {
|
|
43
|
+
type: 'object',
|
|
44
|
+
required: ['name', 'email'],
|
|
45
|
+
properties: {
|
|
46
|
+
name: { type: 'string', min: 1 },
|
|
47
|
+
email: { type: 'string', pattern: /^[^@]+@[^@]+$/ },
|
|
48
|
+
age: { type: 'number', min: 0, max: 150 }
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
memorio.registerSchema('theme', { enum: ['light', 'dark'] })
|
|
53
|
+
|
|
54
|
+
memorio.registerSchema('items', {
|
|
55
|
+
type: 'array',
|
|
56
|
+
validator: (val: any) => Array.isArray(val) && val.every((i: any) => typeof i === 'string')
|
|
57
|
+
? true
|
|
58
|
+
: 'items must be an array of strings'
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
memorio.registerSchema('counter', (val: any) => {
|
|
62
|
+
if (typeof val !== 'number') return 'counter must be a number'
|
|
63
|
+
if (val < 0) return 'counter must be >= 0'
|
|
64
|
+
return true
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
// These writes are rejected at runtime:
|
|
68
|
+
state.user = { name: 'Sara' } // missing 'email' - rejected
|
|
69
|
+
state.theme = 'purple' // not in enum - rejected
|
|
70
|
+
state.counter = -5 // fails custom validator - rejected
|
|
71
|
+
|
|
72
|
+
// These pass:
|
|
73
|
+
state.user = { name: 'Sara', email: 'sara@test.com', age: 30 }
|
|
74
|
+
state.theme = 'light'
|
|
75
|
+
state.counter = 5
|
|
76
|
+
|
|
77
|
+
// Manual validation:
|
|
78
|
+
memorio.validate('user', { name: 'Sara', email: 'sara@test.com' })
|
|
79
|
+
memorio.validate('theme', 'dark')
|
|
80
|
+
|
|
81
|
+
memorio.listSchemas()
|
|
82
|
+
memorio.unregisterSchema('counter')
|
|
83
|
+
|
|
84
|
+
// =============================================================================
|
|
85
|
+
// TYPED + SCHEMA - combine both for full safety
|
|
86
|
+
// =============================================================================
|
|
87
|
+
|
|
88
|
+
memorio.registerSchema('profile', {
|
|
89
|
+
type: 'object',
|
|
90
|
+
required: ['bio'],
|
|
91
|
+
properties: {
|
|
92
|
+
bio: { type: 'string', min: 1 },
|
|
93
|
+
avatar: { type: 'string' }
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
interface ProfileState {
|
|
98
|
+
profile: { bio: string; avatar?: string }
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const typedProfile = memorio.typed<ProfileState>()
|
|
102
|
+
typedProfile.profile = { bio: 'Developer', avatar: 'pic.png' }
|
package/examples/useObserver.tsx
CHANGED
|
@@ -1,141 +1,140 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Memorio useObserver Example
|
|
3
|
-
*
|
|
4
|
-
* This example demonstrates the useObserver hook for React.
|
|
5
|
-
* useObserver automatically tracks state changes and triggers re-renders.
|
|
6
|
-
*
|
|
7
|
-
* Note: This example requires React to be present in the environment.
|
|
8
|
-
* Run in a React environment.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import '
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
// ============================================
|
|
29
|
-
|
|
30
|
-
//
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
// ============================================
|
|
42
|
-
|
|
43
|
-
//
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
// ============================================
|
|
56
|
-
|
|
57
|
-
//
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
// ============================================
|
|
71
|
-
|
|
72
|
-
//
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
store
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
todos[
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
(state.filter === '
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
<
|
|
114
|
-
<
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
console.debug('useObserver example - run in React environment!')
|
|
1
|
+
/**
|
|
2
|
+
* Memorio useObserver Example
|
|
3
|
+
*
|
|
4
|
+
* This example demonstrates the useObserver hook for React.
|
|
5
|
+
* useObserver automatically tracks state changes and triggers re-renders.
|
|
6
|
+
*
|
|
7
|
+
* Note: This example requires React to be present in the environment.
|
|
8
|
+
* Run in a React environment (e.g. Vite/CRA), not directly with ts-node.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { useState } from 'react'
|
|
12
|
+
import { useObserver, state, store } from 'memorio'
|
|
13
|
+
|
|
14
|
+
// ============================================
|
|
15
|
+
// BASIC USEOBSERVER
|
|
16
|
+
// ============================================
|
|
17
|
+
|
|
18
|
+
function Counter() {
|
|
19
|
+
useObserver(() => {
|
|
20
|
+
console.debug('Count changed:', state.count)
|
|
21
|
+
}, [state.count])
|
|
22
|
+
|
|
23
|
+
return <div>{state.count}</div>
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// ============================================
|
|
27
|
+
// MULTIPLE DEPENDENCIES
|
|
28
|
+
// ============================================
|
|
29
|
+
|
|
30
|
+
// Example 2: Watch multiple values
|
|
31
|
+
function UserProfile() {
|
|
32
|
+
useObserver(() => {
|
|
33
|
+
console.debug('User or theme changed')
|
|
34
|
+
}, [state.user, state.theme])
|
|
35
|
+
|
|
36
|
+
return <div>{state.user.name}</div>
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// ============================================
|
|
40
|
+
// AUTO-DISCOVERY
|
|
41
|
+
// ============================================
|
|
42
|
+
|
|
43
|
+
// Example 3: No dependencies - auto-discovers all state access
|
|
44
|
+
function AutoDiscovery() {
|
|
45
|
+
useObserver(() => {
|
|
46
|
+
// Automatically tracks state.user, state.items, state.count
|
|
47
|
+
console.debug('Changed:', state.user.name, state.items.length, state.count)
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
return <div>{state.user.name}</div>
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ============================================
|
|
54
|
+
// SYNC WITH USESTATE
|
|
55
|
+
// ============================================
|
|
56
|
+
|
|
57
|
+
// Example 4: Sync with useState
|
|
58
|
+
function SyncedComponent() {
|
|
59
|
+
const [localData, setLocalData] = useState(null)
|
|
60
|
+
|
|
61
|
+
useObserver((newValue: unknown) => {
|
|
62
|
+
setLocalData(newValue)
|
|
63
|
+
}, [state.data])
|
|
64
|
+
|
|
65
|
+
return <div>{localData}</div>
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ============================================
|
|
69
|
+
// PRACTICAL EXAMPLE
|
|
70
|
+
// ============================================
|
|
71
|
+
|
|
72
|
+
// Real-world React component example:
|
|
73
|
+
interface Todo {
|
|
74
|
+
text: string
|
|
75
|
+
done: boolean
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function TodoApp() {
|
|
79
|
+
// Track todos
|
|
80
|
+
useObserver(() => {
|
|
81
|
+
console.debug('Todos updated:', state.todos)
|
|
82
|
+
// Optionally save to store
|
|
83
|
+
store.set('todos', state.todos)
|
|
84
|
+
}, [state.todos])
|
|
85
|
+
|
|
86
|
+
// Track filter
|
|
87
|
+
useObserver(() => {
|
|
88
|
+
console.debug('Filter changed:', state.filter)
|
|
89
|
+
}, [state.filter])
|
|
90
|
+
|
|
91
|
+
const addTodo = (text: string) => {
|
|
92
|
+
state.todos = [...(state.todos || []), { text, done: false }]
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const toggleTodo = (index: number) => {
|
|
96
|
+
const todos: Todo[] = [...state.todos]
|
|
97
|
+
todos[index].done = !todos[index].done
|
|
98
|
+
state.todos = todos
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const filteredTodos: Todo[] = (state.todos || []).filter((t: Todo) =>
|
|
102
|
+
state.filter === 'all' ||
|
|
103
|
+
(state.filter === 'done' && t.done) ||
|
|
104
|
+
(state.filter === 'todo' && !t.done)
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
return (
|
|
108
|
+
<div>
|
|
109
|
+
<select
|
|
110
|
+
value={state.filter}
|
|
111
|
+
onChange={e => { state.filter = e.target.value }}
|
|
112
|
+
>
|
|
113
|
+
<option value="all">All</option>
|
|
114
|
+
<option value="done">Done</option>
|
|
115
|
+
<option value="todo">Todo</option>
|
|
116
|
+
</select>
|
|
117
|
+
<ul>
|
|
118
|
+
{filteredTodos.map((todo, i) => (
|
|
119
|
+
<li
|
|
120
|
+
key={i}
|
|
121
|
+
onClick={() => toggleTodo(i)}
|
|
122
|
+
style={{ textDecoration: todo.done ? 'line-through' : 'none' }}
|
|
123
|
+
>
|
|
124
|
+
{todo.text}
|
|
125
|
+
</li>
|
|
126
|
+
))}
|
|
127
|
+
</ul>
|
|
128
|
+
<input
|
|
129
|
+
onKeyDown={e => {
|
|
130
|
+
if (e.key === 'Enter') {
|
|
131
|
+
addTodo((e.target as HTMLInputElement).value)
|
|
132
|
+
;(e.target as HTMLInputElement).value = ''
|
|
133
|
+
}
|
|
134
|
+
}}
|
|
135
|
+
/>
|
|
136
|
+
</div>
|
|
137
|
+
)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export { Counter, UserProfile, AutoDiscovery, SyncedComponent, TodoApp }
|