@zeix/cause-effect 0.17.1 → 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 (57) hide show
  1. package/.ai-context.md +13 -0
  2. package/.github/copilot-instructions.md +4 -0
  3. package/.zed/settings.json +3 -0
  4. package/CLAUDE.md +41 -7
  5. package/README.md +48 -25
  6. package/archive/benchmark.ts +0 -5
  7. package/archive/collection.ts +6 -65
  8. package/archive/composite.ts +85 -0
  9. package/archive/computed.ts +18 -20
  10. package/archive/list.ts +7 -75
  11. package/archive/memo.ts +15 -15
  12. package/archive/state.ts +2 -1
  13. package/archive/store.ts +8 -78
  14. package/archive/task.ts +20 -25
  15. package/index.dev.js +508 -526
  16. package/index.js +1 -1
  17. package/index.ts +9 -11
  18. package/package.json +6 -6
  19. package/src/classes/collection.ts +70 -107
  20. package/src/classes/computed.ts +165 -149
  21. package/src/classes/list.ts +145 -107
  22. package/src/classes/ref.ts +19 -17
  23. package/src/classes/state.ts +21 -17
  24. package/src/classes/store.ts +125 -73
  25. package/src/diff.ts +2 -1
  26. package/src/effect.ts +17 -10
  27. package/src/errors.ts +14 -1
  28. package/src/resolve.ts +1 -1
  29. package/src/signal.ts +3 -2
  30. package/src/system.ts +159 -61
  31. package/src/util.ts +0 -6
  32. package/test/batch.test.ts +4 -11
  33. package/test/benchmark.test.ts +4 -2
  34. package/test/collection.test.ts +106 -107
  35. package/test/computed.test.ts +351 -112
  36. package/test/effect.test.ts +2 -2
  37. package/test/list.test.ts +62 -102
  38. package/test/ref.test.ts +128 -2
  39. package/test/state.test.ts +16 -22
  40. package/test/store.test.ts +101 -108
  41. package/test/util/dependency-graph.ts +2 -2
  42. package/tsconfig.build.json +11 -0
  43. package/tsconfig.json +5 -7
  44. package/types/index.d.ts +3 -3
  45. package/types/src/classes/collection.d.ts +9 -10
  46. package/types/src/classes/computed.d.ts +17 -20
  47. package/types/src/classes/list.d.ts +8 -6
  48. package/types/src/classes/ref.d.ts +8 -12
  49. package/types/src/classes/state.d.ts +5 -8
  50. package/types/src/classes/store.d.ts +14 -13
  51. package/types/src/effect.d.ts +1 -2
  52. package/types/src/errors.d.ts +2 -1
  53. package/types/src/signal.d.ts +3 -2
  54. package/types/src/system.d.ts +47 -34
  55. package/types/src/util.d.ts +1 -2
  56. package/src/classes/composite.ts +0 -176
  57. package/types/src/classes/composite.d.ts +0 -15
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 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.1
3
+ * @version 0.17.3
4
4
  * @author Esther Brunner
5
5
  */
6
6
 
@@ -77,17 +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
- emitNotification,
84
- flushPendingReactions,
85
- type Listener,
86
- type Listeners,
87
- type Notifications,
88
- notifyWatchers,
89
- subscribeActiveWatcher,
90
- trackSignalReads,
83
+ flush,
84
+ notifyOf,
85
+ type SignalOptions,
86
+ subscribeTo,
87
+ track,
88
+ UNSET,
89
+ untrack,
91
90
  type Watcher,
92
91
  } from './src/system'
