@zeix/cause-effect 0.17.1 → 0.17.2

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.
Files changed (48) hide show
  1. package/.ai-context.md +7 -0
  2. package/.github/copilot-instructions.md +4 -0
  3. package/CLAUDE.md +96 -1
  4. package/README.md +44 -7
  5. package/archive/collection.ts +23 -25
  6. package/archive/computed.ts +3 -2
  7. package/archive/list.ts +21 -28
  8. package/archive/memo.ts +2 -1
  9. package/archive/state.ts +2 -1
  10. package/archive/store.ts +21 -32
  11. package/archive/task.ts +4 -7
  12. package/index.dev.js +356 -198
  13. package/index.js +1 -1
  14. package/index.ts +15 -6
  15. package/package.json +1 -1
  16. package/src/classes/collection.ts +69 -53
  17. package/src/classes/composite.ts +28 -33
  18. package/src/classes/computed.ts +87 -28
  19. package/src/classes/list.ts +31 -26
  20. package/src/classes/ref.ts +33 -5
  21. package/src/classes/state.ts +41 -8
  22. package/src/classes/store.ts +47 -30
  23. package/src/diff.ts +2 -1
  24. package/src/effect.ts +19 -9
  25. package/src/errors.ts +10 -1
  26. package/src/resolve.ts +1 -1
  27. package/src/signal.ts +0 -1
  28. package/src/system.ts +159 -43
  29. package/src/util.ts +0 -6
  30. package/test/collection.test.ts +279 -20
  31. package/test/computed.test.ts +268 -11
  32. package/test/effect.test.ts +2 -2
  33. package/test/list.test.ts +249 -21
  34. package/test/ref.test.ts +154 -0
  35. package/test/state.test.ts +13 -13
  36. package/test/store.test.ts +473 -28
  37. package/types/index.d.ts +3 -3
  38. package/types/src/classes/collection.d.ts +8 -7
  39. package/types/src/classes/composite.d.ts +4 -4
  40. package/types/src/classes/computed.d.ts +17 -0
  41. package/types/src/classes/list.d.ts +2 -2
  42. package/types/src/classes/ref.d.ts +10 -1
  43. package/types/src/classes/state.d.ts +9 -0
  44. package/types/src/classes/store.d.ts +4 -4
  45. package/types/src/effect.d.ts +1 -2
  46. package/types/src/errors.d.ts +4 -1
  47. package/types/src/system.d.ts +40 -24
  48. package/types/src/util.d.ts +1 -2
