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.
Files changed (76) hide show
  1. package/AGENTS.md +3 -3
  2. package/README.md +307 -359
  3. package/SECURITY.md +17 -1
  4. package/SUMMARY.md +59 -45
  5. package/adr/001-state-proxy-model.md +96 -0
  6. package/adr/002-observer-semantics.md +180 -0
  7. package/adr/003-deep-mutation-semantics.md +129 -0
  8. package/adr/004-array-mutation-semantics.md +128 -0
  9. package/adr/005-scheduler-contract.md +149 -0
  10. package/adr/006-context-isolation.md +92 -0
  11. package/adr/007-mutation-records.md +118 -0
  12. package/adr/008-transactions.md +106 -0
  13. package/adr/009-history-model.md +110 -0
  14. package/adr/README.md +46 -0
  15. package/adr/template.md +49 -0
  16. package/examples/basic.ts +115 -115
  17. package/examples/browser-vanilla.html +358 -358
  18. package/examples/cache.ts +72 -72
  19. package/examples/cross-platform-guards.ts +57 -57
  20. package/examples/history.ts +104 -0
  21. package/examples/idb.ts +109 -109
  22. package/examples/multi-tenant-context.ts +44 -44
  23. package/examples/node-server.ts +308 -308
  24. package/examples/observer.ts +60 -60
  25. package/examples/platform.ts +115 -115
  26. package/examples/react-app.tsx +362 -362
  27. package/examples/react-observer.tsx +63 -63
  28. package/examples/semantic-memory.ts +60 -60
  29. package/examples/session-advanced.ts +91 -91
  30. package/examples/sqlite-batched-writes.ts +57 -57
  31. package/examples/state-advanced.ts +89 -89
  32. package/examples/store-advanced.ts +117 -117
  33. package/examples/sync.ts +90 -0
  34. package/examples/typed-and-schema.ts +102 -100
  35. package/examples/useObserver.tsx +140 -141
  36. package/global.cjs +4594 -0
  37. package/global.d.ts +8 -0
  38. package/global.js +4532 -0
  39. package/index.cjs +700 -678
  40. package/index.d.ts +1 -0
  41. package/index.js +680 -677
  42. package/llms.txt +72 -4
  43. package/markdown/AUDIT-REPORT.md +135 -0
  44. package/markdown/CACHE.md +100 -0
  45. package/markdown/CHANGELOG.md +243 -0
  46. package/markdown/DEVTOOLS.md +129 -0
  47. package/markdown/DISPATCH.md +177 -0
  48. package/markdown/HISTORY.md +199 -0
  49. package/markdown/IDB.md +178 -0
  50. package/markdown/IMPORT.md +153 -0
  51. package/markdown/INSPECT.md +123 -0
  52. package/markdown/LOGGER.md +154 -0
  53. package/markdown/MEMORY-ATTACHMENT.md +96 -0
  54. package/markdown/MEMORY.md +162 -0
  55. package/markdown/OBSERVER.md +209 -0
  56. package/markdown/PLATFORM.md +271 -0
  57. package/markdown/PROJECT.md +311 -0
  58. package/markdown/SCHEMA.md +176 -0
  59. package/markdown/SECURITY.md +330 -0
  60. package/markdown/SESSION.md +165 -0
  61. package/markdown/SQLITE.md +190 -0
  62. package/markdown/STATE.md +160 -0
  63. package/markdown/STORE.md +171 -0
  64. package/markdown/SYNC.md +319 -0
  65. package/markdown/TYPED.md +165 -0
  66. package/markdown/USEOBSERVER.md +257 -0
  67. package/modules/redux.cjs +320 -167
  68. package/modules/redux.cjs.map +1 -1
  69. package/modules/redux.js +320 -167
  70. package/modules/redux.js.map +1 -1
  71. package/package.json +13 -3
  72. package/types/env.d.ts +19 -9
  73. package/types/exports.d.ts +20 -0
  74. package/types/history.d.ts +13 -1
  75. package/types/memorio.d.ts +17 -5
  76. package/types/mutation.d.ts +75 -0