93
92
  export {
@@ -100,6 +99,5 @@ export {
100
99
  isRecordOrArray,
101
100
  isString,
102
101
  isSymbol,
103
- UNSET,
104
102
  valueString,
105
103
  } from './src/util'
package/package.json CHANGED
@@ -1,9 +1,11 @@
1
1
  {
2
2
  "name": "@zeix/cause-effect",
3
- "version": "0.17.1",
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,17 +1,15 @@
1
1
  import { InvalidCollectionSourceError, validateCallback } from '../errors'
2
2
  import type { Signal } from '../signal'
3
3
  import {
4
- type Cleanup,
5
4
  createWatcher,
6
- emitNotification,
7
- type Listener,
8
- type Listeners,
9
- notifyWatchers,
10
- subscribeActiveWatcher,
11
- trackSignalReads,
5
+ notifyOf,
6
+ registerWatchCallbacks,
7
+ type SignalOptions,
8
+ subscribeTo,
9
+ UNSET,
12
10
  type Watcher,
13
11
  } from '../system'
14
- import { isAsyncFunction, isFunction, isObjectOfType, UNSET } from '../util'
12
+ import { isAsyncFunction, isFunction, isObjectOfType } from '../util'
15
13
  import { type Computed, createComputed } from './computed'
16
14
  import { isList, type List } from './list'
17
15
 
@@ -27,12 +25,12 @@ type Collection<T extends {}> = {
27
25
  readonly [Symbol.toStringTag]: 'Collection'
28
26
  readonly [Symbol.isConcatSpreadable]: true
29
27
  [Symbol.iterator](): IterableIterator<Signal<T>>
28
+ keys(): IterableIterator<string>
30
29
  get: () => T[]
31
30
  at: (index: number) => Signal<T> | undefined
32
31
  byKey: (key: string) => Signal<T> | undefined
33
32
  keyAt: (index: number) => string | undefined
34
33
  indexOfKey: (key: string) => number | undefined
35
- on: <K extends keyof Listeners>(type: K, listener: Listener<K>) => Cleanup
36
34
  deriveCollection: <R extends {}>(
37
35
  callback: CollectionCallback<R, T>,
38
36
  ) => DerivedCollection<R, T>
@@ -46,28 +44,23 @@ const TYPE_COLLECTION = 'Collection' as const
46
44
  /* === Class === */
47
45
 
48
46
  class DerivedCollection<T extends {}, U extends {}> implements Collection<T> {
49
- #watchers = new Set<Watcher>()
50
47
  #source: CollectionSource<U>
51
48
  #callback: CollectionCallback<T, U>
52
49
  #signals = new Map<string, Computed<T>>()
53
- #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
- }
60
- #order: string[] = []
50
+ #keys: string[] = []
51
+ #dirty = true
52
+ #watcher: Watcher | undefined
61
53
 
62
54
  constructor(
63
55
  source: CollectionSource<U> | (() => CollectionSource<U>),
64
56
  callback: CollectionCallback<T, U>,
57
+ options?: SignalOptions<T[]>,
65
58
  ) {
66
- validateCallback('collection', callback)
59
+ validateCallback(TYPE_COLLECTION, callback)
67
60
 
68
61
  if (isFunction(source)) source = source()
69
62
  if (!isCollectionSource(source))
70
- throw new InvalidCollectionSourceError('derived collection', source)
63
+ throw new InvalidCollectionSourceError(TYPE_COLLECTION, source)
71
64
  this.#source = source
72
65
 
73
66
  this.#callback = callback
@@ -79,61 +72,52 @@ class DerivedCollection<T extends {}, U extends {}> implements Collection<T> {
79
72
  this.#add(key)
80
73
  }
81
74
 
82
- this.#source.on('add', additions => {
83
- for (const key of additions) {
84
- if (!this.#signals.has(key)) {
85
- this.#add(key)
86
- // For async computations, trigger initial computation
87
- const signal = this.#signals.get(key)
88
- if (signal && isAsyncCollectionCallback(this.#callback))
89
- signal.get()
90
- }
91
- }
92
- notifyWatchers(this.#watchers)
93
- emitNotification(this.#listeners.add, additions)
94
- })
75
+ if (options?.watched)
76
+ registerWatchCallbacks(this, options.watched, options.unwatched)
77
+ }
95
78
 
96
- this.#source.on('remove', removals => {
97
- for (const key of removals) {
98
- if (!this.#signals.has(key)) continue
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[] = []
99
90
 
100
- this.#signals.delete(key)
101
- const index = this.#order.indexOf(key)
102
- if (index >= 0) this.#order.splice(index, 1)
91
+ for (const key of allKeys) {
92
+ const oldHas = this.#keys.includes(key)
93
+ const newHas = newKeys.includes(key)
103
94
 
104
- const watcher = this.#ownWatchers.get(key)
105
- if (watcher) {
106
- watcher.stop()
107
- this.#ownWatchers.delete(key)
95
+ if (!oldHas && newHas) addedKeys.push(key)
96
+ else if (oldHas && !newHas) removedKeys.push(key)
108
97
  }
109
- }
110
- this.#order = this.#order.filter(() => true) // Compact array
111
- notifyWatchers(this.#watchers)
112
- emitNotification(this.#listeners.remove, removals)
113
- })
114
98
 
115
- this.#source.on('sort', newOrder => {
116
- this.#order = [...newOrder]
117
- notifyWatchers(this.#watchers)
118
- emitNotification(this.#listeners.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
119
107
  })
108
+
109
+ return this.#watcher
120
110
  }
121
111
 
122
112
  #add(key: string): boolean {
123
113
  const computedCallback = isAsyncCollectionCallback<T>(this.#callback)
124
114
  ? async (_: T, abort: AbortSignal) => {
125
- const sourceSignal = this.#source.byKey(key)
126
- if (!sourceSignal) return UNSET
127
-
128
- const sourceValue = sourceSignal.get() as U
115
+ const sourceValue = this.#source.byKey(key)?.get() as U
129
116
  if (sourceValue === UNSET) return UNSET
130
117
  return this.#callback(sourceValue, abort)
131
118
  }
132
119
  : () => {
133
- const sourceSignal = this.#source.byKey(key)
134
- if (!sourceSignal) return UNSET
135
-
136
- const sourceValue = sourceSignal.get() as U
120
+ const sourceValue = this.#source.byKey(key)?.get() as U
137
121
  if (sourceValue === UNSET) return UNSET
138
122
  return (this.#callback as (sourceValue: U) => T)(
139
123
  sourceValue,
@@ -143,21 +127,10 @@ class DerivedCollection<T extends {}, U extends {}> implements Collection<T> {
143
127
  const signal = createComputed(computedCallback)
144
128
 
145
129
  this.#signals.set(key, signal)
146
- if (!this.#order.includes(key)) this.#order.push(key)
147
- if (this.#listeners.change.size) this.#addWatcher(key)
130
+ if (!this.#keys.includes(key)) this.#keys.push(key)
148
131
  return true
149
132
  }
150
133
 
151
- #addWatcher(key: string): void {
152
- const watcher = createWatcher(() => {
153
- trackSignalReads(watcher, () => {
154
- this.#signals.get(key)?.get() // Subscribe to the signal
155
- })
156
- })
157
- this.#ownWatchers.set(key, watcher)
158
- watcher()
159
- }
160
-
161
134
  get [Symbol.toStringTag](): 'Collection' {
162
135
  return TYPE_COLLECTION
163
136
  }
@@ -167,30 +140,29 @@ class DerivedCollection<T extends {}, U extends {}> implements Collection<T> {
167
140
  }
168
141
 
169
142
  *[Symbol.iterator](): IterableIterator<Computed<T>> {
170
- for (const key of this.#order) {
143
+ for (const key of this.#keys) {
171
144
  const signal = this.#signals.get(key)
172
145
  if (signal) yield signal as Computed<T>
173
146
  }
174
147
  }
175
148
 
176
- get length(): number {
177
- subscribeActiveWatcher(this.#watchers)
178
- return this.#order.length
149
+ keys(): IterableIterator<string> {
150
+ subscribeTo(this)
151
+ if (this.#dirty) this.#getWatcher().run()
152
+ return this.#keys.values()
179
153
  }
180
154
 
181
155
  get(): T[] {
182
- subscribeActiveWatcher(this.#watchers)
183
- return this.#order
156
+ subscribeTo(this)
157
+
158
+ if (this.#dirty) this.#getWatcher().run()
159
+ return this.#keys
184
160
  .map(key => this.#signals.get(key)?.get())
185
161
  .filter(v => v != null && v !== UNSET) as T[]
186
162
  }
187
163
 
188
164
  at(index: number): Computed<T> | undefined {
189
- return this.#signals.get(this.#order[index])
190
- }
191
-
192
- keys(): IterableIterator<string> {
193
- return this.#order.values()
165
+ return this.#signals.get(this.#keys[index])
194
166
  }
195
167
 
196
168
  byKey(key: string): Computed<T> | undefined {
@@ -198,41 +170,32 @@ class DerivedCollection<T extends {}, U extends {}> implements Collection<T> {
198
170
  }
199
171
 
200
172
  keyAt(index: number): string | undefined {
201
- return this.#order[index]
173
+ return this.#keys[index]
202
174
  }
203
175
 
204
176
  indexOfKey(key: string): number {
205
- return this.#order.indexOf(key)
206
- }
207
-
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
- }
213
-
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()
221
- }
222
- }
223
- }
177
+ return this.#keys.indexOf(key)
224
178
  }
225
179
 
226
180
  deriveCollection<R extends {}>(
227
181
  callback: (sourceValue: T) => R,
182
+ options?: SignalOptions<R[]>,
228
183
  ): DerivedCollection<R, T>
229
184
  deriveCollection<R extends {}>(
230
185
  callback: (sourceValue: T, abort: AbortSignal) => Promise<R>,
186
+ options?: SignalOptions<R[]>,
231
187
  ): DerivedCollection<R, T>
232
188
  deriveCollection<R extends {}>(
233
189
  callback: CollectionCallback<R, T>,
190
+ options?: SignalOptions<R[]>,
234
191
  ): DerivedCollection<R, T> {
235
- return new DerivedCollection(this, callback)
192
+ return new DerivedCollection(this, callback, options)
193
+ }
194
+
195
+ get length(): number {
196
+ subscribeTo(this)
197
+ if (this.#dirty) this.#getWatcher().run()
198
+ return this.#keys.length
236
199
  }
237
200
  }
238
201
 
@@ -241,13 +204,13 @@ class DerivedCollection<T extends {}, U extends {}> implements Collection<T> {
241
204
  /**
242
205
  * Check if a value is a collection signal
243
206
  *
244
- * @since 0.17.0
207
+ * @since 0.17.2
245
208
  * @param {unknown} value - Value to check
246
209
  * @returns {boolean} - True if value is a collection signal, false otherwise
247
210
  */
248
- const isCollection = /*#__PURE__*/ <T extends {}, U extends {}>(
211
+ const isCollection = /*#__PURE__*/ <T extends {}>(
249
212
  value: unknown,
250
- ): value is DerivedCollection<T, U> => isObjectOfType(value, TYPE_COLLECTION)
213
+ ): value is Collection<T> => isObjectOfType(value, TYPE_COLLECTION)
251
214
 
252
215
  /**
253
216
  * Check if a value is a collection source