memorio 4.3.2 → 4.4.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/llms.txt CHANGED
@@ -19,7 +19,7 @@ Memorio provides 6 storage modules plus utilities:
19
19
  | `session` | sessionStorage | Dies with browser tab |
20
20
  | `cache` | In-memory cache | Fastest read, no persistence |
21
21
  | `idb` | IndexedDB | Structured, async, persistent (browser-only) |
22
- | `observer` | Object watcher | Legacy, deprecated |
22
+ | `observer` | Object watcher | Legacy |
23
23
  | `useObserver` | React hook | Auto-discovery of state paths |
24
24
 
25
25
  ## Quick Start
@@ -33,7 +33,7 @@ state.counter = 0
33
33
 
34
34
  // Observe changes (React hook)
35
35
  useObserver(
36
- () => { console.log('counter:', state.counter) },
36
+ () => { console.debug('counter:', state.counter) },
37
37
  [state.counter]
38
38
  )
39
39
  ```
@@ -54,7 +54,7 @@ state.counter = 0
54
54
  const name = state.user.name // 'Sara'
55
55
 
56
56
  // List all keys
57
- console.log(state.list) // ['user', 'items', 'counter']
57
+ console.debug(state.list) // ['user', 'items', 'counter']
58
58
 
59
59
  // Remove one key
60
60
  state.remove('items')
@@ -100,10 +100,10 @@ store.removeAll()
100
100
  store.clearAll()
101
101
 
102
102
  // Check size
103
- console.log(store.size(), 'chars stored')
103
+ console.debug(store.size(), 'chars stored')
104
104
 
105
105
  // Check if persistent (real localStorage vs memory fallback)
106
- console.log(store.isPersistent) // true → real localStorage
106
+ console.debug(store.isPersistent) // true → real localStorage
107
107
 
108
108
  // Estimate quota usage (returns [0, 0] for localStorage)
109
109
  await store.quota() // [usage, quota] in KB
@@ -136,10 +136,10 @@ session.removeAll()
136
136
  session.clearAll()
137
137
 
138
138
  // Check size
139
- console.log(session.size(), 'chars stored')
139
+ console.debug(session.size(), 'chars stored')
140
140
 
141
141
  // Check if persistent
142
- console.log(session.isPersistent)
142
+ console.debug(session.isPersistent)
143
143
  ```
144
144
 
145
145
  ### `cache` — In-Memory Cache
@@ -215,14 +215,14 @@ const [usage, quota] = await idb.db.quota()
215
215
 
216
216
  **Note:** In Node.js/Deno, `idb` is disabled with a warning. Use `store` or `session` instead.
217
217
 
218
- ### `observer` — Object Watcher (DEPRECATED)
218
+ ### `observer` — Object Watcher
219
219
 
220
220
  Legacy observer API. Use `useObserver` instead.
221
221
 
222
222
  ```javascript
223
223
  // Listen to state changes
224
224
  observer('state.user', (newVal, oldVal) => {
225
- console.log('user changed:', newVal, oldVal)
225
+ console.debug('user changed:', newVal, oldVal)
226
226
  })
227
227
 
228
228
  // Toggle listening (no callback)
@@ -232,7 +232,7 @@ observer('state.counter')
232
232
  observer.remove('state.user')
233
233
 
234
234
  // List all observers
235
- console.log(observer.list)
235
+ console.debug(observer.list)
236
236
 
237
237
  // Remove all observers
238
238
  observer.removeAll()
@@ -251,12 +251,12 @@ function Counter() {
251
251
 
252
252
  // Auto-discovery mode (no deps)
253
253
  useObserver(() => {
254
- console.log('counter:', state.counter)
254
+ console.debug('counter:', state.counter)
255
255
  }, state.counter)
256
256
 
257
257
  // Explicit deps mode
258
258
  useObserver(
259
- () => { console.log('user:', state.user) },
259
+ () => { console.debug('user:', state.user) },
260
260
  [state.user]
261
261
  )
262
262
 
@@ -308,8 +308,8 @@ Track every state change with timestamps.
308
308
 
309
309
  ```javascript
310
310
  // Configure logger
