@zeix/cause-effect 0.17.2 → 0.17.3

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 (50) hide show
  1. package/.ai-context.md +11 -5
  2. package/.github/copilot-instructions.md +1 -1
  3. package/.zed/settings.json +3 -0
  4. package/CLAUDE.md +18 -79
  5. package/README.md +23 -37
  6. package/archive/benchmark.ts +0 -5
  7. package/archive/collection.ts +5 -62
  8. package/archive/composite.ts +85 -0
  9. package/archive/computed.ts +17 -20
  10. package/archive/list.ts +6 -67
  11. package/archive/memo.ts +13 -14
  12. package/archive/store.ts +7 -66
  13. package/archive/task.ts +18 -20
  14. package/index.dev.js +438 -614
  15. package/index.js +1 -1
  16. package/index.ts +8 -19
  17. package/package.json +6 -6
  18. package/src/classes/collection.ts +59 -112
  19. package/src/classes/computed.ts +146 -189
  20. package/src/classes/list.ts +138 -105
  21. package/src/classes/ref.ts +16 -42
  22. package/src/classes/state.ts +16 -45
  23. package/src/classes/store.ts +107 -72
  24. package/src/effect.ts +9 -12
  25. package/src/errors.ts +12 -8
  26. package/src/signal.ts +3 -1
  27. package/src/system.ts +136 -154
  28. package/test/batch.test.ts +4 -11
  29. package/test/benchmark.test.ts +4 -2
  30. package/test/collection.test.ts +46 -306
  31. package/test/computed.test.ts +205 -223
  32. package/test/list.test.ts +35 -303
  33. package/test/ref.test.ts +38 -66
  34. package/test/state.test.ts +6 -12
  35. package/test/store.test.ts +37 -489
  36. package/test/util/dependency-graph.ts +2 -2
  37. package/tsconfig.build.json +11 -0
  38. package/tsconfig.json +5 -7
  39. package/types/index.d.ts +2 -2
  40. package/types/src/classes/collection.d.ts +4 -6
  41. package/types/src/classes/computed.d.ts +17 -37
  42. package/types/src/classes/list.d.ts +8 -6
  43. package/types/src/classes/ref.d.ts +7 -20
  44. package/types/src/classes/state.d.ts +5 -17
  45. package/types/src/classes/store.d.ts +12 -11
  46. package/types/src/errors.d.ts +2 -4
  47. package/types/src/signal.d.ts +3 -2
  48. package/types/src/system.d.ts +41 -44
  49. package/src/classes/composite.ts +0 -171
  50. package/types/src/classes/composite.d.ts +0 -15
