memorio 2.6.6 → 2.7.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 +102 -0
- package/examples/basic.ts +41 -1
- package/examples/browser-vanilla.html +358 -0
- package/examples/node-server.ts +308 -0
- package/examples/platform.ts +115 -0
- package/examples/react-app.tsx +357 -0
- package/examples/session-advanced.ts +13 -0
- package/examples/store-advanced.ts +13 -0
- package/index.cjs +331 -119
- package/index.js +331 -119
- package/package.json +17 -12
- package/types/memorio.d.ts +22 -0
- package/types/session.d.ts +7 -6
- package/types/state.d.ts +1 -6
- package/types/store.d.ts +6 -7
|
@@ -0,0 +1,357 @@
|
|
|
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 '../index'
|
|
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>
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ------------------------------
|
|
164
|
+
// Add to Cart Button
|
|
165
|
+
// ------------------------------
|
|
166
|
+
function AddToCartButton({ product }: { product: { id: number, name: string, price: number } }) {
|
|
167
|
+
return (
|
|
168
|
+
<button onClick={() => {
|
|
169
|
+
// Initialize cart if not exists
|
|
170
|
+
if (!state.cart) {
|
|
171
|
+
state.cart = []
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Add item or increment quantity
|
|
175
|
+
const existing = state.cart.find(item => item.id === product.id)
|
|
176
|
+
if (existing) {
|
|
177
|
+
existing.quantity++
|
|
178
|
+
} else {
|
|
179
|
+
state.cart.push({
|
|
180
|
+
id: product.id,
|
|
181
|
+
name: product.name,
|
|
182
|
+
price: product.price,
|
|
183
|
+
quantity: 1
|
|
184
|
+
})
|
|
185
|
+
}
|
|
186
|
+
}}>
|
|
187
|
+
Add to Cart
|
|
188
|
+
</button>
|
|
189
|
+
)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// ------------------------------
|
|
193
|
+
// Notifications Component
|
|
194
|
+
// ------------------------------
|
|
195
|
+
function Notifications() {
|
|
196
|
+
const [notifications, setNotifications] = useState<Notification[]>([])
|
|
197
|
+
|
|
198
|
+
useObserver(() => {
|
|
199
|
+
setNotifications(state.notifications || [])
|
|
200
|
+
}, [state.notifications])
|
|
201
|
+
|
|
202
|
+
const unread = notifications.filter(n => !n.read).length
|
|
203
|
+
|
|
204
|
+
return (
|
|
205
|
+
<div className="notifications">
|
|
206
|
+
<span>🔔 {unread} unread</span>
|
|
207
|
+
{notifications.map(n => (
|
|
208
|
+
<div key={n.id} className={n.read ? 'read' : 'unread'}>
|
|
209
|
+
{n.message}
|
|
210
|
+
</div>
|
|
211
|
+
))}
|
|
212
|
+
</div>
|
|
213
|
+
)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// ------------------------------
|
|
217
|
+
// Settings Component
|
|
218
|
+
// ------------------------------
|
|
219
|
+
function Settings() {
|
|
220
|
+
const [theme, setTheme] = useState(state.theme)
|
|
221
|
+
const [savedSettings, setSavedSettings] = useState<any>(null)
|
|
222
|
+
|
|
223
|
+
// Load saved settings from store
|
|
224
|
+
useEffect(() => {
|
|
225
|
+
const settings = store.get('appSettings')
|
|
226
|
+
if (settings) {
|
|
227
|
+
setSavedSettings(settings)
|
|
228
|
+
}
|
|
229
|
+
}, [])
|
|
230
|
+
|
|
231
|
+
const saveSettings = () => {
|
|
232
|
+
store.set('appSettings', { theme: state.theme })
|
|
233
|
+
alert('Settings saved!')
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return (
|
|
237
|
+
<div className="settings">
|
|
238
|
+
<h2>Settings</h2>
|
|
239
|
+
|
|
240
|
+
<label>
|
|
241
|
+
Theme:
|
|
242
|
+
<select
|
|
243
|
+
value={theme}
|
|
244
|
+
onChange={e => state.theme = e.target.value as 'light' | 'dark'}
|
|
245
|
+
>
|
|
246
|
+
<option value="light">Light</option>
|
|
247
|
+
<option value="dark">Dark</option>
|
|
248
|
+
</select>
|
|
249
|
+
</label>
|
|
250
|
+
|
|
251
|
+
<button onClick={saveSettings}>Save Settings</button>
|
|
252
|
+
|
|
253
|
+
{savedSettings && (
|
|
254
|
+
<p>Saved: {savedSettings.theme}</p>
|
|
255
|
+
)}
|
|
256
|
+
</div>
|
|
257
|
+
)
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// ============================================
|
|
261
|
+
// 4. MAIN APP COMPONENT
|
|
262
|
+
// ============================================
|
|
263
|
+
|
|
264
|
+
function App() {
|
|
265
|
+
// Initialize default state
|
|
266
|
+
useEffect(() => {
|
|
267
|
+
// Load theme from store
|
|
268
|
+
const savedTheme = store.get('appSettings')?.theme
|
|
269
|
+
if (savedTheme) {
|
|
270
|
+
state.theme = savedTheme
|
|
271
|
+
} else {
|
|
272
|
+
state.theme = 'light'
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Initialize notifications
|
|
276
|
+
state.notifications = [
|
|
277
|
+
{ id: '1', message: 'Welcome to Memorio!', read: false },
|
|
278
|
+
{ id: '2', message: 'Check out our new features', read: false }
|
|
279
|
+
]
|
|
280
|
+
|
|
281
|
+
// Check for returning user
|
|
282
|
+
const lastUser = store.get('lastUser')
|
|
283
|
+
if (lastUser) {
|
|
284
|
+
console.log('Welcome back,', lastUser)
|
|
285
|
+
}
|
|
286
|
+
}, [])
|
|
287
|
+
|
|
288
|
+
return (
|
|
289
|
+
<div className={`app app--${state.theme}`}>
|
|
290
|
+
<Header />
|
|
291
|
+
<main>
|
|
292
|
+
{state.user ? (
|
|
293
|
+
<>
|
|
294
|
+
<UserProfile />
|
|
295
|
+
<Cart />
|
|
296
|
+
<Notifications />
|
|
297
|
+
<Settings />
|
|
298
|
+
|
|
299
|
+
<button onClick={() => {
|
|
300
|
+
// Logout - clear user but keep settings
|
|
301
|
+
state.user = null
|
|
302
|
+
}}>
|
|
303
|
+
Logout
|
|
304
|
+
</button>
|
|
305
|
+
</>
|
|
306
|
+
) : (
|
|
307
|
+
<LoginForm />
|
|
308
|
+
)}
|
|
309
|
+
</main>
|
|
310
|
+
</div>
|
|
311
|
+
)
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export default App
|
|
315
|
+
|
|
316
|
+
// ============================================
|
|
317
|
+
// 5. USAGE SUMMARY
|
|
318
|
+
// ============================================
|
|
319
|
+
|
|
320
|
+
/*
|
|
321
|
+
MEMORIO REACT USAGE GUIDE:
|
|
322
|
+
|
|
323
|
+
1. IMPORT ONCE (index.js or App.tsx):
|
|
324
|
+
import 'memorio'
|
|
325
|
+
|
|
326
|
+
2. SET STATE:
|
|
327
|
+
state.user = { name: 'Mario', email: 'mario@example.com' }
|
|
328
|
+
state.theme = 'dark'
|
|
329
|
+
state.cart = []
|
|
330
|
+
|
|
331
|
+
3. READ STATE:
|
|
332
|
+
const value = state.user.name
|
|
333
|
+
const theme = state.theme
|
|
334
|
+
|
|
335
|
+
4. REACT TO CHANGES:
|
|
336
|
+
useObserver(() => {
|
|
337
|
+
console.log('State changed!')
|
|
338
|
+
}, [state.user])
|
|
339
|
+
|
|
340
|
+
5. AUTO-DISCOVERY (watch all):
|
|
341
|
+
useObserver(() => {
|
|
342
|
+
console.log(state.user, state.theme)
|
|
343
|
+
})
|
|
344
|
+
|
|
345
|
+
6. PERSIST DATA:
|
|
346
|
+
store.set('settings', { theme: 'dark' })
|
|
347
|
+
const settings = store.get('settings')
|
|
348
|
+
|
|
349
|
+
7. SESSION DATA (cleared on tab close):
|
|
350
|
+
session.set('token', 'abc123')
|
|
351
|
+
const token = session.get('token')
|
|
352
|
+
|
|
353
|
+
8. CLEAR DATA:
|
|
354
|
+
state.removeAll() // Clear all state
|
|
355
|
+
store.removeAll() // Clear all persisted
|
|
356
|
+
session.removeAll() // Clear all session
|
|
357
|
+
*/
|
|
@@ -9,6 +9,19 @@
|
|
|
9
9
|
|
|
10
10
|
import '../index'
|
|
11
11
|
|
|
12
|
+
// ============================================
|
|
13
|
+
// CHECK PERSISTENCE
|
|
14
|
+
// ============================================
|
|
15
|
+
|
|
16
|
+
console.log('=== Session Persistence Check ===')
|
|
17
|
+
console.log('Is persistent (survives tab close):', session.isPersistent)
|
|
18
|
+
// In browser: true (sessionStorage)
|
|
19
|
+
// In Node.js/Deno: false (memory fallback)
|
|
20
|
+
|
|
21
|
+
if (!session.isPersistent) {
|
|
22
|
+
console.log('⚠️ Warning: Using in-memory storage. Data will be lost on process restart!')
|
|
23
|
+
}
|
|
24
|
+
|
|
12
25
|
// ============================================
|
|
13
26
|
// AUTHENTICATION
|
|
14
27
|
// ============================================
|
|
@@ -8,6 +8,19 @@
|
|
|
8
8
|
|
|
9
9
|
import '../index'
|
|
10
10
|
|
|
11
|
+
// ============================================
|
|
12
|
+
// CHECK PERSISTENCE
|
|
13
|
+
// ============================================
|
|
14
|
+
|
|
15
|
+
console.log('=== Store Persistence Check ===')
|
|
16
|
+
console.log('Is persistent (survives restart):', store.isPersistent)
|
|
17
|
+
// In browser: true (localStorage)
|
|
18
|
+
// In Node.js/Deno: false (memory fallback)
|
|
19
|
+
|
|
20
|
+
if (!store.isPersistent) {
|
|
21
|
+
console.log('⚠️ Warning: Using in-memory storage. Data will be lost on restart!')
|
|
22
|
+
}
|
|
23
|
+
|
|
11
24
|
// ============================================
|
|
12
25
|
// PERSIST USER PREFERENCES
|
|
13
26
|
// ============================================
|