@yoltra/core 0.1.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/LICENSE +21 -0
- package/README.es.md +473 -0
- package/README.md +468 -0
- package/dist/types/eventBus/EventBus.d.ts +127 -0
- package/dist/types/eventBus/LooseEventBus.d.ts +219 -0
- package/dist/types/eventBus/index.d.ts +2 -0
- package/dist/types/index.d.ts +15 -0
- package/dist/types/reducer/Reducer.d.ts +81 -0
- package/dist/types/store/Store.d.ts +834 -0
- package/dist/types/types.d.ts +939 -0
- package/dist/types/utils/detectChangedProps.d.ts +67 -0
- package/dist/types/utils/immutability.d.ts +47 -0
- package/dist/types/utils/index.d.ts +2 -0
- package/dist/yoltra.cjs.js +8 -0
- package/dist/yoltra.esm.js +1557 -0
- package/dist/yoltra.umd.js +8 -0
- package/package.json +83 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @yoltra/core
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Computes the list of **dotted leaf paths** that changed between two values.
|
|
6
|
+
*
|
|
7
|
+
* The algorithm performs a deep structural comparison with special handling for:
|
|
8
|
+
* - **Primitives / null** → treated as leafs (change = current `path`)
|
|
9
|
+
* - **Date** → compares `getTime()`
|
|
10
|
+
* - **RegExp** → compares `source` and `flags`
|
|
11
|
+
* - **Arrays** → if lengths differ, the whole array path is marked changed; otherwise compares
|
|
12
|
+
* element-by-element producing paths like `"items.0.title"`
|
|
13
|
+
* - **Objects** → compares by the **union of keys**, recursing into shared keys and marking
|
|
14
|
+
* added/removed keys as changed at their **full path**
|
|
15
|
+
*
|
|
16
|
+
* Cycles and repeated object aliases are handled via **pair-wise** tracking using a
|
|
17
|
+
* `WeakMap<object, WeakSet<object>>` so recursion doesn't loop and shared subgraphs do not
|
|
18
|
+
* produce false negatives.
|
|
19
|
+
*
|
|
20
|
+
* @param oldState - Previous value to diff.
|
|
21
|
+
* @param newState - Next value to diff.
|
|
22
|
+
* @param path - Current dotted path (callers pass `""` for root; recursion appends segments).
|
|
23
|
+
* @param seenPairs - (Advanced) Pair tracker for cycle/alias detection. You generally never pass this.
|
|
24
|
+
* @returns An array of **dotted leaf paths** that changed. Paths use `"."` as a separator and
|
|
25
|
+
* indices for arrays (e.g., `"todos.0.title"`). If nothing changed, returns `[]`.
|
|
26
|
+
*
|
|
27
|
+
* @example Basic object leaf
|
|
28
|
+
* ```ts
|
|
29
|
+
* detectChangedProps(
|
|
30
|
+
* { user: { name: 'Ada', age: 37 } },
|
|
31
|
+
* { user: { name: 'Grace', age: 37 } }
|
|
32
|
+
* );
|
|
33
|
+
* // => ['user.name']
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @example Array element change
|
|
37
|
+
* ```ts
|
|
38
|
+
* detectChangedProps(
|
|
39
|
+
* { items: [{ title: 'A' }, { title: 'B' }] },
|
|
40
|
+
* { items: [{ title: 'A+' }, { title: 'B' }] }
|
|
41
|
+
* );
|
|
42
|
+
* // => ['items.0.title']
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* @example Array length change (marks the array path)
|
|
46
|
+
* ```ts
|
|
47
|
+
* detectChangedProps({ nums: [1,2] }, { nums: [1,2,3] });
|
|
48
|
+
* // => ['nums']
|
|
49
|
+
* ```
|
|
50
|
+
*
|
|
51
|
+
* @example Dates & RegExps
|
|
52
|
+
* ```ts
|
|
53
|
+
* detectChangedProps(new Date(0), new Date(0), 'createdAt'); // => []
|
|
54
|
+
* detectChangedProps(new Date(0), new Date(1), 'createdAt'); // => ['createdAt']
|
|
55
|
+
* detectChangedProps(/a/i, /a/i, 'pattern'); // => []
|
|
56
|
+
* detectChangedProps(/a/i, /a/g, 'pattern'); // => ['pattern']
|
|
57
|
+
* ```
|
|
58
|
+
*
|
|
59
|
+
* @remarks
|
|
60
|
+
* - If `oldState === newState` (same reference), returns `[]` immediately.
|
|
61
|
+
* - For objects, only **own enumerable** keys are compared (via `Object.keys`).
|
|
62
|
+
* - Returned paths are **leaf paths** where a primitive/terminal difference was detected; for arrays,
|
|
63
|
+
* a length change is treated as a leaf change at the array path.
|
|
64
|
+
*
|
|
65
|
+
* @public
|
|
66
|
+
*/
|
|
67
|
+
export declare function detectChangedProps(oldState: any, newState: any, path?: string, seenPairs?: WeakMap<object, WeakSet<object>>): string[];
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { DeepReadonly } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Deep-freezes a value **in place** and returns it as {@link DeepReadonly | `DeepReadonly<T>`}.
|
|
4
|
+
*
|
|
5
|
+
* @typeParam T - The input value type to freeze.
|
|
6
|
+
* @param obj - Any value; objects and arrays are frozen recursively.
|
|
7
|
+
* @param seen - (Advanced) A `WeakSet` used to track visited objects for cycle/alias safety.
|
|
8
|
+
* @returns The **same** reference as `obj`, but frozen and typed as `DeepReadonly<T>`.
|
|
9
|
+
*
|
|
10
|
+
* @remarks
|
|
11
|
+
* - **In-place**: this function mutates the input by freezing it and its children, then returns it.
|
|
12
|
+
* - **Early exits**:
|
|
13
|
+
* - Primitives and `null` are returned as-is.
|
|
14
|
+
* - Already-frozen objects (`Object.isFrozen(obj)`) are returned as-is.
|
|
15
|
+
* - Previously seen objects (by identity) are returned as-is to avoid infinite recursion on cycles.
|
|
16
|
+
* - **Arrays**: freezes each element, then `Object.freeze(array)`. Length/property descriptors are not rewritten.
|
|
17
|
+
* - **Objects**: iterates **own** string and symbol keys. Only **data properties** are recursed (getters/setters are skipped).
|
|
18
|
+
* - **Strict mode**: Mutating a frozen object throws; in non-strict mode it is a no-op (per JS semantics).
|
|
19
|
+
*
|
|
20
|
+
* @example Basic usage
|
|
21
|
+
* ```ts
|
|
22
|
+
* const state = { user: { name: 'Ada' }, items: [1, { id: 1 }] };
|
|
23
|
+
* const frozen = freezeState(state);
|
|
24
|
+
*
|
|
25
|
+
* Object.isFrozen(frozen); // true
|
|
26
|
+
* Object.isFrozen(frozen.user); // true
|
|
27
|
+
* Object.isFrozen(frozen.items); // true
|
|
28
|
+
* Object.isFrozen(frozen.items[1]); // true
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* @example Safe with cycles
|
|
32
|
+
* ```ts
|
|
33
|
+
* const a: any = {};
|
|
34
|
+
* a.self = a; // cycle
|
|
35
|
+
* freezeState(a); // does not recurse infinitely
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* @example Already frozen objects are returned as-is
|
|
39
|
+
* ```ts
|
|
40
|
+
* const o = Object.freeze({ x: 1 });
|
|
41
|
+
* const out = freezeState(o);
|
|
42
|
+
* out === o; // true
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* @public
|
|
46
|
+
*/
|
|
47
|
+
export declare function freezeState<T>(obj: T, seen?: WeakSet<object>): DeepReadonly<T>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* @yoltra/core v0.1.0
|
|
3
|
+
* (c) 2026 Manu Ramirez <@pixerael>
|
|
4
|
+
* License: MIT
|
|
5
|
+
* Homepage: https://yoltra.dev
|
|
6
|
+
*/
|
|
7
|
+
"use strict";var k=Object.defineProperty;var _=(a,e,t)=>e in a?k(a,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):a[e]=t;var u=(a,e,t)=>_(a,typeof e!="symbol"?e+"":e,t);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class M{constructor(){u(this,"handlers",new Map)}on(e,t,s){let n=this.handlers.get(e);n||(n=new Map,this.handlers.set(e,n));let r=n.get(t);return r||(r=new Set,n.set(t,r)),r.add(s),()=>this.off(e,t,s)}off(e,t,s){const n=this.handlers.get(e);if(!n)return;const r=n.get(t);r&&(r.delete(s),r.size===0&&n.delete(t),n.size===0&&this.handlers.delete(e))}emit(e,t,s){const n=this.handlers.get(e);if(!n)return;const r=n.get(t);if(!(!r||r.size===0))for(const o of[...r])try{o(s)}catch(i){console.error("EventBus handler error:",i)}}clear(){this.handlers.clear()}}class P{constructor(){u(this,"handlers",new Map);u(this,"patternHandlers",new Map)}on(e,t,s){const n=String(t);if(this.isPattern(n)){const r=n;this.patternHandlers.has(e)||this.patternHandlers.set(e,new Map);const o=this.patternHandlers.get(e);return o.has(r)||o.set(r,[]),o.get(r).push(s),()=>this.offPattern(e,r,s)}else{const r=this.normalizeTypeKey(n);this.handlers.has(e)||this.handlers.set(e,new Map);const o=this.handlers.get(e);return o.has(r)||o.set(r,[]),o.get(r).push(s),()=>this.offExactNormalized(e,r,s)}}off(e,t,s){const n=this.normalizeTypeKey(String(t));this.offExactNormalized(e,n,s)}offExactNormalized(e,t,s){const n=this.handlers.get(e);if(!n)return;const r=n.get(t);if(!r)return;const o=r.indexOf(s);o!==-1&&r.splice(o,1),r.length===0&&n.delete(t),n.size===0&&this.handlers.delete(e)}offPattern(e,t,s){const n=this.patternHandlers.get(e);if(!n)return;const r=n.get(t);if(!r)return;const o=r.indexOf(s);o!==-1&&r.splice(o,1),r.length===0&&n.delete(t),n.size===0&&this.patternHandlers.delete(e)}emit(e,t,s){const n=String(t),r=this.normalizeTypeKey(n),o=this.handlers.get(e)?.get(r)??[],i=this.patternHandlers.get(e),c=[];if(i&&i.size){const l=this.normalizeTypeKey(n);for(const[p,d]of i.entries())this.matchPattern(p,l)&&c.push(d)}const f=new Set,h=l=>{for(const p of[...l])if(!f.has(p)){f.add(p);try{p(s)}catch(d){console.error(d);continue}}};h(o);for(const l of c)h(l)}isPattern(e){return e.includes("*")}normalizeTypeKey(e){return e.replace(/^\./,"")}splitPath(e){return this.normalizeTypeKey(e).split(".").filter(Boolean)}matchPattern(e,t){const s=this.splitPath(e),n=this.splitPath(t);let r=0,o=0;for(;r<s.length&&o<n.length;){const i=s[r];if(i==="**"){if(r===s.length-1||s.slice(r).filter(f=>f!=="**").length===0)return!0;for(let f=o;f<=n.length;f++)if(this.matchPattern(s.slice(r+1).join("."),n.slice(f).join(".")))return!0;return!1}if(i==="*"||i===n[o]){r++,o++;continue}return!1}if(o===n.length){for(;r<s.length;r++)if(s[r]!=="**")return!1;return!0}return!1}clear(){this.handlers.clear(),this.patternHandlers.clear()}__introspect(){const e=[];for(const[t,s]of this.handlers)for(const[n,r]of s)r.length>0&&e.push({channel:t,type:n,count:r.length});for(const[t,s]of this.patternHandlers)for(const[n,r]of s)r.length>0&&e.push({channel:t,type:n,count:r.length});return e}}class z{constructor(e){u(this,"_reduce");this._reduce=e}reduce(e,t){return this._reduce(e,t)}}function b(a,e,t="",s=new WeakMap){if(a===e)return[];if(typeof a!="object"||typeof e!="object"||a===null||e===null)return[t];if(a instanceof Date&&e instanceof Date)return a.getTime()===e.getTime()?[]:[t];if(a instanceof RegExp&&e instanceof RegExp)return a.source===e.source&&e.flags===a.flags?[]:[t];const n=a,r=e;let o=s.get(n);if(o){if(o.has(r))return[];o.add(r)}else o=new WeakSet,o.add(r),s.set(n,o);const i=Array.isArray(a),c=Array.isArray(e);if(i!==c)return[t];const f=[];if(i){const d=a,m=e;if(d.length!==m.length)return[t];for(let y=0;y<d.length;y++){const g=t?`${t}.${y}`:`${y}`,w=b(d[y],m[y],g,s);f.push(...w)}return f.filter(Boolean)}const h=Object.keys(a),l=Object.keys(e),p=new Set([...h,...l]);for(const d of p){const m=t?`${t}.${d}`:d,y=Object.prototype.hasOwnProperty.call(a,d),g=Object.prototype.hasOwnProperty.call(e,d);if(!y){f.push(m);continue}if(!g){f.push(m);continue}const w=b(a[d],e[d],m,s);f.push(...w)}return f.filter(Boolean)}function E(a,e=new WeakSet){if(a===null||typeof a!="object"||e.has(a)||Object.isFrozen(a))return a;if(e.add(a),Array.isArray(a)){const t=a;for(let s=0;s<t.length;s++)t[s]=E(t[s],e);return Object.freeze(t)}for(const t of Object.getOwnPropertyNames(a)){const s=Object.getOwnPropertyDescriptor(a,t);!s||!("value"in s)||(a[t]=E(a[t],e))}for(const t of Object.getOwnPropertySymbols(a)){const s=Object.getOwnPropertyDescriptor(a,t);!s||!("value"in s)||(a[t]=E(a[t],e))}return Object.freeze(a)}class v{constructor(e){u(this,"name");u(this,"middleware");u(this,"reducers");u(this,"state");u(this,"reducerBus");u(this,"connectorBus");u(this,"listeners",new Set);u(this,"effects",new Map);u(this,"patternEffects",new Set);u(this,"committedEventSubscribers",new Map);u(this,"uncommittedEventSubscribers",new Map);u(this,"allEventSubscribers",new Map);u(this,"sliceUnsubs",new Map);u(this,"patternReducers",new Map);u(this,"replayEnabled");u(this,"eventQueue",[]);u(this,"isProcessingQueue",!1);u(this,"processedEvents",new Map);u(this,"dedupConfig");u(this,"eventCleanupTimer",null);this.name=e.name??"yoltra Store",this.reducerBus=new M,this.connectorBus=new P,this.middleware=[...e.middleware??[]],this.reducers={},this.state={},this.replayEnabled=e.devtools?.allowReplay??!1;const t=process.env.NODE_ENV==="production"?100:50;if(this.dedupConfig={windowMs:e.dedupWindowMs??t,maxCacheSize:1e3},Object.entries(e.reducer).forEach(([s,n])=>{this.mountSlice(s,n,{preserveState:!1})}),e.effects?.length)for(const s of e.effects)this.registerEffect(s);this.eventCleanupTimer=setInterval(()=>{this.pruneProcessedEvents(Date.now())},5e3),this.dispose=this.dispose.bind(this),this.notifyEffects=this.notifyEffects.bind(this),this.forwardEvent=this.forwardEvent.bind(this),this.__applyExternalState=this.__applyExternalState.bind(this),this.__replayEvents=this.__replayEvents.bind(this),this.__devtoolsIntrospect=this.__devtoolsIntrospect.bind(this),this.mountSlice=this.mountSlice.bind(this),this.unmountSlice=this.unmountSlice.bind(this),this.getAtPath=this.getAtPath.bind(this),this.emit=this.emit.bind(this),this.subscribe=this.subscribe.bind(this),this.connect=this.connect.bind(this),this.onEffect=this.onEffect.bind(this),this.onEvent=this.onEvent.bind(this),this.getState=this.getState.bind(this),this.registerEffect=this.registerEffect.bind(this),this.registerMiddleware=this.registerMiddleware.bind(this),this.registerReducer=this.registerReducer.bind(this),this.replaceMiddleware=this.replaceMiddleware.bind(this),this.replaceEffects=this.replaceEffects.bind(this),this.replaceReducers=this.replaceReducers.bind(this),this.hotReplace=this.hotReplace.bind(this)}dispose(){this.eventCleanupTimer&&(clearInterval(this.eventCleanupTimer),this.eventCleanupTimer=null),this.processedEvents.clear(),this.effects.clear(),this.patternEffects.clear()}fingerprint(e,t,s){const n=`${e}::${t}`;try{if(s==null)return`${n}::null`;if(typeof s!="object")return`${n}::${String(s)}`;const r=JSON.stringify(s);return`${n}::${r}`}catch{return`${n}::${Date.now()}::${Math.random()}`}}shouldDedupe(e){const t=Date.now(),s=this.processedEvents.get(e);return s!==void 0&&t-s<this.dedupConfig.windowMs?!0:(this.processedEvents.set(e,t),this.processedEvents.size>this.dedupConfig.maxCacheSize&&this.pruneProcessedEvents(t),!1)}pruneProcessedEvents(e){const t=e-this.dedupConfig.windowMs*2;for(const[s,n]of this.processedEvents)n<t&&this.processedEvents.delete(s)}matchesWhen(e,t){return!e||"any"in e&&e.any===!0?!0:"keys"in e?e.keys.some(([s,n])=>t.channel===s&&t.type===n):"channel"in e?t.channel===e.channel:"channels"in e?e.channels.includes(t.channel):!1}getMiddlewareFunction(e){return typeof e=="function"?e:e.middleware}getMiddlewareWhen(e){if(typeof e!="function")return e.when}async notifyEffects(e){const t=`${String(e.channel)}::${String(e.type)}`,s=this.effects.get(t);if(s&&s.size>0)for(const n of[...s])try{await n(e,this.getState,this.emit)}catch(r){console.error("Effect error:",r)}for(const{effect:n,when:r}of this.patternEffects)if(this.matchesWhen(r,e))try{await n(e,this.getState,this.emit)}catch(o){console.error("Effect error:",o)}}async notifyEventSubscribers(e,t){const s=`${String(e.channel)}::${String(e.type)}`,r=(t==="committed"?this.committedEventSubscribers:this.uncommittedEventSubscribers).get(s);if(r?.size)for(const i of[...r])try{await i(e,this.getState,this.emit,t)}catch(c){console.error("Event subscription error:",c)}const o=this.allEventSubscribers.get(s);if(o?.size)for(const i of[...o])try{await i(e,this.getState,this.emit,t)}catch(c){console.error("Event subscription error:",c)}}forwardEvent(e,t){const s=this.state[e],n=this.reducers[e].reduce(s,t);if(s===n)return!1;const r=b(s,n).filter(Boolean);if(r.length===0)return!1;const o=E(structuredClone(n));this.state={...this.state,[e]:o};const i=new Set;for(const c of r)for(const f of v.buildAncestorPaths(c))i.add(f);for(const c of i){const f=this.getAtPath(s,c),h=this.getAtPath(o,c);this.connectorBus.emit(e,c,{oldValue:f,newValue:h,path:c})}return!0}__devtoolsIntrospect(){const e=Object.keys(this.reducers).map(i=>{const c=this.patternReducers.get(i);return{name:i,when:c}}),t=[];for(const[i,c]of this.effects){if(c.size===0)continue;const[f,h]=i.split("::");for(const l of c){const p=l.__quoMeta;t.push({channel:f,type:h,name:p?.name,description:p?.description})}}for(const i of this.patternEffects){const c=i.effect.__quoMeta;t.push({channel:"*",type:"*",name:c?.name,description:c?.description})}const s=[];for(const i of this.middleware)typeof i=="function"?s.push({name:i.name||void 0}):s.push({name:i.meta?.name,description:i.meta?.description,when:i.when});const n=[];for(const i of this.connectorBus.__introspect())for(let c=0;c<i.count;c++)n.push({reducer:i.channel,property:i.type});const r=[];for(const[i,c]of this.committedEventSubscribers){if(c.size===0)continue;const[f,h]=i.split("::");for(let l=0;l<c.size;l++)r.push({channel:f,type:h,phase:"committed"})}for(const[i,c]of this.uncommittedEventSubscribers){if(c.size===0)continue;const[f,h]=i.split("::");for(let l=0;l<c.size;l++)r.push({channel:f,type:h,phase:"uncommitted"})}for(const[i,c]of this.allEventSubscribers){if(c.size===0)continue;const[f,h]=i.split("::");for(let l=0;l<c.size;l++)r.push({channel:f,type:h,phase:"all"})}const o=this.listeners.size;return{reducers:e,effects:t,middleware:s,atomic:n,event:r,coarse:o}}__applyExternalState(e){const t=this.state,s=e;let n={...this.state},r=!1;Object.keys(this.reducers).forEach(o=>{const i=t?.[o],c=s?.[o];if(i===c)return;const f=E(structuredClone(c));n[o]=f,r=!0;const h=b(i,c).filter(Boolean);if(h.length===0)return;const l=new Set;for(const p of h)for(const d of v.buildAncestorPaths(p))l.add(d);for(const p of l){const d=this.getAtPath(i,p),m=this.getAtPath(f,p);this.connectorBus.emit(o,p,{oldValue:d,newValue:m,path:p})}}),r&&(this.state=n),r&&this.listeners.forEach(o=>o())}__replayEvents(e,t){if(!this.replayEnabled)throw new Error("[yoltra] Event replay is disabled. Enable it with createStore({ devtools: { allowReplay: true } })");this.__applyExternalState(e);for(const s of t){const n=s,r=this.state;this.reducerBus.emit(n.channel,n.type,n.payload);for(const[c,f]of this.patternReducers)this.matchesWhen(f,n)&&this.forwardEvent(c,n);const o=this.state,i=r!==o;this.notifyEventSubscribers(n,"committed"),i&&this.listeners.forEach(c=>c())}}async emit(e,t,s){const n=this.fingerprint(e,t,s);if(this.shouldDedupe(n))return;const r=crypto.randomUUID();if(this.eventQueue.push({channel:e,type:t,payload:s,id:r}),!this.isProcessingQueue){this.isProcessingQueue=!0;try{for(;this.eventQueue.length;){const{channel:o,type:i,payload:c,id:f}=this.eventQueue.shift(),h={channel:o,type:i,payload:c,id:f};let l=!0;for(const y of this.middleware){const g=this.getMiddlewareWhen(y);if(!this.matchesWhen(g,h))continue;const w=this.getMiddlewareFunction(y);try{if(!await w(this.state,h,this.emit)){l=!1;break}}catch(S){console.error("Middleware error:",S),l=!1;break}}if(!l){await this.notifyEventSubscribers(h,"uncommitted");continue}const p=this.state;this.reducerBus.emit(o,i,c);for(const[y,g]of this.patternReducers)this.matchesWhen(g,h)&&this.forwardEvent(y,h);const d=this.state,m=p!==d;await this.notifyEventSubscribers(h,"committed"),await this.notifyEffects(h),m&&this.listeners.forEach(y=>y())}}catch(o){console.error("Emit queue error:",o)}finally{this.isProcessingQueue=!1}}}connect(e,t){return this.connectorBus.on(e.reducer,e.property,t)}onEvent(e,t,s,n="committed"){const r=`${e}::${String(t)}`,o=n==="committed"?this.committedEventSubscribers:n==="uncommitted"?this.uncommittedEventSubscribers:this.allEventSubscribers;return o.has(r)||o.set(r,new Set),o.get(r).add(s),()=>{const i=o.get(r);i&&(i.delete(s),i.size===0&&o.delete(r))}}subscribe(e){return this.listeners.add(e),()=>this.listeners.delete(e)}getState(){return this.state}registerMiddleware(e){return this.middleware.push(e),()=>{const t=this.middleware.indexOf(e);t!==-1&&this.middleware.splice(t,1)}}registerReducer(e,t){if(e in this.reducers)throw new Error(`Reducer ${e} already exists`);return this.mountSlice(e,t,{preserveState:!1}),this.listeners.forEach(s=>s()),()=>{this.unmountSlice(e,{deleteState:!0}),this.listeners.forEach(s=>s())}}registerEffect(e){const{effect:t,meta:s,when:n}=e,r=[];if(s&&(t.__quoMeta=s),n&&("any"in n&&n.any===!0||"channel"in n||"channels"in n)){const c={effect:t,when:n};return this.patternEffects.add(c),()=>{this.patternEffects.delete(c)}}const i=this.normalizeEventKeys(e);if(i.length===0&&!n&&!e.events){const c={effect:t,when:{any:!0}};return this.patternEffects.add(c),()=>{this.patternEffects.delete(c)}}for(const[c,f]of i){const h=`${String(c)}::${String(f)}`;this.effects.has(h)||this.effects.set(h,new Set),this.effects.get(h).add(t),r.push(()=>{const l=this.effects.get(h);l&&(l.delete(t),l.size===0&&this.effects.delete(h))})}return()=>{for(const c of r)c()}}onEffect(e,t,s){const n=async(r,o,i)=>{if(r.channel!==e||r.type!==t)return;const c=r;return s(c.payload,o,i,c)};return this.registerEffect({events:[[e,t]],effect:n})}replaceMiddleware(e){this.middleware.length=0;for(const t of e)this.middleware.push(t)}replaceEffects(e){this.effects.clear(),this.patternEffects.clear();for(const t of e)this.registerEffect(t)}replaceReducers(e,t={}){const s=t.preserveState!==!1,n=new Set(Object.keys(this.reducers)),r=Object.entries(e),o=new Set(r.map(([i])=>i));for(const i of n)o.has(i)||this.unmountSlice(i,{deleteState:!0});for(const[i,c]of r)n.has(i)?(this.unmountSlice(i,{deleteState:!1}),this.mountSlice(i,c,{preserveState:s})):this.mountSlice(i,c,{preserveState:!1})}hotReplace(e){e.middleware&&this.replaceMiddleware(e.middleware),e.effects&&this.replaceEffects(e.effects),e.reducer&&this.replaceReducers(e.reducer,{preserveState:e.preserveState})}mountSlice(e,t,s){const n=e,{events:r,reducer:o,state:i,when:c}=t;if(this.reducers[e]=new z(o),(!s.preserveState||this.state[n]===void 0)&&(this.state[n]=E(structuredClone(i))),c&&("any"in c&&c.any===!0||"channel"in c||"channels"in c)){this.patternReducers.set(e,c),this.sliceUnsubs.set(n,[]);return}const h=this.normalizeEventKeys(t);if(h.length===0&&!c&&!r){this.patternReducers.set(e,{any:!0}),this.sliceUnsubs.set(n,[]);return}const l=[];for(const[p,d]of h){const m=this.reducerBus.on(p,d,y=>{const g={channel:p,type:d,payload:y,id:crypto.randomUUID()};this.forwardEvent(e,g)});l.push(m)}this.sliceUnsubs.set(n,l)}unmountSlice(e,t){const s=e;this.patternReducers.delete(e);const n=this.sliceUnsubs.get(s);if(n){for(const r of n)try{r()}catch(o){console.error(`[Store error]: ${o}`)}this.sliceUnsubs.delete(s)}delete this.reducers[e],t.deleteState&&delete this.state[s]}normalizeEventKeys(e){if(e.when){const t=e.when;if("any"in t&&t.any===!0)return[];if("keys"in t)return t.keys;if("channel"in t)return[];if("channels"in t)return[]}return e.events?e.events:[]}getAtPath(e,t){if(!t)return e;const n=(t[0]==="."?t.slice(1):t).split(".");let r=e;for(const o of n){if(r==null)return;r=r[o]}return r}static buildAncestorPaths(e){if(!e)return[];const s=(e[0]==="."?e.slice(1):e).split("."),n=[];for(let r=0;r<s.length;r++)n.push(s.slice(0,r+1).join("."));return n}}function O(a){return new v({name:a.name,reducer:a.reducer??{},middleware:a.middleware??[],effects:a.effects??[],dedupWindowMs:a.dedupWindowMs,devtools:a.devtools})}const B=a=>(e,t)=>t.map(s=>[e,s]),x=()=>a=>a;exports.EventBus=M;exports.LooseEventBus=P;exports.Reducer=z;exports.Store=v;exports.createStore=O;exports.detectChangedProps=b;exports.eventKeys=x;exports.freezeState=E;exports.typedEvents=B;
|
|
8
|
+
//# sourceMappingURL=yoltra.cjs.js.map
|