@@ -1,362 +1,362 @@
1
- /**
2
- * Memorio React App Example
3
- *
4
- * This example shows how to use Memorio state in a React application.
5
- *
6
- * Run: npx ts-node --esm examples/react-app.tsx
7
- * Or copy to your React project
8
- */
9
-
10
- import React, { useState, useEffect } from 'react'
11
- import 'memorio'
12
-
13
- // ============================================
14
- // 1. SETUP - Import once at app start
15
- // ============================================
16
-
17
- // In your main App.tsx or index.js:
18
- // import 'memorio'
19
-
20
- // ============================================
21
- // 2. DEFINE YOUR STATE SHAPE
22
- // ============================================
23
-
24
- interface AppState {
25
- user: {
26
- name: string
27
- email: string
28
- avatar?: string
29
- } | null
30
- theme: 'light' | 'dark'
31
- notifications: Notification[]
32
- cart: CartItem[]
33
- isLoading: boolean
34
- }
35
-
36
- interface Notification {
37
- id: string
38
- message: string
39
- read: boolean
40
- }
41
-
42
- interface CartItem {
43
- id: number
44
- name: string
45
- price: number
46
- quantity: number
47
- }
48
-
49
- // ============================================
50
- // 3. USE STATE IN COMPONENTS
51
- // ============================================
52
-
53
- // ------------------------------
54
- // Header Component
55
- // ------------------------------
56
- function Header() {
57
- // Using useObserver to react to state changes
58
- const [theme, setTheme] = useState(state.theme)
59
-
60
- useObserver(() => {
61
- setTheme(state.theme)
62
- }, [state.theme])
63
-
64
- return (
65
- <header className={`header header--${theme}`}>
66
- <h1>My App</h1>
67
- <button onClick={() => {
68
- state.theme = state.theme === 'light' ? 'dark' : 'light'
69
- }}>
70
- Toggle Theme
71
- </button>
72
- </header>
73
- )
74
- }
75
-
76
- // ------------------------------
77
- // User Profile Component
78
- // ------------------------------
79
- function UserProfile() {
80
- const [user, setUser] = useState(state.user)
81
-
82
- // Auto-discover all state changes
83
- useObserver(() => {
84
- setUser(state.user)
85
- })
86
-
87
- if (!user) {
88
- return <div>Please log in</div>
89
- }
90
-
91
- return (
92
- <div className="profile">
93
- <img src={user.avatar} alt={user.name} />
94
- <h2>{user.name}</h2>
95
- <p>{user.email}</p>
96
- </div>
97
- )
98
- }
99
-
100
- // ------------------------------
101
- // Login Form Component
102
- // ------------------------------
103
- function LoginForm() {
104
- const [name, setName] = useState('')
105
- const [email, setEmail] = useState('')
106
-
107
- const handleSubmit = (e: React.FormEvent) => {
108
- e.preventDefault()
109
-
110
- // Set user state
111
- state.user = {
112
- name,
113
- email,
114
- avatar: `https://api.dicebear.com/7.x/avataaars/svg?seed=${name}`
115
- }
116
-
117
- // Also persist to store
118
- store.set('lastUser', name)
119
- }
120
-
121
- return (
122
- <form onSubmit={handleSubmit}>
123
- <input
124
- value={name}
125
- onChange={e => setName(e.target.value)}
126
- placeholder="Name"
127
- />
128
- <input
129
- value={email}
130
- onChange={e => setEmail(e.target.value)}
131
- placeholder="Email"
132
- />
133
- <button type="submit">Login</button>
134
- </form>
135
- )
136
- }
137
-
138
- // ------------------------------
139
- // Shopping Cart Component
140
- // ------------------------------
141
- function Cart() {
142
- const [items, setItems] = useState<CartItem[]>([])
143
-
144
- useObserver(() => {
145
- setItems(state.cart || [])
146
- }, [state.cart])
147
-
148
- const total = items.reduce((sum, item) => sum + item.price * item.quantity, 0)
149
-
150
- return (
151
- <div className="cart">
152
- <h2>Cart ({items.length})</h2>
153
- {items.map(item => (
154
- <div key={item.id}>
155
- {item.name} - ${item.price} x {item.quantity}
156
- </div>
157
- ))}
158
- <strong>Total: ${total}</strong>
159
- <div style={{ marginTop: '1rem' }}>
160
- <AddToCartButton
161
- product={{ id: 1, name: 'Sample Product', price: 9.99 }}
162
- />
163
- </div>
164
- </div>
165
- )
166
- }
167
-
168
- // ------------------------------
169
- // Add to Cart Button
170
- // ------------------------------
171
- function AddToCartButton({ product }: { product: { id: number, name: string, price: number } }) {
172
- return (
173
- <button onClick={() => {
174
- // Initialize cart if not exists
175
- if (!state.cart) {
176
- state.cart = []
177
- }
178
-
179
- // Add item or increment quantity
180
- const existing = state.cart.find(item => item.id === product.id)
181
- if (existing) {
182
- existing.quantity++
183
- } else {
184
- state.cart.push({
185
- id: product.id,
186
- name: product.name,
187
- price: product.price,
188
- quantity: 1
189
- })
190
- }
191
- }}>
192
- Add to Cart
193
- </button>
194
- )
195
- }
196
-
197
- // ------------------------------
198
- // Notifications Component
199
- // ------------------------------
200
- function Notifications() {
201
- const [notifications, setNotifications] = useState<Notification[]>([])
202
-
203
- useObserver(() => {
204
- setNotifications(state.notifications || [])
205
- }, [state.notifications])
206
-
207
- const unread = notifications.filter(n => !n.read).length
208
-
209
- return (
210
- <div className="notifications">
211
- <span>🔔 {unread} unread</span>
212
- {notifications.map(n => (
213
- <div key={n.id} className={n.read ? 'read' : 'unread'}>
214
- {n.message}
215
- </div>
216
- ))}
217
- </div>
218
- )
219
- }
220
-
221
- // ------------------------------
222
- // Settings Component
223
- // ------------------------------
224
- function Settings() {
225
- const [theme] = useState(state.theme)
226
- const [savedSettings, setSavedSettings] = useState<any>(null)
227
-
228
- // Load saved settings from store
229
- useEffect(() => {
230
- const settings = store.get('appSettings')
231
- if (settings) {
232
- setSavedSettings(settings)
233
- }
234
- }, [])
235
-
236
- const saveSettings = () => {
237
- store.set('appSettings', { theme: state.theme })
238
- alert('Settings saved!')
239
- }
240
-
241
- return (
242
- <div className="settings">
243
- <h2>Settings</h2>
244
-
245
- <label>
246
- Theme:
247
- <select
248
- value={theme}
249
- onChange={e => state.theme = e.target.value as 'light' | 'dark'}
250
- >
251
- <option value="light">Light</option>
252
- <option value="dark">Dark</option>
253
- </select>
254
- </label>
255
-
256
- <button onClick={saveSettings}>Save Settings</button>
257
-
258
- {savedSettings && (
259
- <p>Saved: {savedSettings.theme}</p>
260
- )}
261
- </div>
262
- )
263
- }
264
-
265
- // ============================================
266
- // 4. MAIN APP COMPONENT
267
- // ============================================
268
-
269
- function App() {
270
- // Initialize default state
271
- useEffect(() => {
272
- // Load theme from store
273
- const savedTheme = store.get('appSettings')?.theme
274
- if (savedTheme) {
275
- state.theme = savedTheme
276
- } else {
277
- state.theme = 'light'
278
- }
279
-
280
- // Initialize notifications
281
- state.notifications = [
282
- { id: '1', message: 'Welcome to Memorio!', read: false },
283
- { id: '2', message: 'Check out our new features', read: false }
284
- ]
285
-
286
- // Check for returning user
287
- const lastUser = store.get('lastUser')
288
- if (lastUser) {
289
- console.debug('Welcome back,', lastUser)
290
- }
291
- }, [])
292
-
293
- return (
294
- <div className={`app app--${state.theme}`}>
295
- <Header />
296
- <main>
297
- {state.user ? (
298
- <>
299
- <UserProfile />
300
- <Cart />
301
- <Notifications />
302
- <Settings />
303
-
304
- <button onClick={() => {
305
- // Logout - clear user but keep settings
306
- state.user = null
307
- }}>
308
- Logout
309
- </button>
310
- </>
311
- ) : (
312
- <LoginForm />
313
- )}
314
- </main>
315
- </div>
316
- )
317
- }
318
-
319
- export default App
320
-
321
- // ============================================
322
- // 5. USAGE SUMMARY
323
- // ============================================
324
-
325
- /*
326
- MEMORIO REACT USAGE GUIDE:
327
-
328
- 1. IMPORT ONCE (index.js or App.tsx):
329
- import 'memorio'
330
-
331
- 2. SET STATE:
332
- state.user = { name: 'Mario', email: 'mario@example.com' }
333
- state.theme = 'dark'
334
- state.cart = []
335
-
336
- 3. READ STATE:
337
- const value = state.user.name
338
- const theme = state.theme
339
-
340
- 4. REACT TO CHANGES:
341
- useObserver(() => {
342
- console.debug('State changed!')
343
- }, [state.user])
344
-
345
- 5. AUTO-DISCOVERY (watch all):
346
- useObserver(() => {
347
- console.debug(state.user, state.theme)
348
- })
349
-
350
- 6. PERSIST DATA:
351
- store.set('settings', { theme: 'dark' })
352
- const settings = store.get('settings')
353
-
354
- 7. SESSION DATA (cleared on tab close):
355
- session.set('token', 'abc123')
356
- const token = session.get('token')
357
-
358
- 8. CLEAR DATA:
359
- state.removeAll() // Clear all state
360
- store.removeAll() // Clear all persisted
361
- session.removeAll() // Clear all session
362
- */
1
+ /**
2
+ * Memorio React App Example
3
+ *
4
+ * This example shows how to use Memorio state in a React application.
5
+ *
6
+ * Run: npx ts-node --esm examples/react-app.tsx
7
+ * Or copy to your React project
8
+ */
9
+
10
+ import React, { useState, useEffect } from 'react'
11
+ import { state, store, session, useObserver, memorio } from 'memorio'
12
+
13
+ // ============================================
14
+ // 1. SETUP - Import once at app start
15
+ // ============================================
16
+
17
+ // In your main App.tsx or index.js:
18
+ // import { state, store, session, useObserver, memorio } from 'memorio'
19
+
20
+ // ============================================
21
+ // 2. DEFINE YOUR STATE SHAPE
22
+ // ============================================
23
+
24
+ interface AppState {
25
+ user: {
26
+ name: string
27
+ email: string
28
+ avatar?: string
29
+ } | null
30
+ theme: 'light' | 'dark'
31
+ notifications: Notification[]
32
+ cart: CartItem[]
33
+ isLoading: boolean
34
+ }
35
+
36
+ interface Notification {
37
+ id: string
38
+ message: string
39
+ read: boolean
40
+ }
41
+
42
+ interface CartItem {
43
+ id: number
44
+ name: string
45
+ price: number
46
+ quantity: number
47
+ }
48
+
49
+ // ============================================
50
+ // 3. USE STATE IN COMPONENTS
51
+ // ============================================
52
+
53
+ // ------------------------------
54
+ // Header Component
55
+ // ------------------------------
56
+ function Header() {
57
+ // Using useObserver to react to state changes
58
+ const [theme, setTheme] = useState(state.theme)
59
+
60
+ useObserver(() => {
61
+ setTheme(state.theme)
62
+ }, [state.theme])
63
+
64
+ return (
65
+ <header className={`header header--${theme}`}>
66
+ <h1>My App</h1>
67
+ <button onClick={() => {
68
+ state.theme = state.theme === 'light' ? 'dark' : 'light'
69
+ }}>
70
+ Toggle Theme
71
+ </button>
72
+ </header>
73
+ )
74
+ }
75
+
76
+ // ------------------------------
77
+ // User Profile Component
78
+ // ------------------------------
79
+ function UserProfile() {
80
+ const [user, setUser] = useState(state.user)
81
+
82
+ // Auto-discover all state changes
83
+ useObserver(() => {
84
+ setUser(state.user)
85
+ })
86
+
87
+ if (!user) {
88
+ return <div>Please log in</div>
89
+ }
90
+
91
+ return (
92
+ <div className="profile">
93
+ <img src={user.avatar} alt={user.name} />
94
+ <h2>{user.name}</h2>
95
+ <p>{user.email}</p>
96
+ </div>
97
+ )
98
+ }
99
+
100
+ // ------------------------------
101
+ // Login Form Component
102
+ // ------------------------------
103
+ function LoginForm() {
104
+ const [name, setName] = useState('')
105
+ const [email, setEmail] = useState('')
106
+
107
+ const handleSubmit = (e: React.FormEvent) => {
108
+ e.preventDefault()
109
+
110
+ // Set user state
111
+ state.user = {
112
+ name,
113
+ email,
114
+ avatar: `https://api.dicebear.com/7.x/avataaars/svg?seed=${name}`
115
+ }
116
+
117
+ // Also persist to store
118
+ store.set('lastUser', name)
119
+ }
120
+
121
+ return (
122
+ <form onSubmit={handleSubmit}>
123
+ <input
124
+ value={name}
125
+ onChange={e => setName(e.target.value)}
126
+ placeholder="Name"
127
+ />
128
+ <input
129
+ value={email}
130
+ onChange={e => setEmail(e.target.value)}
131
+ placeholder="Email"
132
+ />
133
+ <button type="submit">Login</button>
134
+ </form>
135
+ )
136
+ }
137
+
138
+ // ------------------------------
139
+ // Shopping Cart Component
140
+ // ------------------------------
141
+ function Cart() {
142
+ const [items, setItems] = useState<CartItem[]>([])
143
+
144
+ useObserver(() => {
145
+ setItems(state.cart || [])
146
+ }, [state.cart])
147
+
148
+ const total = items.reduce((sum, item) => sum + item.price * item.quantity, 0)
149
+
150
+ return (
151
+ <div className="cart">
152
+ <h2>Cart ({items.length})</h2>
153
+ {items.map(item => (
154
+ <div key={item.id}>
155
+ {item.name} - ${item.price} x {item.quantity}
156
+ </div>
157
+ ))}
158
+ <strong>Total: ${total}</strong>
159
+ <div style={{ marginTop: '1rem' }}>
160
+ <AddToCartButton
161
+ product={{ id: 1, name: 'Sample Product', price: 9.99 }}
162
+ />
163
+ </div>
164
+ </div>
165
+ )
166
+ }
167
+
168
+ // ------------------------------
169
+ // Add to Cart Button
170
+ // ------------------------------
171
+ function AddToCartButton({ product }: { product: { id: number, name: string, price: number } }) {
172
+ return (
173
+ <button onClick={() => {
174
+ // Initialize cart if not exists
175
+ if (!state.cart) {
176
+ state.cart = []
177
+ }
178
+
179
+ // Add item or increment quantity
180
+ const existing = state.cart.find(item => item.id === product.id)
181
+ if (existing) {
182
+ existing.quantity++
183
+ } else {
184
+ state.cart.push({
185
+ id: product.id,
186
+ name: product.name,
187
+ price: product.price,
188
+ quantity: 1
189
+ })
190
+ }
191
+ }}>
192
+ Add to Cart
193
+ </button>
194
+ )
195
+ }
196
+
197
+ // ------------------------------
198
+ // Notifications Component
199
+ // ------------------------------
200
+ function Notifications() {
201
+ const [notifications, setNotifications] = useState<Notification[]>([])
202
+
203
+ useObserver(() => {
204
+ setNotifications(state.notifications || [])
205
+ }, [state.notifications])
206
+
207
+ const unread = notifications.filter(n => !n.read).length
208
+
209
+ return (
210
+ <div className="notifications">
211
+ <span>🔔 {unread} unread</span>
212
+ {notifications.map(n => (
213
+ <div key={n.id} className={n.read ? 'read' : 'unread'}>
214
+ {n.message}
215
+ </div>
216
+ ))}
217
+ </div>
218
+ )
219
+ }
220
+
221
+ // ------------------------------
222
+ // Settings Component
223
+ // ------------------------------
224
+ function Settings() {
225
+ const [theme] = useState(state.theme)
226
+ const [savedSettings, setSavedSettings] = useState<any>(null)
227
+
228
+ // Load saved settings from store
229
+ useEffect(() => {
230
+ const settings = store.get('appSettings')
231
+ if (settings) {
232
+ setSavedSettings(settings)
233
+ }
234
+ }, [])
235
+
236
+ const saveSettings = () => {
237
+ store.set('appSettings', { theme: state.theme })
238
+ alert('Settings saved!')
239
+ }
240
+
241
+ return (
242
+ <div className="settings">
243
+ <h2>Settings</h2>
244
+
245
+ <label>
246
+ Theme:
247
+ <select
248
+ value={theme}
249
+ onChange={e => state.theme = e.target.value as 'light' | 'dark'}
250
+ >
251
+ <option value="light">Light</option>
252
+ <option value="dark">Dark</option>
253
+ </select>
254
+ </label>
255
+
256
+ <button onClick={saveSettings}>Save Settings</button>
257
+
258
+ {savedSettings && (
259
+ <p>Saved: {savedSettings.theme}</p>
260
+ )}
261
+ </div>
262
+ )
263
+ }
264
+
265
+ // ============================================
266
+ // 4. MAIN APP COMPONENT
267
+ // ============================================
268
+
269
+ function App() {
270
+ // Initialize default state
271
+ useEffect(() => {
272
+ // Load theme from store
273
+ const savedTheme = store.get('appSettings')?.theme
274
+ if (savedTheme) {
275
+ state.theme = savedTheme
276
+ } else {
277
+ state.theme = 'light'
278
+ }
279
+
280
+ // Initialize notifications
281
+ state.notifications = [
282
+ { id: '1', message: 'Welcome to Memorio!', read: false },
283
+ { id: '2', message: 'Check out our new features', read: false }
284
+ ]
285
+
286
+ // Check for returning user
287
+ const lastUser = store.get('lastUser')
288
+ if (lastUser) {
289
+ console.debug('Welcome back,', lastUser)
290
+ }
291
+ }, [])
292
+
293
+ return (
294
+ <div className={`app app--${state.theme}`}>
295
+ <Header />
296
+ <main>
297
+ {state.user ? (
298
+ <>
299
+ <UserProfile />
300
+ <Cart />
301
+ <Notifications />
302
+ <Settings />
303
+
304
+ <button onClick={() => {
305
+ // Logout - clear user but keep settings
306
+ state.user = null
307
+ }}>
308
+ Logout
309
+ </button>
310
+ </>
311
+ ) : (
312
+ <LoginForm />
313
+ )}
314
+ </main>
315
+ </div>
316
+ )
317
+ }
318
+
319
+ export default App
320
+
321
+ // ============================================
322
+ // 5. USAGE SUMMARY
323
+ // ============================================
324
+
325
+ /*
326
+ MEMORIO REACT USAGE GUIDE:
327
+
328
+ 1. IMPORT ONCE (index.js or App.tsx):
329
+ import { state, store, session, useObserver } from 'memorio'
330
+
331
+ 2. SET STATE:
332
+ state.user = { name: 'Mario', email: 'mario@example.com' }
333
+ state.theme = 'dark'
334
+ state.cart = []
335
+
336
+ 3. READ STATE:
337
+ const value = state.user.name
338
+ const theme = state.theme
339
+
340
+ 4. REACT TO CHANGES:
341
+ useObserver(() => {
342
+ console.debug('State changed!')
343
+ }, [state.user])
344
+
345
+ 5. AUTO-DISCOVERY (watch all):
346
+ useObserver(() => {
347
+ console.debug(state.user, state.theme)
348
+ })
349
+
350
+ 6. PERSIST DATA:
351
+ store.set('settings', { theme: 'dark' })
352
+ const settings = store.get('settings')
353
+
354
+ 7. SESSION DATA (cleared on tab close):
355
+ session.set('token', 'abc123')
356
+ const token = session.get('token')
357
+
358
+ 8. CLEAR DATA:
359
+ state.removeAll() // Clear all state
360
+ store.removeAll() // Clear all persisted
361
+ session.removeAll() // Clear all session
362
+ */