package/index.js CHANGED
@@ -1 +1 @@
1
- var B=Symbol(),o=($)=>typeof $==="string",s=($)=>typeof $==="number",E=($)=>typeof $==="symbol",M=($)=>typeof $==="function",j=($)=>M($)&&$.constructor.name==="AsyncFunction",P$=($)=>M($)&&$.constructor.name!=="AsyncFunction",J$=($)=>$!=null&&typeof $==="object",K=($,G)=>Object.prototype.toString.call($)===`[object ${G}]`,D=($)=>K($,"Object"),c=($)=>D($)||Array.isArray($),z$=($,G=(J)=>J!=null)=>Array.isArray($)&&$.every(G);var S=($)=>$ instanceof DOMException&&$.name==="AbortError",T=($)=>o($)?`"${$}"`:!!$&&typeof $==="object"?JSON.stringify($):String($);var L=($,G,J)=>{if(Object.is($,G))return!0;if(typeof $!==typeof G)return!1;if(!J$($)||!J$(G))return!1;if(!J)J=new WeakSet;if(J.has($)||J.has(G))throw new U("isEqual");J.add($),J.add(G);try{if(Array.isArray($)&&Array.isArray(G)){if($.length!==G.length)return!1;for(let z=0;z<$.length;z++)if(!L($[z],G[z],J))return!1;return!0}if(Array.isArray($)!==Array.isArray(G))return!1;if(D($)&&D(G)){let z=Object.keys($),X=Object.keys(G);if(z.length!==X.length)return!1;for(let Z of z){if(!(Z in G))return!1;if(!L($[Z],G[Z],J))return!1}return!0}return!1}finally{J.delete($),J.delete(G)}},w=($,G)=>{let J=c($),z=c(G);if(!J||!z){let H=!Object.is($,G);return{changed:H,add:H&&z?G:{},change:{},remove:H&&J?$:{}}}let X=new WeakSet,Z={},Q={},I={},f=Object.keys($),u=Object.keys(G),_=new Set([...f,...u]);for(let H of _){let W=H in $,m=H in G;if(!W&&m){Z[H]=G[H];continue}else if(W&&!m){I[H]=B;continue}let N$=$[H],Y$=G[H];if(!L(N$,Y$,X))Q[H]=Y$}return{add:Z,change:Q,remove:I,changed:!!(Object.keys(Z).length||Object.keys(Q).length||Object.keys(I).length)}};var O,g=new Set,i=0,Y=($)=>{let G=new Set,J=$;return J.onCleanup=(z)=>{G.add(z)},J.stop=()=>{for(let z of G)z();G.clear()},J},q=($)=>{if(O&&!$.has(O)){let G=O;G.onCleanup(()=>$.delete(G)),$.add(G)}},x=($)=>{for(let G of $)if(i)g.add(G);else G()},p=()=>{while(g.size){let $=Array.from(g);g.clear();for(let G of $)G()}},X$=($)=>{i++;try{$()}finally{p(),i--}},P=($,G)=>{let J=O;O=$||void 0;try{G()}finally{O=J}},A=($,G)=>{for(let J of $)if(i)g.add(()=>J(G));else J(G)};var t="Computed";class b{#G=new Set;#J;#z;#$;#X=!0;#Q=!1;#Z;constructor($,G=B){N("memo",$,l),F("memo",G),this.#J=$,this.#z=G,this.#Z=Y(()=>{if(this.#X=!0,this.#G.size)x(this.#G);else this.#Z.stop()})}get[Symbol.toStringTag](){return t}get(){if(q(this.#G),p(),this.#X)P(this.#Z,()=>{if(this.#Q)throw new U("memo");let $;this.#Q=!0;try{$=this.#J(this.#z)}catch(G){this.#z=B,this.#$=R(G),this.#Q=!1;return}if($==null||B===$)this.#z=B,this.#$=void 0;else this.#z=$,this.#$=void 0,this.#X=!1;this.#Q=!1});if(this.#$)throw this.#$;return this.#z}}class y{#G=new Set;#J;#z;#$;#X=!0;#Q=!1;#Z=!1;#x;#B;constructor($,G=B){N("task",$,r),F("task",G),this.#J=$,this.#z=G,this.#x=Y(()=>{if(this.#X=!0,this.#B?.abort(),this.#G.size)x(this.#G);else this.#x.stop()}),this.#x.onCleanup(()=>{this.#B?.abort()})}get[Symbol.toStringTag](){return t}get(){q(this.#G),p();let $=(Z)=>{if(!L(Z,this.#z))this.#z=Z,this.#Z=!0;this.#$=void 0,this.#X=!1},G=()=>{this.#Z=B!==this.#z,this.#z=B,this.#$=void 0},J=(Z)=>{let Q=R(Z);this.#Z=!this.#$||Q.name!==this.#$.name||Q.message!==this.#$.message,this.#z=B,this.#$=Q},z=(Z)=>(Q)=>{if(this.#Q=!1,this.#B=void 0,Z(Q),this.#Z)x(this.#G)},X=()=>P(this.#x,()=>{if(this.#Q)throw new U("task");if(this.#Z=!1,this.#B)return this.#z;this.#B=new AbortController,this.#B.signal.addEventListener("abort",()=>{this.#Q=!1,this.#B=void 0,X()},{once:!0});let Z;this.#Q=!0;try{Z=this.#J(this.#z,this.#B.signal)}catch(Q){if(S(Q))G();else J(Q);this.#Q=!1;return}if(Z instanceof Promise)Z.then(z($),z(J));else if(Z==null||B===Z)G();else $(Z);this.#Q=!1});if(this.#X)X();if(this.#$)throw this.#$;return this.#z}}var Z$=($,G=B)=>j($)?new y($,G):new b($,G),Q$=($)=>K($,t),l=($)=>P$($)&&$.length<2,r=($)=>j($)&&$.length<3;class k{signals=new Map;#G;#J;#z=new Map;#$={add:new Set,change:new Set,remove:new Set};#X=!1;constructor($,G,J){this.#G=G,this.#J=J,this.change({add:$,change:{},remove:{},changed:!0},!0)}#Q($){let G=Y(()=>{P(G,()=>{if(this.signals.get($)?.get(),!this.#X)A(this.#$.change,[$])})});this.#z.set($,G),G()}add($,G){if(!this.#G($,G))return!1;if(this.signals.set($,this.#J(G)),this.#$.change.size)this.#Q($);if(!this.#X)A(this.#$.add,[$]);return!0}remove($){if(!this.signals.delete($))return!1;let J=this.#z.get($);if(J)J.stop(),this.#z.delete($);if(!this.#X)A(this.#$.remove,[$]);return!0}change($,G){if(this.#X=!0,Object.keys($.add).length){for(let z in $.add)this.add(z,$.add[z]);let J=()=>A(this.#$.add,Object.keys($.add));if(G)setTimeout(J,0);else J()}if(Object.keys($.change).length)X$(()=>{for(let J in $.change){let z=$.change[J];if(!this.#G(J,z))continue;let X=this.signals.get(J);if(B$(`list item "${J}"`,z,X))X.set(z)}}),A(this.#$.change,Object.keys($.change));if(Object.keys($.remove).length){for(let J in $.remove)this.remove(J);A(this.#$.remove,Object.keys($.remove))}return this.#X=!1,$.changed}clear(){let $=Array.from(this.signals.keys());return this.signals.clear(),this.#z.clear(),A(this.#$.remove,$),!0}on($,G){if(this.#$[$].add(G),$==="change"&&!this.#z.size){this.#X=!0;for(let J of this.signals.keys())this.#Q(J);this.#X=!1}return()=>{if(this.#$[$].delete(G),$==="change"&&!this.#$.change.size){if(this.#z.size){for(let J of this.#z.values())J.stop();this.#z.clear()}}}}}var x$="State";class C{#G=new Set;#J;constructor($){F("state",$),this.#J=$}get[Symbol.toStringTag](){return x$}get(){return q(this.#G),this.#J}set($){if(F("state",$),L(this.#J,$))return;if(this.#J=$,x(this.#G),B===this.#J)this.#G.clear()}update($){N("state update",$),this.set($(this.#J))}}var a=($)=>K($,x$);var H$="List";class h{#G;#J=new Set;#z={sort:new Set};#$=[];#X;constructor($,G){F("list",$,Array.isArray);let J=0;this.#X=o(G)?()=>`${G}${J++}`:M(G)?(z)=>G(z):()=>String(J++),this.#G=new k(this.#Q($),(z,X)=>{return F(`list for key "${z}"`,X),!0},(z)=>new C(z))}#Q($){let G={};for(let J=0;J<$.length;J++){let z=$[J];if(z===void 0)continue;let X=this.#$[J];if(!X)X=this.#X(z),this.#$[J]=X;G[X]=z}return G}get#Z(){return this.#$.map(($)=>this.#G.signals.get($)?.get()).filter(($)=>$!==void 0)}get[Symbol.toStringTag](){return H$}get[Symbol.isConcatSpreadable](){return!0}*[Symbol.iterator](){for(let $ of this.#$){let G=this.#G.signals.get($);if(G)yield G}}get length(){return q(this.#J),this.#$.length}get(){return q(this.#J),this.#Z}set($){if(B===$){this.#G.clear(),x(this.#J),this.#J.clear();return}let G=this.#Z,J=w(this.#Q(G),this.#Q($)),z=Object.keys(J.remove);if(this.#G.change(J)){for(let Z of z){let Q=this.#$.indexOf(Z);if(Q!==-1)this.#$.splice(Q,1)}this.#$=this.#$.filter(()=>!0),x(this.#J)}}update($){this.set($(this.get()))}at($){return this.#G.signals.get(this.#$[$])}keys(){return this.#$.values()}byKey($){return this.#G.signals.get($)}keyAt($){return this.#$[$]}indexOfKey($){return this.#$.indexOf($)}add($){let G=this.#X($);if(this.#G.signals.has(G))throw new V("store",G,$);if(!this.#$.includes(G))this.#$.push(G);if(this.#G.add(G,$))x(this.#J);return G}remove($){let G=s($)?this.#$[$]:$;if(this.#G.remove(G)){let z=s($)?$:this.#$.indexOf(G);if(z>=0)this.#$.splice(z,1);this.#$=this.#$.filter(()=>!0),x(this.#J)}}sort($){let J=this.#$.map((z)=>[z,this.#G.signals.get(z)?.get()]).sort(M($)?(z,X)=>$(z[1],X[1]):(z,X)=>String(z[1]).localeCompare(String(X[1]))).map(([z])=>z);if(!L(this.#$,J))this.#$=J,x(this.#J),A(this.#z.sort,this.#$)}splice($,G,...J){let z=this.#$.length,X=$<0?Math.max(0,z+$):Math.min($,z),Z=Math.max(0,Math.min(G??Math.max(0,z-Math.max(0,X)),z-X)),Q={},I={};for(let _=0;_<Z;_++){let H=X+_,W=this.#$[H];if(W){let m=this.#G.signals.get(W);if(m)I[W]=m.get()}}let f=this.#$.slice(0,X);for(let _ of J){let H=this.#X(_);f.push(H),Q[H]=_}f.push(...this.#$.slice(X+Z));let u=!!(Object.keys(Q).length||Object.keys(I).length);if(u)this.#G.change({add:Q,change:{},remove:I,changed:u}),this.#$=f.filter(()=>!0),x(this.#J);return Object.values(I)}on($,G){if($==="sort")return this.#z.sort.add(G),()=>{this.#z.sort.delete(G)};return this.#G.on($,G)}deriveCollection($){return new d(this,$)}}var v=($)=>K($,H$);var M$="Store";class q${#G;#J=new Set;constructor($){F("store",$,D),this.#G=new k($,(G,J)=>{return F(`store for key "${G}"`,J),!0},(G)=>U$(G))}get#z(){let $={};for(let[G,J]of this.#G.signals.entries())$[G]=J.get();return $}get[Symbol.toStringTag](){return M$}get[Symbol.isConcatSpreadable](){return!1}*[Symbol.iterator](){for(let[$,G]of this.#G.signals.entries())yield[$,G]}get(){return q(this.#J),this.#z}set($){if(B===$){this.#G.clear(),x(this.#J),this.#J.clear();return}let G=this.#z;if(this.#G.change(w(G,$)))x(this.#J)}keys(){return this.#G.signals.keys()}byKey($){return this.#G.signals.get($)}update($){this.set($(this.get()))}add($,G){if(this.#G.signals.has($))throw new V("store",$,G);if(this.#G.add($,G))x(this.#J);return $}remove($){if(this.#G.remove($))x(this.#J)}on($,G){return this.#G.on($,G)}}var e=($)=>{let G=new q$($);return new Proxy(G,{get(J,z){if(z in J){let X=Reflect.get(J,z);return M(X)?X.bind(J):X}if(!E(z))return J.byKey(z)},has(J,z){if(z in J)return!0;return J.byKey(String(z))!==void 0},ownKeys(J){return Array.from(J.keys())},getOwnPropertyDescriptor(J,z){if(z in J)return Reflect.getOwnPropertyDescriptor(J,z);if(E(z))return;let X=J.byKey(String(z));return X?{enumerable:!0,configurable:!0,writable:!0,value:X}:void 0}})},$$=($)=>K($,M$);var T$=($)=>a($)||Q$($)||$$($),A$=($)=>a($)||$$($)||v($);function C$($){if(l($))return new b($);if(r($))return new y($);if(z$($))return new h($);if(D($))return e($);return new C($)}function U$($){if(z$($))return new h($);if(D($))return e($);return new C($)}class U extends Error{constructor($){super(`Circular dependency detected in ${$}`);this.name="CircularDependencyError"}}class V extends Error{constructor($,G,J){super(`Could not add ${$} key "${G}"${J?` with value ${T(J)}`:""} because it already exists`);this.name="DuplicateKeyError"}}class n extends TypeError{constructor($,G){super(`Invalid ${$} callback ${T(G)}`);this.name="InvalidCallbackError"}}class G$ extends TypeError{constructor($,G){super(`Invalid ${$} source ${T(G)}`);this.name="InvalidCollectionSourceError"}}class F$ extends TypeError{constructor($,G){super(`Invalid signal value ${T(G)} in ${$}`);this.name="InvalidSignalValueError"}}class I$ extends TypeError{constructor($){super(`Nullish signal values are not allowed in ${$}`);this.name="NullishSignalValueError"}}class K$ extends Error{constructor($,G){super(`Could not set ${$} to ${T(G)} because signal is read-only`);this.name="ReadonlySignalError"}}var R=($)=>$ instanceof Error?$:Error(String($)),N=($,G,J=M)=>{if(!J(G))throw new n($,G)},F=($,G,J=()=>!(E(G)&&G!==B)||M(G))=>{if(G==null)throw new I$($);if(!J(G))throw new F$($,G)},B$=($,G,J)=>{if(!A$(J))throw new K$($,G);return!0};var D$="Collection";class d{#G=new Set;#J;#z;#$=new Map;#X=new Map;#Q={add:new Set,change:new Set,remove:new Set,sort:new Set};#Z=[];constructor($,G){if(N("collection",G),M($))$=$();if(!W$($))throw new G$("derived collection",$);this.#J=$,this.#z=G;for(let J=0;J<this.#J.length;J++){let z=this.#J.keyAt(J);if(!z)continue;this.#x(z)}this.#J.on("add",(J)=>{for(let z of J)if(!this.#$.has(z)){this.#x(z);let X=this.#$.get(z);if(X&&_$(this.#z))X.get()}x(this.#G),A(this.#Q.add,J)}),this.#J.on("remove",(J)=>{for(let z of J){if(!this.#$.has(z))continue;this.#$.delete(z);let X=this.#Z.indexOf(z);if(X>=0)this.#Z.splice(X,1);let Z=this.#X.get(z);if(Z)Z.stop(),this.#X.delete(z)}this.#Z=this.#Z.filter(()=>!0),x(this.#G),A(this.#Q.remove,J)}),this.#J.on("sort",(J)=>{this.#Z=[...J],x(this.#G),A(this.#Q.sort,J)})}#x($){let G=_$(this.#z)?async(z,X)=>{let Z=this.#J.byKey($);if(!Z)return B;let Q=Z.get();if(Q===B)return B;return this.#z(Q,X)}:()=>{let z=this.#J.byKey($);if(!z)return B;let X=z.get();if(X===B)return B;return this.#z(X)},J=Z$(G);if(this.#$.set($,J),!this.#Z.includes($))this.#Z.push($);if(this.#Q.change.size)this.#B($);return!0}#B($){let G=Y(()=>{P(G,()=>{this.#$.get($)?.get()})});this.#X.set($,G),G()}get[Symbol.toStringTag](){return D$}get[Symbol.isConcatSpreadable](){return!0}*[Symbol.iterator](){for(let $ of this.#Z){let G=this.#$.get($);if(G)yield G}}get length(){return q(this.#G),this.#Z.length}get(){return q(this.#G),this.#Z.map(($)=>this.#$.get($)?.get()).filter(($)=>$!=null&&$!==B)}at($){return this.#$.get(this.#Z[$])}keys(){return this.#Z.values()}byKey($){return this.#$.get($)}keyAt($){return this.#Z[$]}indexOfKey($){return this.#Z.indexOf($)}on($,G){if(this.#Q[$].add(G),$==="change"&&!this.#X.size)for(let J of this.#$.keys())this.#B(J);return()=>{if(this.#Q[$].delete(G),$==="change"&&!this.#Q.change.size){if(this.#X.size){for(let J of this.#X.values())J.stop();this.#X.clear()}}}}deriveCollection($){return new d(this,$)}}var j$=($)=>K($,D$),W$=($)=>v($)||j$($),_$=($)=>j($);var L$="Ref";class R${#G=new Set;#J;constructor($,G){F("ref",$,G),this.#J=$}get[Symbol.toStringTag](){return L$}get(){return q(this.#G),this.#J}notify(){x(this.#G)}}var E$=($)=>K($,L$);var S$=($)=>{if(!M($)||$.length>1)throw new n("effect",$);let G=j($),J=!1,z,X=Y(()=>P(X,()=>{if(J)throw new U("effect");J=!0,z?.abort(),z=void 0;let Z;try{if(G){z=new AbortController;let Q=z;$(z.signal).then((I)=>{if(M(I)&&z===Q)X.onCleanup(I)}).catch((I)=>{if(!S(I))console.error("Async effect error:",I)})}else if(Z=$(),M(Z))X.onCleanup(Z)}catch(Q){if(!S(Q))console.error("Effect callback error:",Q)}J=!1}));return X(),()=>{z?.abort(),X.stop()}};function O$($,G){try{if($.pending)G.nil?.();else if($.errors)G.err?.($.errors);else if($.ok)G.ok($.values)}catch(J){let z=R(J);if(G.err&&(!$.errors||!$.errors.includes(z)))G.err($.errors?[...$.errors,z]:[z]);else throw z}}function V$($){let G=[],J=!1,z={};for(let[X,Z]of Object.entries($))try{let Q=Z.get();if(Q===B)J=!0;else z[X]=Q}catch(Q){G.push(R(Q))}if(J)return{ok:!1,pending:!0};if(G.length>0)return{ok:!1,errors:G};return{ok:!0,values:z}}export{T as valueString,F as validateSignalValue,N as validateCallback,P as trackSignalReads,q as subscribeActiveWatcher,V$ as resolve,x as notifyWatchers,O$ as match,r as isTaskCallback,E as isSymbol,o as isString,$$ as isStore,a as isState,T$ as isSignal,E$ as isRef,c as isRecordOrArray,D as isRecord,K as isObjectOfType,s as isNumber,A$ as isMutableSignal,l as isMemoCallback,v as isList,M as isFunction,L as isEqual,Q$ as isComputed,j$ as isCollection,j as isAsyncFunction,S as isAbortError,B$ as guardMutableSignal,p as flushPendingReactions,A as emitNotification,w as diff,Y as createWatcher,e as createStore,C$ as createSignal,R as createError,S$ as createEffect,Z$ as createComputed,X$ as batchSignalWrites,B as UNSET,y as Task,M$ as TYPE_STORE,x$ as TYPE_STATE,L$ as TYPE_REF,H$ as TYPE_LIST,t as TYPE_COMPUTED,D$ as TYPE_COLLECTION,C as State,R$ as Ref,K$ as ReadonlySignalError,I$ as NullishSignalValueError,b as Memo,h as List,F$ as InvalidSignalValueError,G$ as InvalidCollectionSourceError,n as InvalidCallbackError,V as DuplicateKeyError,d as DerivedCollection,U as CircularDependencyError,q$ as BaseStore};
1
+ var Z$=($)=>typeof $==="string",Q$=($)=>typeof $==="number",p=($)=>typeof $==="symbol",q=($)=>typeof $==="function",H=($)=>q($)&&$.constructor.name==="AsyncFunction",_$=($)=>q($)&&$.constructor.name!=="AsyncFunction",U$=($)=>$!=null&&typeof $==="object",L=($,J)=>Object.prototype.toString.call($)===`[object ${J}]`,P=($)=>L($,"Object"),B$=($)=>P($)||Array.isArray($),Y$=($,J=(z)=>z!=null)=>Array.isArray($)&&$.every(J);var y=($)=>$ instanceof DOMException&&$.name==="AbortError",C=($)=>Z$($)?`"${$}"`:!!$&&typeof $==="object"?JSON.stringify($):String($);var b,o=new WeakMap,x$=new Set,L$=0,x=Symbol(),A="add",N="change",m="cleanup",K="remove",v="sort",F="watch",S=($)=>{let J=new Set,z=$;return z.on=(X,Z)=>{if(X===m)J.add(Z);else throw new M("watcher",X)},z.stop=()=>{try{for(let X of J)X()}finally{J.clear()}},z},I=($,J)=>{if(!$.size&&J?.size){let z=j(J);if(z){let X=o.get($)??new Set;if(X.add(z),!o.has($))o.set($,X)}}if(b&&!$.has(b)){let z=b;z.on(m,()=>{if($.delete(z),!$.size){let X=o.get($);if(X)try{for(let Z of X)Z()}finally{X.clear(),o.delete($)}}}),$.add(z)}},G=($)=>{if(!$.size)return!1;for(let J of $)if(L$)x$.add(J);else J();return!0},s=()=>{while(x$.size){let $=Array.from(x$);x$.clear();for(let J of $)J()}},N$=($)=>{L$++;try{$()}finally{s(),L$--}},W=($,J)=>{let z=b;b=$||void 0;try{J()}finally{b=z}},j=($,J)=>{if(!$)return;let z=[],X=[],Z=(Q)=>{if(X.length){if(X.length===1)throw X[0];throw AggregateError(X,`Errors in hook ${Q?"cleanup":"callback"}:`)}};for(let Q of $)try{let B=Q(J);if(q(B))z.push(B)}catch(B){X.push(R(B))}if(Z(),!z.length)return;if(z.length===1)return z[0];return()=>{for(let Q of z)try{Q()}catch(B){X.push(R(B))}Z(!0)}},f=($,J)=>J.includes($);var V=($,J,z)=>{if(Object.is($,J))return!0;if(typeof $!==typeof J)return!1;if(!U$($)||!U$(J))return!1;if(!z)z=new WeakSet;if(z.has($)||z.has(J))throw new _("isEqual");z.add($),z.add(J);try{if(Array.isArray($)&&Array.isArray(J)){if($.length!==J.length)return!1;for(let X=0;X<$.length;X++)if(!V($[X],J[X],z))return!1;return!0}if(Array.isArray($)!==Array.isArray(J))return!1;if(P($)&&P(J)){let X=Object.keys($),Z=Object.keys(J);if(X.length!==Z.length)return!1;for(let Q of X){if(!(Q in J))return!1;if(!V($[Q],J[Q],z))return!1}return!0}return!1}finally{z.delete($),z.delete(J)}},t=($,J)=>{let z=B$($),X=B$(J);if(!z||!X){let D=!Object.is($,J);return{changed:D,add:D&&X?J:{},change:{},remove:D&&z?$:{}}}let Z=new WeakSet,Q={},B={},Y={},k=Object.keys($),X$=Object.keys(J),T=new Set([...k,...X$]);for(let D of T){let w=D in $,i=D in J;if(!w&&i){Q[D]=J[D];continue}else if(w&&!i){Y[D]=x;continue}let g$=$[D],m$=J[D];if(!V(g$,m$,Z))B[D]=m$}return{add:Q,change:B,remove:Y,changed:!!(Object.keys(Q).length||Object.keys(B).length||Object.keys(Y).length)}};var G$="Computed";class c{#z=new Set;#X;#J;#$;#B=!0;#Q=!1;#Z;#x;constructor($,J=x){E(this.constructor.name,$,q$),U(this.constructor.name,J),this.#X=$,this.#J=J}#G(){if(!this.#Z)this.#Z=S(()=>{if(this.#B=!0,!G(this.#z))this.#Z?.stop()}),this.#Z.on(m,()=>{this.#Z=void 0});return this.#Z}get[Symbol.toStringTag](){return G$}get(){if(I(this.#z,this.#x),s(),this.#B){let $=this.#G();W($,()=>{if(this.#Q)throw new _("memo");let J;this.#Q=!0;try{J=this.#X(this.#J)}catch(z){this.#J=x,this.#$=R(z),this.#Q=!1;return}if(J==null||x===J)this.#J=x,this.#$=void 0;else this.#J=J,this.#$=void 0,this.#B=!1;this.#Q=!1})}if(this.#$)throw this.#$;return this.#J}on($,J){if($===F)return this.#x||=new Set,this.#x.add(J),()=>{this.#x?.delete(J)};throw new M(this.constructor.name,$)}}class r{#z=new Set;#X;#J;#$;#B=!0;#Q=!1;#Z=!1;#x;#G;#q;constructor($,J=x){E(this.constructor.name,$,F$),U(this.constructor.name,J),this.#X=$,this.#J=J}#F(){if(!this.#x)this.#x=S(()=>{if(this.#B=!0,this.#G?.abort(),!G(this.#z))this.#x?.stop()}),this.#x.on(m,()=>{this.#G?.abort(),this.#G=void 0,this.#x=void 0});return this.#x}get[Symbol.toStringTag](){return G$}get(){I(this.#z,this.#q),s();let $=(Q)=>{if(!V(Q,this.#J))this.#J=Q,this.#Z=!0;this.#$=void 0,this.#B=!1},J=()=>{this.#Z=x!==this.#J,this.#J=x,this.#$=void 0},z=(Q)=>{let B=R(Q);this.#Z=!this.#$||B.name!==this.#$.name||B.message!==this.#$.message,this.#J=x,this.#$=B},X=(Q)=>(B)=>{if(this.#Q=!1,this.#G=void 0,Q(B),this.#Z&&!G(this.#z))this.#x?.stop()},Z=()=>W(this.#F(),()=>{if(this.#Q)throw new _("task");if(this.#Z=!1,this.#G)return this.#J;this.#G=new AbortController,this.#G.signal.addEventListener("abort",()=>{this.#Q=!1,this.#G=void 0,Z()},{once:!0});let Q;this.#Q=!0;try{Q=this.#X(this.#J,this.#G.signal)}catch(B){if(y(B))J();else z(B);this.#Q=!1;return}if(Q instanceof Promise)Q.then(X($),X(z));else if(Q==null||x===Q)J();else $(Q);this.#Q=!1});if(this.#B)Z();if(this.#$)throw this.#$;return this.#J}on($,J){if($===F)return this.#q||=new Set,this.#q.add(J),()=>{this.#q?.delete(J)};throw new M(this.constructor.name,$)}}var R$=($,J=x)=>H($)?new r($,J):new c($,J),P$=($)=>L($,G$),q$=($)=>_$($)&&$.length<2,F$=($)=>H($)&&$.length<3;class l{signals=new Map;#z;#X;#J=new Map;#$={};#B=!1;constructor($,J,z){this.#z=J,this.#X=z,this.change({add:$,change:{},remove:{},changed:!0},!0)}#Q($){let J=S(()=>{W(J,()=>{if(this.signals.get($)?.get(),!this.#B)j(this.#$.change,[$])})});this.#J.set($,J),J()}add($,J){if(!this.#z($,J))return!1;if(this.signals.set($,this.#X(J)),this.#$.change?.size)this.#Q($);if(!this.#B)j(this.#$.add,[$]);return!0}remove($){if(!this.signals.delete($))return!1;let z=this.#J.get($);if(z)z.stop(),this.#J.delete($);if(!this.#B)j(this.#$.remove,[$]);return!0}change($,J){if(this.#B=!0,Object.keys($.add).length){for(let X in $.add)this.add(X,$.add[X]);let z=()=>j(this.#$.add,Object.keys($.add));if(J)setTimeout(z,0);else z()}if(Object.keys($.change).length)N$(()=>{for(let z in $.change){let X=$.change[z];if(!this.#z(z,X))continue;let Z=this.signals.get(z);if(A$(`list item "${z}"`,X,Z))Z.set(X)}}),j(this.#$.change,Object.keys($.change));if(Object.keys($.remove).length){for(let z in $.remove)this.remove(z);j(this.#$.remove,Object.keys($.remove))}return this.#B=!1,$.changed}clear(){let $=Array.from(this.signals.keys());return this.signals.clear(),this.#J.clear(),j(this.#$.remove,$),!0}on($,J){if(!f($,[A,N,K]))throw new M("Composite",$);if(this.#$[$]||=new Set,this.#$[$].add(J),$===N&&!this.#J.size){this.#B=!0;for(let z of this.signals.keys())this.#Q(z);this.#B=!1}return()=>{if(this.#$[$]?.delete(J),$===N&&!this.#$.change?.size){if(this.#J.size){for(let z of this.#J.values())z.stop();this.#J.clear()}}}}}var n="State";class g{#z=new Set;#X;#J;constructor($){U(n,$),this.#X=$}get[Symbol.toStringTag](){return n}get(){return I(this.#z,this.#J),this.#X}set($){if(U(n,$),V(this.#X,$))return;if(this.#X=$,this.#z.size)G(this.#z);if(x===this.#X)this.#z.clear()}update($){E(`${n} update`,$),this.set($(this.#X))}on($,J){if($===F)return this.#J||=new Set,this.#J.add(J),()=>{this.#J?.delete(J)};throw new M(this.constructor.name,$)}}var M$=($)=>L($,n);var d="List";class a{#z;#X=new Set;#J={};#$=[];#B;constructor($,J){U(d,$,Array.isArray);let z=0;this.#B=Z$(J)?()=>`${J}${z++}`:q(J)?(X)=>J(X):()=>String(z++),this.#z=new l(this.#Q($),(X,Z)=>{return U(`${d} for key "${X}"`,Z),!0},(X)=>new g(X))}#Q($){let J={};for(let z=0;z<$.length;z++){let X=$[z];if(X===void 0)continue;let Z=this.#$[z];if(!Z)Z=this.#B(X),this.#$[z]=Z;J[Z]=X}return J}get#Z(){return this.#$.map(($)=>this.#z.signals.get($)?.get()).filter(($)=>$!==void 0)}get[Symbol.toStringTag](){return d}get[Symbol.isConcatSpreadable](){return!0}*[Symbol.iterator](){for(let $ of this.#$){let J=this.#z.signals.get($);if(J)yield J}}get length(){return I(this.#X,this.#J[F]),this.#$.length}get(){return I(this.#X,this.#J[F]),this.#Z}set($){if(x===$){this.#z.clear(),G(this.#X),this.#X.clear();return}let J=this.#Z,z=t(this.#Q(J),this.#Q($)),X=Object.keys(z.remove);if(this.#z.change(z)){for(let Q of X){let B=this.#$.indexOf(Q);if(B!==-1)this.#$.splice(B,1)}this.#$=this.#$.filter(()=>!0),G(this.#X)}}update($){this.set($(this.get()))}at($){return this.#z.signals.get(this.#$[$])}keys(){return this.#$.values()}byKey($){return this.#z.signals.get($)}keyAt($){return this.#$[$]}indexOfKey($){return this.#$.indexOf($)}add($){let J=this.#B($);if(this.#z.signals.has(J))throw new h("store",J,$);if(!this.#$.includes(J))this.#$.push(J);if(this.#z.add(J,$))G(this.#X);return J}remove($){let J=Q$($)?this.#$[$]:$;if(this.#z.remove(J)){let X=Q$($)?$:this.#$.indexOf(J);if(X>=0)this.#$.splice(X,1);this.#$=this.#$.filter(()=>!0),G(this.#X)}}sort($){let z=this.#$.map((X)=>[X,this.#z.signals.get(X)?.get()]).sort(q($)?(X,Z)=>$(X[1],Z[1]):(X,Z)=>String(X[1]).localeCompare(String(Z[1]))).map(([X])=>X);if(!V(this.#$,z))this.#$=z,G(this.#X),j(this.#J.sort,this.#$)}splice($,J,...z){let X=this.#$.length,Z=$<0?Math.max(0,X+$):Math.min($,X),Q=Math.max(0,Math.min(J??Math.max(0,X-Math.max(0,Z)),X-Z)),B={},Y={};for(let T=0;T<Q;T++){let D=Z+T,w=this.#$[D];if(w){let i=this.#z.signals.get(w);if(i)Y[w]=i.get()}}let k=this.#$.slice(0,Z);for(let T of z){let D=this.#B(T);k.push(D),B[D]=T}k.push(...this.#$.slice(Z+Q));let X$=!!(Object.keys(B).length||Object.keys(Y).length);if(X$)this.#z.change({add:B,change:{},remove:Y,changed:X$}),this.#$=k.filter(()=>!0),G(this.#X);return Object.values(Y)}on($,J){if(f($,[v,F]))return this.#J[$]||=new Set,this.#J[$].add(J),()=>{this.#J[$]?.delete(J)};else if(f($,[A,N,K]))return this.#z.on($,J);throw new M(d,$)}deriveCollection($){return new $$(this,$)}}var e=($)=>L($,d);var O="Store";class K${#z;#X=new Set;#J;constructor($){U(O,$,P),this.#z=new l($,(J,z)=>{return U(`${O} for key "${J}"`,z),!0},(J)=>T$(J))}get#$(){let $={};for(let[J,z]of this.#z.signals.entries())$[J]=z.get();return $}get[Symbol.toStringTag](){return O}get[Symbol.isConcatSpreadable](){return!1}*[Symbol.iterator](){for(let[$,J]of this.#z.signals.entries())yield[$,J]}keys(){return this.#z.signals.keys()}byKey($){return this.#z.signals.get($)}get(){return I(this.#X,this.#J),this.#$}set($){if(x===$){this.#z.clear(),G(this.#X),this.#X.clear();return}let J=this.#$;if(this.#z.change(t(J,$)))G(this.#X)}update($){this.set($(this.get()))}add($,J){if(this.#z.signals.has($))throw new h(O,$,J);if(this.#z.add($,J))G(this.#X);return $}remove($){if(this.#z.remove($))G(this.#X)}on($,J){if($===F)return this.#J||=new Set,this.#J.add(J),()=>{this.#J?.delete(J)};else if(f($,[A,N,K]))return this.#z.on($,J);throw new M(O,$)}}var D$=($)=>{let J=new K$($);return new Proxy(J,{get(z,X){if(X in z){let Z=Reflect.get(z,X);return q(Z)?Z.bind(z):Z}if(!p(X))return z.byKey(X)},has(z,X){if(X in z)return!0;return z.byKey(String(X))!==void 0},ownKeys(z){return Array.from(z.keys())},getOwnPropertyDescriptor(z,X){if(X in z)return Reflect.getOwnPropertyDescriptor(z,X);if(p(X))return;let Z=z.byKey(String(X));return Z?{enumerable:!0,configurable:!0,writable:!0,value:Z}:void 0}})},I$=($)=>L($,O);var O$=($)=>M$($)||P$($)||I$($),S$=($)=>M$($)||I$($)||e($);function w$($){if(q$($))return new c($);if(F$($))return new r($);if(Y$($))return new a($);if(P($))return D$($);return new g($)}function T$($){if(Y$($))return new a($);if(P($))return D$($);return new g($)}class _ extends Error{constructor($){super(`Circular dependency detected in ${$}`);this.name="CircularDependencyError"}}class h extends Error{constructor($,J,z){super(`Could not add ${$} key "${J}"${z?` with value ${C(z)}`:""} because it already exists`);this.name="DuplicateKeyError"}}class J$ extends TypeError{constructor($,J){super(`Invalid ${$} callback ${C(J)}`);this.name="InvalidCallbackError"}}class j$ extends TypeError{constructor($,J){super(`Invalid ${$} source ${C(J)}`);this.name="InvalidCollectionSourceError"}}class M extends TypeError{constructor($,J){super(`Invalid hook "${J}" in ${$}`);this.name="InvalidHookError"}}class W$ extends TypeError{constructor($,J){super(`Invalid signal value ${C(J)} in ${$}`);this.name="InvalidSignalValueError"}}class f$ extends TypeError{constructor($){super(`Nullish signal values are not allowed in ${$}`);this.name="NullishSignalValueError"}}class V$ extends Error{constructor($,J){super(`Could not set ${$} to ${C(J)} because signal is read-only`);this.name="ReadonlySignalError"}}var R=($)=>$ instanceof Error?$:Error(String($)),E=($,J,z=q)=>{if(!z(J))throw new J$($,J)},U=($,J,z=()=>!(p(J)&&J!==x)||q(J))=>{if(J==null)throw new f$($);if(!z(J))throw new W$($,J)},A$=($,J,z)=>{if(!S$(z))throw new V$($,J);return!0};var u="Collection";class $${#z=new Set;#X;#J;#$=new Map;#B=new Map;#Q={};#Z=[];constructor($,J){if(E(u,J),q($))$=$();if(!p$($))throw new j$(u,$);this.#X=$,this.#J=J;for(let z=0;z<this.#X.length;z++){let X=this.#X.keyAt(z);if(!X)continue;this.#x(X)}this.#X.on(A,(z)=>{if(!z)return;for(let X of z)if(!this.#$.has(X)){this.#x(X);let Z=this.#$.get(X);if(Z&&H$(this.#J))Z.get()}G(this.#z),j(this.#Q.add,z)}),this.#X.on(K,(z)=>{if(!z)return;for(let X of z){if(!this.#$.has(X))continue;this.#$.delete(X);let Z=this.#Z.indexOf(X);if(Z>=0)this.#Z.splice(Z,1);let Q=this.#B.get(X);if(Q)Q.stop(),this.#B.delete(X)}this.#Z=this.#Z.filter(()=>!0),G(this.#z),j(this.#Q.remove,z)}),this.#X.on(v,(z)=>{if(z)this.#Z=[...z];G(this.#z),j(this.#Q.sort,z)})}#x($){let J=H$(this.#J)?async(X,Z)=>{let Q=this.#X.byKey($)?.get();if(Q===x)return x;return this.#J(Q,Z)}:()=>{let X=this.#X.byKey($)?.get();if(X===x)return x;return this.#J(X)},z=R$(J);if(this.#$.set($,z),!this.#Z.includes($))this.#Z.push($);if(this.#Q.change?.size)this.#G($);return!0}#G($){let J=S(()=>{W(J,()=>{this.#$.get($)?.get()})});this.#B.set($,J),J()}get[Symbol.toStringTag](){return u}get[Symbol.isConcatSpreadable](){return!0}*[Symbol.iterator](){for(let $ of this.#Z){let J=this.#$.get($);if(J)yield J}}keys(){return this.#Z.values()}get(){return I(this.#z,this.#Q[F]),this.#Z.map(($)=>this.#$.get($)?.get()).filter(($)=>$!=null&&$!==x)}at($){return this.#$.get(this.#Z[$])}byKey($){return this.#$.get($)}keyAt($){return this.#Z[$]}indexOfKey($){return this.#Z.indexOf($)}on($,J){if(f($,[A,N,K,v,F])){if(this.#Q[$]||=new Set,this.#Q[$].add(J),$===N&&!this.#B.size)for(let z of this.#$.keys())this.#G(z);return()=>{if(this.#Q[$]?.delete(J),$===N&&!this.#Q.change?.size){if(this.#B.size){for(let z of this.#B.values())z.stop();this.#B.clear()}}}}throw new M(u,$)}deriveCollection($){return new $$(this,$)}get length(){return I(this.#z,this.#Q[F]),this.#Z.length}}var E$=($)=>L($,u),p$=($)=>e($)||E$($),H$=($)=>H($);var z$="Ref";class C${#z=new Set;#X;#J;constructor($,J){U(z$,$,J),this.#X=$}get[Symbol.toStringTag](){return z$}get(){return I(this.#z,this.#J),this.#X}notify(){G(this.#z)}on($,J){if($===F)return this.#J||=new Set,this.#J.add(J),()=>{this.#J?.delete(J)};throw new M(z$,$)}}var y$=($)=>L($,z$);var b$=($)=>{if(!q($)||$.length>1)throw new J$("effect",$);let J=H($),z=!1,X,Z=S(()=>W(Z,()=>{if(z)throw new _("effect");z=!0,X?.abort(),X=void 0;let Q;try{if(J){X=new AbortController;let B=X;$(X.signal).then((Y)=>{if(q(Y)&&X===B)Z.on(m,Y)}).catch((Y)=>{if(!y(Y))console.error("Error in async effect callback:",Y)})}else if(Q=$(),q(Q))Z.on(m,Q)}catch(B){if(!y(B))console.error("Error in effect callback:",B)}z=!1}));return Z(),()=>{X?.abort();try{Z.stop()}catch(Q){console.error("Error in effect cleanup:",Q)}}};function v$($,J){try{if($.pending)J.nil?.();else if($.errors)J.err?.($.errors);else if($.ok)J.ok($.values)}catch(z){let X=R(z);if(J.err&&(!$.errors||!$.errors.includes(X)))J.err($.errors?[...$.errors,X]:[X]);else throw X}}function n$($){let J=[],z=!1,X={};for(let[Z,Q]of Object.entries($))try{let B=Q.get();if(B===x)z=!0;else X[Z]=B}catch(B){J.push(R(B))}if(z)return{ok:!1,pending:!0};if(J.length>0)return{ok:!1,errors:J};return{ok:!0,values:X}}export{C as valueString,U as validateSignalValue,E as validateCallback,j as triggerHook,W as trackSignalReads,I as subscribeActiveWatcher,n$ as resolve,G as notifyWatchers,v$ as match,F$ as isTaskCallback,p as isSymbol,Z$ as isString,I$ as isStore,M$ as isState,O$ as isSignal,y$ as isRef,B$ as isRecordOrArray,P as isRecord,L as isObjectOfType,Q$ as isNumber,S$ as isMutableSignal,q$ as isMemoCallback,e as isList,f as isHandledHook,q as isFunction,V as isEqual,P$ as isComputed,E$ as isCollection,H as isAsyncFunction,y as isAbortError,A$ as guardMutableSignal,s as flushPendingReactions,t as diff,S as createWatcher,D$ as createStore,w$ as createSignal,R as createError,b$ as createEffect,R$ as createComputed,N$ as batchSignalWrites,x as UNSET,r as Task,O as TYPE_STORE,n as TYPE_STATE,z$ as TYPE_REF,d as TYPE_LIST,G$ as TYPE_COMPUTED,u as TYPE_COLLECTION,g as State,C$ as Ref,V$ as ReadonlySignalError,f$ as NullishSignalValueError,c as Memo,a as List,W$ as InvalidSignalValueError,j$ as InvalidCollectionSourceError,J$ as InvalidCallbackError,F as HOOK_WATCH,v as HOOK_SORT,K as HOOK_REMOVE,m as HOOK_CLEANUP,N as HOOK_CHANGE,A as HOOK_ADD,h as DuplicateKeyError,$$ as DerivedCollection,_ as CircularDependencyError,K$ as BaseStore};
package/index.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @name Cause & Effect
3
- * @version 0.17.1
3
+ * @version 0.17.2
4
4
  * @author Esther Brunner
5
5
  */
6
6
 
@@ -80,14 +80,24 @@ export {
80
80
  batchSignalWrites,
81
81
  type Cleanup,
82
82
  createWatcher,
83
- emitNotification,
84
83
  flushPendingReactions,
85
- type Listener,
86
- type Listeners,
87
- type Notifications,
84
+ HOOK_ADD,
85
+ HOOK_CHANGE,
86
+ HOOK_CLEANUP,
87
+ HOOK_REMOVE,
88
+ HOOK_SORT,
89
+ HOOK_WATCH,
90
+ type Hook,
91
+ type CleanupHook,
92
+ type WatchHook,
93
+ type HookCallback,
94
+ type HookCallbacks,
95
+ isHandledHook,
88
96
  notifyWatchers,
89
97
  subscribeActiveWatcher,
90
98
  trackSignalReads,
99
+ triggerHook,
100
+ UNSET,
91
101
  type Watcher,
92
102
  } from './src/system'
93
103
  export {
@@ -100,6 +110,5 @@ export {
100
110
  isRecordOrArray,
101
111
  isString,
102
112
  isSymbol,
103
- UNSET,
104
113
  valueString,
105
114
  } from './src/util'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeix/cause-effect",
3
- "version": "0.17.1",
3
+ "version": "0.17.2",
4
4
  "author": "Esther Brunner",
5
5
  "main": "index.js",
6
6
  "module": "index.ts",
@@ -1,17 +1,29 @@
1
- import { InvalidCollectionSourceError, validateCallback } from '../errors'
1
+ import {
2
+ InvalidCollectionSourceError,
3
+ InvalidHookError,
4
+ validateCallback,
5
+ } from '../errors'
2
6
  import type { Signal } from '../signal'
3
7
  import {
4
8
  type Cleanup,
5
9
  createWatcher,
6
- emitNotification,
7
- type Listener,
8
- type Listeners,
10
+ HOOK_ADD,
11
+ HOOK_CHANGE,
12
+ HOOK_REMOVE,
13
+ HOOK_SORT,
14
+ HOOK_WATCH,
15
+ type Hook,
16
+ type HookCallback,
17
+ type HookCallbacks,
18
+ isHandledHook,
9
19
  notifyWatchers,
10
20
  subscribeActiveWatcher,
11
21
  trackSignalReads,
22
+ triggerHook,
23
+ UNSET,
12
24
  type Watcher,
13
25
  } from '../system'
14
- import { isAsyncFunction, isFunction, isObjectOfType, UNSET } from '../util'
26
+ import { isAsyncFunction, isFunction, isObjectOfType } from '../util'
15
27
  import { type Computed, createComputed } from './computed'
16
28
  import { isList, type List } from './list'
17
29
 
@@ -27,12 +39,13 @@ type Collection<T extends {}> = {
27
39
  readonly [Symbol.toStringTag]: 'Collection'
28
40
  readonly [Symbol.isConcatSpreadable]: true
29
41
  [Symbol.iterator](): IterableIterator<Signal<T>>
42
+ keys(): IterableIterator<string>
30
43
  get: () => T[]
31
44
  at: (index: number) => Signal<T> | undefined
32
45
  byKey: (key: string) => Signal<T> | undefined
33
46
  keyAt: (index: number) => string | undefined
34
47
  indexOfKey: (key: string) => number | undefined
35
- on: <K extends keyof Listeners>(type: K, listener: Listener<K>) => Cleanup
48
+ on: <K extends Hook>(type: K, callback: HookCallback) => Cleanup
36
49
  deriveCollection: <R extends {}>(
37
50
  callback: CollectionCallback<R, T>,
38
51
  ) => DerivedCollection<R, T>
@@ -51,23 +64,18 @@ class DerivedCollection<T extends {}, U extends {}> implements Collection<T> {
51
64
  #callback: CollectionCallback<T, U>
52
65
  #signals = new Map<string, Computed<T>>()
53
66
  #ownWatchers = new Map<string, Watcher>()
54
- #listeners: Listeners = {
55
- add: new Set<Listener<'add'>>(),
56
- change: new Set<Listener<'change'>>(),
57
- remove: new Set<Listener<'remove'>>(),
58
- sort: new Set<Listener<'sort'>>(),
59
- }
67
+ #hookCallbacks: HookCallbacks = {}
60
68
  #order: string[] = []
61
69
 
62
70
  constructor(
63
71
  source: CollectionSource<U> | (() => CollectionSource<U>),
64
72
  callback: CollectionCallback<T, U>,
65
73
  ) {
66
- validateCallback('collection', callback)
74
+ validateCallback(TYPE_COLLECTION, callback)
67
75
 
68
76
  if (isFunction(source)) source = source()
69
77
  if (!isCollectionSource(source))
70
- throw new InvalidCollectionSourceError('derived collection', source)
78
+ throw new InvalidCollectionSourceError(TYPE_COLLECTION, source)
71
79
  this.#source = source
72
80
 
73
81
  this.#callback = callback
@@ -79,7 +87,8 @@ class DerivedCollection<T extends {}, U extends {}> implements Collection<T> {
79
87
  this.#add(key)
80
88
  }
81
89
 
82
- this.#source.on('add', additions => {
90
+ this.#source.on(HOOK_ADD, additions => {
91
+ if (!additions) return
83
92
  for (const key of additions) {
84
93
  if (!this.#signals.has(key)) {
85
94
  this.#add(key)
@@ -90,10 +99,11 @@ class DerivedCollection<T extends {}, U extends {}> implements Collection<T> {
90
99
  }
91
100
  }
92
101
  notifyWatchers(this.#watchers)
93
- emitNotification(this.#listeners.add, additions)
102
+ triggerHook(this.#hookCallbacks.add, additions)
94
103
  })
95
104
 
96
- this.#source.on('remove', removals => {
105
+ this.#source.on(HOOK_REMOVE, removals => {
106
+ if (!removals) return
97
107
  for (const key of removals) {
98
108
  if (!this.#signals.has(key)) continue
99
109
 
@@ -109,31 +119,25 @@ class DerivedCollection<T extends {}, U extends {}> implements Collection<T> {
109
119
  }
110
120
  this.#order = this.#order.filter(() => true) // Compact array
111
121
  notifyWatchers(this.#watchers)
112
- emitNotification(this.#listeners.remove, removals)
122
+ triggerHook(this.#hookCallbacks.remove, removals)
113
123
  })
114
124
 
115
- this.#source.on('sort', newOrder => {
116
- this.#order = [...newOrder]
125
+ this.#source.on(HOOK_SORT, newOrder => {
126
+ if (newOrder) this.#order = [...newOrder]
117
127
  notifyWatchers(this.#watchers)
118
- emitNotification(this.#listeners.sort, newOrder)
128
+ triggerHook(this.#hookCallbacks.sort, newOrder)
119
129
  })
120
130
  }
121
131
 
122
132
  #add(key: string): boolean {
123
133
  const computedCallback = isAsyncCollectionCallback<T>(this.#callback)
124
134
  ? async (_: T, abort: AbortSignal) => {
125
- const sourceSignal = this.#source.byKey(key)
126
- if (!sourceSignal) return UNSET
127
-
128
- const sourceValue = sourceSignal.get() as U
135
+ const sourceValue = this.#source.byKey(key)?.get() as U
129
136
  if (sourceValue === UNSET) return UNSET
130
137
  return this.#callback(sourceValue, abort)
131
138
  }
132
139
  : () => {
133
- const sourceSignal = this.#source.byKey(key)
134
- if (!sourceSignal) return UNSET
135
-
136
- const sourceValue = sourceSignal.get() as U
140
+ const sourceValue = this.#source.byKey(key)?.get() as U
137
141
  if (sourceValue === UNSET) return UNSET
138
142
  return (this.#callback as (sourceValue: U) => T)(
139
143
  sourceValue,
@@ -144,7 +148,7 @@ class DerivedCollection<T extends {}, U extends {}> implements Collection<T> {
144
148
 
145
149
  this.#signals.set(key, signal)
146
150
  if (!this.#order.includes(key)) this.#order.push(key)
147
- if (this.#listeners.change.size) this.#addWatcher(key)
151
+ if (this.#hookCallbacks.change?.size) this.#addWatcher(key)
148
152
  return true
149
153
  }
150
154
 
@@ -173,13 +177,12 @@ class DerivedCollection<T extends {}, U extends {}> implements Collection<T> {
173
177
  }
174
178
  }
175
179
 
176
- get length(): number {
177
- subscribeActiveWatcher(this.#watchers)
178
- return this.#order.length
180
+ keys(): IterableIterator<string> {
181
+ return this.#order.values()
179
182
  }
180
183
 
181
184
  get(): T[] {
182
- subscribeActiveWatcher(this.#watchers)
185
+ subscribeActiveWatcher(this.#watchers, this.#hookCallbacks[HOOK_WATCH])
183
186
  return this.#order
184
187
  .map(key => this.#signals.get(key)?.get())
185
188
  .filter(v => v != null && v !== UNSET) as T[]
@@ -189,10 +192,6 @@ class DerivedCollection<T extends {}, U extends {}> implements Collection<T> {
189
192
  return this.#signals.get(this.#order[index])
190
193
  }
191
194
 
192
- keys(): IterableIterator<string> {
193
- return this.#order.values()
194
- }
195
-
196
195
  byKey(key: string): Computed<T> | undefined {
197
196
  return this.#signals.get(key)
198
197
  }
@@ -205,22 +204,34 @@ class DerivedCollection<T extends {}, U extends {}> implements Collection<T> {
205
204
  return this.#order.indexOf(key)
206
205
  }
207
206
 
208
- on<K extends keyof Listeners>(type: K, listener: Listener<K>): Cleanup {
209
- this.#listeners[type].add(listener)
210
- if (type === 'change' && !this.#ownWatchers.size) {
211
- for (const key of this.#signals.keys()) this.#addWatcher(key)
212
- }
207
+ on(type: Hook, callback: HookCallback): Cleanup {
208
+ if (
209
+ isHandledHook(type, [
210
+ HOOK_ADD,
211
+ HOOK_CHANGE,
212
+ HOOK_REMOVE,
213
+ HOOK_SORT,
214
+ HOOK_WATCH,
215
+ ])
216
+ ) {
217
+ this.#hookCallbacks[type] ||= new Set()
218
+ this.#hookCallbacks[type].add(callback)
219
+ if (type === HOOK_CHANGE && !this.#ownWatchers.size) {
220
+ for (const key of this.#signals.keys()) this.#addWatcher(key)
221
+ }
213
222
 
214
- return () => {
215
- this.#listeners[type].delete(listener)
216
- if (type === 'change' && !this.#listeners.change.size) {
217
- if (this.#ownWatchers.size) {
218
- for (const watcher of this.#ownWatchers.values())
219
- watcher.stop()
220
- this.#ownWatchers.clear()
223
+ return () => {
224
+ this.#hookCallbacks[type]?.delete(callback)
225
+ if (type === HOOK_CHANGE && !this.#hookCallbacks.change?.size) {
226
+ if (this.#ownWatchers.size) {
227
+ for (const watcher of this.#ownWatchers.values())
228
+ watcher.stop()
229
+ this.#ownWatchers.clear()
230
+ }
221
231
  }
222
232
  }
223
233
  }
234
+ throw new InvalidHookError(TYPE_COLLECTION, type)
224
235
  }
225
236
 
226
237
  deriveCollection<R extends {}>(
@@ -234,6 +245,11 @@ class DerivedCollection<T extends {}, U extends {}> implements Collection<T> {
234
245
  ): DerivedCollection<R, T> {
235
246
  return new DerivedCollection(this, callback)
236
247
  }
248
+
249
+ get length(): number {
250
+ subscribeActiveWatcher(this.#watchers, this.#hookCallbacks[HOOK_WATCH])
251
+ return this.#order.length
252
+ }
237
253
  }
238
254
 
239
255
  /* === Functions === */
@@ -241,13 +257,13 @@ class DerivedCollection<T extends {}, U extends {}> implements Collection<T> {
241
257
  /**
242
258
  * Check if a value is a collection signal
243
259
  *
244
- * @since 0.17.0
260
+ * @since 0.17.2
245
261
  * @param {unknown} value - Value to check
246
262
  * @returns {boolean} - True if value is a collection signal, false otherwise
247
263
  */
248
- const isCollection = /*#__PURE__*/ <T extends {}, U extends {}>(
264
+ const isCollection = /*#__PURE__*/ <T extends {}>(
249
265
  value: unknown,
250
- ): value is DerivedCollection<T, U> => isObjectOfType(value, TYPE_COLLECTION)
266
+ ): value is Collection<T> => isObjectOfType(value, TYPE_COLLECTION)
251
267
 
252
268
  /**
253
269
  * Check if a value is a collection source
@@ -1,20 +1,24 @@
1
1
  import type { DiffResult, UnknownRecord } from '../diff'
2
- import { guardMutableSignal } from '../errors'
2
+ import { guardMutableSignal, InvalidHookError } from '../errors'
3
3
  import type { Signal } from '../signal'
4
4
  import {
5
5
  batchSignalWrites,
6
6
  type Cleanup,
7
7
  createWatcher,
8
- emitNotification,
9
- type Listener,
10
- type Listeners,
8
+ HOOK_ADD,
9
+ HOOK_CHANGE,
10
+ HOOK_REMOVE,
11
+ type HookCallback,
12
+ type HookCallbacks,
13
+ isHandledHook,
11
14
  trackSignalReads,
15
+ triggerHook,
12
16
  type Watcher,
13
17
  } from '../system'
14
18
 
15
19
  /* === Types === */
16
20
 
17
- type CompositeListeners = Pick<Listeners, 'add' | 'change' | 'remove'>
21
+ type CompositeHook = 'add' | 'change' | 'remove'
18
22
 
19
23
  /* === Class Definitions === */
20
24
 
@@ -26,11 +30,7 @@ class Composite<T extends UnknownRecord, S extends Signal<T[keyof T] & {}>> {
26
30
  ) => value is T[K] & {}
27
31
  #create: <V extends T[keyof T] & {}>(value: V) => S
28
32
  #watchers = new Map<string, Watcher>()
29
- #listeners: CompositeListeners = {
30
- add: new Set<Listener<'add'>>(),
31
- change: new Set<Listener<'change'>>(),
32
- remove: new Set<Listener<'remove'>>(),
33
- }
33
+ #hookCallbacks: HookCallbacks = {}
34
34
  #batching = false
35
35
 
36
36
  constructor(
@@ -59,7 +59,7 @@ class Composite<T extends UnknownRecord, S extends Signal<T[keyof T] & {}>> {
59
59
  trackSignalReads(watcher, () => {
60
60
  this.signals.get(key)?.get() // Subscribe to the signal
61
61
  if (!this.#batching)
62
- emitNotification(this.#listeners.change, [key])
62
+ triggerHook(this.#hookCallbacks.change, [key])
63
63
  })
64
64
  })
65
65
  this.#watchers.set(key, watcher)
@@ -70,9 +70,9 @@ class Composite<T extends UnknownRecord, S extends Signal<T[keyof T] & {}>> {
70
70
  if (!this.#validate(key, value)) return false
71
71
 
72
72
  this.signals.set(key, this.#create(value))
73
- if (this.#listeners.change.size) this.#addWatcher(key)
73
+ if (this.#hookCallbacks.change?.size) this.#addWatcher(key)
74
74
 
75
- if (!this.#batching) emitNotification(this.#listeners.add, [key])
75
+ if (!this.#batching) triggerHook(this.#hookCallbacks.add, [key])
76
76
  return true
77
77
  }
78
78
 
@@ -86,7 +86,7 @@ class Composite<T extends UnknownRecord, S extends Signal<T[keyof T] & {}>> {
86
86
  this.#watchers.delete(key)
87
87
  }
88
88
 
89
- if (!this.#batching) emitNotification(this.#listeners.remove, [key])
89
+ if (!this.#batching) triggerHook(this.#hookCallbacks.remove, [key])
90
90
  return true
91
91
  }
92
92
 
@@ -103,7 +103,7 @@ class Composite<T extends UnknownRecord, S extends Signal<T[keyof T] & {}>> {
103
103
 
104
104
  // Queue initial additions event to allow listeners to be added first
105
105
  const notify = () =>
106
- emitNotification(this.#listeners.add, Object.keys(changes.add))
106
+ triggerHook(this.#hookCallbacks.add, Object.keys(changes.add))
107
107
  if (initialRun) setTimeout(notify, 0)
108
108
  else notify()
109
109
  }
@@ -121,20 +121,14 @@ class Composite<T extends UnknownRecord, S extends Signal<T[keyof T] & {}>> {
121
121
  signal.set(value)
122
122
  }
123
123
  })
124
- emitNotification(
125
- this.#listeners.change,
126
- Object.keys(changes.change),
127
- )
124
+ triggerHook(this.#hookCallbacks.change, Object.keys(changes.change))
128
125
  }
129
126
 
130
127
  // Removals
131
128
  if (Object.keys(changes.remove).length) {
132
129
  for (const key in changes.remove)
133
130
  this.remove(key as keyof T & string)
134
- emitNotification(
135
- this.#listeners.remove,
136
- Object.keys(changes.remove),
137
- )
131
+ triggerHook(this.#hookCallbacks.remove, Object.keys(changes.remove))
138
132
  }
139
133
 
140
134
  this.#batching = false
@@ -145,24 +139,25 @@ class Composite<T extends UnknownRecord, S extends Signal<T[keyof T] & {}>> {
145
139
  const keys = Array.from(this.signals.keys())
146
140
  this.signals.clear()
147
141
  this.#watchers.clear()
148
- emitNotification(this.#listeners.remove, keys)
142
+ triggerHook(this.#hookCallbacks.remove, keys)
149
143
  return true
150
144
  }
151
145
 
152
- on<K extends keyof CompositeListeners>(
153
- type: K,
154
- listener: Listener<K>,
155
- ): Cleanup {
156
- this.#listeners[type].add(listener)
157
- if (type === 'change' && !this.#watchers.size) {
146
+ on(type: CompositeHook, callback: HookCallback): Cleanup {
147
+ if (!isHandledHook(type, [HOOK_ADD, HOOK_CHANGE, HOOK_REMOVE]))
148
+ throw new InvalidHookError('Composite', type)
149
+
150
+ this.#hookCallbacks[type] ||= new Set()
151
+ this.#hookCallbacks[type].add(callback)
152
+ if (type === HOOK_CHANGE && !this.#watchers.size) {
158
153
  this.#batching = true
159
154
  for (const key of this.signals.keys()) this.#addWatcher(key)
160
155
  this.#batching = false
161
156
  }
162
157
 
163
158
  return () => {
164
- this.#listeners[type].delete(listener)
165
- if (type === 'change' && !this.#listeners.change.size) {
159
+ this.#hookCallbacks[type]?.delete(callback)
160
+ if (type === HOOK_CHANGE && !this.#hookCallbacks.change?.size) {
166
161
  if (this.#watchers.size) {
167
162
  for (const watcher of this.#watchers.values())
168
163
  watcher.stop()
@@ -173,4 +168,4 @@ class Composite<T extends UnknownRecord, S extends Signal<T[keyof T] & {}>> {
173
168
  }
174
169
  }
175
170
 
176
- export { Composite, type CompositeListeners }
171
+ export { Composite, type CompositeHook as CompositeListeners }