memorio 4.9.7 → 4.9.10

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.
@@ -0,0 +1,57 @@
1
+ /**
2
+ * sqlite-batched-writes.ts
3
+ *
4
+ * Scenario: importing or writing many rows into memorio's sqlite layer.
5
+ *
6
+ * Persistence there serializes the ENTIRE database on every flush — not
7
+ * incremental. Calling a persisting write inside a per-row loop is the
8
+ * single most common way to accidentally make this layer slow.
9
+ */
10
+ import { memorio } from 'memorio'
11
+
12
+ interface UserRow {
13
+ id: number
14
+ name: string
15
+ role: string
16
+ }
17
+
18
+ export async function importUsers(users: UserRow[]) {
19
+ await memorio.sqlite.ready
20
+ await memorio.sqlite.db.create('app', { persistence: true })
21
+
22
+ await memorio.sqlite.query.run(
23
+ 'app',
24
+ `CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT NOT NULL, role TEXT)`
25
+ )
26
+
27
+ // Bad: this would serialize the whole DB after every single insert.
28
+ //
29
+ // for (const user of users) {
30
+ // await memorio.sqlite.query.run('app', 'INSERT INTO users ...', [...])
31
+ // await memorio.sqlite.db.flush('app') // <- don't do this per row
32
+ // }
33
+
34
+ // Good: run every insert first, persist once after the batch.
35
+ for (const user of users) {
36
+ await memorio.sqlite.query.run(
37
+ 'app',
38
+ `INSERT INTO users (id, name, role) VALUES (?, ?, ?)`,
39
+ [user.id, user.name, user.role]
40
+ )
41
+ }
42
+
43
+ // Persist explicitly, once, after the whole batch — check the installed
44
+ // version's API for the exact flush/persist call name if it differs from
45
+ // automatic persistence-on-close.
46
+ const admins = await memorio.sqlite.query.select(
47
+ 'app',
48
+ `SELECT * FROM users WHERE role = ?`,
49
+ ['admin']
50
+ )
51
+
52
+ return admins
53
+ }
54
+
55
+ // For read-heavy or scratch-space use where durability doesn't matter,
56
+ // skip { persistence: true } entirely — sqlite runs in memory by default,
57
+ // which avoids the serialize cost altogether.
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Memorio State Advanced Example
3
+ *
4
+ * This example shows advanced state features: locking, path tracking, and nesting.
5
+ *
6
+ * Run: npx ts-node examples/state-advanced.ts
7
+ */
8
+
9
+ import 'memorio'
10
+
11
+ // ============================================
12
+ // NESTED OBJECTS
13
+ // ============================================
14
+
15
+ // Create nested state
16
+ state.user = {
17
+ name: 'Mario',
18
+ profile: {
19
+ email: 'mario@example.com',
20
+ settings: {
21
+ theme: 'dark',
22
+ notifications: true
23
+ }
24
+ }
25
+ }
26
+
27
+ // Access nested values
28
+ console.debug('User name:', state.user.name)
29
+ console.debug('Email:', state.user.profile.email)
30
+ console.debug('Theme:', state.user.profile.settings.theme)
31
+
32
+ // ============================================
33
+ // ARRAYS
34
+ // ============================================
35
+
36
+ // State arrays work like regular arrays
37
+ state.items = [1, 2, 3]
38
+ state.items.push(4)
39
+ console.debug('Items:', state.items) // [1, 2, 3, 4]
40
+
41
+ state.users = [
42
+ { name: 'Mario', id: 1 },
43
+ { name: 'Luigi', id: 2 }
44
+ ]
45
+ state.users.push({ name: 'Peach', id: 3 })
46
+ console.debug('Users:', state.users)
47
+
48
+ // ============================================
49
+ // LOCKING STATE
50
+ // ============================================
51
+
52
+ // Lock a value to prevent modifications
53
+ state.config = { maxUsers: 100, timeout: 30 }
54
+ state.config.lock()
55
+
56
+ // This will fail:
57
+ // state.config.maxUsers = 200 // Error: state 'config' is locked
58
+
59
+ console.debug('Config locked:', state.config)
60
+
61
+ // ============================================
62
+ // PATH TRACKING
63
+ // ============================================
64
+
65
+ // Get path information
66
+ console.debug('Path:', state.user.__path) // "state.user"
67
+
68
+ // Use path tracker for debugging
69
+ const path = state.user.profile
70
+ console.debug('Profile path:', path.email.toString()) // "state.user.profile.email"
71
+
72
+ // ============================================
73
+ // LIST ALL STATES
74
+ // ============================================
75
+
76
+ // Get all current state keys
77
+ console.debug('All states:', state.list)
78
+
79
+ // ============================================
80
+ // REMOVE STATE
81
+ // ============================================
82
+
83
+ // Remove specific state
84
+ state.remove('items')
85
+
86
+ // Remove all states
87
+ // state.removeAll()
88
+
89
+ console.debug('State advanced example complete!')
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Memorio Store Advanced Example
3
+ *
4
+ * This example shows advanced store (localStorage) operations.
5
+ *
6
+ * Run: npx ts-node examples/store-advanced.ts
7
+ */
8
+
9
+ import 'memorio'
10
+
11
+ // ============================================
12
+ // CHECK PERSISTENCE
13
+ // ============================================
14
+
15
+ console.debug('=== Store Persistence Check ===')
16
+ console.debug('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.debug('⚠️ Warning: Using in-memory storage. Data will be lost on restart!')
22
+ }
23
+
24
+ // ============================================
25
+ // PERSIST USER PREFERENCES
26
+ // ============================================
27
+
28
+ // Save user preferences
29
+ store.set('preferences', {
30
+ theme: 'dark',
31
+ language: 'en',
32
+ notifications: true,
33
+ fontSize: 16
34
+ })
35
+
36
+ // ============================================
37
+ // CHECK AND LOAD PREFERENCES
38
+ // ============================================
39
+
40
+ const savedPrefs = store.get('preferences')
41
+ if (savedPrefs) {
42
+ console.debug('Loaded preferences:', savedPrefs)
43
+ } else {
44
+ console.debug('No preferences found, using defaults')
45
+ store.set('preferences', { theme: 'light', language: 'en' })
46
+ }
47
+
48
+ // ============================================
49
+ // STORAGE QUOTA
50
+ // ============================================
51
+
52
+ // Get storage size
53
+ const currentSize = store.size()
54
+ console.debug('Current storage size:', currentSize, 'bytes')
55
+
56
+ // ============================================
57
+ // ALIAS METHODS
58
+ // ============================================
59
+
60
+ // store.delete() is alias for store.remove()
61
+ store.set('temp', 'value')
62
+ store.delete('temp')
63
+
64
+ // store.clearAll() is alias for store.removeAll()
65
+ // store.clearAll()
66
+
67
+ // ============================================
68
+ // ERROR HANDLING
69
+ // ============================================
70
+
71
+ // Try-catch for large data
72
+ try {
73
+ // Store large data
74
+ const largeData = {
75
+ items: Array(1000).fill(null).map((_, i) => ({ id: i, data: 'x'.repeat(100) }))
76
+ }
77
+ store.set('largeData', largeData)
78
+ console.debug('Large data stored successfully')
79
+ } catch (error) {
80
+ console.error('Storage full:', error)
81
+ }
82
+
83
+ // ============================================
84
+ // DATA SERIALIZATION
85
+ // ============================================
86
+
87
+ // Store supports all JSON-serializable types
88
+ store.set('string', 'hello')
89
+ store.set('number', 42)
90
+ store.set('boolean', true)
91
+ store.set('array', [1, 2, 3])
92
+ store.set('object', { nested: { value: 'deep' } })
93
+ store.set('null', null)
94
+
95
+ // Functions are not supported (logged as error)
96
+ store.set('function', () => { }) // logs: "It's not secure to store functions."
97
+
98
+ // ============================================
99
+ // PRACTICAL EXAMPLE: APP STATE
100
+ // ============================================
101
+
102
+ // Save app state
103
+ const appState = {
104
+ lastPage: '/dashboard',
105
+ sidebarOpen: true,
106
+ recentFiles: ['file1.txt', 'file2.pdf'],
107
+ lastSaved: Date.now()
108
+ }
109
+ store.set('appState', appState)
110
+
111
+ // Load on next visit
112
+ const restored = store.get('appState')
113
+ if (restored) {
114
+ console.debug('Restored app state:', restored.lastPage)
115
+ }
116
+
117
+ console.debug('Store advanced example complete!')
@@ -0,0 +1,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: `import 'memorio'` (side-effect import) makes all globals
8
+ * available, including the `memorio` namespace object.
9
+ *
10
+ * @module memorio/examples/typed-and-schema
11
+ */
12
+
13
+ import 'memorio'
14
+
15
+ // =============================================================================
16
+ // TYPED STORES - compile-time type safety on the global `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
+ memorio.registerSchema('user', {
41
+ type: 'object',
42
+ required: ['name', 'email'],
43
+ properties: {
44
+ name: { type: 'string', min: 1 },
45
+ email: { type: 'string', pattern: /^[^@]+@[^@]+$/ },
46
+ age: { type: 'number', min: 0, max: 150 }
47
+ }
48
+ })
49
+
50
+ memorio.registerSchema('theme', { enum: ['light', 'dark'] })
51
+
52
+ memorio.registerSchema('items', {
53
+ type: 'array',
54
+ validator: (val: any) => Array.isArray(val) && val.every((i: any) => typeof i === 'string')
55
+ ? true
56
+ : 'items must be an array of strings'
57
+ })
58
+
59
+ memorio.registerSchema('counter', (val: any) => {
60
+ if (typeof val !== 'number') return 'counter must be a number'
61
+ if (val < 0) return 'counter must be >= 0'
62
+ return true
63
+ })
64
+
65
+ // These writes are rejected at runtime:
66
+ state.user = { name: 'Sara' } // missing 'email' - rejected
67
+ state.theme = 'purple' // not in enum - rejected
68
+ state.counter = -5 // fails custom validator - rejected
69
+
70
+ // These pass:
71
+ state.user = { name: 'Sara', email: 'sara@test.com', age: 30 }
72
+ state.theme = 'light'
73
+ state.counter = 5
74
+
75
+ // Manual validation:
76
+ memorio.validate('user', { name: 'Sara', email: 'sara@test.com' })
77
+ memorio.validate('theme', 'dark')
78
+
79
+ memorio.listSchemas()
80
+ memorio.unregisterSchema('counter')
81
+
82
+ // =============================================================================
83
+ // TYPED + SCHEMA - combine both for full safety
84
+ // =============================================================================
85
+
86
+ memorio.registerSchema('profile', {
87
+ type: 'object',
88
+ required: ['bio'],
89
+ properties: {
90
+ bio: { type: 'string', min: 1 },
91
+ avatar: { type: 'string' }
92
+ }
93
+ })
94
+
95
+ interface ProfileState {
96
+ profile: { bio: string; avatar?: string }
97
+ }
98
+
99
+ const typedProfile = memorio.typed<ProfileState>()
100
+ typedProfile.profile = { bio: 'Developer', avatar: 'pic.png' }
@@ -0,0 +1,141 @@
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 'memorio'
12
+
13
+ // ============================================
14
+ // BASIC USEOBSERVER
15
+ // ============================================
16
+
17
+ // Example 1: Watch single value
18
+ useObserver(callback, [state.value])
19
+
20
+ function Counter() {
21
+ useObserver(() => {
22
+ console.debug('Count changed:', state.count)
23
+ }, [state.count])
24
+
25
+ return <div>{state.count} </div>
26
+ }
27
+
28
+ // ============================================
29
+ // MULTIPLE DEPENDENCIES
30
+ // ============================================
31
+
32
+ // Example 2: Watch multiple values
33
+ function UserProfile() {
34
+ useObserver(() => {
35
+ console.debug('User or theme changed')
36
+ }, [state.user, state.theme])
37
+
38
+ return <div>{state.user.name} </div>
39
+ }
40
+
41
+ // ============================================
42
+ // AUTO-DISCOVERY
43
+ // ============================================
44
+
45
+ // Example 3: No dependencies - auto-discovers all state access
46
+ function AutoDiscovery() {
47
+ useObserver(() => {
48
+ // Automatically tracks state.user, state.items, state.count
49
+ console.debug('Changed:', state.user.name, state.items.length, state.count)
50
+ })
51
+
52
+ return <div>{state.user.name} </div>
53
+ }
54
+
55
+ // ============================================
56
+ // SYNC WITH USESTATE
57
+ // ============================================
58
+
59
+ // Example 4: Sync with useState
60
+ function SyncedComponent() {
61
+ const [localData, setLocalData] = useState(null)
62
+
63
+ useObserver((newValue) => {
64
+ setLocalData(newValue)
65
+ }, [state.data])
66
+
67
+ return <div>{localData} </div>
68
+ }
69
+
70
+ // ============================================
71
+ // PRACTICAL EXAMPLE
72
+ // ============================================
73
+
74
+ // Real-world React component example:
75
+ import 'memorio'
76
+
77
+ function TodoApp() {
78
+ // Track todos
79
+ useObserver(() => {
80
+ console.debug('Todos updated:', state.todos)
81
+ // Optionally save to store
82
+ store.set('todos', state.todos)
83
+ }, [state.todos])
84
+
85
+ // Track filter
86
+ useObserver(() => {
87
+ console.debug('Filter changed:', state.filter)
88
+ }, [state.filter])
89
+
90
+ const addTodo = (text) => {
91
+ state.todos = [...(state.todos || []), { text, done: false }]
92
+ }
93
+
94
+ const toggleTodo = (index) => {
95
+ const todos = [...state.todos]
96
+ todos[index].done = !todos[index].done
97
+ state.todos = todos
98
+ }
99
+
100
+ const filteredTodos = (state.todos || []).filter(t =>
101
+ state.filter === 'all' ||
102
+ (state.filter === 'done' && t.done) ||
103
+ (state.filter === 'todo' && !t.done)
104
+ )
105
+
106
+ return (
107
+ <div>
108
+ <select
109
+ value={state.filter}
110
+ onChange={e => state.filter = e.target.value}
111
+ >
112
+ <option value="all" > All </option>
113
+ < option value="done" > Done </option>
114
+ < option value="todo" > Todo </option>
115
+ </select>
116
+ <ul>
117
+ {
118
+ filteredTodos.map((todo, i) => (
119
+ <li
120
+ key={i}
121
+ onClick={() => toggleTodo(i)}
122
+ style={{ textDecoration: todo.done ? 'line-through' : 'none' }
123
+ }
124
+ >
125
+ {todo.text}
126
+ </li>
127
+ ))}
128
+ </ul>
129
+ < input
130
+ onKeyDown={e => {
131
+ if (e.key === 'Enter') {
132
+ addTodo(e.target.value)
133
+ e.target.value = ''
134
+ }
135
+ }}
136
+ />
137
+ </div>
138
+ )
139
+ }
140
+
141
+ console.debug('useObserver example - run in React environment!')