@zeix/cause-effect 0.15.1 → 0.15.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.
package/index.js CHANGED
@@ -1 +1 @@
1
- var q,f=new Set,p=0,h=new Map,k,r=()=>{k=void 0;let $=Array.from(h.values());h.clear();for(let B of $)B()},F$=()=>{if(k)cancelAnimationFrame(k);k=requestAnimationFrame(r)};queueMicrotask(r);var E=($)=>{let B=new Set,W=$;return W.off=(F)=>{B.add(F)},W.cleanup=()=>{for(let F of B)F();B.clear()},W},O=($)=>{if(q&&!$.has(q)){let B=q;$.add(B),q.off(()=>{$.delete(B)})}},C=($)=>{for(let B of $)if(p)f.add(B);else B()},w=()=>{while(f.size){let $=Array.from(f);f.clear();for(let B of $)B()}},v=($)=>{p++;try{$()}finally{w(),p--}},T=($,B)=>{let W=q;q=B;try{$()}finally{q=W}},G$=($,B)=>new Promise((W,F)=>{h.set(B||Symbol(),()=>{try{W($())}catch(Q){F(Q)}}),F$()});var a=($)=>typeof $==="string",e=($)=>typeof $==="number",D=($)=>typeof $==="function",y=($)=>D($)&&$.constructor.name==="AsyncFunction",P=($,B)=>Object.prototype.toString.call($)===`[object ${B}]`,R=($)=>P($,"Object"),$$=($)=>{if(!$.length)return null;let B=$.map((W)=>a(W)?parseInt(W,10):e(W)?W:NaN);return B.every((W)=>Number.isFinite(W)&&W>=0)?B.sort((W,F)=>W-F):null},B$=($,B)=>(B in $)&&D($[B]),V=($)=>$ instanceof DOMException&&$.name==="AbortError",Y=($)=>$ instanceof Error?$:Error(String($));class I extends Error{constructor($){super(`Circular dependency in ${$} detected`);this.name="CircularDependencyError"}}var d="State",S=($)=>{let B=new Set,W=$,F={[Symbol.toStringTag]:d,get:()=>{return O(B),W},set:(Q)=>{if(K(W,Q))return;if(W=Q,C(B),z===W)B.clear()},update:(Q)=>{F.set(Q(W))}};return F},b=($)=>P($,d);var o=($)=>{let B=y($),W=!1,F,Q=E(()=>T(()=>{if(W)throw new I("effect");W=!0,F?.abort(),F=void 0;let Z;try{if(B){F=new AbortController;let A=F;$(F.signal).then((H)=>{if(D(H)&&F===A)Q.off(H)}).catch((H)=>{if(!V(H))console.error("Async effect error:",H)})}else if(Z=$(),D(Z))Q.off(Z)}catch(A){if(!V(A))console.error("Effect callback error:",A)}W=!1},Q));return Q(),()=>{F?.abort(),Q.cleanup()}};var c="Store",m=($)=>{let B=new Set,W=new EventTarget,F=new Map,Q=new Map,Z=S(0),A=()=>{let M=Array.from(F.keys()),L=$$(M);if(L)return L.map((J)=>F.get(String(J))?.get());let G={};for(let[J,X]of F)G[J]=X.get();return G},H=(M,L)=>W.dispatchEvent(new CustomEvent(M,{detail:L})),x=(M,L)=>{let G=String(M),J=n(L);F.set(G,J);let X=o(()=>{let j=J.get();if(j!=null)H("store-change",{[M]:j})});Q.set(G,X)},U=(M)=>{let L=String(M);F.delete(L);let G=Q.get(L);if(G)G();Q.delete(L)},N=(M,L)=>{let G=u(M,L);return v(()=>{if(Object.keys(G.add).length){for(let J in G.add){let X=G.add[J];if(X!=null)x(J,X)}H("store-add",G.add)}if(Object.keys(G.change).length){for(let J in G.change){let X=F.get(J),j=G.change[J];if(X&&j!=null&&B$(X,"set"))X.set(j)}H("store-change",G.change)}if(Object.keys(G.remove).length){for(let J in G.remove)U(J);H("store-remove",G.remove)}Z.set(F.size)}),G.changed};N({},$),setTimeout(()=>{let M=new CustomEvent("store-add",{detail:$});W.dispatchEvent(M)},0);let _=["add","get","remove","set","update","addEventListener","removeEventListener","dispatchEvent","size"];return new Proxy({},{get(M,L){switch(L){case"add":return(G,J)=>{if(!F.has(G))x(G,J),C(B),H("store-add",{[G]:J}),Z.set(F.size)};case"get":return()=>{return O(B),A()};case"remove":return(G)=>{if(F.has(G))U(G),C(B),H("store-remove",{[G]:z}),Z.set(F.size)};case"set":return(G)=>{if(N(A(),G)){if(C(B),z===G)B.clear()}};case"update":return(G)=>{let J=A(),X=G(J);if(N(J,X)){if(C(B),z===X)B.clear()}};case"addEventListener":return W.addEventListener.bind(W);case"removeEventListener":return W.removeEventListener.bind(W);case"dispatchEvent":return W.dispatchEvent.bind(W);case"size":return Z}if(L===Symbol.toStringTag)return c;if(L===Symbol.iterator)return function*(){for(let[G,J]of F)yield[G,J]};return F.get(String(L))},has(M,L){let G=String(L);return F.has(G)||_.includes(G)||L===Symbol.toStringTag||L===Symbol.iterator},ownKeys(){return Array.from(F.keys()).map((M)=>String(M))},getOwnPropertyDescriptor(M,L){let G=F.get(String(L));return G?{enumerable:!0,configurable:!0,writable:!0,value:G}:void 0}})},g=($)=>P($,c);var z=Symbol(),W$=($)=>b($)||t($)||g($);function J$($){if(W$($))return $;if(s($))return i($);if(Array.isArray($))return m($);if(Array.isArray($)||R($))return m($);return S($)}function n($){if(b($)||g($))return $;if(Array.isArray($))return m($);if(R($))return m($);return S($)}var K=($,B,W)=>{if(Object.is($,B))return!0;if(typeof $!==typeof B)return!1;if(typeof $!=="object"||$===null||B===null)return!1;if(!W)W=new WeakSet;if(W.has($)||W.has(B))throw new I("isEqual");W.add($),W.add(B);try{if(Array.isArray($)&&Array.isArray(B)){if($.length!==B.length)return!1;for(let F=0;F<$.length;F++)if(!K($[F],B[F],W))return!1;return!0}if(Array.isArray($)!==Array.isArray(B))return!1;if(R($)&&R(B)){let F=Object.keys($),Q=Object.keys(B);if(F.length!==Q.length)return!1;for(let Z of F){if(!(Z in B))return!1;if(!K($[Z],B[Z],W))return!1}return!0}return!1}finally{W.delete($),W.delete(B)}},u=($,B)=>{let W=new WeakSet;return((Q,Z)=>{let A={},H={},x={},U=Object.keys(Q),N=Object.keys(Z),_=new Set([...U,...N]);for(let L of _){let G=L in Q,J=L in Z;if(!G&&J){A[L]=Z[L];continue}else if(G&&!J){x[L]=z;continue}let X=Q[L],j=Z[L];if(!K(X,j,W))H[L]=j}return{changed:Object.keys(A).length>0||Object.keys(H).length>0||Object.keys(x).length>0,add:A,change:H,remove:x}})($,B)};var l="Computed",i=($)=>{let B=new Set,W=z,F,Q,Z=!0,A=!1,H=!1,x=(J)=>{if(!K(J,W))W=J,A=!0;F=void 0,Z=!1},U=()=>{A=z!==W,W=z,F=void 0},N=(J)=>{let X=Y(J);A=!F||X.name!==F.name||X.message!==F.message,W=z,F=X},_=(J)=>(X)=>{if(H=!1,Q=void 0,J(X),A)C(B)},M=E(()=>{if(Z=!0,Q?.abort(),B.size)C(B);else M.cleanup()});M.off(()=>{Q?.abort()});let L=()=>T(()=>{if(H)throw new I("computed");if(A=!1,y($)){if(Q)return W;Q=new AbortController,Q.signal.addEventListener("abort",()=>{H=!1,Q=void 0,L()},{once:!0})}let J;H=!0;try{J=Q?$(Q.signal):$()}catch(X){if(V(X))U();else N(X);H=!1;return}if(J instanceof Promise)J.then(_(x),_(N));else if(J==null||z===J)U();else x(J);H=!1},M);return{[Symbol.toStringTag]:l,get:()=>{if(O(B),w(),Z)L();if(F)throw F;return W}}},t=($)=>P($,l),s=($)=>D($)&&$.length<2;function L$($,B){try{if($.pending)B.nil?.();else if($.errors)B.err?.($.errors);else B.ok?.($.values)}catch(W){if(B.err&&(!$.errors||!$.errors.includes(Y(W))))B.err($.errors?[...$.errors,Y(W)]:[Y(W)]);else throw W}}function Q$($){let B=[],W=!1,F={};for(let[Q,Z]of Object.entries($))try{let A=Z.get();if(A===z)W=!0;else F[Q]=A}catch(A){B.push(Y(A))}if(W)return{ok:!1,pending:!0};if(B.length>0)return{ok:!1,errors:B};return{ok:!0,values:F}}export{E as watch,J$ as toSignal,n as toMutableSignal,Y as toError,O as subscribe,m as store,S as state,Q$ as resolve,T as observe,C as notify,L$ as match,a as isString,g as isStore,b as isState,W$ as isSignal,e as isNumber,D as isFunction,K as isEqual,s as isComputedCallback,t as isComputed,y as isAsyncFunction,V as isAbortError,w as flush,G$ as enqueue,o as effect,u as diff,i as computed,v as batch,z as UNSET,c as TYPE_STORE,d as TYPE_STATE,l as TYPE_COMPUTED,I as CircularDependencyError};
1
+ class A extends Error{constructor($){super(`Circular dependency detected in ${$}`);this.name="CircularDependencyError"}}class c extends TypeError{constructor($,B){super(`Invalid signal value ${B} in ${$}`);this.name="InvalidSignalValueError"}}class V extends TypeError{constructor($){super(`Nullish signal values are not allowed in ${$}`);this.name="NullishSignalValueError"}}class n extends Error{constructor($,B){super(`Could not add store key "${$}" with value ${B} because it already exists`);this.name="StoreKeyExistsError"}}class v extends RangeError{constructor($){super(`Could not remove store index ${String($)} because it is out of range`);this.name="StoreKeyRangeError"}}class t extends Error{constructor($,B){super(`Could not set store key "${$}" to ${B} because it is readonly`);this.name="StoreKeyReadonlyError"}}var z=Symbol(),$$=($)=>typeof $==="string",M$=($)=>typeof $==="number",w=($)=>typeof $==="symbol",q=($)=>typeof $==="function",g=($)=>q($)&&$.constructor.name==="AsyncFunction",U$=($)=>!!$&&typeof $==="object",f=($,B)=>Object.prototype.toString.call($)===`[object ${B}]`,L=($)=>f($,"Object"),u=($)=>L($)||Array.isArray($),q$=($)=>{if(!$.length)return null;let B=$.map((Q)=>$$(Q)?parseInt(Q,10):M$(Q)?Q:NaN);return B.every((Q)=>Number.isFinite(Q)&&Q>=0)?B.sort((Q,X)=>Q-X):null};var O=($)=>$ instanceof DOMException&&$.name==="AbortError",N=($)=>$ instanceof Error?$:Error(String($));var s=($)=>{let B=q$(Object.keys($));if(B===null)return $;let Q=[];for(let X of B)Q.push($[String(X)]);return Q},i=($)=>$$($)?`"${$}"`:U$($)?JSON.stringify($):String($);var P=($,B,Q)=>{if(Object.is($,B))return!0;if(typeof $!==typeof B)return!1;if(typeof $!=="object"||$===null||B===null)return!1;if(!Q)Q=new WeakSet;if(Q.has($)||Q.has(B))throw new A("isEqual");Q.add($),Q.add(B);try{if(Array.isArray($)&&Array.isArray(B)){if($.length!==B.length)return!1;for(let X=0;X<$.length;X++)if(!P($[X],B[X],Q))return!1;return!0}if(Array.isArray($)!==Array.isArray(B))return!1;if(L($)&&L(B)){let X=Object.keys($),Z=Object.keys(B);if(X.length!==Z.length)return!1;for(let R of X){if(!(R in B))return!1;if(!P($[R],B[R],Q))return!1}return!0}return!1}finally{Q.delete($),Q.delete(B)}},B$=($,B)=>{let Q=u($),X=u(B);if(!Q||!X){let M=!Object.is($,B);return{changed:M,add:M&&X?B:{},change:{},remove:M&&Q?$:{}}}let Z=new WeakSet,R={},K={},H={},Y=Object.keys($),m=Object.keys(B),S=new Set([...Y,...m]);for(let M of S){let D=M in $,U=M in B;if(!D&&U){R[M]=B[M];continue}else if(D&&!U){H[M]=z;continue}let W=$[M],J=B[M];if(!P(W,J,Z))K[M]=J}return{changed:Object.keys(R).length>0||Object.keys(K).length>0||Object.keys(H).length>0,add:R,change:K,remove:H}};var T,r=new Set,J$=0,W$=new Map,l,z$=()=>{l=void 0;let $=Array.from(W$.values());W$.clear();for(let B of $)B()},L$=()=>{if(l)cancelAnimationFrame(l);l=requestAnimationFrame(z$)};queueMicrotask(z$);var b=($)=>{let B=new Set,Q=$;return Q.off=(X)=>{B.add(X)},Q.cleanup=()=>{for(let X of B)X();B.clear()},Q},j=($)=>{if(T&&!$.has(T)){let B=T;$.add(B),T.off(()=>{$.delete(B)})}},I=($)=>{for(let B of $)if(J$)r.add(B);else B()},a=()=>{while(r.size){let $=Array.from(r);r.clear();for(let B of $)B()}},Q$=($)=>{J$++;try{$()}finally{a(),J$--}},k=($,B)=>{let Q=T;T=B;try{$()}finally{T=Q}},N$=($,B)=>new Promise((Q,X)=>{W$.set(B||Symbol(),()=>{try{Q($())}catch(Z){X(Z)}}),L$()});var X$="Computed",Z$=($)=>{let B=new Set,Q=z,X,Z,R=!0,K=!1,H=!1,Y=(W)=>{if(!P(W,Q))Q=W,K=!0;X=void 0,R=!1},m=()=>{K=z!==Q,Q=z,X=void 0},S=(W)=>{let J=N(W);K=!X||J.name!==X.name||J.message!==X.message,Q=z,X=J},_=(W)=>(J)=>{if(H=!1,Z=void 0,W(J),K)I(B)},M=b(()=>{if(R=!0,Z?.abort(),B.size)I(B);else M.cleanup()});M.off(()=>{Z?.abort()});let D=()=>k(()=>{if(H)throw new A("computed");if(K=!1,g($)){if(Z)return Q;Z=new AbortController,Z.signal.addEventListener("abort",()=>{H=!1,Z=void 0,D()},{once:!0})}let W;H=!0;try{W=Z?$(Z.signal):$()}catch(J){if(O(J))m();else S(J);H=!1;return}if(W instanceof Promise)W.then(_(Y),_(S));else if(W==null||z===W)m();else Y(W);H=!1},M);return{[Symbol.toStringTag]:X$,get:()=>{if(j(B),a(),R)D();if(X)throw X;return Q}}},h=($)=>f($,X$),x$=($)=>q($)&&$.length<2;var C$=($)=>{let B=g($),Q=!1,X,Z=b(()=>k(()=>{if(Q)throw new A("effect");Q=!0,X?.abort(),X=void 0;let R;try{if(B){X=new AbortController;let K=X;$(X.signal).then((H)=>{if(q(H)&&X===K)Z.off(H)}).catch((H)=>{if(!O(H))console.error("Async effect error:",H)})}else if(R=$(),q(R))Z.off(R)}catch(K){if(!O(K))console.error("Effect callback error:",K)}Q=!1},Z));return Z(),()=>{X?.abort(),Z.cleanup()}};function A$($,B){try{if($.pending)B.nil?.();else if($.errors)B.err?.($.errors);else if($.ok)B.ok($.values)}catch(Q){if(B.err&&(!$.errors||!$.errors.includes(N(Q))))B.err($.errors?[...$.errors,N(Q)]:[N(Q)]);else throw Q}}function P$($){let B=[],Q=!1,X={};for(let[Z,R]of Object.entries($))try{let K=R.get();if(K===z)Q=!0;else X[Z]=K}catch(K){B.push(N(K))}if(Q)return{ok:!1,pending:!0};if(B.length>0)return{ok:!1,errors:B};return{ok:!0,values:X}}var G$="State",y=($)=>{let B=new Set,Q=$,X={[Symbol.toStringTag]:G$,get:()=>{return j(B),Q},set:(Z)=>{if(Z==null)throw new V("state");if(P(Q,Z))return;if(Q=Z,I(B),z===Q)B.clear()},update:(Z)=>{X.set(Z(Q))}};return X},E=($)=>f($,G$);var e="Store",R$="store-add",F$="store-change",Y$="store-remove",j$="store-sort",p=($)=>{let B=new Set,Q=new EventTarget,X=new Map,Z=new Map,R=Array.isArray($),K=y(0),H=()=>{let W={};for(let[J,C]of X)W[J]=C.get();return W},Y=(W,J)=>Q.dispatchEvent(new CustomEvent(W,{detail:J})),m=()=>Array.from(X.keys()).map((W)=>Number(W)).filter((W)=>Number.isInteger(W)).sort((W,J)=>W-J),S=(W,J)=>{if(J==null)throw new V(`store for key "${W}"`);if(J===z)return!0;if(w(J)||q(J)||h(J))throw new c(`store for key "${W}"`,i(J));return!0},_=(W,J,C=!1)=>{if(!S(W,J))return!1;let x=E(J)||o(J)?J:L(J)?p(J):Array.isArray(J)?p(J):y(J);X.set(W,x);let G=C$(()=>{let F=x.get();if(F!=null)Y(F$,{[W]:F})});if(Z.set(W,G),C)K.set(X.size),I(B),Y(R$,{[W]:J});return!0},M=(W,J=!1)=>{let C=X.delete(W);if(C){let x=Z.get(W);if(x)x();Z.delete(W)}if(J)K.set(X.size),I(B),Y(Y$,{[W]:z});return C},D=(W,J,C)=>{let x=B$(W,J);return Q$(()=>{if(Object.keys(x.add).length){for(let G in x.add){let F=x.add[G]??z;_(G,F)}if(C)setTimeout(()=>{Y(R$,x.add)},0);else Y(R$,x.add)}if(Object.keys(x.change).length){for(let G in x.change){let F=x.change[G];if(!S(G,F))continue;let d=X.get(G);if(K$(d))d.set(F);else throw new t(G,i(F))}Y(F$,x.change)}if(Object.keys(x.remove).length){for(let G in x.remove)M(G);Y(Y$,x.remove)}K.set(X.size)}),x.changed};D({},$,!0);let U={add:R?(W)=>{let J=X.size,C=String(J);_(C,W,!0)}:(W,J)=>{if(!X.has(W))_(W,J,!0);else throw new n(W,i(J))},get:()=>{return j(B),s(H())},remove:R?(W)=>{let J=s(H()),C=X.size;if(!Array.isArray(J)||W<=-C||W>=C)throw new v(W);let x=[...J];if(x.splice(W,1),D(J,x))I(B)}:(W)=>{if(X.has(W))M(W,!0)},set:(W)=>{if(D(H(),W)){if(I(B),z===W)B.clear()}},update:(W)=>{let J=H(),C=W(s(J));if(D(J,C)){if(I(B),z===C)B.clear()}},sort:(W)=>{let J=Array.from(X.entries()).map(([G,F])=>[G,F.get()]).sort(W?(G,F)=>W(G[1],F[1]):(G,F)=>String(G[1]).localeCompare(String(F[1]))),C=J.map(([G])=>String(G)),x=new Map;J.forEach(([G],F)=>{let d=String(G),D$=R?String(F):String(G),H$=X.get(d);if(H$)x.set(D$,H$)}),X.clear(),x.forEach((G,F)=>X.set(F,G)),I(B),Y(j$,C)},addEventListener:Q.addEventListener.bind(Q),removeEventListener:Q.removeEventListener.bind(Q),dispatchEvent:Q.dispatchEvent.bind(Q),size:K};return new Proxy({},{get(W,J){if(J===Symbol.toStringTag)return e;if(J===Symbol.isConcatSpreadable)return R;if(J===Symbol.iterator)return R?function*(){let C=m();for(let x of C){let G=X.get(String(x));if(G)yield G}}:function*(){for(let[C,x]of X)yield[C,x]};if(w(J))return;if(J in U)return U[J];if(J==="length"&&R)return j(B),K.get();return X.get(J)},has(W,J){let C=String(J);return C&&X.has(C)||Object.keys(U).includes(C)||J===Symbol.toStringTag||J===Symbol.iterator||J===Symbol.isConcatSpreadable||J==="length"&&R},ownKeys(){return R?m().map((W)=>String(W)).concat(["length"]):Array.from(X.keys()).map((W)=>String(W))},getOwnPropertyDescriptor(W,J){let C=(G)=>({enumerable:!1,configurable:!0,writable:!1,value:G});if(J==="length"&&R)return{enumerable:!0,configurable:!0,writable:!1,value:K.get()};if(J===Symbol.isConcatSpreadable)return C(R);if(J===Symbol.toStringTag)return C(e);if(w(J))return;if(Object.keys(U).includes(J))return C(U[J]);let x=X.get(J);return x?{enumerable:!0,configurable:!0,writable:!0,value:x}:void 0}})},o=($)=>f($,e);var I$=($)=>E($)||h($)||o($),K$=($)=>E($)||o($);function m$($){if(I$($))return $;if(x$($))return Z$($);if(Array.isArray($)||L($))return p($);return y($)}export{b as watch,m$ as toSignal,N as toError,j as subscribe,p as store,y as state,P$ as resolve,k as observe,I as notify,A$ as match,w as isSymbol,$$ as isString,o as isStore,E as isState,I$ as isSignal,u as isRecordOrArray,L as isRecord,M$ as isNumber,K$ as isMutableSignal,q as isFunction,P as isEqual,x$ as isComputedCallback,h as isComputed,g as isAsyncFunction,O as isAbortError,a as flush,N$ as enqueue,C$ as effect,B$ as diff,Z$ as computed,Q$ as batch,z as UNSET,e as TYPE_STORE,G$ as TYPE_STATE,X$ as TYPE_COMPUTED,t as StoreKeyReadonlyError,v as StoreKeyRangeError,n as StoreKeyExistsError,V as NullishSignalValueError,c as InvalidSignalValueError,A as CircularDependencyError};
package/index.ts CHANGED
@@ -16,10 +16,19 @@ export {
16
16
  type DiffResult,
17
17
  diff,
18
18
  isEqual,
19
+ type UnknownArray,
19
20
  type UnknownRecord,
20
21
  type UnknownRecordOrArray,
21
22
  } from './src/diff'
