memorio 2.7.4 → 2.9.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 +470 -370
- package/docs/README.md +470 -0
- package/docs/SUMMARY.md +26 -0
- package/docs/_config.yml +1 -0
- package/docs/markdown/CACHE.md +90 -0
- package/docs/markdown/CHANGELOG.md +184 -0
- package/docs/markdown/DEVTOOLS.md +122 -0
- package/docs/markdown/IDB.md +169 -0
- package/docs/markdown/LOGGER.md +147 -0
- package/docs/markdown/OBSERVER.md +180 -0
- package/docs/markdown/PLATFORM.md +260 -0
- package/docs/markdown/SECURITY.md +306 -0
- package/docs/markdown/SESSION.md +154 -0
- package/docs/markdown/STATE.md +150 -0
- package/docs/markdown/STORE.md +161 -0
- package/docs/markdown/USEOBSERVER.md +158 -0
- package/examples/basic.ts +1 -1
- package/examples/cache.ts +1 -1
- package/examples/idb.ts +1 -1
- package/examples/node-server.ts +1 -1
- package/examples/observer.ts +1 -1
- package/examples/platform.ts +1 -1
- package/examples/react-app.tsx +1 -1
- package/examples/session-advanced.ts +1 -1
- package/examples/state-advanced.ts +1 -1
- package/examples/store-advanced.ts +1 -1
- package/examples/{useObserver.ts → useObserver.tsx} +35 -35
- package/index.cjs +47 -29
- package/index.js +47 -27
- package/package.json +1 -1
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
# Memorio Security Documentation
|
|
2
|
+
|
|
3
|
+
> Last Updated: v2.7.0
|
|
4
|
+
|
|
5
|
+
This document describes the security measures implemented in Memorio to protect against common vulnerabilities and ensure safe operation across different platforms.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Security Overview
|
|
10
|
+
|
|
11
|
+
Memorio implements multiple layers of security to protect user data and prevent common attack vectors:
|
|
12
|
+
|
|
13
|
+
| Security Feature | Status | Description |
|
|
14
|
+
|------------------|--------|-------------|
|
|
15
|
+
| Cryptographically Secure IDs | ✅ Enabled | Session/Context IDs use crypto.randomUUID |
|
|
16
|
+
| Input Validation | ✅ Enabled | Key length limits + character filtering |
|
|
17
|
+
| Session Isolation | ✅ Enabled | Unique namespaces per session |
|
|
18
|
+
| Context Isolation | ✅ Enabled | Separate storage per tenant |
|
|
19
|
+
| No Code Injection | ✅ Enabled | No eval() or dynamic code execution |
|
|
20
|
+
| XSS Prevention | ✅ Enabled | No innerHTML or document.write |
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 1. Cryptographically Secure Random Generation
|
|
25
|
+
|
|
26
|
+
### Implementation
|
|
27
|
+
|
|
28
|
+
Session and context IDs are generated using cryptographically secure random values:
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
// config/platform.ts
|
|
32
|
+
function generateSessionId(): string {
|
|
33
|
+
// Priority 1: crypto.randomUUID (most secure)
|
|
34
|
+
if (typeof crypto !== 'undefined' && crypto.randomUUID) {
|
|
35
|
+
return crypto.randomUUID()
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Priority 2: crypto.getRandomValues (secure fallback)
|
|
39
|
+
if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
|
|
40
|
+
const array = new Uint8Array(16)
|
|
41
|
+
crypto.getRandomValues(array)
|
|
42
|
+
return Array.from(array, b => b.toString(16).padStart(2, '0')).join('')
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Priority 3: Math.random (last resort - less secure)
|
|
46
|
+
return `session_${Date.now()}_${Math.random().toString(36).substring(2, 15)}`
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Random Source Priority
|
|
51
|
+
|
|
52
|
+
| Priority | Method | Security Level |
|
|
53
|
+
|----------|--------|----------------|
|
|
54
|
+
| 1 | `crypto.randomUUID()` | 🔒 FIPS 140-2 compliant |
|
|
55
|
+
| 2 | `crypto.getRandomValues()` | 🔒 Cryptographically secure |
|
|
56
|
+
| 3 | `Math.random()` | ⚠️ Not for security purposes |
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## 2. Input Validation & Key Sanitization
|
|
61
|
+
|
|
62
|
+
All storage keys are validated before use to prevent injection attacks:
|
|
63
|
+
|
|
64
|
+
### Validation Rules
|
|
65
|
+
|
|
66
|
+
| Rule | Limit | Action on Violation |
|
|
67
|
+
|------|-------|---------------------|
|
|
68
|
+
| Key Length | Max 512 chars | Reject with debug message |
|
|
69
|
+
| Character Set | `[a-zA-Z0-9_.-]` | Reject with debug message |
|
|
70
|
+
| Type Check | Must be string | Return empty/null |
|
|
71
|
+
|
|
72
|
+
### Implementation
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
function _prefixKey(name: string): string {
|
|
76
|
+
// Validate key
|
|
77
|
+
if (!name || typeof name !== 'string') return ''
|
|
78
|
+
if (name.length > 512) {
|
|
79
|
+
console.debug('Key too long (max 512 characters)')
|
|
80
|
+
return ''
|
|
81
|
+
}
|
|
82
|
+
// Sanitize: only allow alphanumeric, underscore, dash, dot
|
|
83
|
+
if (!/^[a-zA-Z0-9_.-]+$/.test(name)) {
|
|
84
|
+
console.debug('Key contains invalid characters')
|
|
85
|
+
return ''
|
|
86
|
+
}
|
|
87
|
+
return _sessionPrefix + name
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Allowed Characters Table
|
|
92
|
+
|
|
93
|
+
| Character Type | Allowed | Example |
|
|
94
|
+
|----------------|---------|---------|
|
|
95
|
+
| Lowercase | ✅ | `username`, `user_data` |
|
|
96
|
+
| Uppercase | ✅ | `USER`, `UserName` |
|
|
97
|
+
| Numbers | ✅ | `user123`, `data_2024` |
|
|
98
|
+
| Underscore | ✅ | `user_name`, `_private` |
|
|
99
|
+
| Dash | ✅ | `user-id`, `data-set` |
|
|
100
|
+
| Dot | ✅ | `user.profile`, `data.json` |
|
|
101
|
+
| Special Chars | ❌ | `<script>`, `../../../etc` |
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## 3. Session Isolation
|
|
106
|
+
|
|
107
|
+
Each session gets a unique namespace to prevent data leakage:
|
|
108
|
+
|
|
109
|
+
### Isolation Mechanism
|
|
110
|
+
|
|
111
|
+
| Component | Isolation Method |
|
|
112
|
+
|-----------|-----------------|
|
|
113
|
+
| Session ID | `crypto.randomUUID()` |
|
|
114
|
+
| Store Keys | `memorio_store_[uuid]_keyname` |
|
|
115
|
+
| Session Keys | `memorio_session_[uuid]_keyname` |
|
|
116
|
+
| State | In-memory (per-instance) |
|
|
117
|
+
|
|
118
|
+
### Key Prefix Format
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
memorio_store_[session-uuid]_username
|
|
122
|
+
memorio_session_[session-uuid]_auth-token
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Cross-Session Protection
|
|
126
|
+
|
|
127
|
+
| Scenario | Protection |
|
|
128
|
+
|----------|------------|
|
|
129
|
+
| Browser Tabs | Each tab has unique session ID |
|
|
130
|
+
| Server Requests | Each request can use separate context |
|
|
131
|
+
| Multi-Tenant | `memorio.createContext()` isolates tenants |
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## 4. Context Isolation (Multi-Tenant)
|
|
136
|
+
|
|
137
|
+
For server-side applications, contexts provide complete data isolation:
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
// Create isolated context per tenant
|
|
141
|
+
const tenantA = memorio.createContext('tenant-A')
|
|
142
|
+
const tenantB = memorio.createContext('tenant-B')
|
|
143
|
+
|
|
144
|
+
// Each context has completely separate storage
|
|
145
|
+
tenantA.state.secret = 'Tenant A data' // Isolated
|
|
146
|
+
tenantB.state.secret = 'Tenant B data' // Isolated
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Context Security
|
|
150
|
+
|
|
151
|
+
| Feature | Description |
|
|
152
|
+
|---------|-------------|
|
|
153
|
+
| Unique ID | Each context gets unique identifier |
|
|
154
|
+
| Separate Storage | State, Store, Session, Cache all isolated |
|
|
155
|
+
| No Cross-Context Access | Impossible to read other contexts |
|
|
156
|
+
| Cleanup | `deleteContext()` removes all data |
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## 5. Data Serialization Security
|
|
161
|
+
|
|
162
|
+
### Safe Operations
|
|
163
|
+
|
|
164
|
+
| Operation | Security Measure |
|
|
165
|
+
|-----------|-----------------|
|
|
166
|
+
| `store.set()` | JSON.stringify only allowed types |
|
|
167
|
+
| `store.get()` | JSON.parse with try-catch |
|
|
168
|
+
| Functions | Blocked with debug message |
|
|
169
|
+
| Objects | Deep-cloned on read |
|
|
170
|
+
|
|
171
|
+
### Blocked Types
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
// These are blocked and logged:
|
|
175
|
+
store.set('myFunc', () => {}) // "It's not secure to store functions."
|
|
176
|
+
store.set('mySymbol', Symbol('test')) // Would fail serialization
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## 6. Platform-Specific Security
|
|
182
|
+
|
|
183
|
+
### Browser Environment
|
|
184
|
+
|
|
185
|
+
| Feature | Security |
|
|
186
|
+
|---------|----------|
|
|
187
|
+
| localStorage | Same-origin policy applies |
|
|
188
|
+
| sessionStorage | Tab isolation |
|
|
189
|
+
| IndexedDB | Same-origin policy |
|
|
190
|
+
| HTTPS Required | Recommended for production |
|
|
191
|
+
|
|
192
|
+
### Server Environment (Node.js/Deno)
|
|
193
|
+
|
|
194
|
+
| Feature | Security |
|
|
195
|
+
|---------|----------|
|
|
196
|
+
| In-Memory Storage | Process-scoped only |
|
|
197
|
+
| Context Isolation | Per-request isolation recommended |
|
|
198
|
+
| No Persistence | Data lost on restart (by design) |
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## 7. Security Best Practices
|
|
203
|
+
|
|
204
|
+
### For Developers
|
|
205
|
+
|
|
206
|
+
1. **Use Contexts in Server Apps**
|
|
207
|
+
```typescript
|
|
208
|
+
// Express middleware
|
|
209
|
+
app.use((req, res, next) => {
|
|
210
|
+
req.memorio = memorio.createContext(`req-${req.id}`)
|
|
211
|
+
next()
|
|
212
|
+
})
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
2. **Validate Keys**
|
|
216
|
+
```typescript
|
|
217
|
+
// Don't use user input directly as keys
|
|
218
|
+
const safeKey = sanitize(userInput) // Input validation
|
|
219
|
+
store.set(safeKey, value)
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
3. **Check Persistence**
|
|
223
|
+
```typescript
|
|
224
|
+
if (!store.isPersistent) {
|
|
225
|
+
console.warn('Data not persisted!')
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
4. **Clear Sensitive Data**
|
|
230
|
+
```typescript
|
|
231
|
+
// On logout
|
|
232
|
+
session.removeAll()
|
|
233
|
+
state.removeAll()
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### For Security Audits
|
|
237
|
+
|
|
238
|
+
| Check | Location |
|
|
239
|
+
|-------|----------|
|
|
240
|
+
| Random Generation | `config/platform.ts:44` |
|
|
241
|
+
| Key Validation | `functions/store/index.ts:31` |
|
|
242
|
+
| Session Isolation | `functions/session/index.ts:27` |
|
|
243
|
+
| Context System | `config/platform.ts:301` |
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## 8. Vulnerability Prevention
|
|
248
|
+
|
|
249
|
+
### Prevention Matrix
|
|
250
|
+
|
|
251
|
+
| Vulnerability | Prevention | Status |
|
|
252
|
+
|---------------|------------|--------|
|
|
253
|
+
| XSS | No innerHTML/document.write | ✅ |
|
|
254
|
+
| Code Injection | No eval/Function | ✅ |
|
|
255
|
+
| Key Injection | Character whitelist | ✅ |
|
|
256
|
+
| DoS | 512 char key limit | ✅ |
|
|
257
|
+
| Session Hijacking | Unique session IDs | ✅ |
|
|
258
|
+
| Data Leakage | Namespace isolation | ✅ |
|
|
259
|
+
| CSRF | Browser Same-Origin | ✅ |
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
## 9. Compliance
|
|
264
|
+
|
|
265
|
+
### Standards Alignment
|
|
266
|
+
|
|
267
|
+
| Standard | Compliance |
|
|
268
|
+
|----------|------------|
|
|
269
|
+
| NIST SP 800-53 | ✅ Cryptographic standards |
|
|
270
|
+
| OWASP Top 10 | ✅ Key injection prevention |
|
|
271
|
+
| CWE | ✅ Common weaknesses addressed |
|
|
272
|
+
| FIPS 140-2 | ✅ crypto.randomUUID |
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
## 10. Reporting Security Issues
|
|
277
|
+
|
|
278
|
+
If you discover a security vulnerability in Memorio:
|
|
279
|
+
|
|
280
|
+
1. **Do NOT** open a public GitHub issue
|
|
281
|
+
2. **Email**: security@example.com (replace with actual contact)
|
|
282
|
+
3. **Include**: Vulnerability details, steps to reproduce, potential impact
|
|
283
|
+
|
|
284
|
+
### Response Timeline
|
|
285
|
+
|
|
286
|
+
| Phase | Timeline |
|
|
287
|
+
|-------|----------|
|
|
288
|
+
| Acknowledgment | 48 hours |
|
|
289
|
+
| Initial Assessment | 7 days |
|
|
290
|
+
| Fix Released | Based on severity |
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
## Security Changelog
|
|
295
|
+
|
|
296
|
+
### v2.7.0 (Current)
|
|
297
|
+
|
|
298
|
+
- ✅ Added crypto.getRandomValues() fallback
|
|
299
|
+
- ✅ Added key length validation (512 chars)
|
|
300
|
+
- ✅ Added character whitelist validation
|
|
301
|
+
- ✅ Improved session isolation
|
|
302
|
+
- ✅ Context isolation for multi-tenancy
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
*This document was last updated for Memorio v2.7.0*
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# Session - Memorio
|
|
2
|
+
|
|
3
|
+
> 🖥️ **Browser & Edge**: Uses sessionStorage for persistence
|
|
4
|
+
> ⚙️ **Node.js/Deno**: Falls back to in-memory storage (not persistent)
|
|
5
|
+
|
|
6
|
+
Session provides temporary storage using browser sessionStorage. Data persists until the tab or window is closed.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install memorio
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
```javascript
|
|
15
|
+
import 'memorio';
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Quick Examples
|
|
21
|
+
|
|
22
|
+
### Example 1: Basic Usage
|
|
23
|
+
|
|
24
|
+
```javascript
|
|
25
|
+
// Save session data
|
|
26
|
+
session.set('token', 'abc123');
|
|
27
|
+
session.set('userId', 42);
|
|
28
|
+
|
|
29
|
+
// Read session data
|
|
30
|
+
console.log(session.get('token')); // "abc123"
|
|
31
|
+
|
|
32
|
+
// Check persistence
|
|
33
|
+
console.log(session.isPersistent); // true in browser, false in Node.js/Deno
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Example 2: Intermediate
|
|
37
|
+
|
|
38
|
+
```javascript
|
|
39
|
+
// Store objects
|
|
40
|
+
session.set('user', { name: 'Mario', role: 'admin' });
|
|
41
|
+
|
|
42
|
+
// Remove specific item
|
|
43
|
+
session.remove('token');
|
|
44
|
+
|
|
45
|
+
// Clear all session data
|
|
46
|
+
session.removeAll();
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Example 3: Advanced
|
|
50
|
+
|
|
51
|
+
```javascript
|
|
52
|
+
// Check if session has data
|
|
53
|
+
if (session.get('authToken')) {
|
|
54
|
+
// User is logged in
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Get storage quota (returns Promise<[usage, quota]> in KB)
|
|
58
|
+
const [used, total] = await session.quota();
|
|
59
|
+
console.log(`Using ${used} out of ${total} KB`);
|
|
60
|
+
|
|
61
|
+
// Get total size in characters
|
|
62
|
+
const size = session.size();
|
|
63
|
+
console.log(`${size} bytes`);
|
|
64
|
+
|
|
65
|
+
// Handle session expiry
|
|
66
|
+
window.addEventListener('storage', (e) => {
|
|
67
|
+
if (e.key === 'session' && !e.newValue) {
|
|
68
|
+
// Session cleared
|
|
69
|
+
redirectToLogin();
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## API Reference
|
|
77
|
+
|
|
78
|
+
### Methods
|
|
79
|
+
|
|
80
|
+
| Method | Parameters | Returns | Description |
|
|
81
|
+
|--------|------------|---------|-------------|
|
|
82
|
+
| `session.get(name)` | `name: string` | `any` | Get value from session |
|
|
83
|
+
| `session.set(name, value)` | `name: string, value: any` | `void` | Save value to session |
|
|
84
|
+
| `session.remove(name)` | `name: string` | `boolean` | Remove single item |
|
|
85
|
+
| `session.delete(name)` | `name: string` | `boolean` | Alias for remove |
|
|
86
|
+
| `session.removeAll()` | `none` | `boolean` | Clear all session data |
|
|
87
|
+
| `session.clearAll()` | `none` | `boolean` | Alias for removeAll |
|
|
88
|
+
| `session.size()` | `none` | `number` | Get total size in characters |
|
|
89
|
+
| `session.quota()` | `none` | `Promise<[number, number]>` | Get storage usage/quota in KB |
|
|
90
|
+
|
|
91
|
+
### Properties
|
|
92
|
+
|
|
93
|
+
| Property | Type | Description |
|
|
94
|
+
|----------|------|-------------|
|
|
95
|
+
| `session.isPersistent` | `boolean` | `true` if using real sessionStorage, `false` if in-memory fallback |
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Store vs Session
|
|
100
|
+
|
|
101
|
+
| Feature | Store | Session |
|
|
102
|
+
|---------|-------|---------|
|
|
103
|
+
| Storage | localStorage | sessionStorage |
|
|
104
|
+
| Lifetime | Forever | Until tab closes |
|
|
105
|
+
| Use case | User preferences | Temporary auth |
|
|
106
|
+
| Shared across tabs | Yes | No |
|
|
107
|
+
| Platform | Browser/Edge | Browser/Edge |
|
|
108
|
+
| Persistence | ✅ Always | ✅ Browser only |
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Platform Notes
|
|
113
|
+
|
|
114
|
+
| Platform | Behavior |
|
|
115
|
+
|----------|----------|
|
|
116
|
+
| Browser | Uses real sessionStorage - data persists until tab closes |
|
|
117
|
+
| Edge Worker | Uses real sessionStorage |
|
|
118
|
+
| Node.js | In-memory fallback - data lost on process restart |
|
|
119
|
+
| Deno | In-memory fallback - data lost on process restart |
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Best Practices
|
|
124
|
+
|
|
125
|
+
1. Use for auth tokens: `session.set('token', jwt)`
|
|
126
|
+
2. Clear on logout: `session.removeAll()`
|
|
127
|
+
3. Don't use for persistent data
|
|
128
|
+
4. Check for null: `session.get('key') || defaultValue`
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Common Use Cases
|
|
133
|
+
|
|
134
|
+
### Authentication
|
|
135
|
+
|
|
136
|
+
```javascript
|
|
137
|
+
// Login
|
|
138
|
+
session.set('authToken', response.token);
|
|
139
|
+
session.set('user', response.user);
|
|
140
|
+
|
|
141
|
+
// Logout
|
|
142
|
+
session.removeAll();
|
|
143
|
+
router.push('/login');
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Form Progress
|
|
147
|
+
|
|
148
|
+
```javascript
|
|
149
|
+
// Save form draft
|
|
150
|
+
session.set('formDraft', formData);
|
|
151
|
+
|
|
152
|
+
// Restore on page refresh
|
|
153
|
+
const draft = session.get('formDraft');
|
|
154
|
+
if (draft) restoreForm(draft);
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# State - Memorio
|
|
2
|
+
|
|
3
|
+
> ✅ **Universal**: Works in Browser, Node.js, Deno, and Edge Workers
|
|
4
|
+
|
|
5
|
+
State is a reactive global state manager using JavaScript Proxies. It's simple, powerful, and requires no setup. Data persists only in memory during the session.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install memorio
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
```javascript
|
|
14
|
+
import 'memorio';
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
That's it. `state` is now global.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Quick Examples
|
|
22
|
+
|
|
23
|
+
### Example 1: Basic Usage
|
|
24
|
+
|
|
25
|
+
```javascript
|
|
26
|
+
// Set a value
|
|
27
|
+
state.name = 'Mario';
|
|
28
|
+
state.age = 25;
|
|
29
|
+
|
|
30
|
+
// Get a value
|
|
31
|
+
console.log(state.name); // "Mario"
|
|
32
|
+
|
|
33
|
+
// Simple object
|
|
34
|
+
state.user = { name: 'Luigi', level: 1 };
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Example 2: Intermediate
|
|
38
|
+
|
|
39
|
+
```javascript
|
|
40
|
+
// Array operations
|
|
41
|
+
state.items = [1, 2, 3];
|
|
42
|
+
state.items.push(4);
|
|
43
|
+
console.log(state.items); // [1, 2, 3, 4]
|
|
44
|
+
|
|
45
|
+
// Nested objects
|
|
46
|
+
state.config = { theme: 'dark', lang: 'en' };
|
|
47
|
+
state.config.theme = 'light';
|
|
48
|
+
|
|
49
|
+
// List all states
|
|
50
|
+
console.log(state.list);
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Example 3: Advanced
|
|
54
|
+
|
|
55
|
+
```javascript
|
|
56
|
+
// Lock state to prevent modifications
|
|
57
|
+
state.frozenConfig = { maxUsers: 100 };
|
|
58
|
+
state.frozenConfig.lock();
|
|
59
|
+
// Now state.frozenConfig cannot be modified
|
|
60
|
+
|
|
61
|
+
// Path tracking
|
|
62
|
+
const path = state.user.path;
|
|
63
|
+
console.log(path.name); // "user"
|
|
64
|
+
console.log(path.profile.name); // "user.profile"
|
|
65
|
+
|
|
66
|
+
// Get full path as string
|
|
67
|
+
console.log(state.user.__path); // "state.user"
|
|
68
|
+
|
|
69
|
+
// Protected keys (internal use)
|
|
70
|
+
console.log(protect); // Array of protected keys
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## API Reference
|
|
76
|
+
|
|
77
|
+
### Properties
|
|
78
|
+
|
|
79
|
+
| Property | Type | Description |
|
|
80
|
+
|----------|------|-------------|
|
|
81
|
+
| `state.list` | Array | Get all current state keys (deep copy) |
|
|
82
|
+
| `state.path` | Object | Get path tracker for current location |
|
|
83
|
+
| `state.__path` | string | Get full path as string |
|
|
84
|
+
|
|
85
|
+
### Methods
|
|
86
|
+
|
|
87
|
+
| Method | Parameters | Description |
|
|
88
|
+
|--------|------------|-------------|
|
|
89
|
+
| `state.remove(key)` | `key: string` | Remove a specific state |
|
|
90
|
+
| `state.removeAll()` | none | Clear all states |
|
|
91
|
+
|
|
92
|
+
### Lock
|
|
93
|
+
|
|
94
|
+
```javascript
|
|
95
|
+
// Lock an object or array
|
|
96
|
+
state.myArray = [1, 2, 3];
|
|
97
|
+
state.myArray.lock();
|
|
98
|
+
|
|
99
|
+
// Now any modification will fail
|
|
100
|
+
state.myArray.push(4); // Error: state 'myArray' is locked
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## How It Works
|
|
106
|
+
|
|
107
|
+
Memorio uses JavaScript `Proxy` to intercept get/set operations on the global `state` object. This allows:
|
|
108
|
+
|
|
109
|
+
1. **Reactivity** - Any change can trigger observers
|
|
110
|
+
2. **Nested objects** - Deep path tracking
|
|
111
|
+
3. **Type safety** - Full TypeScript support
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Platform Notes
|
|
116
|
+
|
|
117
|
+
| Platform | Support | Notes |
|
|
118
|
+
|----------|---------|-------|
|
|
119
|
+
| Browser | ✅ Full | In-memory, lost on refresh |
|
|
120
|
+
| Node.js | ✅ Full | In-memory, lost on restart |
|
|
121
|
+
| Deno | ✅ Full | In-memory, lost on restart |
|
|
122
|
+
| Edge Workers | ✅ Full | In-memory, lost on function cold start |
|
|
123
|
+
|
|
124
|
+
**Note**: In server environments (Node.js/Deno), use `memorio.createContext()` for request isolation.
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Best Practices
|
|
129
|
+
|
|
130
|
+
1. Use descriptive keys: `state.userProfile` not `state.up`
|
|
131
|
+
2. Group related data: `state.cart.items` not `state.cartItems`
|
|
132
|
+
3. Lock static config: `state.appConfig.lock()`
|
|
133
|
+
4. Clean up on logout: `state.removeAll()`
|
|
134
|
+
5. Use path tracking for debugging: `state.myData.__path`
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## Common Errors
|
|
139
|
+
|
|
140
|
+
```javascript
|
|
141
|
+
// Error: protected key
|
|
142
|
+
state._internal = 'value';
|
|
143
|
+
// Output: "key _internal is protected"
|
|
144
|
+
|
|
145
|
+
// Error: locked state
|
|
146
|
+
state.locked = { x: 1 };
|
|
147
|
+
state.locked.lock();
|
|
148
|
+
state.locked.x = 2;
|
|
149
|
+
// Output: "Error: state 'locked' is locked"
|
|
150
|
+
```
|