package/index.js CHANGED
@@ -1 +1 @@
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};
1
+ var M,V=new WeakMap,U$=new WeakMap,Y$=new WeakMap,s=new Set,B$=0,Z=Symbol(),Y=($,G)=>{let J=new Set,z=$;return z.run=()=>{let Q=M;M=z;try{G()}finally{M=Q}},z.onCleanup=(Q)=>{J.add(Q)},z.stop=()=>{try{for(let Q of J)Q()}finally{J.clear()}},z},q$=($)=>{let G=M;M=void 0;try{$()}finally{M=G}},K=($,G,J)=>{if(U$.set($,G),J)Y$.set($,J)},q=($)=>{if(!M||V.get($)?.has(M))return!1;let G=M;if(!V.has($))V.set($,new Set);let J=V.get($);if(j$(J),!J.size){let z=U$.get($);if(z)q$(z)}return J.add(G),G.onCleanup(()=>{if(J.delete(G),!J.size){let z=Y$.get($);if(z)q$(z)}}),!0};var C=($)=>{let G=V.get($);if(!G)return;for(let J of G)J.stop();G.clear()},x=($)=>{let G=V.get($);if(!G?.size)return!1;for(let J of G)if(B$)s.add(J);else J();return!0};var y=()=>{while(s.size){let $=Array.from(s);s.clear();for(let G of $)G()}},d=($)=>{B$++;try{$()}finally{y(),B$--}},C$=($,G)=>{let J=M;M=$||void 0;try{G()}finally{M=J}};var l=($)=>typeof $==="string",a=($)=>typeof $==="number",E=($)=>typeof $==="symbol",H=($)=>typeof $==="function",j=($)=>H($)&&$.constructor.name==="AsyncFunction",P$=($)=>H($)&&$.constructor.name!=="AsyncFunction",A$=($)=>$!=null&&typeof $==="object",I=($,G)=>Object.prototype.toString.call($)===`[object ${G}]`,L=($)=>I($,"Object"),e=($)=>L($)||Array.isArray($),H$=($,G=(J)=>J!=null)=>Array.isArray($)&&$.every(G);var T=($)=>$ instanceof DOMException&&$.name==="AbortError",_=($)=>l($)?`"${$}"`:!!$&&typeof $==="object"?JSON.stringify($):String($);var N=($,G,J)=>{if(Object.is($,G))return!0;if(typeof $!==typeof G)return!1;if(!A$($)||!A$(G))return!1;if(!J)J=new WeakSet;if(J.has($)||J.has(G))throw new R("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(!N($[z],G[z],J))return!1;return!0}if(Array.isArray($)!==Array.isArray(G))return!1;if(L($)&&L(G)){let z=Object.keys($),Q=Object.keys(G);if(z.length!==Q.length)return!1;for(let X of z){if(!(X in G))return!1;if(!N($[X],G[X],J))return!1}return!0}return!1}finally{J.delete($),J.delete(G)}},k=($,G)=>{let J=e($),z=e(G);if(!J||!z){let A=!Object.is($,G);return{changed:A,add:A&&z?G:{},change:{},remove:A&&J?$:{}}}let Q=new WeakSet,X={},B={},F={},g=Object.keys($),t=Object.keys(G),U=new Set([...g,...t]);for(let A of U){let S=A in $,b=A in G;if(!S&&b){X[A]=G[A];continue}else if(S&&!b){F[A]=Z;continue}let V$=$[A],R$=G[A];if(!N(V$,R$,Q))B[A]=R$}return{add:X,change:B,remove:F,changed:!!(Object.keys(X).length||Object.keys(B).length||Object.keys(F).length)}};var $$="Computed";class v{#G;#$;#J;#z=!0;#Q=!1;#X;constructor($,G){m(this.constructor.name,$,G$);let J=G?.initialValue??Z;if(D(this.constructor.name,J,G?.guard),this.#G=$,this.#$=J,G?.watched)K(this,G.watched,G.unwatched)}#Z(){return this.#X||=Y(()=>{if(this.#z=!0,!x(this))this.#X?.stop()},()=>{if(this.#Q)throw new R("memo");let $;this.#Q=!0;try{$=this.#G(this.#$)}catch(G){this.#$=Z,this.#J=P(G),this.#Q=!1;return}if($==null||Z===$)this.#$=Z,this.#J=void 0;else this.#$=$,this.#J=void 0,this.#z=!1;this.#Q=!1}),this.#X.onCleanup(()=>{this.#X=void 0}),this.#X}get[Symbol.toStringTag](){return $$}get(){if(q(this),y(),this.#z)this.#Z().run();if(this.#J)throw this.#J;return this.#$}}class h{#G;#$;#J;#z=!0;#Q=!1;#X=!1;#Z;#x;constructor($,G){m(this.constructor.name,$,J$);let J=G?.initialValue??Z;if(D(this.constructor.name,J,G?.guard),this.#G=$,this.#$=J,G?.watched)K(this,G.watched,G.unwatched)}#B(){if(!this.#Z){let $=(Q)=>{if(!N(Q,this.#$))this.#$=Q,this.#X=!0;this.#J=void 0,this.#z=!1},G=()=>{this.#X=Z!==this.#$,this.#$=Z,this.#J=void 0},J=(Q)=>{let X=P(Q);this.#X=!this.#J||X.name!==this.#J.name||X.message!==this.#J.message,this.#$=Z,this.#J=X},z=(Q)=>(X)=>{if(this.#Q=!1,this.#x=void 0,Q(X),this.#X&&!x(this))this.#Z?.stop()};this.#Z=Y(()=>{if(this.#z=!0,this.#x?.abort(),!x(this))this.#Z?.stop()},()=>{if(this.#Q)throw new R("task");if(this.#X=!1,this.#x)return this.#$;this.#x=new AbortController,this.#x.signal.addEventListener("abort",()=>{this.#Q=!1,this.#x=void 0,this.#B().run()},{once:!0});let Q;this.#Q=!0;try{Q=this.#G(this.#$,this.#x.signal)}catch(X){if(T(X))G();else J(X);this.#Q=!1;return}if(Q instanceof Promise)Q.then(z($),z(J));else if(Q==null||Z===Q)G();else $(Q);this.#Q=!1}),this.#Z.onCleanup(()=>{this.#x?.abort(),this.#x=void 0,this.#Z=void 0})}return this.#Z}get[Symbol.toStringTag](){return $$}get(){if(q(this),y(),this.#z)this.#B().run();if(this.#J)throw this.#J;return this.#$}}var D$=($,G)=>j($)?new h($,G):new v($,G),F$=($)=>I($,$$),G$=($)=>P$($)&&$.length<2,J$=($)=>j($)&&$.length<3;var f="State";class W{#G;constructor($,G){if(D(f,$,G?.guard),this.#G=$,G?.watched)K(this,G.watched,G.unwatched)}get[Symbol.toStringTag](){return f}get(){return q(this),this.#G}set($){if(D(f,$),N(this.#G,$))return;if(this.#G=$,x(this),Z===this.#G)C(this)}update($){m(`${f} update`,$),this.set($(this.#G))}}var z$=($)=>I($,f);var O="List";class o{#G=new Map;#$=[];#J;#z;constructor($,G){D(O,$,Array.isArray);let J=0,z=G?.keyConfig;if(this.#J=l(z)?()=>`${z}${J++}`:H(z)?(Q)=>z(Q):()=>String(J++),this.#z=(Q,X)=>{return D(`${O} item for key "${Q}"`,X,G?.guard),!0},this.#Z({add:this.#Q($),change:{},remove:{},changed:!0}),G?.watched)K(this,G.watched,G.unwatched)}#Q($){let G={};for(let J=0;J<$.length;J++){let z=$[J];if(z===void 0)continue;let Q=this.#$[J];if(!Q)Q=this.#J(z),this.#$[J]=Q;G[Q]=z}return G}#X($,G){if(!this.#z($,G))return!1;return this.#G.set($,new W(G)),!0}#Z($){if(Object.keys($.add).length)for(let G in $.add)this.#X(G,$.add[G]);if(Object.keys($.change).length)d(()=>{for(let G in $.change){let J=$.change[G];if(!this.#z(G,J))continue;let z=this.#G.get(G);if(u(`${O} item "${G}"`,J,z))z.set(J)}});if(Object.keys($.remove).length){for(let G in $.remove){this.#G.delete(G);let J=this.#$.indexOf(G);if(J!==-1)this.#$.splice(J,1)}this.#$=this.#$.filter(()=>!0)}return $.changed}get#x(){return this.#$.map(($)=>this.#G.get($)?.get()).filter(($)=>$!==void 0)}get[Symbol.toStringTag](){return O}get[Symbol.isConcatSpreadable](){return!0}*[Symbol.iterator](){for(let $ of this.#$){let G=this.#G.get($);if(G)yield G}}get length(){return q(this),this.#$.length}get(){return q(this),this.#x}set($){if(Z===$){this.#G.clear(),x(this),C(this);return}let G=k(this.#Q(this.#x),this.#Q($));if(this.#Z(G))x(this)}update($){this.set($(this.get()))}at($){return this.#G.get(this.#$[$])}keys(){return q(this),this.#$.values()}byKey($){return this.#G.get($)}keyAt($){return this.#$[$]}indexOfKey($){return this.#$.indexOf($)}add($){let G=this.#J($);if(this.#G.has(G))throw new w("store",G,$);if(!this.#$.includes(G))this.#$.push(G);if(this.#X(G,$))x(this);return G}remove($){let G=a($)?this.#$[$]:$;if(this.#G.delete(G)){let z=a($)?$:this.#$.indexOf(G);if(z>=0)this.#$.splice(z,1);this.#$=this.#$.filter(()=>!0),x(this)}}sort($){let J=this.#$.map((z)=>[z,this.#G.get(z)?.get()]).sort(H($)?(z,Q)=>$(z[1],Q[1]):(z,Q)=>String(z[1]).localeCompare(String(Q[1]))).map(([z])=>z);if(!N(this.#$,J))this.#$=J,x(this)}splice($,G,...J){let z=this.#$.length,Q=$<0?Math.max(0,z+$):Math.min($,z),X=Math.max(0,Math.min(G??Math.max(0,z-Math.max(0,Q)),z-Q)),B={},F={};for(let U=0;U<X;U++){let A=Q+U,S=this.#$[A];if(S){let b=this.#G.get(S);if(b)F[S]=b.get()}}let g=this.#$.slice(0,Q);for(let U of J){let A=this.#J(U);g.push(A),B[A]=U}g.push(...this.#$.slice(Q+X));let t=!!(Object.keys(B).length||Object.keys(F).length);if(t)this.#Z({add:B,change:{},remove:F,changed:t}),this.#$=g.filter(()=>!0),x(this);return Object.values(F)}deriveCollection($,G){return new i(this,$,G)}}var n=($)=>I($,O);var p="Store";class I${#G=new Map;constructor($,G){if(D(p,$,G?.guard??L),this.#Q({add:$,change:{},remove:{},changed:!0}),G?.watched)K(this,G.watched,G.unwatched)}get#$(){let $={};for(let[G,J]of this.#G.entries())$[G]=J.get();return $}#J($,G){return D(`${p} for key "${$}"`,G),!0}#z($,G){if(!this.#J($,G))return!1;return this.#G.set($,m$(G)),!0}#Q($){if(Object.keys($.add).length)for(let G in $.add)this.#z(G,$.add[G]);if(Object.keys($.change).length)d(()=>{for(let G in $.change){let J=$.change[G];if(!this.#J(G,J))continue;let z=this.#G.get(G);if(u(`list item "${G}"`,J,z))z.set(J)}});if(Object.keys($.remove).length)for(let G in $.remove)this.remove(G);return $.changed}get[Symbol.toStringTag](){return p}get[Symbol.isConcatSpreadable](){return!1}*[Symbol.iterator](){for(let[$,G]of this.#G.entries())yield[$,G]}keys(){return q(this),this.#G.keys()}byKey($){return this.#G.get($)}get(){return q(this),this.#$}set($){if(Z===$){this.#G.clear(),x(this),C(this);return}if(this.#Q(k(this.#$,$)))x(this)}update($){this.set($(this.get()))}add($,G){if(this.#G.has($))throw new w(p,$,G);if(this.#z($,G))x(this);return $}remove($){if(this.#G.delete($))x(this)}}var Q$=($,G)=>{let J=new I$($,G);return new Proxy(J,{get(z,Q){if(Q in z){let X=Reflect.get(z,Q);return H(X)?X.bind(z):X}if(!E(Q))return z.byKey(Q)},has(z,Q){if(Q in z)return!0;return z.byKey(String(Q))!==void 0},ownKeys(z){return Array.from(z.keys())},getOwnPropertyDescriptor(z,Q){if(Q in z)return Reflect.getOwnPropertyDescriptor(z,Q);if(E(Q))return;let X=z.byKey(String(Q));return X?{enumerable:!0,configurable:!0,writable:!0,value:X}:void 0}})},X$=($)=>I($,p);var E$=($)=>z$($)||F$($)||X$($),K$=($)=>z$($)||X$($)||n($);function T$($){if(G$($))return new v($);if(J$($))return new h($);if(H$($))return new o($);if(L($))return Q$($);return new W($)}function m$($){if(H$($))return new o($);if(L($))return Q$($);return new W($)}class R extends Error{constructor($){super(`Circular dependency detected in ${$}`);this.name="CircularDependencyError"}}class w extends Error{constructor($,G,J){super(`Could not add ${$} key "${G}"${J?` with value ${_(J)}`:""} because it already exists`);this.name="DuplicateKeyError"}}class _$ extends Error{constructor($="unexpected condition"){super(`Assertion failed: ${$}`);this.name="FailedAssertionError"}}class c extends TypeError{constructor($,G){super(`Invalid ${$} callback ${_(G)}`);this.name="InvalidCallbackError"}}class Z$ extends TypeError{constructor($,G){super(`Invalid ${$} source ${_(G)}`);this.name="InvalidCollectionSourceError"}}class M$ extends TypeError{constructor($,G){super(`Invalid signal value ${_(G)} in ${$}`);this.name="InvalidSignalValueError"}}class L$ extends TypeError{constructor($){super(`Nullish signal values are not allowed in ${$}`);this.name="NullishSignalValueError"}}class N$ extends Error{constructor($,G){super(`Could not set ${$} to ${_(G)} because signal is read-only`);this.name="ReadonlySignalError"}}function j$($,G){if(!$)throw new _$(G)}var P=($)=>$ instanceof Error?$:Error(String($)),m=($,G,J=H)=>{if(!J(G))throw new c($,G)},D=($,G,J=()=>!(E(G)&&G!==Z)||H(G))=>{if(G==null)throw new L$($);if(!J(G))throw new M$($,G)},u=($,G,J)=>{if(!K$(J))throw new N$($,G);return!0};var r="Collection";class i{#G;#$;#J=new Map;#z=[];#Q=!0;#X;constructor($,G,J){if(m(r,G),H($))$=$();if(!f$($))throw new Z$(r,$);this.#G=$,this.#$=G;for(let z=0;z<this.#G.length;z++){let Q=this.#G.keyAt(z);if(!Q)continue;this.#x(Q)}if(J?.watched)K(this,J.watched,J.unwatched)}#Z(){return this.#X||=Y(()=>{if(this.#Q=!0,!x(this))this.#X?.stop()},()=>{let $=Array.from(this.#G.keys()),G=new Set([...this.#z,...$]),J=[],z=[];for(let Q of G){let X=this.#z.includes(Q),B=$.includes(Q);if(!X&&B)J.push(Q);else if(X&&!B)z.push(Q)}for(let Q of z)this.#J.delete(Q);for(let Q of J)this.#x(Q);this.#z=$,this.#Q=!1}),this.#X.onCleanup(()=>{this.#X=void 0}),this.#X}#x($){let G=O$(this.#$)?async(z,Q)=>{let X=this.#G.byKey($)?.get();if(X===Z)return Z;return this.#$(X,Q)}:()=>{let z=this.#G.byKey($)?.get();if(z===Z)return Z;return this.#$(z)},J=D$(G);if(this.#J.set($,J),!this.#z.includes($))this.#z.push($);return!0}get[Symbol.toStringTag](){return r}get[Symbol.isConcatSpreadable](){return!0}*[Symbol.iterator](){for(let $ of this.#z){let G=this.#J.get($);if(G)yield G}}keys(){if(q(this),this.#Q)this.#Z().run();return this.#z.values()}get(){if(q(this),this.#Q)this.#Z().run();return this.#z.map(($)=>this.#J.get($)?.get()).filter(($)=>$!=null&&$!==Z)}at($){return this.#J.get(this.#z[$])}byKey($){return this.#J.get($)}keyAt($){return this.#z[$]}indexOfKey($){return this.#z.indexOf($)}deriveCollection($,G){return new i(this,$,G)}get length(){if(q(this),this.#Q)this.#Z().run();return this.#z.length}}var W$=($)=>I($,r),f$=($)=>n($)||W$($),O$=($)=>j($);var x$="Ref";class S${#G;constructor($,G){if(D(x$,$,G?.guard),this.#G=$,G?.watched)K(this,G.watched,G.unwatched)}get[Symbol.toStringTag](){return x$}get(){return q(this),this.#G}notify(){x(this)}}var w$=($)=>I($,x$);var p$=($)=>{if(!H($)||$.length>1)throw new c("effect",$);let G=j($),J=!1,z,Q=Y(()=>{Q.run()},()=>{if(J)throw new R("effect");J=!0,z?.abort(),z=void 0;let X;try{if(G){z=new AbortController;let B=z;$(z.signal).then((F)=>{if(H(F)&&z===B)Q.onCleanup(F)}).catch((F)=>{if(!T(F))console.error("Error in async effect callback:",F)})}else if(X=$(),H(X))Q.onCleanup(X)}catch(B){if(!T(B))console.error("Error in effect callback:",B)}J=!1});return Q(),()=>{z?.abort();try{Q.stop()}catch(X){console.error("Error in effect cleanup:",X)}}};function g$($,G){try{if($.pending)G.nil?.();else if($.errors)G.err?.($.errors);else if($.ok)G.ok($.values)}catch(J){let z=P(J);if(G.err&&(!$.errors||!$.errors.includes(z)))G.err($.errors?[...$.errors,z]:[z]);else throw z}}function b$($){let G=[],J=!1,z={};for(let[Q,X]of Object.entries($))try{let B=X.get();if(B===Z)J=!0;else z[Q]=B}catch(B){G.push(P(B))}if(J)return{ok:!1,pending:!0};if(G.length>0)return{ok:!1,errors:G};return{ok:!0,values:z}}export{_ as valueString,D as validateSignalValue,m as validateCallback,q$ as untrack,C$ as track,q as subscribeTo,b$ as resolve,x as notifyOf,g$ as match,J$ as isTaskCallback,E as isSymbol,l as isString,X$ as isStore,z$ as isState,E$ as isSignal,w$ as isRef,e as isRecordOrArray,L as isRecord,I as isObjectOfType,a as isNumber,K$ as isMutableSignal,G$ as isMemoCallback,n as isList,H as isFunction,N as isEqual,F$ as isComputed,W$ as isCollection,j as isAsyncFunction,T as isAbortError,u as guardMutableSignal,y as flush,k as diff,Y as createWatcher,Q$ as createStore,T$ as createSignal,P as createError,p$ as createEffect,D$ as createComputed,d as batch,Z as UNSET,h as Task,p as TYPE_STORE,f as TYPE_STATE,x$ as TYPE_REF,O as TYPE_LIST,$$ as TYPE_COMPUTED,r as TYPE_COLLECTION,W as State,S$ as Ref,N$ as ReadonlySignalError,L$ as NullishSignalValueError,v as Memo,o as List,M$ as InvalidSignalValueError,Z$ as InvalidCollectionSourceError,c as InvalidCallbackError,w as DuplicateKeyError,i as DerivedCollection,R as CircularDependencyError,I$ as BaseStore};
package/index.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @name Cause & Effect
3
- * @version 0.17.2
3
+ * @version 0.17.3
4
4
  * @author Esther Brunner
5
5
  */
6
6
 
@@ -77,27 +77,16 @@ export {
77
77
  type UnknownSignalRecord,
78
78
  } from './src/signal'
79
79
  export {
80
- batchSignalWrites,
80
+ batch,
81
81
  type Cleanup,
82
82
  createWatcher,
83
- flushPendingReactions,
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,
96
- notifyWatchers,
97
- subscribeActiveWatcher,
98
- trackSignalReads,
99
- triggerHook,
83
+ flush,
84
+ notifyOf,
85
+ type SignalOptions,
86
+ subscribeTo,
87
+ track,
100
88
  UNSET,
89
+ untrack,
101
90
  type Watcher,
102
91
  } from './src/system'
103
92
  export {
package/package.json CHANGED
@@ -1,9 +1,11 @@
1
1
  {
2
2
  "name": "@zeix/cause-effect",
3
- "version": "0.17.2",
3
+ "version": "0.17.3",
4
4
  "author": "Esther Brunner",
5
+ "type": "module",
5
6
  "main": "index.js",
6
7
  "module": "index.ts",
8
+ "types": "types/index.d.ts",
7
9
  "devDependencies": {
8
10
  "@biomejs/biome": "2.1.4",
9
11
  "@types/bun": "latest",
@@ -12,7 +14,7 @@
12
14
  "peerDependencies": {
13
15
  "typescript": "^5.6.3"
14
16
  },
15
- "description": "Cause & Effect - reactive state management with signals.",
17
+ "description": "Cause & Effect - fine-grained reactive state management library.",
16
18
  "license": "MIT",
17
19
  "keywords": [
18
20
  "Cause & Effect",
@@ -24,10 +26,8 @@
24
26
  "access": "public"
25
27
  },
26
28
  "scripts": {
27
- "build": "bunx tsc && bun build index.ts --outdir ./ --minify && bun build index.ts --outfile index.dev.js",
29
+ "build": "bunx tsc --project tsconfig.build.json && bun build index.ts --outdir ./ --minify && bun build index.ts --outfile index.dev.js",
28
30
  "test": "bun test",
29
31
  "lint": "bunx biome lint --write"
30
- },
31
- "type": "module",
32
- "types": "types/index.d.ts"
32
+ }
33
33
  }
@@ -1,25 +1,11 @@
1
- import {
2
- InvalidCollectionSourceError,
3
- InvalidHookError,
4
- validateCallback,
5
- } from '../errors'
1
+ import { InvalidCollectionSourceError, validateCallback } from '../errors'
6
2
  import type { Signal } from '../signal'
7
3
  import {
8
- type Cleanup,
9
4
  createWatcher,
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,
19
- notifyWatchers,
20
- subscribeActiveWatcher,
21
- trackSignalReads,
22
- triggerHook,
5
+ notifyOf,
6
+ registerWatchCallbacks,
7
+ type SignalOptions,
8
+ subscribeTo,
23
9
  UNSET,
24
10
  type Watcher,
25
11
  } from '../system'
@@ -45,7 +31,6 @@ type Collection<T extends {}> = {
45
31
  byKey: (key: string) => Signal<T> | undefined
46
32
  keyAt: (index: number) => string | undefined
47
33
  indexOfKey: (key: string) => number | undefined
48
- on: <K extends Hook>(type: K, callback: HookCallback) => Cleanup
49
34
  deriveCollection: <R extends {}>(
50
35
  callback: CollectionCallback<R, T>,
51
36
  ) => DerivedCollection<R, T>
@@ -59,17 +44,17 @@ const TYPE_COLLECTION = 'Collection' as const
59
44
  /* === Class === */
60
45
 
61
46
  class DerivedCollection<T extends {}, U extends {}> implements Collection<T> {
62
- #watchers = new Set<Watcher>()
63
47
  #source: CollectionSource<U>
64
48
  #callback: CollectionCallback<T, U>
65
49
  #signals = new Map<string, Computed<T>>()
66
- #ownWatchers = new Map<string, Watcher>()
67
- #hookCallbacks: HookCallbacks = {}
68
- #order: string[] = []
50
+ #keys: string[] = []
51
+ #dirty = true
52
+ #watcher: Watcher | undefined
69
53
 
70
54
  constructor(
71
55
  source: CollectionSource<U> | (() => CollectionSource<U>),
72
56
  callback: CollectionCallback<T, U>,
57
+ options?: SignalOptions<T[]>,
73
58
  ) {
74
59
  validateCallback(TYPE_COLLECTION, callback)
75
60
 
@@ -87,46 +72,41 @@ class DerivedCollection<T extends {}, U extends {}> implements Collection<T> {
87
72
  this.#add(key)
88
73
  }
89
74
 
90
- this.#source.on(HOOK_ADD, additions => {
91
- if (!additions) return
92
- for (const key of additions) {
93
- if (!this.#signals.has(key)) {
94
- this.#add(key)
95
- // For async computations, trigger initial computation
96
- const signal = this.#signals.get(key)
97
- if (signal && isAsyncCollectionCallback(this.#callback))
98
- signal.get()
99
- }
100
- }
101
- notifyWatchers(this.#watchers)
102
- triggerHook(this.#hookCallbacks.add, additions)
103
- })
104
-
105
- this.#source.on(HOOK_REMOVE, removals => {
106
- if (!removals) return
107
- for (const key of removals) {
108
- if (!this.#signals.has(key)) continue
109
-
110
- this.#signals.delete(key)
111
- const index = this.#order.indexOf(key)
112
- if (index >= 0) this.#order.splice(index, 1)
75
+ if (options?.watched)
76
+ registerWatchCallbacks(this, options.watched, options.unwatched)
77
+ }
113
78
 
114
- const watcher = this.#ownWatchers.get(key)
115
- if (watcher) {
116
- watcher.stop()
117
- this.#ownWatchers.delete(key)
79
+ #getWatcher(): Watcher {
80
+ this.#watcher ||= createWatcher(
81
+ () => {
82
+ this.#dirty = true
83
+ if (!notifyOf(this)) this.#watcher?.stop()
84
+ },
85
+ () => {
86
+ const newKeys = Array.from(this.#source.keys())
87
+ const allKeys = new Set([...this.#keys, ...newKeys])
88
+ const addedKeys: string[] = []
89
+ const removedKeys: string[] = []
90
+
91
+ for (const key of allKeys) {
92
+ const oldHas = this.#keys.includes(key)
93
+ const newHas = newKeys.includes(key)
94
+
95
+ if (!oldHas && newHas) addedKeys.push(key)
96
+ else if (oldHas && !newHas) removedKeys.push(key)
118
97
  }
119
- }
120
- this.#order = this.#order.filter(() => true) // Compact array
121
- notifyWatchers(this.#watchers)
122
- triggerHook(this.#hookCallbacks.remove, removals)
123
- })
124
98
 
125
- this.#source.on(HOOK_SORT, newOrder => {
126
- if (newOrder) this.#order = [...newOrder]
127
- notifyWatchers(this.#watchers)
128
- triggerHook(this.#hookCallbacks.sort, newOrder)
99
+ for (const key of removedKeys) this.#signals.delete(key)
100
+ for (const key of addedKeys) this.#add(key)
101
+ this.#keys = newKeys
102
+ this.#dirty = false
103
+ },
104
+ )
105
+ this.#watcher.onCleanup(() => {
106
+ this.#watcher = undefined
129
107
  })
108
+
109
+ return this.#watcher
130
110
  }
131
111
 
132
112
  #add(key: string): boolean {
@@ -147,21 +127,10 @@ class DerivedCollection<T extends {}, U extends {}> implements Collection<T> {
147
127
  const signal = createComputed(computedCallback)
148
128
 
149
129
  this.#signals.set(key, signal)
150
- if (!this.#order.includes(key)) this.#order.push(key)
151
- if (this.#hookCallbacks.change?.size) this.#addWatcher(key)
130
+ if (!this.#keys.includes(key)) this.#keys.push(key)
152
131
  return true
153
132
  }
154
133
 
155
- #addWatcher(key: string): void {
156
- const watcher = createWatcher(() => {
157
- trackSignalReads(watcher, () => {
158
- this.#signals.get(key)?.get() // Subscribe to the signal
159
- })
160
- })
161
- this.#ownWatchers.set(key, watcher)
162
- watcher()
163
- }
164
-
165
134
  get [Symbol.toStringTag](): 'Collection' {
166
135
  return TYPE_COLLECTION
167
136
  }
@@ -171,25 +140,29 @@ class DerivedCollection<T extends {}, U extends {}> implements Collection<T> {
171
140
  }
172
141
 
173
142
  *[Symbol.iterator](): IterableIterator<Computed<T>> {
174
- for (const key of this.#order) {
143
+ for (const key of this.#keys) {
175
144
  const signal = this.#signals.get(key)
176
145
  if (signal) yield signal as Computed<T>
177
146
  }
178
147
  }
179
148
 
180
149
  keys(): IterableIterator<string> {
181
- return this.#order.values()
150
+ subscribeTo(this)
151
+ if (this.#dirty) this.#getWatcher().run()
152
+ return this.#keys.values()
182
153
  }
183
154
 
184
155
  get(): T[] {
185
- subscribeActiveWatcher(this.#watchers, this.#hookCallbacks[HOOK_WATCH])
186
- return this.#order
156
+ subscribeTo(this)
157
+
158
+ if (this.#dirty) this.#getWatcher().run()
159
+ return this.#keys
187
160
  .map(key => this.#signals.get(key)?.get())
188
161
  .filter(v => v != null && v !== UNSET) as T[]
189
162
  }
190
163
 
191
164
  at(index: number): Computed<T> | undefined {
192
- return this.#signals.get(this.#order[index])
165
+ return this.#signals.get(this.#keys[index])
193
166
  }
194
167
 
195
168
  byKey(key: string): Computed<T> | undefined {
@@ -197,58 +170,32 @@ class DerivedCollection<T extends {}, U extends {}> implements Collection<T> {
197
170
  }
198
171
 
199
172
  keyAt(index: number): string | undefined {
200
- return this.#order[index]
173
+ return this.#keys[index]
201
174
  }
202
175
 
203
176
  indexOfKey(key: string): number {
204
- return this.#order.indexOf(key)
205
- }
206
-
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
- }
222
-
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
- }
231
- }
232
- }
233
- }
234
- throw new InvalidHookError(TYPE_COLLECTION, type)
177
+ return this.#keys.indexOf(key)
235
178
  }
236
179
 
237
180
  deriveCollection<R extends {}>(
238
181
  callback: (sourceValue: T) => R,
182
+ options?: SignalOptions<R[]>,
239
183
  ): DerivedCollection<R, T>
240
184
  deriveCollection<R extends {}>(
241
185
  callback: (sourceValue: T, abort: AbortSignal) => Promise<R>,
186
+ options?: SignalOptions<R[]>,
242
187
  ): DerivedCollection<R, T>
243
188
  deriveCollection<R extends {}>(
244
189
  callback: CollectionCallback<R, T>,
190
+ options?: SignalOptions<R[]>,
245
191
  ): DerivedCollection<R, T> {
246
- return new DerivedCollection(this, callback)
192
+ return new DerivedCollection(this, callback, options)
247
193
  }
248
194
 
249
195
  get length(): number {
250
- subscribeActiveWatcher(this.#watchers, this.#hookCallbacks[HOOK_WATCH])
251
- return this.#order.length
196
+ subscribeTo(this)
197
+ if (this.#dirty) this.#getWatcher().run()
198
+ return this.#keys.length
252
199
  }
253
200
  }
254
201