@jucie.io/state-matcher 1.0.1
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 +402 -0
- package/dist/main.js +2 -0
- package/dist/main.js.map +7 -0
- package/package.json +37 -0
package/README.md
ADDED
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
# @jucio.io/state/matcher
|
|
2
|
+
|
|
3
|
+
Path-based state watching plugin for @jucio.io/state that allows you to watch specific paths in your state tree and react to changes. **Your handlers receive the current data at the watched path, not change objects.**
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🎯 **Path Matching**: Watch specific paths in your state tree
|
|
8
|
+
- 📊 **Data Values**: Handlers receive actual data at the path, not change objects
|
|
9
|
+
- 🌳 **Hierarchical Watching**: Match exact paths, parent paths, or child paths
|
|
10
|
+
- 📦 **Automatic Batching**: Changes are automatically batched and debounced
|
|
11
|
+
- 🔄 **Smart Consolidation**: Multiple changes to the same path are consolidated
|
|
12
|
+
- 🎬 **Declarative Setup**: Define matchers during state initialization
|
|
13
|
+
- 🔌 **Plugin Architecture**: Seamlessly integrates with @jucie.io/state
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @jucio.io/state
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Note:** Matcher plugin is included in the main package.
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
```javascript
|
|
26
|
+
import { createState } from '@jucio.io/state';
|
|
27
|
+
import { Matcher, createMatcher } from '@jucio.io/state/matcher';
|
|
28
|
+
|
|
29
|
+
// Create a matcher
|
|
30
|
+
const userMatcher = createMatcher(['user'], (userData) => {
|
|
31
|
+
console.log('User data:', userData);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// Create state and install matcher plugin
|
|
35
|
+
const state = createState({
|
|
36
|
+
user: { name: 'Alice', age: 30 },
|
|
37
|
+
settings: { theme: 'dark' }
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Install with initial matchers
|
|
41
|
+
state.install(Matcher.configure({
|
|
42
|
+
matchers: [userMatcher]
|
|
43
|
+
}));
|
|
44
|
+
|
|
45
|
+
// Change user data - matcher receives the NEW data
|
|
46
|
+
state.set(['user', 'name'], 'Bob');
|
|
47
|
+
// Console: "User data: { name: 'Bob', age: 30 }"
|
|
48
|
+
|
|
49
|
+
// Change settings - matcher doesn't fire (different path)
|
|
50
|
+
state.set(['settings', 'theme'], 'light');
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## API Reference
|
|
54
|
+
|
|
55
|
+
### Creating Matchers
|
|
56
|
+
|
|
57
|
+
#### `createMatcher(path, handler)`
|
|
58
|
+
|
|
59
|
+
Create a matcher that watches a specific path in the state tree.
|
|
60
|
+
|
|
61
|
+
**The handler receives the current data at the watched path, not change objects.**
|
|
62
|
+
|
|
63
|
+
```javascript
|
|
64
|
+
import { createMatcher } from '@jucio.io/state/matcher';
|
|
65
|
+
|
|
66
|
+
const matcher = createMatcher(['users', 'profile'], (profileData) => {
|
|
67
|
+
console.log('Profile is now:', profileData);
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Parameters:**
|
|
72
|
+
- `path` (Array): Path to watch (e.g., `['user']`, `['users', 'profile']`)
|
|
73
|
+
- `handler` (Function): Callback function that receives the **current data** at the path
|
|
74
|
+
|
|
75
|
+
**Returns:** Matcher function that can be added to the plugin
|
|
76
|
+
|
|
77
|
+
### Plugin Actions
|
|
78
|
+
|
|
79
|
+
When using the Matcher plugin with a state instance, you get access to these actions:
|
|
80
|
+
|
|
81
|
+
#### `state.matcher.createMatcher(path, handler)`
|
|
82
|
+
|
|
83
|
+
Create and automatically register a matcher.
|
|
84
|
+
|
|
85
|
+
```javascript
|
|
86
|
+
import { createState } from '@jucio.io/state';
|
|
87
|
+
import { Matcher } from '@jucio.io/state/matcher';
|
|
88
|
+
|
|
89
|
+
const state = createState({ user: { name: 'Alice' } });
|
|
90
|
+
state.install(Matcher);
|
|
91
|
+
|
|
92
|
+
const unsubscribe = state.matcher.createMatcher(['user'], (userData) => {
|
|
93
|
+
console.log('User is now:', userData);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Later: remove the matcher
|
|
97
|
+
unsubscribe();
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**Returns:** Unsubscribe function
|
|
101
|
+
|
|
102
|
+
#### `state.matcher.addMatcher(matcher)`
|
|
103
|
+
|
|
104
|
+
Add an existing matcher to the plugin.
|
|
105
|
+
|
|
106
|
+
```javascript
|
|
107
|
+
const matcher = createMatcher(['user'], (userData) => {
|
|
108
|
+
console.log('User:', userData);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
state.matcher.addMatcher(matcher);
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
#### `state.matcher.removeMatcher(matcher)`
|
|
115
|
+
|
|
116
|
+
Remove a matcher from the plugin.
|
|
117
|
+
|
|
118
|
+
```javascript
|
|
119
|
+
state.matcher.removeMatcher(matcher);
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Match Types
|
|
123
|
+
|
|
124
|
+
Matchers use hierarchical matching with three types. **The handler always receives the current data at the matched path:**
|
|
125
|
+
|
|
126
|
+
### Exact Match
|
|
127
|
+
|
|
128
|
+
Watches the exact path specified:
|
|
129
|
+
|
|
130
|
+
```javascript
|
|
131
|
+
const matcher = createMatcher(['user', 'profile'], (profileData) => {
|
|
132
|
+
console.log('Profile data:', profileData);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
state.set(['user', 'profile'], { bio: 'Hello' }); // ✅ Fires with { bio: 'Hello' }
|
|
136
|
+
state.set(['user', 'profile', 'bio'], 'Hi'); // ✅ Fires with { bio: 'Hi' }
|
|
137
|
+
state.set(['user'], { profile: { bio: 'Hi' } }); // ✅ Fires with { bio: 'Hi' }
|
|
138
|
+
state.set(['user', 'settings'], {}); // ❌ Doesn't fire
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Parent Match
|
|
142
|
+
|
|
143
|
+
Fires when a parent path or any descendant changes:
|
|
144
|
+
|
|
145
|
+
```javascript
|
|
146
|
+
const matcher = createMatcher(['user'], (userData) => {
|
|
147
|
+
console.log('User data:', userData);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
state.set(['user', 'name'], 'Alice'); // ✅ Fires with entire user object
|
|
151
|
+
state.set(['user', 'profile', 'bio'], 'Hello'); // ✅ Fires with entire user object
|
|
152
|
+
state.set(['user'], { name: 'Bob' }); // ✅ Fires with { name: 'Bob' }
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Child Match
|
|
156
|
+
|
|
157
|
+
When watching a parent and children change, child changes are consolidated:
|
|
158
|
+
|
|
159
|
+
```javascript
|
|
160
|
+
const matcher = createMatcher(['users'], (usersData) => {
|
|
161
|
+
console.log('Users data:', usersData);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
state.set(['users', 'alice'], { name: 'Alice' });
|
|
165
|
+
state.set(['users', 'bob'], { name: 'Bob' });
|
|
166
|
+
|
|
167
|
+
// Both changes are batched. Handler receives the full current state:
|
|
168
|
+
// { alice: { name: 'Alice' }, bob: { name: 'Bob' } }
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Configuration
|
|
172
|
+
|
|
173
|
+
### Initialize with Matchers
|
|
174
|
+
|
|
175
|
+
```javascript
|
|
176
|
+
import { createState } from '@jucio.io/state';
|
|
177
|
+
import { Matcher, createMatcher } from '@jucio.io/state/matcher';
|
|
178
|
+
|
|
179
|
+
const userMatcher = createMatcher(['user'], (user) => {
|
|
180
|
+
console.log('User:', user);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
const settingsMatcher = createMatcher(['settings'], (settings) => {
|
|
184
|
+
console.log('Settings:', settings);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
const state = createState({ user: {}, settings: {} });
|
|
188
|
+
state.install(Matcher.configure({
|
|
189
|
+
matchers: [userMatcher, settingsMatcher]
|
|
190
|
+
}));
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Define Matchers as Objects
|
|
194
|
+
|
|
195
|
+
```javascript
|
|
196
|
+
import { createState } from '@jucio.io/state';
|
|
197
|
+
import { Matcher } from '@jucio.io/state/matcher';
|
|
198
|
+
|
|
199
|
+
const state = createState({ user: {}, settings: {} });
|
|
200
|
+
state.install(Matcher.configure({
|
|
201
|
+
matchers: [
|
|
202
|
+
{
|
|
203
|
+
path: ['user'],
|
|
204
|
+
handler: (user) => console.log('User:', user)
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
path: ['settings'],
|
|
208
|
+
handler: (settings) => console.log('Settings:', settings)
|
|
209
|
+
}
|
|
210
|
+
]
|
|
211
|
+
}));
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## Advanced Usage
|
|
215
|
+
|
|
216
|
+
### Multiple Matchers on Same Path
|
|
217
|
+
|
|
218
|
+
```javascript
|
|
219
|
+
const logger = createMatcher(['user'], (userData) => {
|
|
220
|
+
console.log('User changed:', userData);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
const validator = createMatcher(['user'], (userData) => {
|
|
224
|
+
if (!userData.email) {
|
|
225
|
+
console.warn('User has no email!');
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
state.matcher.addMatcher(logger);
|
|
230
|
+
state.matcher.addMatcher(validator);
|
|
231
|
+
|
|
232
|
+
state.set(['user'], { name: 'Alice' });
|
|
233
|
+
// Both matchers fire with { name: 'Alice' }
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Dynamic Matcher Management
|
|
237
|
+
|
|
238
|
+
```javascript
|
|
239
|
+
// Add matcher conditionally
|
|
240
|
+
if (process.env.NODE_ENV === 'development') {
|
|
241
|
+
const debugMatcher = state.matcher.createMatcher(['*'], (data) => {
|
|
242
|
+
console.log('DEBUG: State changed:', data);
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Add/remove based on user settings
|
|
247
|
+
function toggleAuditLog(enabled) {
|
|
248
|
+
if (enabled) {
|
|
249
|
+
const auditMatcher = createMatcher(['data'], (data) => {
|
|
250
|
+
logToServer('data-change', data);
|
|
251
|
+
});
|
|
252
|
+
state.matcher.addMatcher(auditMatcher);
|
|
253
|
+
return () => state.matcher.removeMatcher(auditMatcher);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Nested Path Watching
|
|
259
|
+
|
|
260
|
+
```javascript
|
|
261
|
+
// Watch different levels of nesting
|
|
262
|
+
const userMatcher = createMatcher(['user'], (userData) => {
|
|
263
|
+
console.log('Entire user object:', userData);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
const profileMatcher = createMatcher(['user', 'profile'], (profileData) => {
|
|
267
|
+
console.log('Just profile:', profileData);
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
const nameMatcher = createMatcher(['user', 'profile', 'name'], (name) => {
|
|
271
|
+
console.log('Just name:', name);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
state.set(['user', 'profile', 'name'], 'Alice');
|
|
275
|
+
// All three matchers fire:
|
|
276
|
+
// - userMatcher gets the entire user object
|
|
277
|
+
// - profileMatcher gets just the profile object
|
|
278
|
+
// - nameMatcher gets just 'Alice'
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Batching and Consolidation
|
|
282
|
+
|
|
283
|
+
```javascript
|
|
284
|
+
const matcher = createMatcher(['items'], (itemsData) => {
|
|
285
|
+
console.log('Items:', itemsData);
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
// Multiple rapid changes are batched
|
|
289
|
+
state.set(['items', 'item1'], { value: 1 });
|
|
290
|
+
state.set(['items', 'item2'], { value: 2 });
|
|
291
|
+
state.set(['items', 'item3'], { value: 3 });
|
|
292
|
+
|
|
293
|
+
// Single callback with current state:
|
|
294
|
+
// { item1: { value: 1 }, item2: { value: 2 }, item3: { value: 3 } }
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
## Common Patterns
|
|
298
|
+
|
|
299
|
+
### Form Field Validation
|
|
300
|
+
|
|
301
|
+
```javascript
|
|
302
|
+
const emailMatcher = createMatcher(['form', 'email'], (email) => {
|
|
303
|
+
const isValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
|
|
304
|
+
state.set(['form', 'errors', 'email'], isValid ? null : 'Invalid email');
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
state.matcher.addMatcher(emailMatcher);
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Persistence
|
|
311
|
+
|
|
312
|
+
```javascript
|
|
313
|
+
const persistMatcher = createMatcher(['user', 'preferences'], (preferences) => {
|
|
314
|
+
localStorage.setItem('preferences', JSON.stringify(preferences));
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
state.matcher.addMatcher(persistMatcher);
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Analytics Tracking
|
|
321
|
+
|
|
322
|
+
```javascript
|
|
323
|
+
const analyticsMatcher = createMatcher(['analytics', 'events'], (events) => {
|
|
324
|
+
Object.entries(events).forEach(([key, event]) => {
|
|
325
|
+
trackEvent(event.name, event.properties);
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
state.matcher.addMatcher(analyticsMatcher);
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### Derived State Updates
|
|
333
|
+
|
|
334
|
+
```javascript
|
|
335
|
+
// Update derived state when source changes
|
|
336
|
+
const cartMatcher = createMatcher(['cart', 'items'], (items) => {
|
|
337
|
+
const total = items.reduce((sum, item) => sum + item.price * item.quantity, 0);
|
|
338
|
+
state.set(['cart', 'total'], total);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
state.matcher.addMatcher(cartMatcher);
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### API Synchronization
|
|
345
|
+
|
|
346
|
+
```javascript
|
|
347
|
+
const syncMatcher = createMatcher(['user'], async (userData) => {
|
|
348
|
+
try {
|
|
349
|
+
await fetch('/api/user', {
|
|
350
|
+
method: 'PUT',
|
|
351
|
+
body: JSON.stringify(userData)
|
|
352
|
+
});
|
|
353
|
+
console.log('User synced to server');
|
|
354
|
+
} catch (error) {
|
|
355
|
+
console.error('Failed to sync user:', error);
|
|
356
|
+
}
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
state.matcher.addMatcher(syncMatcher);
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
## Performance Considerations
|
|
363
|
+
|
|
364
|
+
1. **Automatic Batching**: Matchers automatically batch changes using `setTimeout(fn, 0)`, so multiple synchronous changes trigger the handler only once
|
|
365
|
+
|
|
366
|
+
2. **Smart Consolidation**: Multiple changes to the same path are consolidated into a single update
|
|
367
|
+
|
|
368
|
+
3. **Efficient Matching**: Uses marker comparison for fast path matching
|
|
369
|
+
|
|
370
|
+
4. **Cleanup**: Always unsubscribe matchers when they're no longer needed to prevent memory leaks
|
|
371
|
+
|
|
372
|
+
```javascript
|
|
373
|
+
// Good: Clean up when done
|
|
374
|
+
const unsubscribe = state.matcher.createMatcher(['temp'], handler);
|
|
375
|
+
// ... later
|
|
376
|
+
unsubscribe();
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
## Comparison with OnChange Plugin
|
|
380
|
+
|
|
381
|
+
| Feature | Matcher | OnChange |
|
|
382
|
+
|---------|---------|----------|
|
|
383
|
+
| What handler receives | Current data at path | Change objects with metadata |
|
|
384
|
+
| Scope | Specific paths | Global changes |
|
|
385
|
+
| Batching | Automatic | Automatic |
|
|
386
|
+
| Consolidation | Smart path-based | By change address |
|
|
387
|
+
| Performance | Optimized for specific paths | Tracks all changes |
|
|
388
|
+
| Use Case | Watch specific data, get values | Track all changes, get metadata |
|
|
389
|
+
|
|
390
|
+
**Use Matcher when:** You want the current data at specific paths
|
|
391
|
+
**Use OnChange when:** You need change metadata (from/to values, method, etc.)
|
|
392
|
+
|
|
393
|
+
## License
|
|
394
|
+
|
|
395
|
+
See the root [LICENSE](../../LICENSE) file for license information.
|
|
396
|
+
|
|
397
|
+
## Related Packages
|
|
398
|
+
- [@jucio.io/react](https://github.com/adrianjonmiller/react) - React integration
|
|
399
|
+
- [@jucio.io/state](../../core) - Core state management system
|
|
400
|
+
- [@jucio.io/state/history](../history) - Undo/redo functionality
|
|
401
|
+
- [@jucio.io/state/on-change](../on-change) - Global change listeners
|
|
402
|
+
|
package/dist/main.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var A=class{static name=null;static options={};static configure(e){return e={...this.options,...e},{install:n=>this.install(n,e),name:this.name,options:e}}static install(e,n){n={...this.options,...n};let r=new this(e,n);return Object.defineProperty(r,"state",{value:e,writable:!1,configurable:!1}),Object.defineProperty(r,"options",{value:n,writable:!1,configurable:!1}),r}};var u="*",B=Symbol("STATE_CONTEXT"),M=Symbol("MATCHER");var E=1,f=2,g=4,m=8,h=0,d=1,y=2,C=-1;function D(t){let e="";for(let n=0;n<t.length;n++){let r=t[n];r==="~"?e+="~~":r==="."?e+="~d":e+=r}return e.length===0?"~e":e}function N(t){let e=new Array(t.length);for(let n=0;n<t.length;n++){let r=t[n];if(typeof r=="number"&&Number.isInteger(r))e[n]="n"+String(r);else if(typeof r=="string")e[n]="s"+D(r);else throw new TypeError(`Unsupported segment type at index ${n}: ${r}`)}return e.join(".")}function T(t){return(t.type&E)===E}function G(t){return(t.type&f)===f}function S(t){return(t.type&g)===g}function P(t){return(t.type&m)===m}function v(t){return!t||t.length===0||t===u||t[0]===u}function I(t=[]){let e=t.length;return e===0?[0,[]]:e===1&&t[0]===u?[0,[]]:Array.isArray(t[0])?e===1?[1,[t[0]]]:[e,t]:[1,[[...t]]]}function K(t){if(!Array.isArray(t))return!1;for(let e=0;e<t.length;e++){let n=t[e];if(typeof n=="string"&&n.charCodeAt(0)===46)return!0}return!1}function _(t=[]){if(v(t))return{address:u,isMarker:!0,length:0,path:[],children:null,type:E};let[e,n]=I(t),r=e===1?f:g,o=e===1?n[0]:n,s=e>1?n.map(i=>_(i)):null,c=r===f?K(o):s.some(i=>P(i)),a=r;return c&&(a|=m),{address:r===f?N(o):null,isMarker:!0,length:e,path:o,children:s,type:a}}function b(t,{global:e,path:n,ephemeral:r,error:o}){try{if(!t.isMarker)return;if(T(t))return e?e(t):void 0;if(P(t))return r?r(t):n?n(t):void 0;if(G(t))return n?n(t):void 0;if(S(t)){let s=new Array(t.length),c=0;for(;c<t.length;){let a=t.children[c];s[c]=b(a,{global:e,path:n,ephemeral:r,error:o}),c++}return s}return}catch(s){return o?o(s.message):void 0}}function R(t,e){return T(t)&&T(e)?h:T(e)?d:!t.address||!e.address?C:t.address===e.address?h:t.address.startsWith(e.address+".")?d:e.address.startsWith(t.address+".")?y:C}var H=class extends A{static name="matcher";static options={matchers:[]};#t=new Set;initialize(e,n){if(n.matchers&&n.matchers.length>0)for(let r of n.matchers)if(typeof r=="function"&&r._isMatcher===M)this.#n(r),this.#e(r);else{if(!r.path||!r.handler)throw new Error("Matcher path and handler are required");this.#e(L(r.path,r.handler))}}actions(){return{createMatcher:(e,n)=>{let r=L(e,n);return this.#n(r),this.#e(r),()=>this.#r(r)},addMatcher:e=>this.#e(e),removeMatcher:e=>this.#r(e)}}#n(e){Object.defineProperty(e,"_state",{value:this.state,writable:!1,configurable:!1})}#e(e){return this.#n(e),this.#t.add(e),()=>this.#r(e)}#r(e){this.#t.delete(e)}onStateChange(e,n){this.#t.forEach(r=>r(e,n))}};function L(t,e){if(!Array.isArray(t))throw new Error("matchPath must be an array");if(t.length===0)throw new Error("matchPath must be a non-empty array");if(e===void 0||typeof e!="function")throw new Error("Matcher function is required");let n=new Map,r=!1,o=_(t),s=i=>{R(o,i)>=0&&a(i)};function c(){r||(r=!0,setTimeout(()=>{let i=Array.from(n.values()),l={},p=!1;for(let w of i)b(w,{global:()=>{l=s._state.get(t),p=!0},path:()=>{switch(R(o,w)){case d:case h:l=s._state.get(t),p=!0;break;case y:let x=w.path.slice(0,t.length+1),O=x[x.length-1];l=typeof l=="object"&&l!==null?l:{},l[O]=s._state.get(x),p=!0;break}}});p&&e(l),n.clear(),r=!1},0))}function a(i){n.has(i.address)&&n.delete(i.address),n.set(i.address,i),c()}return Object.defineProperty(s,"_isMatcher",{value:M,writable:!1,enumerable:!1,configurable:!0}),s}export{H as Matcher,L as createMatcher};
|
|
2
|
+
//# sourceMappingURL=main.js.map
|
package/dist/main.js.map
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../core/src/Plugin.js", "../../../core/src/lib/TOKENS.js", "../../../core/src/lib/pathEncoder.js", "../../../core/src/lib/marker.js", "../src/Matcher.js"],
|
|
4
|
+
"sourcesContent": ["export class Plugin {\n\n static name = null;\n static options = {};\n\n static configure(options) {\n options = {...this.options, ...options};\n return {\n install: (state) => this.install(state, options),\n name: this.name,\n options\n }\n }\n\n static install(state, options) {\n options = {...this.options, ...options};\n const pluginInstance = new this(state, options);\n\n Object.defineProperty(pluginInstance, 'state', {\n value: state,\n writable: false,\n configurable: false\n });\n \n Object.defineProperty(pluginInstance, 'options', {\n value: options,\n writable: false,\n configurable: false\n });\n\n \n return pluginInstance;\n }\n}\n\nexport default Plugin;", "export const GLOBAL_TAG = '*';\nexport const STATE_CONTEXT = Symbol('STATE_CONTEXT');\nexport const MATCHER = Symbol('MATCHER');\nexport const CREATED = 'CREATED';\nexport const DELETED = 'DELETED';\nexport const UPDATED = 'UPDATED';\n\n// Marker type bitflags\nexport const MARKER_GLOBAL = 1; // 0b001\nexport const MARKER_SINGLE = 2; // 0b010\nexport const MARKER_MANY = 4; // 0b100\nexport const MARKER_EPHEMERAL = 8; // 0b1000\n\n// Comparison result constants\nexport const MATCH_EXACT = 0; // Markers are identical\nexport const MATCH_PARENT = 1; // controlMarker is child of comparedMarker (parent changed)\nexport const MATCH_CHILD = 2; // comparedMarker is child of controlMarker (child changed)\nexport const MATCH_NONE = -1; // No relationship", "import { GLOBAL_TAG } from './TOKENS.js';\n\n// Fast helpers\nfunction escapeStr(str) {\n // Order matters: escape ~ first, then .\n let out = \"\";\n for (let i = 0; i < str.length; i++) {\n const ch = str[i];\n if (ch === \"~\") out += \"~~\";\n else if (ch === \".\") out += \"~d\";\n else out += ch;\n }\n // Represent empty strings as ~e to avoid trailing-dot filenames\n return out.length === 0 ? \"~e\" : out;\n}\n\nfunction unescapeStr(str) {\n let out = \"\";\n for (let i = 0; i < str.length; i++) {\n const ch = str[i];\n if (ch === \"~\") {\n const next = str[++i];\n if (next === \"~\") out += \"~\";\n else if (next === \"d\") out += \".\";\n else if (next === \"e\") out += \"\"; // empty string marker\n else {\n // Unknown escape: treat as literal (robustness)\n out += \"~\" + (next ?? \"\");\n }\n } else {\n out += ch;\n }\n }\n return out;\n}\n\nexport function encodePath(segments) {\n // segments: array of strings or integers\n // Produces: filename/URL-safe string like \"sfoo.n0.sbaz\"\n const parts = new Array(segments.length);\n for (let i = 0; i < segments.length; i++) {\n const v = segments[i];\n if (typeof v === \"number\" && Number.isInteger(v)) {\n // 'n' tag\n parts[i] = \"n\" + String(v); // decimal; includes \"-\" if negative\n } else if (typeof v === \"string\") {\n // 's' tag\n parts[i] = \"s\" + escapeStr(v);\n } else {\n // If you need more types, add here (e.g., booleans 'b', floats 'f').\n throw new TypeError(`Unsupported segment type at index ${i}: ${v}`);\n }\n }\n // Use '.' as separator (safe in URLs and filenames; we avoid trailing '.')\n return parts.join(\".\");\n}\n\nexport function decodeAddress(address) {\n if (address === GLOBAL_TAG) return GLOBAL_TAG;\n if (address.length === 0) return [];\n const raw = address.split(\".\");\n const out = new Array(raw.length);\n for (let i = 0; i < raw.length; i++) {\n const token = raw[i];\n if (token.length === 0) {\n // Disallow empty tokens (would imply trailing or double dots)\n throw new Error(\"Invalid address: empty token\");\n }\n const tag = token[0];\n const body = token.slice(1);\n if (tag === \"n\") {\n // Fast parse (no regex)\n if (body.length === 0 || !/^[-]?\\d+$/.test(body)) {\n throw new Error(`Invalid numeric token: \"${token}\"`);\n }\n const num = Number(body);\n // Ensure it was an integer\n if (!Number.isInteger(num)) {\n throw new Error(`Non-integer numeric token: \"${token}\"`);\n }\n out[i] = num;\n } else if (tag === \"s\") {\n out[i] = unescapeStr(body);\n } else {\n throw new Error(`Unknown type tag \"${tag}\" in token \"${token}\"`);\n }\n }\n return out;\n}\n", "import { encodePath } from './pathEncoder.js';\nimport { GLOBAL_TAG } from './TOKENS.js';\nimport {\n MARKER_GLOBAL,\n MARKER_SINGLE,\n MARKER_MANY,\n MARKER_EPHEMERAL,\n MATCH_EXACT,\n MATCH_PARENT,\n MATCH_CHILD,\n MATCH_NONE \n} from './TOKENS.js';\n\n// Marker type bitflags\n\n// Helper functions to check marker type\nexport function isGlobalMarker(marker) {\n return (marker.type & MARKER_GLOBAL) === MARKER_GLOBAL;\n}\n\nexport function isPathMarker(marker) {\n return (marker.type & MARKER_SINGLE) === MARKER_SINGLE;\n}\n\nexport function isMarkers(marker) {\n return (marker.type & MARKER_MANY) === MARKER_MANY;\n}\n\nexport function isEphemeralMarker(marker) {\n return (marker.type & MARKER_EPHEMERAL) === MARKER_EPHEMERAL;\n}\n\nexport function isGlobalPath(path) {\n return !path || path.length === 0 || path === GLOBAL_TAG || path[0] === GLOBAL_TAG;\n}\n\nexport function normalizePaths(args = []) { \n const len = args.length;\n if (len === 0) return [0, [] ];\n if (len === 1 && args[0] === GLOBAL_TAG) return [0, [] ];\n \n if (Array.isArray(args[0])) {\n return len === 1 \n ? [1, [args[0]] ]\n : [len, args ];\n }\n \n return [1, [[...args]] ];\n}\n\nfunction pathHasEphemeral(path) {\n if (!Array.isArray(path)) {\n return false;\n }\n\n for (let i = 0; i < path.length; i++) {\n const segment = path[i];\n if (typeof segment === 'string' && segment.charCodeAt(0) === 46) { // '.'\n return true;\n }\n }\n\n return false;\n}\n\n// Marker factory\nexport function createMarker(paths = []) {\n if (isGlobalPath(paths)) {\n return {\n address: GLOBAL_TAG,\n isMarker: true,\n length: 0,\n path: [],\n children: null,\n type: MARKER_GLOBAL\n };\n }\n \n const [length, normalizedPaths] = normalizePaths(paths);\n const type = length === 1 ? MARKER_SINGLE : MARKER_MANY;\n const path = length === 1 ? normalizedPaths[0] : normalizedPaths;\n const children = length > 1 ? normalizedPaths.map(path => createMarker(path)) : null;\n const isEphemeral = type === MARKER_SINGLE\n ? pathHasEphemeral(path)\n : children.some(child => isEphemeralMarker(child));\n\n let markerType = type;\n if (isEphemeral) {\n markerType |= MARKER_EPHEMERAL;\n }\n\n return {\n address: type === MARKER_SINGLE ? encodePath(path) : null,\n isMarker: true,\n length,\n path,\n children,\n type: markerType\n };\n}\n\nexport function createChildMarker(parentMarker, childPaths) {\n if (childPaths.length === 0) {\n return parentMarker;\n }\n // Normalize the child paths\n const [childLength, normalizedChildPaths] = normalizePaths(childPaths);\n \n // Handle global marker - just return marker with child paths\n if (isGlobalMarker(parentMarker)) {\n return createMarker(normalizedChildPaths, parentMarker.state);\n }\n \n // Handle single path marker\n if (isPathMarker(parentMarker)) {\n if (childLength === 0) {\n // No child paths, return parent as-is\n return parentMarker;\n }\n \n if (childLength === 1) {\n // Single child path - append to parent path\n const newPath = [...parentMarker.path, ...normalizedChildPaths[0]];\n return createMarker(newPath, parentMarker.state);\n } else {\n // Multiple child paths - create many markers\n const newPaths = normalizedChildPaths.map(childPath => \n [...parentMarker.path, ...childPath]\n );\n return createMarker(newPaths, parentMarker.state);\n }\n }\n \n // Handle many markers - recursively create child markers for each\n if (isMarkers(parentMarker)) {\n const newMarkers = new Array(parentMarker.length);\n let i = 0;\n while (i < parentMarker.length) {\n newMarkers[i] = createChildMarker(parentMarker.children[i], childPaths);\n i++;\n }\n \n // Collect all paths from the new markers\n const allPaths = [];\n i = 0;\n while (i < newMarkers.length) {\n const marker = newMarkers[i];\n if (isPathMarker(marker)) {\n allPaths.push(marker.path);\n } else if (isMarkers(marker)) {\n let j = 0;\n while (j < marker.length) {\n allPaths.push(marker.children[j].path);\n j++;\n }\n }\n i++;\n }\n \n return createMarker(allPaths, parentMarker.state);\n }\n \n // Fallback - shouldn't reach here\n return parentMarker;\n}\n\nexport function dispatch(marker, { global, path, ephemeral, error }) {\n try {\n if (!marker.isMarker) return undefined;\n if (isGlobalMarker(marker)) return global ? global(marker) : undefined;\n if (isEphemeralMarker(marker)) return ephemeral ? ephemeral(marker) : path ? path(marker) : undefined;\n if (isPathMarker(marker)) return path ? path(marker) : undefined;\n if (isMarkers(marker)) {\n const results = new Array(marker.length);\n let i = 0;\n while (i < marker.length) {\n const nestedMarker = marker.children[i];\n results[i] = dispatch(nestedMarker, { global, path, ephemeral, error });\n i++;\n }\n return results;\n }\n \n return undefined;\n } catch (err) {\n return error ? error(err.message) : undefined;\n }\n}\n\nexport function compareMarkers(controlMarker, comparedMarker) {\n // Both are global markers or exact address match\n if (isGlobalMarker(controlMarker) && isGlobalMarker(comparedMarker)) {\n return MATCH_EXACT;\n }\n\n // If comparedMarker is global, it's always a parent\n if (isGlobalMarker(comparedMarker)) {\n return MATCH_PARENT;\n }\n \n // Need addresses to compare\n if (!controlMarker.address || !comparedMarker.address) {\n return MATCH_NONE;\n }\n \n // Exact match\n if (controlMarker.address === comparedMarker.address) {\n return MATCH_EXACT;\n }\n \n // controlMarker is more nested (child) - parent changed\n if (controlMarker.address.startsWith(comparedMarker.address + '.')) {\n return MATCH_PARENT;\n }\n \n // comparedMarker is more nested (child) - child changed\n if (comparedMarker.address.startsWith(controlMarker.address + '.')) {\n return MATCH_CHILD;\n }\n \n return MATCH_NONE;\n}\n\nexport const Marker = {\n compare: compareMarkers,\n create: createMarker,\n createChild: createChildMarker,\n dispatch: dispatch,\n isGlobal: isGlobalMarker,\n isSingle: isPathMarker,\n isMarkers: isMarkers,\n isEphemeral: isEphemeralMarker\n};", "import { Plugin } from '@jucie-state/core/Plugin';\nimport { compareMarkers, createMarker, dispatch } from '@jucie-state/core/lib/marker.js';\nimport { MATCHER } from '@jucie-state/core/lib/TOKENS.js';\n\nimport { MATCH_CHILD, MATCH_PARENT, MATCH_EXACT, MATCH_NONE } from '@jucie-state/core/lib/TOKENS.js';\n\nexport class Matcher extends Plugin {\n static name = 'matcher';\n static options = {\n matchers: [],\n };\n \n #matchers = new Set();\n \n initialize(state, config) {\n if (config.matchers && config.matchers.length > 0) {\n for (const matcher of config.matchers) {\n if (typeof matcher === 'function' && matcher._isMatcher === MATCHER) {\n this.#assignMatcherState(matcher);\n this.#addMatcher(matcher);\n } else {\n if (!matcher.path || !matcher.handler) {\n throw new Error('Matcher path and handler are required');\n }\n this.#addMatcher(createMatcher(matcher.path, matcher.handler));\n }\n }\n }\n }\n\n actions () {\n return {\n createMatcher: (path, handler) => {\n const matcher = createMatcher(path, handler);\n this.#assignMatcherState(matcher);\n this.#addMatcher(matcher);\n return () => this.#removeMatcher(matcher);\n },\n addMatcher: (matcher) => this.#addMatcher(matcher),\n removeMatcher: (matcher) => this.#removeMatcher(matcher),\n }\n }\n\n #assignMatcherState(matcher) {\n Object.defineProperty(matcher, '_state', {\n value: this.state,\n writable: false,\n configurable: false\n });\n }\n\n #addMatcher(matcher) {\n this.#assignMatcherState(matcher);\n this.#matchers.add(matcher);\n return () => this.#removeMatcher(matcher);\n }\n\n #removeMatcher(matcher) {\n this.#matchers.delete(matcher);\n }\n\n onStateChange(marker, change) {\n this.#matchers.forEach(matcher => matcher(marker, change));\n }\n}\n\nexport function createMatcher(matchPath, fn) {\n if (!Array.isArray(matchPath)) {\n throw new Error('matchPath must be an array');\n }\n\n if (matchPath.length === 0) {\n throw new Error('matchPath must be a non-empty array');\n }\n\n if (fn === undefined || typeof fn !== 'function') {\n throw new Error('Matcher function is required');\n }\n\n const changeQueue = new Map();\n let isFlushing = false;\n\n const matchMarker = createMarker(matchPath);\n const matcher = (marker) => {\n if (compareMarkers(matchMarker, marker) >= 0) {\n queueChange(marker);\n };\n }\n\n function flushChanges() {\n if (isFlushing) return;\n isFlushing = true;\n setTimeout(() => {\n const markers = Array.from(changeQueue.values());\n let changes = {};\n let hasChanges = false;\n for (const marker of markers) {\n dispatch(marker, {\n global: () => {\n changes = matcher._state.get(matchPath);\n hasChanges = true;\n },\n path: () => {\n const comp = compareMarkers(matchMarker, marker);\n \n switch (comp) {\n case MATCH_PARENT:\n case MATCH_EXACT:\n changes = matcher._state.get(matchPath);\n hasChanges = true;\n break;\n case MATCH_CHILD:\n const childPath = marker.path.slice(0, matchPath.length + 1);\n const key = childPath[childPath.length - 1];\n changes = typeof changes === 'object' && changes !== null ? changes : {};\n changes[key] = matcher._state.get(childPath);\n hasChanges = true;\n break;\n }\n },\n });\n }\n\n if (hasChanges) {\n fn(changes);\n }\n changeQueue.clear();\n isFlushing = false;\n }, 0);\n }\n\n function queueChange(marker) {\n if (changeQueue.has(marker.address)) {\n changeQueue.delete(marker.address);\n }\n changeQueue.set(marker.address, marker);\n flushChanges();\n }\n\n Object.defineProperty(matcher, '_isMatcher', {\n value: MATCHER,\n writable: false,\n enumerable: false,\n configurable: true\n });\n\n\n return matcher;\n}"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["Plugin", "options", "state", "pluginInstance", "GLOBAL_TAG", "STATE_CONTEXT", "MATCHER", "MARKER_GLOBAL", "MARKER_SINGLE", "MARKER_MANY", "MARKER_EPHEMERAL", "MATCH_EXACT", "MATCH_PARENT", "MATCH_CHILD", "MATCH_NONE", "escapeStr", "str", "out", "i", "ch", "encodePath", "segments", "parts", "i", "v", "escapeStr", "isGlobalMarker", "marker", "MARKER_GLOBAL", "isPathMarker", "MARKER_SINGLE", "isMarkers", "MARKER_MANY", "isEphemeralMarker", "MARKER_EPHEMERAL", "isGlobalPath", "path", "GLOBAL_TAG", "normalizePaths", "args", "len", "pathHasEphemeral", "i", "segment", "createMarker", "paths", "length", "normalizedPaths", "type", "children", "isEphemeral", "child", "markerType", "encodePath", "dispatch", "marker", "global", "path", "ephemeral", "error", "isGlobalMarker", "isEphemeralMarker", "isPathMarker", "isMarkers", "results", "i", "nestedMarker", "err", "compareMarkers", "controlMarker", "comparedMarker", "MATCH_EXACT", "MATCH_PARENT", "MATCH_NONE", "MATCH_CHILD", "Matcher", "Plugin", "#matchers", "state", "config", "matcher", "MATCHER", "#assignMatcherState", "#addMatcher", "createMatcher", "path", "handler", "#removeMatcher", "marker", "change", "matchPath", "fn", "changeQueue", "isFlushing", "matchMarker", "createMarker", "compareMarkers", "queueChange", "flushChanges", "markers", "changes", "hasChanges", "dispatch", "MATCH_PARENT", "MATCH_EXACT", "MATCH_CHILD", "childPath", "key"]
|
|
7
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jucie.io/state-matcher",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Matcher plugin for @jucie.io/state with pattern matching and subscriptions",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/main.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./dist/main.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist/",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"test": "Tests run from root workspace"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"state",
|
|
19
|
+
"matcher",
|
|
20
|
+
"pattern",
|
|
21
|
+
"subscription",
|
|
22
|
+
"plugin"
|
|
23
|
+
],
|
|
24
|
+
"author": "Adrian Miller",
|
|
25
|
+
"license": "SEE LICENSE IN ../../../LICENSE",
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "git+https://github.com/adrianjonmiller/state",
|
|
32
|
+
"directory": "state/plugins/matcher"
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"@jucie.io/state": "^1.0.0"
|
|
36
|
+
}
|
|
37
|
+
}
|