@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 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
@@ -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": "AAAO,IAAMA,EAAN,KAAa,CAElB,OAAO,KAAO,KACd,OAAO,QAAU,CAAC,EAElB,OAAO,UAAUC,EAAS,CACxB,OAAAA,EAAU,CAAC,GAAG,KAAK,QAAS,GAAGA,CAAO,EAC/B,CACL,QAAUC,GAAU,KAAK,QAAQA,EAAOD,CAAO,EAC/C,KAAM,KAAK,KACX,QAAAA,CACF,CACF,CAEA,OAAO,QAAQC,EAAOD,EAAS,CAC7BA,EAAU,CAAC,GAAG,KAAK,QAAS,GAAGA,CAAO,EACtC,IAAME,EAAiB,IAAI,KAAKD,EAAOD,CAAO,EAE9C,cAAO,eAAeE,EAAgB,QAAS,CAC7C,MAAOD,EACP,SAAU,GACV,aAAc,EAChB,CAAC,EAED,OAAO,eAAeC,EAAgB,UAAW,CAC/C,MAAOF,EACP,SAAU,GACV,aAAc,EAChB,CAAC,EAGME,CACT,CACF,ECjCO,IAAMC,EAAa,IACbC,EAAgB,OAAO,eAAe,EACtCC,EAAU,OAAO,SAAS,EAMhC,IAAMC,EAAgB,EAChBC,EAAgB,EAChBC,EAAc,EACdC,EAAmB,EAGnBC,EAAc,EACdC,EAAe,EACfC,EAAc,EACdC,EAAa,GCd1B,SAASC,EAAUC,EAAK,CAEtB,IAAIC,EAAM,GACV,QAASC,EAAI,EAAGA,EAAIF,EAAI,OAAQE,IAAK,CACnC,IAAMC,EAAKH,EAAIE,CAAC,EACZC,IAAO,IAAKF,GAAO,KACdE,IAAO,IAAKF,GAAO,KACvBA,GAAOE,CACd,CAEA,OAAOF,EAAI,SAAW,EAAI,KAAOA,CACnC,CAsBO,SAASG,EAAWC,EAAU,CAGnC,IAAMC,EAAQ,IAAI,MAAMD,EAAS,MAAM,EACvC,QAASE,EAAI,EAAGA,EAAIF,EAAS,OAAQE,IAAK,CACxC,IAAMC,EAAIH,EAASE,CAAC,EACpB,GAAI,OAAOC,GAAM,UAAY,OAAO,UAAUA,CAAC,EAE7CF,EAAMC,CAAC,EAAI,IAAM,OAAOC,CAAC,UAChB,OAAOA,GAAM,SAEtBF,EAAMC,CAAC,EAAI,IAAME,EAAUD,CAAC,MAG5B,OAAM,IAAI,UAAU,qCAAqCD,CAAC,KAAKC,CAAC,EAAE,CAEtE,CAEA,OAAOF,EAAM,KAAK,GAAG,CACvB,CCvCO,SAASI,EAAeC,EAAQ,CACrC,OAAQA,EAAO,KAAOC,KAAmBA,CAC3C,CAEO,SAASC,EAAaF,EAAQ,CACnC,OAAQA,EAAO,KAAOG,KAAmBA,CAC3C,CAEO,SAASC,EAAUJ,EAAQ,CAChC,OAAQA,EAAO,KAAOK,KAAiBA,CACzC,CAEO,SAASC,EAAkBN,EAAQ,CACxC,OAAQA,EAAO,KAAOO,KAAsBA,CAC9C,CAEO,SAASC,EAAaC,EAAM,CACjC,MAAO,CAACA,GAAQA,EAAK,SAAW,GAAKA,IAASC,GAAcD,EAAK,CAAC,IAAMC,CAC1E,CAEO,SAASC,EAAeC,EAAO,CAAC,EAAG,CACxC,IAAMC,EAAMD,EAAK,OACjB,OAAIC,IAAQ,EAAU,CAAC,EAAG,CAAC,CAAE,EACzBA,IAAQ,GAAKD,EAAK,CAAC,IAAMF,EAAmB,CAAC,EAAG,CAAC,CAAE,EAEnD,MAAM,QAAQE,EAAK,CAAC,CAAC,EAChBC,IAAQ,EACX,CAAC,EAAG,CAACD,EAAK,CAAC,CAAC,CAAE,EACd,CAACC,EAAKD,CAAK,EAGV,CAAC,EAAG,CAAC,CAAC,GAAGA,CAAI,CAAC,CAAE,CACzB,CAEA,SAASE,EAAiBL,EAAM,CAC9B,GAAI,CAAC,MAAM,QAAQA,CAAI,EACrB,MAAO,GAGT,QAASM,EAAI,EAAGA,EAAIN,EAAK,OAAQM,IAAK,CACpC,IAAMC,EAAUP,EAAKM,CAAC,EACtB,GAAI,OAAOC,GAAY,UAAYA,EAAQ,WAAW,CAAC,IAAM,GAC3D,MAAO,EAEX,CAEA,MAAO,EACT,CAGO,SAASC,EAAaC,EAAQ,CAAC,EAAG,CACvC,GAAIV,EAAaU,CAAK,EACpB,MAAO,CACL,QAASR,EACT,SAAU,GACV,OAAQ,EACR,KAAM,CAAC,EACP,SAAU,KACV,KAAMT,CACR,EAGF,GAAM,CAACkB,EAAQC,CAAe,EAAIT,EAAeO,CAAK,EAChDG,EAAOF,IAAW,EAAIhB,EAAgBE,EACtCI,EAAOU,IAAW,EAAIC,EAAgB,CAAC,EAAIA,EAC3CE,EAAWH,EAAS,EAAIC,EAAgB,IAAIX,GAAQQ,EAAaR,CAAI,CAAC,EAAI,KAC1Ec,EAAcF,IAASlB,EACzBW,EAAiBL,CAAI,EACrBa,EAAS,KAAKE,GAASlB,EAAkBkB,CAAK,CAAC,EAE/CC,EAAaJ,EACjB,OAAIE,IACFE,GAAclB,GAGT,CACL,QAASc,IAASlB,EAAgBuB,EAAWjB,CAAI,EAAI,KACrD,SAAU,GACV,OAAAU,EACA,KAAAV,EACA,SAAAa,EACA,KAAMG,CACR,CACF,CAmEO,SAASE,EAASC,EAAQ,CAAE,OAAAC,EAAQ,KAAAC,EAAM,UAAAC,EAAW,MAAAC,CAAM,EAAG,CACnE,GAAI,CACF,GAAI,CAACJ,EAAO,SAAU,OACtB,GAAIK,EAAeL,CAAM,EAAG,OAAOC,EAASA,EAAOD,CAAM,EAAI,OAC7D,GAAIM,EAAkBN,CAAM,EAAG,OAAOG,EAAYA,EAAUH,CAAM,EAAIE,EAAOA,EAAKF,CAAM,EAAI,OAC5F,GAAIO,EAAaP,CAAM,EAAG,OAAOE,EAAOA,EAAKF,CAAM,EAAI,OACvD,GAAIQ,EAAUR,CAAM,EAAG,CACrB,IAAMS,EAAU,IAAI,MAAMT,EAAO,MAAM,EACnCU,EAAI,EACR,KAAOA,EAAIV,EAAO,QAAQ,CACxB,IAAMW,EAAeX,EAAO,SAASU,CAAC,EACtCD,EAAQC,CAAC,EAAIX,EAASY,EAAc,CAAE,OAAAV,EAAQ,KAAAC,EAAM,UAAAC,EAAW,MAAAC,CAAM,CAAC,EACtEM,GACF,CACA,OAAOD,CACT,CAEA,MACF,OAASG,EAAK,CACZ,OAAOR,EAAQA,EAAMQ,EAAI,OAAO,EAAI,MACtC,CACF,CAEO,SAASC,EAAeC,EAAeC,EAAgB,CAE5D,OAAIV,EAAeS,CAAa,GAAKT,EAAeU,CAAc,EACzDC,EAILX,EAAeU,CAAc,EACxBE,EAIL,CAACH,EAAc,SAAW,CAACC,EAAe,QACrCG,EAILJ,EAAc,UAAYC,EAAe,QACpCC,EAILF,EAAc,QAAQ,WAAWC,EAAe,QAAU,GAAG,EACxDE,EAILF,EAAe,QAAQ,WAAWD,EAAc,QAAU,GAAG,EACxDK,EAGFD,CACT,CCvNO,IAAME,EAAN,cAAsBC,CAAO,CAClC,OAAO,KAAO,UACd,OAAO,QAAU,CACf,SAAU,CAAC,CACb,EAEAC,GAAY,IAAI,IAEhB,WAAWC,EAAOC,EAAQ,CACxB,GAAIA,EAAO,UAAYA,EAAO,SAAS,OAAS,EAC9C,QAAWC,KAAWD,EAAO,SAC3B,GAAI,OAAOC,GAAY,YAAcA,EAAQ,aAAeC,EAC1D,KAAKC,GAAoBF,CAAO,EAChC,KAAKG,GAAYH,CAAO,MACnB,CACL,GAAI,CAACA,EAAQ,MAAQ,CAACA,EAAQ,QAC5B,MAAM,IAAI,MAAM,uCAAuC,EAEzD,KAAKG,GAAYC,EAAcJ,EAAQ,KAAMA,EAAQ,OAAO,CAAC,CAC/D,CAGN,CAEA,SAAW,CACT,MAAO,CACL,cAAe,CAACK,EAAMC,IAAY,CAChC,IAAMN,EAAUI,EAAcC,EAAMC,CAAO,EAC3C,YAAKJ,GAAoBF,CAAO,EAChC,KAAKG,GAAYH,CAAO,EACjB,IAAM,KAAKO,GAAeP,CAAO,CAC1C,EACA,WAAaA,GAAY,KAAKG,GAAYH,CAAO,EACjD,cAAgBA,GAAY,KAAKO,GAAeP,CAAO,CACzD,CACF,CAEAE,GAAoBF,EAAS,CAC3B,OAAO,eAAeA,EAAS,SAAU,CACvC,MAAO,KAAK,MACZ,SAAU,GACV,aAAc,EAChB,CAAC,CACH,CAEAG,GAAYH,EAAS,CACnB,YAAKE,GAAoBF,CAAO,EAChC,KAAKH,GAAU,IAAIG,CAAO,EACnB,IAAM,KAAKO,GAAeP,CAAO,CAC1C,CAEAO,GAAeP,EAAS,CACtB,KAAKH,GAAU,OAAOG,CAAO,CAC/B,CAEA,cAAcQ,EAAQC,EAAQ,CAC5B,KAAKZ,GAAU,QAAQG,GAAWA,EAAQQ,EAAQC,CAAM,CAAC,CAC3D,CACF,EAEO,SAASL,EAAcM,EAAWC,EAAI,CAC3C,GAAI,CAAC,MAAM,QAAQD,CAAS,EAC1B,MAAM,IAAI,MAAM,4BAA4B,EAG9C,GAAIA,EAAU,SAAW,EACvB,MAAM,IAAI,MAAM,qCAAqC,EAGvD,GAAIC,IAAO,QAAa,OAAOA,GAAO,WACpC,MAAM,IAAI,MAAM,8BAA8B,EAGhD,IAAMC,EAAc,IAAI,IACpBC,EAAa,GAEXC,EAAcC,EAAaL,CAAS,EACpCV,EAAWQ,GAAW,CACtBQ,EAAeF,EAAaN,CAAM,GAAK,GACzCS,EAAYT,CAAM,CAEtB,EAEA,SAASU,GAAe,CAClBL,IACJA,EAAa,GACb,WAAW,IAAM,CACf,IAAMM,EAAU,MAAM,KAAKP,EAAY,OAAO,CAAC,EAC3CQ,EAAU,CAAC,EACXC,EAAa,GACjB,QAAWb,KAAUW,EACnBG,EAASd,EAAQ,CACf,OAAQ,IAAM,CACZY,EAAUpB,EAAQ,OAAO,IAAIU,CAAS,EACtCW,EAAa,EACf,EACA,KAAM,IAAM,CAGV,OAFaL,EAAeF,EAAaN,CAAM,EAEjC,CACZ,KAAKe,EACL,KAAKC,EACHJ,EAAUpB,EAAQ,OAAO,IAAIU,CAAS,EACtCW,EAAa,GACb,MACF,KAAKI,EACH,IAAMC,EAAYlB,EAAO,KAAK,MAAM,EAAGE,EAAU,OAAS,CAAC,EACrDiB,EAAMD,EAAUA,EAAU,OAAS,CAAC,EAC1CN,EAAU,OAAOA,GAAY,UAAYA,IAAY,KAAOA,EAAU,CAAC,EACvEA,EAAQO,CAAG,EAAI3B,EAAQ,OAAO,IAAI0B,CAAS,EAC3CL,EAAa,GACb,KACJ,CACF,CACF,CAAC,EAGCA,GACFV,EAAGS,CAAO,EAEZR,EAAY,MAAM,EAClBC,EAAa,EACf,EAAG,CAAC,EACN,CAEA,SAASI,EAAYT,EAAQ,CACvBI,EAAY,IAAIJ,EAAO,OAAO,GAChCI,EAAY,OAAOJ,EAAO,OAAO,EAEnCI,EAAY,IAAIJ,EAAO,QAASA,CAAM,EACtCU,EAAa,CACf,CAEA,cAAO,eAAelB,EAAS,aAAc,CAC3C,MAAOC,EACP,SAAU,GACV,WAAY,GACZ,aAAc,EAChB,CAAC,EAGMD,CACT",
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
+ }