22
23
  export { type EffectCallback, effect, type MaybeCleanup } from './src/effect'
24
+ export {
25
+ CircularDependencyError,
26
+ InvalidSignalValueError,
27
+ NullishSignalValueError,
28
+ StoreKeyExistsError,
29
+ StoreKeyRangeError,
30
+ StoreKeyReadonlyError,
31
+ } from './src/errors'
23
32
  export { type MatchHandlers, match } from './src/match'
24
33
  export { type ResolveResult, resolve } from './src/resolve'
25
34
  export {
@@ -35,13 +44,11 @@ export {
35
44
  watch,
36
45
  } from './src/scheduler'
37
46
  export {
47
+ isMutableSignal,
38
48
  isSignal,
39
- type MaybeSignal,
40
49
  type Signal,
41
50
  type SignalValues,
42
- toMutableSignal,
43
51
  toSignal,
44
- UNSET,
45
52
  type UnknownSignalRecord,
46
53
  } from './src/signal'
47
54
  export { isState, type State, state, TYPE_STATE } from './src/state'
@@ -52,15 +59,19 @@ export {
52
59
  type StoreChangeEvent,
53
60
  type StoreEventMap,
54
61
  type StoreRemoveEvent,
62
+ type StoreSortEvent,
55
63
  store,
56
64
  TYPE_STORE,
57
65
  } from './src/store'
58
66
  export {
59
- CircularDependencyError,
60
67
  isAbortError,
61
68
  isAsyncFunction,
62
69
  isFunction,
63
70
  isNumber,
71
+ isRecord,
72
+ isRecordOrArray,
64
73
  isString,
74
+ isSymbol,
65
75
  toError,
76
+ UNSET,
66
77
  } from './src/util'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeix/cause-effect",
3
- "version": "0.15.1",
3
+ "version": "0.15.2",
4
4
  "author": "Esther Brunner",
5
5
  "main": "index.js",
6
6
  "module": "index.ts",
package/src/computed.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { isEqual } from './diff'
2
+ import { CircularDependencyError } from './errors'
2
3
  import {
3
4
  flush,
4
5
  notify,
@@ -7,14 +8,13 @@ import {
7
8
  type Watcher,
8
9
  watch,
9
10
  } from './scheduler'
10
- import { UNSET } from './signal'
11
11
  import {
12
- CircularDependencyError,
13
12
  isAbortError,
14
13
  isAsyncFunction,
15
14
  isFunction,
16
15
  isObjectOfType,
17
16
  toError,
17
+ UNSET,
18
18
  } from './util'
19
19
 
20
20
  /* === Types === */
package/src/diff.ts CHANGED
@@ -1,12 +1,14 @@
1
- import { UNSET } from './signal'
2
- import { CircularDependencyError, isRecord } from './util'
1
+ import { CircularDependencyError } from './errors'
2
+ import { isRecord, isRecordOrArray, UNSET } from './util'
3
3
 
4
4
  /* === Types === */
5
5
 
6
6
  type UnknownRecord = Record<string, unknown & {}>
7
- type UnknownRecordOrArray = {
8
- [x: string | number]: unknown & {}
7
+ type UnknownArray = ReadonlyArray<unknown & {}>
8
+ type ArrayToRecord<T extends UnknownArray> = {
9
+ [key: string]: T extends Array<infer U extends {}> ? U : never
9
10
  }
11
+ type UnknownRecordOrArray = UnknownRecord | ArrayToRecord<UnknownArray>
10
12
 
11
13
  type DiffResult<T extends UnknownRecordOrArray = UnknownRecord> = {
12
14
  changed: boolean
@@ -69,6 +71,8 @@ const isEqual = <T>(a: T, b: T, visited?: WeakSet<object>): boolean => {
69
71
  return true
70
72
  }
71
73
 
74
+ // For non-records/non-arrays, they are only equal if they are the same reference
75
+ // (which would have been caught by Object.is at the beginning)
72
76
  return false
73
77
  } finally {
74
78
  visited.delete(a as object)
@@ -88,61 +92,70 @@ const diff = <T extends UnknownRecordOrArray>(
88
92
  oldObj: T,
89
93
  newObj: T,
90
94
  ): DiffResult<T> => {
91
- const visited = new WeakSet<object>()
92
-
93
- const diffRecords = (
94
- oldRecord: Record<string, unknown>,
95
- newRecord: Record<string, unknown>,
96
- ): DiffResult<T> => {
97
- const add: Partial<T> = {}
98
- const change: Partial<T> = {}
99
- const remove: Partial<T> = {}
100
-
101
- const oldKeys = Object.keys(oldRecord)
102
- const newKeys = Object.keys(newRecord)
103
- const allKeys = new Set([...oldKeys, ...newKeys])
104
-
105
- for (const key of allKeys) {
106
- const oldHas = key in oldRecord
107
- const newHas = key in newRecord
108
-
109
- if (!oldHas && newHas) {
110
- add[key as keyof T] = newRecord[key] as T[keyof T]
111
- continue
112
- } else if (oldHas && !newHas) {
113
- remove[key as keyof T] = UNSET
114
- continue
115
- }
95
+ // Guard against non-objects that can't be diffed properly with Object.keys and 'in' operator
96
+ const oldValid = isRecordOrArray(oldObj)
97
+ const newValid = isRecordOrArray(newObj)
98
+ if (!oldValid || !newValid) {
99
+ // For non-objects or non-plain objects, treat as complete change if different
100
+ const changed = !Object.is(oldObj, newObj)
101
+ return {
102
+ changed,
103
+ add: changed && newValid ? newObj : {},
104
+ change: {},
105
+ remove: changed && oldValid ? oldObj : {},
106
+ }
107
+ }
116
108
 
117
- const oldValue = oldRecord[key] as T[keyof T]
118
- const newValue = newRecord[key] as T[keyof T]
109
+ const visited = new WeakSet()
119
110
 
120
- if (!isEqual(oldValue, newValue, visited))
121
- change[key as keyof T] = newValue
122
- }
111
+ const add: Partial<T> = {}
112
+ const change: Partial<T> = {}
113
+ const remove: Partial<T> = {}
123
114
 
124
- const changed =
125
- Object.keys(add).length > 0 ||
126
- Object.keys(change).length > 0 ||
127
- Object.keys(remove).length > 0
115
+ const oldKeys = Object.keys(oldObj)
116
+ const newKeys = Object.keys(newObj)
117
+ const allKeys = new Set([...oldKeys, ...newKeys])
128
118
 
129
- return {
130
- changed,
131
- add,
132
- change,
133
- remove,
119
+ for (const key of allKeys) {
120
+ const oldHas = key in oldObj
121
+ const newHas = key in newObj
122
+
123
+ if (!oldHas && newHas) {
124
+ add[key as keyof T] = newObj[key] as T[keyof T]
125
+ continue
126
+ } else if (oldHas && !newHas) {
127
+ remove[key as keyof T] = UNSET
128
+ continue
134
129
  }
130
+
131
+ const oldValue = oldObj[key] as T[keyof T]
132
+ const newValue = newObj[key] as T[keyof T]
133
+
134
+ if (!isEqual(oldValue, newValue, visited))
135
+ change[key as keyof T] = newValue
135
136
  }
136
137
 
137
- return diffRecords(oldObj, newObj)
138
+ const changed =
139
+ Object.keys(add).length > 0 ||
140
+ Object.keys(change).length > 0 ||
141
+ Object.keys(remove).length > 0
142
+
143
+ return {
144
+ changed,
145
+ add,
146
+ change,
147
+ remove,
148
+ }
138
149
  }
139
150
 
140
151
  /* === Exports === */
141
152
 
142
153
  export {
154
+ type ArrayToRecord,
143
155
  type DiffResult,
144
156
  diff,
145
157
  isEqual,
146
158
  type UnknownRecord,
159
+ type UnknownArray,
147
160
  type UnknownRecordOrArray,
148
161
  }
package/src/effect.ts CHANGED
@@ -1,10 +1,6 @@
1
+ import { CircularDependencyError } from './errors'
1
2
  import { type Cleanup, observe, watch } from './scheduler'
2
- import {
3
- CircularDependencyError,
4
- isAbortError,
5
- isAsyncFunction,
6
- isFunction,
7
- } from './util'
3
+ import { isAbortError, isAsyncFunction, isFunction } from './util'
8
4
 
9
5
  /* === Types === */
10
6
 
package/src/errors.ts ADDED
@@ -0,0 +1,56 @@
1
+ class CircularDependencyError extends Error {
2
+ constructor(where: string) {
3
+ super(`Circular dependency detected in ${where}`)
4
+ this.name = 'CircularDependencyError'
5
+ }
6
+ }
7
+
8
+ class InvalidSignalValueError extends TypeError {
9
+ constructor(where: string, value: string) {
10
+ super(`Invalid signal value ${value} in ${where}`)
11
+ this.name = 'InvalidSignalValueError'
12
+ }
13
+ }
14
+
15
+ class NullishSignalValueError extends TypeError {
16
+ constructor(where: string) {
17
+ super(`Nullish signal values are not allowed in ${where}`)
18
+ this.name = 'NullishSignalValueError'
19
+ }
20
+ }
21
+
22
+ class StoreKeyExistsError extends Error {
23
+ constructor(key: string, value: string) {
24
+ super(
25
+ `Could not add store key "${key}" with value ${value} because it already exists`,
26
+ )
27
+ this.name = 'StoreKeyExistsError'
28
+ }
29
+ }
30
+
31
+ class StoreKeyRangeError extends RangeError {
32
+ constructor(index: number) {
33
+ super(
34
+ `Could not remove store index ${String(index)} because it is out of range`,
35
+ )
36
+ this.name = 'StoreKeyRangeError'
37
+ }
38
+ }
39
+
40
+ class StoreKeyReadonlyError extends Error {
41
+ constructor(key: string, value: string) {
42
+ super(
43
+ `Could not set store key "${key}" to ${value} because it is readonly`,
44
+ )
45
+ this.name = 'StoreKeyReadonlyError'
46
+ }
47
+ }
48
+
49
+ export {
50
+ CircularDependencyError,
51
+ InvalidSignalValueError,
52
+ NullishSignalValueError,
53
+ StoreKeyExistsError,
54
+ StoreKeyRangeError,
55
+ StoreKeyReadonlyError,
56
+ }
package/src/match.ts CHANGED
@@ -5,7 +5,7 @@ import { toError } from './util'
5
5
  /* === Types === */
6
6
 
7
7
  type MatchHandlers<S extends UnknownSignalRecord> = {
8
- ok?: (values: SignalValues<S>) => void
8
+ ok: (values: SignalValues<S>) => void
9
9
  err?: (errors: readonly Error[]) => void
10
10
  nil?: () => void
11
11
  }
@@ -31,7 +31,7 @@ function match<S extends UnknownSignalRecord>(
31
31
  try {
32
32
  if (result.pending) handlers.nil?.()
33
33
  else if (result.errors) handlers.err?.(result.errors)
34
- else handlers.ok?.(result.values as SignalValues<S>)
34
+ else if (result.ok) handlers.ok(result.values)
35
35
  } catch (error) {
36
36
  // If handler throws, try error handler, otherwise rethrow
37
37
  if (
package/src/resolve.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { UnknownRecord } from './diff'
2
- import { type SignalValues, UNSET, type UnknownSignalRecord } from './signal'
3
- import { toError } from './util'
2
+ import type { SignalValues, UnknownSignalRecord } from './signal'
3
+ import { toError, UNSET } from './util'
4
4
 
5
5
  /* === Types === */
6
6
 
package/src/signal.ts CHANGED
@@ -14,7 +14,6 @@ import { isRecord } from './util'
14
14
  type Signal<T extends {}> = {
15
15
  get(): T
16
16
  }
17
- type MaybeSignal<T extends {}> = T | Signal<T> | ComputedCallback<T>
18
17
 
19
18
  type UnknownSignalRecord = Record<string, Signal<unknown & {}>>
20
19
 
@@ -22,15 +21,10 @@ type SignalValues<S extends UnknownSignalRecord> = {
22
21
  [K in keyof S]: S[K] extends Signal<infer T> ? T : never
23
22
  }
24
23
 
25
- /* === Constants === */
26
-
27
- // biome-ignore lint/suspicious/noExplicitAny: Deliberately using any to be used as a placeholder value in any signal
28
- const UNSET: any = Symbol()
29
-
30
24
  /* === Functions === */
31
25
 
32
26
  /**
33
- * Check whether a value is a Signal or not
27
+ * Check whether a value is a Signal
34
28
  *
35
29
  * @since 0.9.0
36
30
  * @param {unknown} value - value to check
@@ -40,6 +34,17 @@ const isSignal = /*#__PURE__*/ <T extends {}>(
40
34
  value: unknown,
41
35
  ): value is Signal<T> => isState(value) || isComputed(value) || isStore(value)
42
36
 
37
+ /**
38
+ * Check whether a value is a State or Store
39
+ *
40
+ * @since 0.15.2
41
+ * @param {unknown} value - value to check
42
+ * @returns {boolean} - true if value is a State or Store, false otherwise
43
+ */
44
+ const isMutableSignal = /*#__PURE__*/ <T extends {}>(
45
+ value: unknown,
46
+ ): value is State<T> | Store<T> => isState(value) || isStore(value)
47
+
43
48
  /**
44
49
  * Convert a value to a Signal if it's not already a Signal
45
50
  *
@@ -47,10 +52,6 @@ const isSignal = /*#__PURE__*/ <T extends {}>(
47
52
  * @param {T} value - value to convert
48
53
  * @returns {Signal<T>} - Signal instance
49
54
  */
50
- function toSignal<T extends {}>(value: T[]): Store<Record<string, T>>
51
- function toSignal<T extends {}>(
52
- value: (() => T) | ((abort: AbortSignal) => Promise<T>),
53
- ): Computed<T>
54
55
  function toSignal<T extends {}>(
55
56
  value: T,
56
57
  ): T extends Store<infer U>
@@ -61,50 +62,27 @@ function toSignal<T extends {}>(
61
62
  ? Computed<U>
62
63
  : T extends Signal<infer U>
63
64
  ? Signal<U>
64
- : T extends Record<string, unknown & {}>
65
- ? Store<{ [K in keyof T]: T[K] }>
66
- : State<T>
67
- function toSignal<T extends {}>(value: MaybeSignal<T>): Signal<T> {
65
+ : T extends ReadonlyArray<infer U extends {}>
66
+ ? Store<U[]>
67
+ : T extends Record<string, unknown & {}>
68
+ ? Store<{ [K in keyof T]: T[K] }>
69
+ : T extends ComputedCallback<infer U extends {}>
70
+ ? Computed<U>
71
+ : State<T>
72
+ function toSignal<T extends {}>(value: T) {
68
73
  if (isSignal<T>(value)) return value
69
74
  if (isComputedCallback(value)) return computed(value)
70
- if (Array.isArray(value)) return store(value as T)
71
75
  if (Array.isArray(value) || isRecord(value)) return store(value)
72
76
  return state(value)
73
77
  }
74
78
 
75
- /**
76
- * Convert a value to a mutable Signal if it's not already a Signal
77
- *
78
- * @since 0.15.0
79
- * @param {T} value - value to convert
80
- * @returns {State<T> | Store<T>} - Signal instance
81
- */
82
- function toMutableSignal<T extends {}>(value: T[]): Store<Record<string, T>>
83
- function toMutableSignal<T extends {}>(
84
- value: T,
85
- ): T extends Store<infer U>
86
- ? Store<U>
87
- : T extends State<infer U>
88
- ? State<U>
89
- : T extends Record<string, unknown & {}>
90
- ? Store<{ [K in keyof T]: T[K] }>
91
- : State<T>
92
- function toMutableSignal<T extends {}>(value: T): State<T> | Store<T> {
93
- if (isState<T>(value) || isStore<T>(value)) return value
94
- if (Array.isArray(value)) return store(value as T)
95
- if (isRecord(value)) return store(value)
96
- return state(value)
97
- }
98
-
99
79
  /* === Exports === */
100
80
 
101
81
  export {
102
82
  type Signal,
103
- type MaybeSignal,
104
83
  type UnknownSignalRecord,
105
84
  type SignalValues,
106
- UNSET,
107
85
  isSignal,
86
+ isMutableSignal,
108
87
  toSignal,
109
- toMutableSignal,
110
88
  }
package/src/state.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { isEqual } from './diff'
2
+ import { NullishSignalValueError } from './errors'
2
3
  import { notify, subscribe, type Watcher } from './scheduler'
3
- import { UNSET } from './signal'
4
- import { isObjectOfType } from './util'
4
+ import { isObjectOfType, UNSET } from './util'
5
5
 
6
6
  /* === Types === */
7
7
 
@@ -51,6 +51,7 @@ const state = /*#__PURE__*/ <T extends {}>(initialValue: T): State<T> => {
51
51
  * @returns {void}
52
52
  */
53
53
  set: (v: T): void => {
54
+ if (v == null) throw new NullishSignalValueError('state')
54
55
  if (isEqual(value, v)) return
55
56
  value = v
56
57
  notify(watchers)