311
- memorio.logger.configure({
312
- enabled: true,
311
+ memorio.logger.configure({
312
+ enabled: true,
313
313
  logToConsole: true,
314
314
  modules: ['state', 'store', 'session', 'cache', 'idb']
315
315
  })
@@ -345,7 +345,7 @@ memorio.createContext('tenant-name')
345
345
  memorio.listContexts()
346
346
  memorio.deleteContext('context-id')
347
347
  memorio.isolate('tenant-name') // alias for createContext
348
- ```
348
+ ```
349
349
 
350
350
  ## Cross-Platform Support
351
351
 
@@ -397,7 +397,7 @@ memorio.dispatch.set('custom:event', { detail: { data: 'value' } })
397
397
 
398
398
  // Listen for an event
399
399
  memorio.dispatch.listen('custom:event', (e) => {
400
- console.log('Event triggered:', e.detail)
400
+ console.debug('Event triggered:', e.detail)
401
401
  })
402
402
 
403
403
  // Remove listener
@@ -56,7 +56,7 @@ All notable changes to this project will be documented in this file.
56
56
 
57
57
  ### 📝 Documentation Updates
58
58
 
59
- - `docs/README.md`: replaced `console.log` with `console.debug` in usage examples; fixed `esbuild` badge → `tsup`
59
+ - `docs/README.md`: replaced `console.debug` with `console.debug` in usage examples; fixed `esbuild` badge → `tsup`
60
60
  - `.github/CHANGELOG.md`: restructured with fix / security / changed sections
61
61
  - `.github/HISTORY.md`: complete rewrite through v3.0.2
62
62
  - `.github/SECURITY.md`: NIST/NSA standard + OWASP Top 10 mapping
@@ -0,0 +1,168 @@
1
+ # Dispatch - Memorio
2
+
3
+ > ⚛️ **Vanilla JS**: This is for non-React applications. For React, use [`useObserver`](USEOBSERVER.md).
4
+
5
+ `memorio.dispatch` is an event system for vanilla JavaScript applications. It enables pub/sub patterns without React hooks.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install memorio
11
+ ```
12
+
13
+ ```javascript
14
+ import 'memorio';
15
+ ```
16
+
17
+ ---
18
+
19
+ ## Quick Examples
20
+
21
+ ### Example 1: Basic Event Listening
22
+
23
+ ```javascript
24
+ // Listen for an event
25
+ memorio.dispatch.listen('my:event', (event) => {
26
+ console.debug('Event triggered:', event.detail);
27
+ });
28
+
29
+ // Trigger the event
30
+ memorio.dispatch.set('my:event', { detail: { data: 'Hello World' } });
31
+ // Output: "Event triggered: { data: 'Hello World' }"
32
+ ```
33
+
34
+ ### Example 2: State Reactivity (Vanilla JS)
35
+
36
+ ```javascript
37
+ // React to state changes without React
38
+ memorio.dispatch.listen('state.counter', (event) => {
39
+ console.debug('Counter is now:', event.detail);
40
+ });
41
+
42
+ // Update state
43
+ state.counter = 1;
44
+ // Output: "Counter is now: 1"
45
+
46
+ state.counter = 5;
47
+ // Output: "Counter is now: 5"
48
+ ```
49
+
50
+ ### Example 3: Remove Listener
51
+
52
+ ```javascript
53
+ // Remove a specific event listener
54
+ memorio.dispatch.remove('my:event');
55
+
56
+ // Or remove all listeners for state changes
57
+ memorio.dispatch.remove('state.user');
58
+ ```
59
+
60
+ ---
61
+
62
+ ## API Reference
63
+
64
+ ### memorio.dispatch.set(name, value)
65
+
66
+ Dispatches a custom event with the specified name and value.
67
+
68
+ | Parameter | Type | Description |
69
+ |-----------|------|-------------|
70
+ | `name` | `string` | Event name (e.g., `'my:event'`, `'state.counter'`) |
71
+ | `value` | `object` | Object with `detail` property (default: `{}`) |
72
+
73
+ ```javascript
74
+ memorio.dispatch.set('custom:event', { detail: { data: 'value' } });
75
+ ```
76
+
77
+ ### memorio.dispatch.listen(name, callback)
78
+
79
+ Listens for the specified event and executes the callback when triggered.
80
+
81
+ | Parameter | Type | Description |
82
+ |-----------|------|-------------|
83
+ | `name` | `string` | Event name to listen for |
84
+ | `callback` | `function` | Function called with the event object |
85
+
86
+ ```javascript
87
+ memorio.dispatch.listen('state.user', (event) => {
88
+ console.debug('User changed:', event.detail);
89
+ });
90
+ ```
91
+
92
+ ### memorio.dispatch.remove(name)
93
+
94
+ Removes the event listener for the specified event name.
95
+
96
+ | Parameter | Type | Description |
97
+ |-----------|------|-------------|
98
+ | `name` | `string` | Event name to stop listening |
99
+
100
+ ```javascript
101
+ memorio.dispatch.remove('state.counter');
102
+ ```
103
+
104
+ ---
105
+
106
+ ## Common Patterns
107
+
108
+ ### Form Validation
109
+
110
+ ```javascript
111
+ memorio.dispatch.listen('state.form.email', (event) => {
112
+ const email = event.detail;
113
+ const isValid = email.includes('@');
114
+ state.form.isValid = isValid;
115
+ });
116
+ ```
117
+
118
+ ### Analytics Tracking
119
+
120
+ ```javascript
121
+ memorio.dispatch.listen('state.page', (event) => {
122
+ const page = event.detail;
123
+ analytics.track('page_view', { page });
124
+ });
125
+ ```
126
+
127
+ ### Auto-save
128
+
129
+ ```javascript
130
+ memorio.dispatch.listen('state.draft', (event) => {
131
+ const content = event.detail;
132
+ store.set('autosave', content);
133
+ });
134
+ ```
135
+
136
+ ### Multiple Listeners
137
+
138
+ ```javascript
139
+ // Listen for multiple state changes
140
+ memorio.dispatch.listen('state.user', (e) => console.log('User:', e.detail));
141
+ memorio.dispatch.listen('state.settings', (e) => console.log('Settings:', e.detail));
142
+ ```
143
+
144
+ ---
145
+
146
+ ## Migration from observer()
147
+
148
+ The `observer()` Replace it with `memorio.dispatch.listen()`:
149
+
150
+ ```javascript
151
+ observer('state.counter', (newValue) => {
152
+ console.debug('Counter:', newValue);
153
+ });
154
+
155
+ // NEW (recommended for vanilla JS)
156
+ memorio.dispatch.listen('state.counter', (event) => {
157
+ console.debug('Counter:', event.detail);
158
+ });
159
+ ```
160
+
161
+ ---
162
+
163
+ ## Best Practices
164
+
165
+ 1. Use specific event names: `'state.user.name'` not `'state'`
166
+ 2. Clean up listeners when no longer needed with `memorio.dispatch.remove()`
167
+ 3. Use `event.detail` to access the value
168
+ 4. For React applications, use [`useObserver`](USEOBSERVER.md) instead
@@ -1,7 +1,5 @@
1
1
  # Observer - Memorio
2
2
 
3
- > ⚠️ **DEPRECATED**: This function is deprecated and will be removed in future versions. Please use [`useObserver`](USEOBSERVER.md) instead.
4
-
5
3
  Observer lets you react to state changes. When a state key changes, your callback function runs.
6
4
 
7
5
  ## Installation
@@ -21,9 +19,6 @@ import 'memorio';
21
19
  ### Example 1: Basic Usage
22
20
 
23
21
  ```javascript
24
- // Simple observer (DEPRECATED - use useObserver instead)
25
- console.warn('observer() is deprecated. Please use useObserver() for React or memorio.dispatch for vanilla JS.');
26
-
27
22
  observer('state.counter', (newValue) => {
28
23
  console.debug('Counter is now:', newValue);
29
24
  });
@@ -35,8 +30,6 @@ state.counter = 5;
35
30
  // Output: "Counter is now: 5"
36
31
  ```
37
32
 
38
- > **Note**: For new projects, use [`useObserver`](USEOBSERVER.md) for React or [`memorio.dispatch`](SUMMARY.md) for vanilla JS.
39
-
40
33
  ### Example 2: Intermediate
41
34
 
42
35
  ```javascript
@@ -55,9 +48,8 @@ state.user = { name: 'Luigi' };
55
48
  ### Example 3: Advanced
56
49
 
57
50
  ```javascript
58
- // Multiple observers
59
- const obs1 = observer('state.data', handler1);
60
- const obs2 = observer('state.data', handler2);
51
+ // Multiple callbacks for same path
52
+ observer('state.data', [handler1, handler2]);
61
53
 
62
54
  // List all observers
63
55
  console.debug(observer.list);
@@ -68,18 +60,45 @@ observer.remove('state.data');
68
60
 
69
61
  // Remove all observers
70
62
  observer.removeAll();
63
+
64
+ // Check if observer exists
65
+ if (observer.has('state.counter')) {
66
+ console.debug('Observer exists for counter');
67
+ }
68
+ ```
69
+
70
+ ---
71
+
72
+ ## Direct Values (Vanilla JS)
73
+
74
+ Observer supports direct state values, not just string paths:
75
+
76
+ ```javascript
77
+ // Direct value - no string needed!
78
+ observer(state.counter, (newValue) => {
79
+ console.debug('Counter:', newValue);
80
+ });
81
+
82
+ // Works with objects too
83
+ observer(state.user, (newValue) => {
84
+ console.debug('User:', newValue?.name);
85
+ });
86
+
87
+ // Multiple callbacks as array
88
+ observer('state.data', [cb1, cb2, cb3]);
71
89
  ```
72
90
 
73
91
  ---
74
92
 
75
93
  ## API Reference
76
94
 
77
- ### observer(path, callback)
95
+ ### observer(path, callback, option)
78
96
 
79
97
  | Parameter | Type | Description |
80
98
  |-----------|------|-------------|
81
- | `path` | `string` | State path to watch (e.g., `'state.counter'`) |
82
- | `callback` | `function` | Function called on change |
99
+ | `path` | `string \| object` | State path (e.g., `'state.counter'`) or direct state value |
100
+ | `callback` | `function \| array` | Function(s) called on change. Can be a single function or array of functions |
101
+ | `option` | `boolean` | Listen continuously (default: `true`) |
83
102
 
84
103
  ### Callback Parameters
85
104
 
@@ -102,6 +121,7 @@ observer('state.key', (newValue, oldValue) => {
102
121
  |--------|------------|-------------|
103
122
  | `observer.remove(name)` | `string` | Remove observer for specific path |
104
123
  | `observer.removeAll()` | none | Remove all observers |
124
+ | `observer.has(name)` | `string` | Check if observer exists for path (returns boolean) |
105
125
 
106
126
  ---
107
127
 
@@ -24,6 +24,7 @@ import 'memorio';
24
24
  import 'memorio';
25
25
 
26
26
  function Counter() {
27
+ // Direct values work! ✅
27
28
  useObserver(() => {
28
29
  console.debug('Counter changed:', state.counter);
29
30
  }, [state.counter]);
@@ -63,14 +64,23 @@ function UserProfile() {
63
64
  ### Example 3: Advanced
64
65
 
65
66
  ```javascript
66
- // Multiple watchers with array
67
+ // Multiple watchers with array - works with direct values
67
68
  function MultiWatch() {
68
69
  useObserver(() => {
69
70
  console.debug('A or B changed:', state.a, state.b);
70
- }, [state.a, state.b]);
71
-
71
+ }, [state.a, state.b]); // Direct values work!
72
+
72
73
  return <div>{state.a} - {state.b}</div>;
73
74
  }
75
+
76
+ // With string path (for store)
77
+ function StoreWatcher() {
78
+ useObserver(() => {
79
+ console.debug('Store changed');
80
+ }, 'store.userPreferences');
81
+
82
+ return <div />;
83
+ }
74
84
  ```
75
85
  // With string path (for store)
76
86
  function StoreWatcher() {
@@ -91,15 +101,49 @@ function StoreWatcher() {
91
101
  | Parameter | Type | Description |
92
102
  | --------- | ---- | ----------- |
93
103
  | `callback` | `function` | Function to run on change |
94
- | `deps` | `function \| string \| array` | State path(s) to watch |
104
+ | `deps` | `function \| string \| array \| proxy` | State path(s) to watch. Supports: |
105
+ | | | - Direct values: `state.counter` |
106
+ | | | - String paths: `'state.counter'` |
107
+ | | | - Arrow functions: `() => state.counter` |
108
+ | | | - Arrays: `[state.a, state.b]` or `['state.a', 'state.b']` |
109
+ | | | - Optional chaining: `[state?.one]` |
110
+
111
+ ### Primitive Values
112
+
113
+ Direct primitive values are now fully supported:
114
+
115
+ ```javascript
116
+ // Direct values work with primitives ✅
117
+ useObserver(() => { console.debug('changed') }, [state.counter])
118
+
119
+ // Arrays of primitives work ✅
120
+ useObserver(() => { console.log(state.a, state.b) }, [state.a, state.b])
121
+
122
+ // Optional chaining works ✅
123
+ useObserver(() => { console.log(state?.one) }, [state?.one])
124
+
125
+ // Strings still work ✅
126
+ useObserver(() => { console.debug('changed') }, ['state.counter'])
127
+
128
+ // Functions still work ✅
129
+ useObserver(() => { console.debug('changed') }, [() => state.counter])
130
+ ```
95
131
 
96
132
  ### Callback Parameters
97
133
 
98
134
  ```javascript
135
+ // Single value (no array needed)
99
136
  useObserver(
100
137
  () => {
101
138
  console.debug('Changed:', state.key);
102
- }, [state.key]
139
+ }, state.key // Single value works!
140
+ );
141
+
142
+ // Array of values
143
+ useObserver(
144
+ () => {
145
+ console.debug('Changed:', state.key);
146
+ }, [state.key] // Array also works!
103
147
  );
104
148
  ```
105
149
 
@@ -129,24 +173,79 @@ Returns a cleanup function:
129
173
 
130
174
  ## Common Patterns
131
175
 
132
- ### Sync with useState
176
+ ### Sync with useState (Recommended for Primitives)
133
177
 
134
178
  ```javascript
135
- function MyComponent() {
136
- const [data, setData] = useState(null);
179
+ function CounterComponent() {
180
+ // Sync memorio state with React state
181
+ const [counter, setCounter] = useState(state.counter)
182
+
183
+ // React useEffect works correctly with primitive values
184
+ useEffect(() => {
185
+ console.log('Counter changed:', counter)
186
+ }, [counter])
187
+
188
+ // Direct values now work with primitives!
189
+ useObserver(() => {
190
+ setCounter(state.counter)
191
+ }, [state.counter]) // ✅ Works now!
192
+
193
+ return (
194
+ <div>
195
+ <button onClick={() => { state.counter++ }}>Increment</button>
196
+ <span>{counter}</span>
197
+ </div>
198
+ )
199
+ }
200
+ ```
137
201
 
138
- useObserver((newVal) => {
139
- setData(newVal);
140
- }, [state.data]);
202
+ ### Safe Access with Optional Chaining (Protection)
141
203
 
142
- return <div>{data}</div>;
204
+ ```javascript
205
+ // Optional chaining is supported - protects against errors
206
+ useObserver(() => {
207
+ if (test?.one) {
208
+ console.log(test.one)
209
+ }
210
+ }, [test?.one])
211
+
212
+ // Works with objects
213
+ function SafeComponent() {
214
+ const [test, setTest] = useState(state.test)
215
+
216
+ useEffect(() => {
217
+ if (test?.one) {
218
+ console.log('test.one:', test.one)
219
+ }
220
+ }, [test?.one])
221
+
222
+ useObserver(() => {
223
+ if (state.test?.one) {
224
+ setTest(state.test)
225
+ }
226
+ }, ['state.test.one'])
227
+
228
+ return <div>{test?.one || 'Loading...'}</div>
143
229
  }
144
230
  ```
145
231
 
146
232
  ### Multiple Watchers
147
233
 
148
234
  ```javascript
149
- }, [state.a, state.b]);
235
+ // Watch multiple properties with strings
236
+ useObserver(() => {
237
+ console.log(state.a, state.b)
238
+ }, ['state.a', 'state.b'])
239
+
240
+ // Watch multiple properties with functions
241
+ useObserver(() => {
242
+ console.log(state.a, state.b)
243
+ }, [() => state.a, () => state.b])
244
+
245
+ // Watch with auto-discovery
246
+ useObserver(() => {
247
+ console.log(state.a, state.b) // Automatically tracks both
248
+ }, [])
150
249
  ```
151
250
 
152
251
  ---
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "memorio",
3
3
  "codeName": "memorio",
4
- "version": "4.3.2",
4
+ "version": "4.4.0",
5
5
  "description": "Memorio, State + Observer, Store and iDB for an easy life - Cross-platform compatible",
6
6
  "main": "./index.cjs",
7
7
  "browser": "./index.cjs",
@@ -55,14 +55,11 @@
55
55
  "url": "https://dario.passariello.ca/",
56
56
  "email": "dariopassariello@gmail.com"
57
57
  },
58
- "dependencies": {
59
- "baseline-browser-mapping": "2.10.43"
58
+ "resolutions": {
59
+ "esbuild": "^0.25.0"
60
60
  },
61
61
  "overrides": {
62
- "object-assign": "npm:@socketregistry/object-assign@^1"
63
- },
64
- "resolutions": {
65
- "object-assign": "npm:@socketregistry/object-assign@^1"
62
+ "esbuild": "^0.25.0"
66
63
  },
67
64
  "exports": {
68
65
  ".": {
@@ -29,6 +29,9 @@ interface _memorio {
29
29
  _tracking?: boolean
30
30
  _trackedPaths?: Set<string>
31
31
  _locked?: boolean
32
+ _lastAccessedPath?: string
33
+ _stateVersion?: number
34
+ _propertyAccessLog?: string[]
32
35
  // Platform detection
33
36
  isBrowser: () => boolean
34
37
  isNode: () => boolean