memorio 4.9.31 → 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 +327 -330
- 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 +706 -649
- package/index.d.ts +1 -0
- package/index.js +686 -648
- 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 +561 -374
- package/modules/redux.cjs.map +1 -1
- package/modules/redux.js +561 -374
- 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,63 +1,63 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* react-observer.tsx
|
|
3
|
-
*
|
|
4
|
-
* Scenario: a React component reading reactive `state`. Shows both
|
|
5
|
-
* `useObserver` modes and when to prefer each, plus `typed<T>()` +
|
|
6
|
-
* `registerSchema()` for the paths you don't want to fail silently on a
|
|
7
|
-
* typo or a later rename.
|
|
8
|
-
*/
|
|
9
|
-
import 'memorio'
|
|
10
|
-
import { useReducer } from 'react'
|
|
11
|
-
|
|
12
|
-
interface AppState {
|
|
13
|
-
counter: number
|
|
14
|
-
user: { name: string; email: string }
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// Compile-time safety: same Proxy as the global `state`, no duplicated store.
|
|
18
|
-
const app = memorio.typed<AppState>()
|
|
19
|
-
|
|
20
|
-
// Runtime safety: reject writes that don't match the shape, independent of
|
|
21
|
-
// whatever calls it (including code you didn't write).
|
|
22
|
-
memorio.registerSchema('user', {
|
|
23
|
-
type: 'object',
|
|
24
|
-
required: ['name', 'email'],
|
|
25
|
-
properties: {
|
|
26
|
-
name: { type: 'string', min: 1 },
|
|
27
|
-
email: { type: 'string', pattern: /^[^@]+@[^@]+$/ },
|
|
28
|
-
},
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
export function Counter() {
|
|
32
|
-
const [, forceUpdate] = useReducer((x: number) => x + 1, 0)
|
|
33
|
-
|
|
34
|
-
// Auto-discovery mode: re-runs when any state path *touched inside the
|
|
35
|
-
// callback* changes. Convenient, but re-runs on anything you read there -
|
|
36
|
-
// don't reach for it in a callback that reads many unrelated paths.
|
|
37
|
-
useObserver(() => {
|
|
38
|
-
forceUpdate()
|
|
39
|
-
}, state.counter)
|
|
40
|
-
|
|
41
|
-
return (
|
|
42
|
-
<div>
|
|
43
|
-
<span>Count: {app.counter}</span>
|
|
44
|
-
<button onClick={() => { app.counter += 1 }}>+1</button>
|
|
45
|
-
</div>
|
|
46
|
-
)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export function UserBadge() {
|
|
50
|
-
const [, forceUpdate] = useReducer((x: number) => x + 1, 0)
|
|
51
|
-
|
|
52
|
-
// Explicit deps mode: only re-runs when the listed path(s) change - use
|
|
53
|
-
// this when you want precise control instead of auto-discovery.
|
|
54
|
-
useObserver(() => {
|
|
55
|
-
forceUpdate()
|
|
56
|
-
}, [state.user])
|
|
57
|
-
|
|
58
|
-
return <span>{app.user?.name ?? 'Guest'}</span>
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Rejected at write time by the schema registered above:
|
|
62
|
-
// app.user = { name: 'Sara' } // ❌ missing "email"
|
|
63
|
-
// app.user = { name: 'Sara', email: 'x@y.com' } // ✅
|
|
1
|
+
/**
|
|
2
|
+
* react-observer.tsx
|
|
3
|
+
*
|
|
4
|
+
* Scenario: a React component reading reactive `state`. Shows both
|
|
5
|
+
* `useObserver` modes and when to prefer each, plus `typed<T>()` +
|
|
6
|
+
* `registerSchema()` for the paths you don't want to fail silently on a
|
|
7
|
+
* typo or a later rename.
|
|
8
|
+
*/
|
|
9
|
+
import { memorio, state, useObserver } from 'memorio'
|
|
10
|
+
import { useReducer } from 'react'
|
|
11
|
+
|
|
12
|
+
interface AppState {
|
|
13
|
+
counter: number
|
|
14
|
+
user: { name: string; email: string }
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Compile-time safety: same Proxy as the global `state`, no duplicated store.
|
|
18
|
+
const app = memorio.typed<AppState>()
|
|
19
|
+
|
|
20
|
+
// Runtime safety: reject writes that don't match the shape, independent of
|
|
21
|
+
// whatever calls it (including code you didn't write).
|
|
22
|
+
memorio.registerSchema('user', {
|
|
23
|
+
type: 'object',
|
|
24
|
+
required: ['name', 'email'],
|
|
25
|
+
properties: {
|
|
26
|
+
name: { type: 'string', min: 1 },
|
|
27
|
+
email: { type: 'string', pattern: /^[^@]+@[^@]+$/ },
|
|
28
|
+
},
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
export function Counter() {
|
|
32
|
+
const [, forceUpdate] = useReducer((x: number) => x + 1, 0)
|
|
33
|
+
|
|
34
|
+
// Auto-discovery mode: re-runs when any state path *touched inside the
|
|
35
|
+
// callback* changes. Convenient, but re-runs on anything you read there -
|
|
36
|
+
// don't reach for it in a callback that reads many unrelated paths.
|
|
37
|
+
useObserver(() => {
|
|
38
|
+
forceUpdate()
|
|
39
|
+
}, state.counter)
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<div>
|
|
43
|
+
<span>Count: {app.counter}</span>
|
|
44
|
+
<button onClick={() => { app.counter += 1 }}>+1</button>
|
|
45
|
+
</div>
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function UserBadge() {
|
|
50
|
+
const [, forceUpdate] = useReducer((x: number) => x + 1, 0)
|
|
51
|
+
|
|
52
|
+
// Explicit deps mode: only re-runs when the listed path(s) change - use
|
|
53
|
+
// this when you want precise control instead of auto-discovery.
|
|
54
|
+
useObserver(() => {
|
|
55
|
+
forceUpdate()
|
|
56
|
+
}, [state.user])
|
|
57
|
+
|
|
58
|
+
return <span>{app.user?.name ?? 'Guest'}</span>
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Rejected at write time by the schema registered above:
|
|
62
|
+
// app.user = { name: 'Sara' } // ❌ missing "email"
|
|
63
|
+
// app.user = { name: 'Sara', email: 'x@y.com' } // ✅
|
|
@@ -1,60 +1,60 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* semantic-memory.ts
|
|
3
|
-
*
|
|
4
|
-
* Scenario: an LLM-backed app that needs to remember facts about a user
|
|
5
|
-
* with confidence, source, and expiry - and later retrieve only what's
|
|
6
|
-
* relevant to the current task.
|
|
7
|
-
*
|
|
8
|
-
* Important: `memory.context()` ranks by tags/type/confidence/recency.
|
|
9
|
-
* It is NOT embedding-based semantic search - don't build a feature that
|
|
10
|
-
* assumes free-text meaning-based retrieval on top of this alone.
|
|
11
|
-
*/
|
|
12
|
-
import { memorio } from 'memorio'
|
|
13
|
-
|
|
14
|
-
export async function rememberPreference(
|
|
15
|
-
key: string,
|
|
16
|
-
value: unknown,
|
|
17
|
-
opts: { confidence: number; source: string; tags: string[] }
|
|
18
|
-
) {
|
|
19
|
-
await memorio.memory.remember(key, value, {
|
|
20
|
-
type: 'preference',
|
|
21
|
-
scope: 'local',
|
|
22
|
-
...opts,
|
|
23
|
-
})
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export async function correctPreference(key: string, newValue: unknown, confidence: number) {
|
|
27
|
-
// update() supersedes, it doesn't overwrite - the old entry becomes
|
|
28
|
-
// status: 'superseded' and stays queryable for history/audit purposes.
|
|
29
|
-
await memorio.memory.update(key, newValue, { confidence })
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export async function getRelevantContext(tags: string[]) {
|
|
33
|
-
// This is rule-based retrieval, not semantic similarity search. It's the
|
|
34
|
-
// right tool for "give me what's tagged/confident/recent enough," not for
|
|
35
|
-
// "find the memory that means the same thing as this new sentence."
|
|
36
|
-
return memorio.memory.context({
|
|
37
|
-
tags,
|
|
38
|
-
types: ['preference', 'decision'],
|
|
39
|
-
minConfidence: 0.7,
|
|
40
|
-
maxEntries: 10,
|
|
41
|
-
})
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// If the task genuinely needs meaning-based retrieval over free text (e.g.
|
|
45
|
-
// "find prior notes about a similar problem" rather than "find notes tagged
|
|
46
|
-
// X with confidence > Y"), pair this with a dedicated embedding store for
|
|
47
|
-
// the similarity search, and keep using memorio.memory for the lifecycle
|
|
48
|
-
// metadata (confidence, TTL, supersession) on top of whatever it finds -
|
|
49
|
-
// don't simulate similarity search with context() alone.
|
|
50
|
-
|
|
51
|
-
// Usage:
|
|
52
|
-
// await rememberPreference('user.language', 'Italian', {
|
|
53
|
-
// confidence: 0.92,
|
|
54
|
-
// source: 'conversation',
|
|
55
|
-
// tags: ['user', 'ui'],
|
|
56
|
-
// })
|
|
57
|
-
//
|
|
58
|
-
// await correctPreference('user.language', 'English', 0.95)
|
|
59
|
-
//
|
|
60
|
-
// const ctx = await getRelevantContext(['user'])
|
|
1
|
+
/**
|
|
2
|
+
* semantic-memory.ts
|
|
3
|
+
*
|
|
4
|
+
* Scenario: an LLM-backed app that needs to remember facts about a user
|
|
5
|
+
* with confidence, source, and expiry - and later retrieve only what's
|
|
6
|
+
* relevant to the current task.
|
|
7
|
+
*
|
|
8
|
+
* Important: `memory.context()` ranks by tags/type/confidence/recency.
|
|
9
|
+
* It is NOT embedding-based semantic search - don't build a feature that
|
|
10
|
+
* assumes free-text meaning-based retrieval on top of this alone.
|
|
11
|
+
*/
|
|
12
|
+
import { memorio } from 'memorio'
|
|
13
|
+
|
|
14
|
+
export async function rememberPreference(
|
|
15
|
+
key: string,
|
|
16
|
+
value: unknown,
|
|
17
|
+
opts: { confidence: number; source: string; tags: string[] }
|
|
18
|
+
) {
|
|
19
|
+
await memorio.memory.remember(key, value, {
|
|
20
|
+
type: 'preference',
|
|
21
|
+
scope: 'local',
|
|
22
|
+
...opts,
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function correctPreference(key: string, newValue: unknown, confidence: number) {
|
|
27
|
+
// update() supersedes, it doesn't overwrite - the old entry becomes
|
|
28
|
+
// status: 'superseded' and stays queryable for history/audit purposes.
|
|
29
|
+
await memorio.memory.update(key, newValue, { confidence })
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function getRelevantContext(tags: string[]) {
|
|
33
|
+
// This is rule-based retrieval, not semantic similarity search. It's the
|
|
34
|
+
// right tool for "give me what's tagged/confident/recent enough," not for
|
|
35
|
+
// "find the memory that means the same thing as this new sentence."
|
|
36
|
+
return memorio.memory.context({
|
|
37
|
+
tags,
|
|
38
|
+
types: ['preference', 'decision'],
|
|
39
|
+
minConfidence: 0.7,
|
|
40
|
+
maxEntries: 10,
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// If the task genuinely needs meaning-based retrieval over free text (e.g.
|
|
45
|
+
// "find prior notes about a similar problem" rather than "find notes tagged
|
|
46
|
+
// X with confidence > Y"), pair this with a dedicated embedding store for
|
|
47
|
+
// the similarity search, and keep using memorio.memory for the lifecycle
|
|
48
|
+
// metadata (confidence, TTL, supersession) on top of whatever it finds -
|
|
49
|
+
// don't simulate similarity search with context() alone.
|
|
50
|
+
|
|
51
|
+
// Usage:
|
|
52
|
+
// await rememberPreference('user.language', 'Italian', {
|
|
53
|
+
// confidence: 0.92,
|
|
54
|
+
// source: 'conversation',
|
|
55
|
+
// tags: ['user', 'ui'],
|
|
56
|
+
// })
|
|
57
|
+
//
|
|
58
|
+
// await correctPreference('user.language', 'English', 0.95)
|
|
59
|
+
//
|
|
60
|
+
// const ctx = await getRelevantContext(['user'])
|
|
@@ -1,91 +1,91 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Memorio Session Advanced Example
|
|
3
|
-
*
|
|
4
|
-
* This example shows advanced session (sessionStorage) operations.
|
|
5
|
-
* Session data is cleared when the browser tab closes.
|
|
6
|
-
*
|
|
7
|
-
* Run: npx ts-node examples/session-advanced.ts
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import 'memorio'
|
|
11
|
-
|
|
12
|
-
// ============================================
|
|
13
|
-
// CHECK PERSISTENCE
|
|
14
|
-
// ============================================
|
|
15
|
-
|
|
16
|
-
console.debug('=== Session Persistence Check ===')
|
|
17
|
-
console.debug('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.debug('⚠️ Warning: Using in-memory storage. Data will be lost on process restart!')
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// ============================================
|
|
26
|
-
// AUTHENTICATION
|
|
27
|
-
// ============================================
|
|
28
|
-
|
|
29
|
-
// Store auth token (temporary - cleared when tab closes)
|
|
30
|
-
session.set('authToken', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...')
|
|
31
|
-
session.set('userId', 12345)
|
|
32
|
-
|
|
33
|
-
// Check if user is logged in
|
|
34
|
-
const token = session.get('authToken')
|
|
35
|
-
if (token) {
|
|
36
|
-
console.debug('User is logged in, token:', token.substring(0, 20) + '...')
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// ============================================
|
|
40
|
-
// FORM PROGRESS
|
|
41
|
-
// ============================================
|
|
42
|
-
|
|
43
|
-
// Save form draft
|
|
44
|
-
session.set('formDraft', {
|
|
45
|
-
name: 'Mario',
|
|
46
|
-
email: 'mario@example.com',
|
|
47
|
-
message: 'Hello world!'
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
// Restore on page refresh
|
|
51
|
-
const draft = session.get('formDraft')
|
|
52
|
-
if (draft) {
|
|
53
|
-
console.debug('Restored draft:', draft)
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// ============================================
|
|
57
|
-
// SHOPPING CART
|
|
58
|
-
// ============================================
|
|
59
|
-
|
|
60
|
-
// Store cart items (temporary)
|
|
61
|
-
session.set('cart', [
|
|
62
|
-
{ id: 1, name: 'Super Mushroom', price: 99, qty: 2 },
|
|
63
|
-
{ id: 2, name: 'Fire Flower', price: 199, qty: 1 }
|
|
64
|
-
])
|
|
65
|
-
|
|
66
|
-
// Calculate total
|
|
67
|
-
const cart = session.get('cart') || []
|
|
68
|
-
const total = cart.reduce((sum, item) => sum + (item.price * item.qty), 0)
|
|
69
|
-
console.debug('Cart total:', total)
|
|
70
|
-
|
|
71
|
-
// ============================================
|
|
72
|
-
// SESSION SIZE
|
|
73
|
-
// ============================================
|
|
74
|
-
|
|
75
|
-
// Get session storage size
|
|
76
|
-
console.debug('Session size:', session.size(), 'kilobytes')
|
|
77
|
-
|
|
78
|
-
// ============================================
|
|
79
|
-
// CLEANUP
|
|
80
|
-
// ============================================
|
|
81
|
-
|
|
82
|
-
// Remove specific item
|
|
83
|
-
session.remove('formDraft')
|
|
84
|
-
|
|
85
|
-
// Clear all session data (logout)
|
|
86
|
-
// session.removeAll()
|
|
87
|
-
|
|
88
|
-
// Or use alias
|
|
89
|
-
// session.clearAll()
|
|
90
|
-
|
|
91
|
-
console.debug('Session advanced example complete!')
|
|
1
|
+
/**
|
|
2
|
+
* Memorio Session Advanced Example
|
|
3
|
+
*
|
|
4
|
+
* This example shows advanced session (sessionStorage) operations.
|
|
5
|
+
* Session data is cleared when the browser tab closes.
|
|
6
|
+
*
|
|
7
|
+
* Run: npx ts-node examples/session-advanced.ts
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { session } from 'memorio'
|
|
11
|
+
|
|
12
|
+
// ============================================
|
|
13
|
+
// CHECK PERSISTENCE
|
|
14
|
+
// ============================================
|
|
15
|
+
|
|
16
|
+
console.debug('=== Session Persistence Check ===')
|
|
17
|
+
console.debug('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.debug('⚠️ Warning: Using in-memory storage. Data will be lost on process restart!')
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// ============================================
|
|
26
|
+
// AUTHENTICATION
|
|
27
|
+
// ============================================
|
|
28
|
+
|
|
29
|
+
// Store auth token (temporary - cleared when tab closes)
|
|
30
|
+
session.set('authToken', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...')
|
|
31
|
+
session.set('userId', 12345)
|
|
32
|
+
|
|
33
|
+
// Check if user is logged in
|
|
34
|
+
const token = session.get('authToken')
|
|
35
|
+
if (token) {
|
|
36
|
+
console.debug('User is logged in, token:', token.substring(0, 20) + '...')
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// ============================================
|
|
40
|
+
// FORM PROGRESS
|
|
41
|
+
// ============================================
|
|
42
|
+
|
|
43
|
+
// Save form draft
|
|
44
|
+
session.set('formDraft', {
|
|
45
|
+
name: 'Mario',
|
|
46
|
+
email: 'mario@example.com',
|
|
47
|
+
message: 'Hello world!'
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
// Restore on page refresh
|
|
51
|
+
const draft = session.get('formDraft')
|
|
52
|
+
if (draft) {
|
|
53
|
+
console.debug('Restored draft:', draft)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ============================================
|
|
57
|
+
// SHOPPING CART
|
|
58
|
+
// ============================================
|
|
59
|
+
|
|
60
|
+
// Store cart items (temporary)
|
|
61
|
+
session.set('cart', [
|
|
62
|
+
{ id: 1, name: 'Super Mushroom', price: 99, qty: 2 },
|
|
63
|
+
{ id: 2, name: 'Fire Flower', price: 199, qty: 1 }
|
|
64
|
+
])
|
|
65
|
+
|
|
66
|
+
// Calculate total
|
|
67
|
+
const cart = session.get('cart') || []
|
|
68
|
+
const total = cart.reduce((sum, item) => sum + (item.price * item.qty), 0)
|
|
69
|
+
console.debug('Cart total:', total)
|
|
70
|
+
|
|
71
|
+
// ============================================
|
|
72
|
+
// SESSION SIZE
|
|
73
|
+
// ============================================
|
|
74
|
+
|
|
75
|
+
// Get session storage size
|
|
76
|
+
console.debug('Session size:', session.size(), 'kilobytes')
|
|
77
|
+
|
|
78
|
+
// ============================================
|
|
79
|
+
// CLEANUP
|
|
80
|
+
// ============================================
|
|
81
|
+
|
|
82
|
+
// Remove specific item
|
|
83
|
+
session.remove('formDraft')
|
|
84
|
+
|
|
85
|
+
// Clear all session data (logout)
|
|
86
|
+
// session.removeAll()
|
|
87
|
+
|
|
88
|
+
// Or use alias
|
|
89
|
+
// session.clearAll()
|
|
90
|
+
|
|
91
|
+
console.debug('Session advanced example complete!')
|
|
@@ -1,57 +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.
|
|
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.
|