@zeix/cause-effect 0.15.0 → 0.15.1

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Cause & Effect
2
2
 
3
- Version 0.15.0
3
+ Version 0.15.1
4
4
 
5
5
  **Cause & Effect** is a lightweight, reactive state management library for JavaScript applications. It uses fine-grained reactivity with signals to create predictable and efficient data flow in your app.
6
6
 
@@ -23,7 +23,7 @@ Version 0.15.0
23
23
  - 🛡️ **Error Handling**: Built-in helper functions for declarative error handling
24
24
  - 🔧 **Helper Functions**: `resolve()` and `match()` for type-safe value extraction and pattern matching for suspense and error boundaries
25
25
  - 🚀 **Performance**: Batching and efficient dependency tracking
26
- - 📦 **Tiny**: Less than 3kB gzipped, zero dependencies
26
+ - 📦 **Tiny**: Less than 3kB gzipped, tree-shakable, zero dependencies
27
27
 
28
28
  ## Quick Start
29
29
 
@@ -445,14 +445,6 @@ document.querySelector('.double-all').addEventListener('click', () => {
445
445
  signals[0].set(NaN)
446
446
  ```
447
447
 
448
- The Cause & Effect library is designed around these principles:
449
-
450
- - **Minimal API**: Core primitives with a small but powerful interface
451
- - **Automatic Dependency Tracking**: Fine-grained reactivity with minimal boilerplate
452
- - **Performance-Focused**: Choose the right tool (functions vs computed) for optimal speed
453
- - **Tree-Shakable**: Import only what you need for optimal bundle size
454
- - **Flexible Integration**: Works with any JavaScript application or framework
455
-
456
448
  ### Cleanup Functions
457
449
 
458
450
  Effects return a cleanup function. When executed, it will unsubscribe from signals and run cleanup functions returned by effect callbacks, for example to remove event listeners.
package/index.dev.js CHANGED
@@ -88,17 +88,17 @@ var enqueue = (fn, dedupe) => new Promise((resolve, reject) => {
88
88
  });
89
89
 
90
90
  // src/util.ts
91
+ var isString = (value) => typeof value === "string";
92
+ var isNumber = (value) => typeof value === "number";
91
93
  var isFunction = (fn) => typeof fn === "function";
92
94
  var isAsyncFunction = (fn) => isFunction(fn) && fn.constructor.name === "AsyncFunction";
93
95
  var isObjectOfType = (value, type) => Object.prototype.toString.call(value) === `[object ${type}]`;
94
96
  var isRecord = (value) => isObjectOfType(value, "Object");
95
- var arrayToRecord = (array) => {
96
- const record = {};
97
- for (let i = 0;i < array.length; i++) {
98
- if (i in array)
99
- record[String(i)] = array[i];
100
- }
101
- return record;
97
+ var validArrayIndexes = (keys) => {
98
+ if (!keys.length)
99
+ return null;
100
+ const indexes = keys.map((k) => isString(k) ? parseInt(k, 10) : isNumber(k) ? k : NaN);
101
+ return indexes.every((index) => Number.isFinite(index) && index >= 0) ? indexes.sort((a, b) => a - b) : null;
102
102
  };
103
103
  var hasMethod = (obj, methodName) => (methodName in obj) && isFunction(obj[methodName]);
104
104
  var isAbortError = (error) => error instanceof DOMException && error.name === "AbortError";
@@ -155,9 +155,8 @@ var effect = (callback) => {
155
155
  controller = new AbortController;
156
156
  const currentController = controller;
157
157
  callback(controller.signal).then((cleanup2) => {
158
- if (isFunction(cleanup2) && controller === currentController) {
158
+ if (isFunction(cleanup2) && controller === currentController)
159
159
  run.off(cleanup2);
160
- }
161
160
  }).catch((error) => {
162
161
  if (!isAbortError(error))
163
162
  console.error("Async effect error:", error);
@@ -189,29 +188,35 @@ var store = (initialValue) => {
189
188
  const cleanups = new Map;
190
189
  const size = state(0);
191
190
  const current = () => {
191
+ const keys = Array.from(signals.keys());
192
+ const arrayIndexes = validArrayIndexes(keys);
193
+ if (arrayIndexes)
194
+ return arrayIndexes.map((index) => signals.get(String(index))?.get());
192
195
  const record = {};
193
- for (const [key, value] of signals) {
194
- record[key] = value.get();
196
+ for (const [key, signal] of signals) {
197
+ record[key] = signal.get();
195
198
  }
196
199
  return record;
197
200
  };
198
201
  const emit = (type, detail) => eventTarget.dispatchEvent(new CustomEvent(type, { detail }));
199
- const addSignalAndEffect = (key, value) => {
202
+ const addProperty = (key, value) => {
203
+ const stringKey = String(key);
200
204
  const signal = toMutableSignal(value);
201
- signals.set(key, signal);
205
+ signals.set(stringKey, signal);
202
206
  const cleanup = effect(() => {
203
- const value2 = signal.get();
204
- if (value2 != null)
205
- emit("store-change", { [key]: value2 });
207
+ const currentValue = signal.get();
208
+ if (currentValue != null)
209
+ emit("store-change", { [key]: currentValue });
206
210
  });
207
- cleanups.set(key, cleanup);
211
+ cleanups.set(stringKey, cleanup);
208
212
  };
209
- const removeSignalAndEffect = (key) => {
210
- signals.delete(key);
211
- const cleanup = cleanups.get(key);
213
+ const removeProperty = (key) => {
214
+ const stringKey = String(key);
215
+ signals.delete(stringKey);
216
+ const cleanup = cleanups.get(stringKey);
212
217
  if (cleanup)
213
218
  cleanup();
214
- cleanups.delete(key);
219
+ cleanups.delete(stringKey);
215
220
  };
216
221
  const reconcile = (oldValue, newValue) => {
217
222
  const changes = diff(oldValue, newValue);
@@ -220,7 +225,7 @@ var store = (initialValue) => {
220
225
  for (const key in changes.add) {
221
226
  const value = changes.add[key];
222
227
  if (value != null)
223
- addSignalAndEffect(key, value);
228
+ addProperty(key, value);
224
229
  }
225
230
  emit("store-add", changes.add);
226
231
  }
@@ -235,7 +240,7 @@ var store = (initialValue) => {
235
240
  }
236
241
  if (Object.keys(changes.remove).length) {
237
242
  for (const key in changes.remove) {
238
- removeSignalAndEffect(key);
243
+ removeProperty(key);
239
244
  }
240
245
  emit("store-remove", changes.remove);
241
246
  }
@@ -263,12 +268,11 @@ var store = (initialValue) => {
263
268
  ];
264
269
  return new Proxy({}, {
265
270
  get(_target, prop) {
266
- const key = String(prop);
267
271
  switch (prop) {
268
272
  case "add":
269
273
  return (k, v) => {
270
274
  if (!signals.has(k)) {
271
- addSignalAndEffect(k, v);
275
+ addProperty(k, v);
272
276
  notify(watchers);
273
277
  emit("store-add", {
274
278
  [k]: v
@@ -284,7 +288,7 @@ var store = (initialValue) => {
284
288
  case "remove":
285
289
  return (k) => {
286
290
  if (signals.has(k)) {
287
- removeSignalAndEffect(k);
291
+ removeProperty(k);
288
292
  notify(watchers);
289
293
  emit("store-remove", { [k]: UNSET });
290
294
  size.set(signals.size);
@@ -321,19 +325,19 @@ var store = (initialValue) => {
321
325
  return TYPE_STORE;
322
326
  if (prop === Symbol.iterator) {
323
327
  return function* () {
324
- for (const [key2, signal] of signals) {
325
- yield [key2, signal];
328
+ for (const [key, signal] of signals) {
329
+ yield [key, signal];
326
330
  }
327
331
  };
328
332
  }
329
- return signals.get(key);
333
+ return signals.get(String(prop));
330
334
  },
331
335
  has(_target, prop) {
332
336
  const key = String(prop);
333
337
  return signals.has(key) || storeProps.includes(key) || prop === Symbol.toStringTag || prop === Symbol.iterator;
334
338
  },
335
339
  ownKeys() {
336
- return Array.from(signals.keys());
340
+ return Array.from(signals.keys()).map((key) => String(key));
337
341
  },
338
342
  getOwnPropertyDescriptor(_target, prop) {
339
343
  const signal = signals.get(String(prop));
@@ -357,8 +361,8 @@ function toSignal(value) {
357
361
  if (isComputedCallback(value))
358
362
  return computed(value);
359
363
  if (Array.isArray(value))
360
- return store(arrayToRecord(value));
361
- if (isRecord(value))
364
+ return store(value);
365
+ if (Array.isArray(value) || isRecord(value))
362
366
  return store(value);
363
367
  return state(value);
364
368
  }
@@ -366,7 +370,7 @@ function toMutableSignal(value) {
366
370
  if (isState(value) || isStore(value))
367
371
  return value;
368
372
  if (Array.isArray(value))
369
- return store(arrayToRecord(value));
373
+ return store(value);
370
374
  if (isRecord(value))
371
375
  return store(value);
372
376
  return state(value);
@@ -554,20 +558,17 @@ var isComputedCallback = (value) => isFunction(value) && value.length < 2;
554
558
  // src/match.ts
555
559
  function match(result, handlers) {
556
560
  try {
557
- if (result.pending) {
561
+ if (result.pending)
558
562
  handlers.nil?.();
559
- } else if (result.errors) {
563
+ else if (result.errors)
560
564
  handlers.err?.(result.errors);
561
- } else {
565
+ else
562
566
  handlers.ok?.(result.values);
563
- }
564
567
  } catch (error) {
565
- if (handlers.err && (!result.errors || !result.errors.includes(toError(error)))) {
566
- const allErrors = result.errors ? [...result.errors, toError(error)] : [toError(error)];
567
- handlers.err(allErrors);
568
- } else {
568
+ if (handlers.err && (!result.errors || !result.errors.includes(toError(error))))
569
+ handlers.err(result.errors ? [...result.errors, toError(error)] : [toError(error)]);
570
+ else
569
571
  throw error;
570
- }
571
572
  }
572
573
  }
573
574
  // src/resolve.ts
@@ -578,26 +579,24 @@ function resolve(signals) {
578
579
  for (const [key, signal] of Object.entries(signals)) {
579
580
  try {
580
581
  const value = signal.get();
581
- if (value === UNSET) {
582
+ if (value === UNSET)
582
583
  pending2 = true;
583
- } else {
584
+ else
584
585
  values[key] = value;
585
- }
586
586
  } catch (e) {
587
587
  errors.push(toError(e));
588
588
  }
589
589
  }
590
- if (pending2) {
590
+ if (pending2)
591
591
  return { ok: false, pending: true };
592
- }
593
- if (errors.length > 0) {
592
+ if (errors.length > 0)
594
593
  return { ok: false, errors };
595
- }
596
594
  return { ok: true, values };
597
595
  }
598
596
  export {
599
597
  watch,
600
598
  toSignal,
599
+ toMutableSignal,
601
600
  toError,
602
601
  subscribe,
603
602
  store,
@@ -606,9 +605,11 @@ export {
606
605
  observe,
607
606
  notify,
608
607
  match,
608
+ isString,
609
609
  isStore,
610
610
  isState,
611
611
  isSignal,
612
+ isNumber,
612
613
  isFunction,
613
614
  isEqual,
614
615
  isComputedCallback,
package/index.js CHANGED
@@ -1 +1 @@
1
- var q,f=new Set,p=0,h=new Map,k,l=()=>{k=void 0;let $=Array.from(h.values());h.clear();for(let B of $)B()},B$=()=>{if(k)cancelAnimationFrame(k);k=requestAnimationFrame(l)};queueMicrotask(l);var m=($)=>{let B=new Set,W=$;return W.off=(x)=>{B.add(x)},W.cleanup=()=>{for(let x of B)x();B.clear()},W},U=($)=>{if(q&&!$.has(q)){let B=q;$.add(B),q.off(()=>{$.delete(B)})}},M=($)=>{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--}},y=($,B)=>{let W=q;q=B;try{$()}finally{q=W}},W$=($,B)=>new Promise((W,x)=>{h.set(B||Symbol(),()=>{try{W($())}catch(J){x(J)}}),B$()});var A=($)=>typeof $==="function",E=($)=>A($)&&$.constructor.name==="AsyncFunction",O=($,B)=>Object.prototype.toString.call($)===`[object ${B}]`,V=($)=>O($,"Object");var d=($)=>{let B={};for(let W=0;W<$.length;W++)if(W in $)B[String(W)]=$[W];return B},a=($,B)=>(B in $)&&A($[B]),R=($)=>$ instanceof DOMException&&$.name==="AbortError",Y=($)=>$ instanceof Error?$:Error(String($));class D extends Error{constructor($){super(`Circular dependency in ${$} detected`);this.name="CircularDependencyError"}}var o="State",S=($)=>{let B=new Set,W=$,x={[Symbol.toStringTag]:o,get:()=>{return U(B),W},set:(J)=>{if(P(W,J))return;if(W=J,M(B),C===W)B.clear()},update:(J)=>{x.set(J(W))}};return x},b=($)=>O($,o);var c=($)=>{let B=E($),W=!1,x,J=m(()=>y(()=>{if(W)throw new D("effect");W=!0,x?.abort(),x=void 0;let F;try{if(B){x=new AbortController;let H=x;$(x.signal).then((z)=>{if(A(z)&&x===H)J.off(z)}).catch((z)=>{if(!R(z))console.error("Async effect error:",z)})}else if(F=$(),A(F))J.off(F)}catch(H){if(!R(H))console.error("Effect callback error:",H)}W=!1},J));return J(),()=>{x?.abort(),J.cleanup()}};var n="Store",T=($)=>{let B=new Set,W=new EventTarget,x=new Map,J=new Map,F=S(0),H=()=>{let Z={};for(let[L,Q]of x)Z[L]=Q.get();return Z},z=(Z,L)=>W.dispatchEvent(new CustomEvent(Z,{detail:L})),K=(Z,L)=>{let Q=e(L);x.set(Z,Q);let G=c(()=>{let X=Q.get();if(X!=null)z("store-change",{[Z]:X})});J.set(Z,G)},N=(Z)=>{x.delete(Z);let L=J.get(Z);if(L)L();J.delete(Z)},j=(Z,L)=>{let Q=u(Z,L);return v(()=>{if(Object.keys(Q.add).length){for(let G in Q.add){let X=Q.add[G];if(X!=null)K(G,X)}z("store-add",Q.add)}if(Object.keys(Q.change).length){for(let G in Q.change){let X=x.get(G),I=Q.change[G];if(X&&I!=null&&a(X,"set"))X.set(I)}z("store-change",Q.change)}if(Object.keys(Q.remove).length){for(let G in Q.remove)N(G);z("store-remove",Q.remove)}F.set(x.size)}),Q.changed};j({},$),setTimeout(()=>{let Z=new CustomEvent("store-add",{detail:$});W.dispatchEvent(Z)},0);let _=["add","get","remove","set","update","addEventListener","removeEventListener","dispatchEvent","size"];return new Proxy({},{get(Z,L){let Q=String(L);switch(L){case"add":return(G,X)=>{if(!x.has(G))K(G,X),M(B),z("store-add",{[G]:X}),F.set(x.size)};case"get":return()=>{return U(B),H()};case"remove":return(G)=>{if(x.has(G))N(G),M(B),z("store-remove",{[G]:C}),F.set(x.size)};case"set":return(G)=>{if(j(H(),G)){if(M(B),C===G)B.clear()}};case"update":return(G)=>{let X=H(),I=G(X);if(j(X,I)){if(M(B),C===I)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 F}if(L===Symbol.toStringTag)return n;if(L===Symbol.iterator)return function*(){for(let[G,X]of x)yield[G,X]};return x.get(Q)},has(Z,L){let Q=String(L);return x.has(Q)||_.includes(Q)||L===Symbol.toStringTag||L===Symbol.iterator},ownKeys(){return Array.from(x.keys())},getOwnPropertyDescriptor(Z,L){let Q=x.get(String(L));return Q?{enumerable:!0,configurable:!0,writable:!0,value:Q}:void 0}})},g=($)=>O($,n);var C=Symbol(),$$=($)=>b($)||t($)||g($);function x$($){if($$($))return $;if(s($))return i($);if(Array.isArray($))return T(d($));if(V($))return T($);return S($)}function e($){if(b($)||g($))return $;if(Array.isArray($))return T(d($));if(V($))return T($);return S($)}var P=($,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 D("isEqual");W.add($),W.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],W))return!1;return!0}if(Array.isArray($)!==Array.isArray(B))return!1;if(V($)&&V(B)){let x=Object.keys($),J=Object.keys(B);if(x.length!==J.length)return!1;for(let F of x){if(!(F in B))return!1;if(!P($[F],B[F],W))return!1}return!0}return!1}finally{W.delete($),W.delete(B)}},u=($,B)=>{let W=new WeakSet;return((J,F)=>{let H={},z={},K={},N=Object.keys(J),j=Object.keys(F),_=new Set([...N,...j]);for(let L of _){let Q=L in J,G=L in F;if(!Q&&G){H[L]=F[L];continue}else if(Q&&!G){K[L]=C;continue}let X=J[L],I=F[L];if(!P(X,I,W))z[L]=I}return{changed:Object.keys(H).length>0||Object.keys(z).length>0||Object.keys(K).length>0,add:H,change:z,remove:K}})($,B)};var r="Computed",i=($)=>{let B=new Set,W=C,x,J,F=!0,H=!1,z=!1,K=(G)=>{if(!P(G,W))W=G,H=!0;x=void 0,F=!1},N=()=>{H=C!==W,W=C,x=void 0},j=(G)=>{let X=Y(G);H=!x||X.name!==x.name||X.message!==x.message,W=C,x=X},_=(G)=>(X)=>{if(z=!1,J=void 0,G(X),H)M(B)},Z=m(()=>{if(F=!0,J?.abort(),B.size)M(B);else Z.cleanup()});Z.off(()=>{J?.abort()});let L=()=>y(()=>{if(z)throw new D("computed");if(H=!1,E($)){if(J)return W;J=new AbortController,J.signal.addEventListener("abort",()=>{z=!1,J=void 0,L()},{once:!0})}let G;z=!0;try{G=J?$(J.signal):$()}catch(X){if(R(X))N();else j(X);z=!1;return}if(G instanceof Promise)G.then(_(K),_(j));else if(G==null||C===G)N();else K(G);z=!1},Z);return{[Symbol.toStringTag]:r,get:()=>{if(U(B),w(),F)L();if(x)throw x;return W}}},t=($)=>O($,r),s=($)=>A($)&&$.length<2;function G$($,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)))){let x=$.errors?[...$.errors,Y(W)]:[Y(W)];B.err(x)}else throw W}}function J$($){let B=[],W=!1,x={};for(let[J,F]of Object.entries($))try{let H=F.get();if(H===C)W=!0;else x[J]=H}catch(H){B.push(Y(H))}if(W)return{ok:!1,pending:!0};if(B.length>0)return{ok:!1,errors:B};return{ok:!0,values:x}}export{m as watch,x$ as toSignal,Y as toError,U as subscribe,T as store,S as state,J$ as resolve,y as observe,M as notify,G$ as match,g as isStore,b as isState,$$ as isSignal,A as isFunction,P as isEqual,s as isComputedCallback,t as isComputed,E as isAsyncFunction,R as isAbortError,w as flush,W$ as enqueue,c as effect,u as diff,i as computed,v as batch,C as UNSET,n as TYPE_STORE,o as TYPE_STATE,r as TYPE_COMPUTED,D as CircularDependencyError};
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};
package/index.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @name Cause & Effect
3
- * @version 0.15.0
3
+ * @version 0.15.1
4
4
  * @author Esther Brunner
5
5
  */
6
6
 
@@ -12,7 +12,13 @@ export {
12
12
  isComputedCallback,
13
13
  TYPE_COMPUTED,
14
14
  } from './src/computed'
15
- export { type DiffResult, diff, isEqual, type UnknownRecord } from './src/diff'
15
+ export {
16
+ type DiffResult,
17
+ diff,
18
+ isEqual,
19
+ type UnknownRecord,
20
+ type UnknownRecordOrArray,
21
+ } from './src/diff'
16
22
  export { type EffectCallback, effect, type MaybeCleanup } from './src/effect'
17
23
  export { type MatchHandlers, match } from './src/match'
18
24
  export { type ResolveResult, resolve } from './src/resolve'
@@ -33,8 +39,10 @@ export {
33
39
  type MaybeSignal,
34
40
  type Signal,
35
41
  type SignalValues,
42
+ toMutableSignal,
36
43
  toSignal,
37
44
  UNSET,
45
+ type UnknownSignalRecord,
38
46
  } from './src/signal'
39
47
  export { isState, type State, state, TYPE_STATE } from './src/state'
40
48
  export {
@@ -52,5 +60,7 @@ export {
52
60
  isAbortError,
53
61
  isAsyncFunction,
54
62
  isFunction,
63
+ isNumber,
64
+ isString,
55
65
  toError,
56
66
  } from './src/util'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeix/cause-effect",
3
- "version": "0.15.0",
3
+ "version": "0.15.1",
4
4
  "author": "Esther Brunner",
5
5
  "main": "index.js",
6
6
  "module": "index.ts",
package/src/diff.ts CHANGED
@@ -4,8 +4,11 @@ import { CircularDependencyError, isRecord } from './util'
4
4
  /* === Types === */
5
5
 
6
6
  type UnknownRecord = Record<string, unknown & {}>
7
+ type UnknownRecordOrArray = {
8
+ [x: string | number]: unknown & {}
9
+ }
7
10
 
8
- type DiffResult<T extends UnknownRecord = UnknownRecord> = {
11
+ type DiffResult<T extends UnknownRecordOrArray = UnknownRecord> = {
9
12
  changed: boolean
10
13
  add: Partial<T>
11
14
  change: Partial<T>
@@ -81,7 +84,10 @@ const isEqual = <T>(a: T, b: T, visited?: WeakSet<object>): boolean => {
81
84
  * @param {T} newObj - The new record to compare
82
85
  * @returns {DiffResult<T>} The result of the comparison
83
86
  */
84
- const diff = <T extends UnknownRecord>(oldObj: T, newObj: T): DiffResult<T> => {
87
+ const diff = <T extends UnknownRecordOrArray>(
88
+ oldObj: T,
89
+ newObj: T,
90
+ ): DiffResult<T> => {
85
91
  const visited = new WeakSet<object>()
86
92
 
87
93
  const diffRecords = (
@@ -133,4 +139,10 @@ const diff = <T extends UnknownRecord>(oldObj: T, newObj: T): DiffResult<T> => {
133
139
 
134
140
  /* === Exports === */
135
141
 
136
- export { type DiffResult, diff, isEqual, type UnknownRecord }
142
+ export {
143
+ type DiffResult,
144
+ diff,
145
+ isEqual,
146
+ type UnknownRecord,
147
+ type UnknownRecordOrArray,
148
+ }
package/src/effect.ts CHANGED
@@ -55,9 +55,8 @@ const effect = (callback: EffectCallback): Cleanup => {
55
55
  if (
56
56
  isFunction(cleanup) &&
57
57
  controller === currentController
58
- ) {
58
+ )
59
59
  run.off(cleanup)
60
- }
61
60
  })
62
61
  .catch(error => {
63
62
  if (!isAbortError(error))
package/src/match.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import type { ResolveResult } from './resolve'
2
- import type { Signal, SignalValues } from './signal'
2
+ import type { SignalValues, UnknownSignalRecord } from './signal'
3
3
  import { toError } from './util'
4
4
 
5
5
  /* === Types === */
6
6
 
7
- type MatchHandlers<S extends Record<string, Signal<unknown & {}>>> = {
7
+ type MatchHandlers<S extends UnknownSignalRecord> = {
8
8
  ok?: (values: SignalValues<S>) => void
9
9
  err?: (errors: readonly Error[]) => void
10
10
  nil?: () => void
@@ -24,31 +24,26 @@ type MatchHandlers<S extends Record<string, Signal<unknown & {}>>> = {
24
24
  * @param {MatchHandlers<S>} handlers - Handlers for different states (side effects only)
25
25
  * @returns {void} - Always returns void
26
26
  */
27
- function match<S extends Record<string, Signal<unknown & {}>>>(
27
+ function match<S extends UnknownSignalRecord>(
28
28
  result: ResolveResult<S>,
29
29
  handlers: MatchHandlers<S>,
30
30
  ): void {
31
31
  try {
32
- if (result.pending) {
33
- handlers.nil?.()
34
- } else if (result.errors) {
35
- handlers.err?.(result.errors)
36
- } else {
37
- handlers.ok?.(result.values as SignalValues<S>)
38
- }
32
+ if (result.pending) handlers.nil?.()
33
+ else if (result.errors) handlers.err?.(result.errors)
34
+ else handlers.ok?.(result.values as SignalValues<S>)
39
35
  } catch (error) {
40
36
  // If handler throws, try error handler, otherwise rethrow
41
37
  if (
42
38
  handlers.err &&
43
39
  (!result.errors || !result.errors.includes(toError(error)))
44
- ) {
45
- const allErrors = result.errors
46
- ? [...result.errors, toError(error)]
47
- : [toError(error)]
48
- handlers.err(allErrors)
49
- } else {
50
- throw error
51
- }
40
+ )
41
+ handlers.err(
42
+ result.errors
43
+ ? [...result.errors, toError(error)]
44
+ : [toError(error)],
45
+ )
46
+ else throw error
52
47
  }
53
48
  }
54
49
 
package/src/resolve.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import type { UnknownRecord } from './diff'
2
- import { type Signal, type SignalValues, UNSET } from './signal'
2
+ import { type SignalValues, UNSET, type UnknownSignalRecord } from './signal'
3
3
  import { toError } from './util'
4
4
 
5
5
  /* === Types === */
6
6
 
7
- type ResolveResult<S extends Record<string, Signal<unknown & {}>>> =
7
+ type ResolveResult<S extends UnknownSignalRecord> =
8
8
  | { ok: true; values: SignalValues<S>; errors?: never; pending?: never }
9
9
  | { ok: false; errors: readonly Error[]; values?: never; pending?: never }
10
10
  | { ok: false; pending: true; values?: never; errors?: never }
@@ -21,9 +21,7 @@ type ResolveResult<S extends Record<string, Signal<unknown & {}>>> =
21
21
  * @param {S} signals - Signals to resolve
22
22
  * @returns {ResolveResult<S>} - Discriminated union result
23
23
  */
24
- function resolve<S extends Record<string, Signal<unknown & {}>>>(
25
- signals: S,
26
- ): ResolveResult<S> {
24
+ function resolve<S extends UnknownSignalRecord>(signals: S): ResolveResult<S> {
27
25
  const errors: Error[] = []
28
26
  let pending = false
29
27
  const values: UnknownRecord = {}
@@ -32,24 +30,16 @@ function resolve<S extends Record<string, Signal<unknown & {}>>>(
32
30
  for (const [key, signal] of Object.entries(signals)) {
33
31
  try {
34
32
  const value = signal.get()
35
-
36
- if (value === UNSET) {
37
- pending = true
38
- } else {
39
- values[key] = value
40
- }
33
+ if (value === UNSET) pending = true
34
+ else values[key] = value
41
35
  } catch (e) {
42
36
  errors.push(toError(e))
43
37
  }
44
38
  }
45
39
 
46
40
  // Return discriminated union
47
- if (pending) {
48
- return { ok: false, pending: true }
49
- }
50
- if (errors.length > 0) {
51
- return { ok: false, errors }
52
- }
41
+ if (pending) return { ok: false, pending: true }
42
+ if (errors.length > 0) return { ok: false, errors }
53
43
  return { ok: true, values: values as SignalValues<S> }
54
44
  }
55
45
 
package/src/signal.ts CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  } from './computed'
8
8
  import { isState, type State, state } from './state'
9
9
  import { isStore, type Store, store } from './store'
10
- import { arrayToRecord, isRecord } from './util'
10
+ import { isRecord } from './util'
11
11
 
12
12
  /* === Types === */
13
13
 
@@ -16,7 +16,9 @@ type Signal<T extends {}> = {
16
16
  }
17
17
  type MaybeSignal<T extends {}> = T | Signal<T> | ComputedCallback<T>
18
18
 
19
- type SignalValues<S extends Record<string, Signal<unknown & {}>>> = {
19
+ type UnknownSignalRecord = Record<string, Signal<unknown & {}>>
20
+
21
+ type SignalValues<S extends UnknownSignalRecord> = {
20
22
  [K in keyof S]: S[K] extends Signal<infer T> ? T : never
21
23
  }
22
24
 
@@ -42,45 +44,56 @@ const isSignal = /*#__PURE__*/ <T extends {}>(
42
44
  * Convert a value to a Signal if it's not already a Signal
43
45
  *
44
46
  * @since 0.9.6
47
+ * @param {T} value - value to convert
48
+ * @returns {Signal<T>} - Signal instance
45
49
  */
46
- function toSignal<T extends Array<unknown & {}>>(
47
- value: T[],
48
- ): Store<Record<string, T>>
49
- function toSignal<T extends Record<keyof T, T[keyof T]>>(value: T): Store<T>
50
- function toSignal<T extends {}>(value: ComputedCallback<T>): Computed<T>
51
- function toSignal<T extends {}>(value: Signal<T>): Signal<T>
52
- function toSignal<T extends {}>(value: T): State<T>
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>
53
54
  function toSignal<T extends {}>(
54
- value: MaybeSignal<T> | T[],
55
- ): Signal<T> | Store<Record<string, T>> {
55
+ value: T,
56
+ ): T extends Store<infer U>
57
+ ? Store<U>
58
+ : T extends State<infer U>
59
+ ? State<U>
60
+ : T extends Computed<infer U>
61
+ ? Computed<U>
62
+ : T extends Signal<infer U>
63
+ ? 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> {
56
68
  if (isSignal<T>(value)) return value
57
- if (isComputedCallback<T>(value)) return computed(value)
58
- if (Array.isArray(value)) return store(arrayToRecord(value))
59
- if (isRecord(value)) return store(value as T)
60
- return state(value as T)
69
+ if (isComputedCallback(value)) return computed(value)
70
+ if (Array.isArray(value)) return store(value as T)
71
+ if (Array.isArray(value) || isRecord(value)) return store(value)
72
+ return state(value)
61
73
  }
62
74
 
63
75
  /**
64
76
  * Convert a value to a mutable Signal if it's not already a Signal
65
77
  *
66
- * @since 0.9.6
78
+ * @since 0.15.0
79
+ * @param {T} value - value to convert
80
+ * @returns {State<T> | Store<T>} - Signal instance
67
81
  */
68
- function toMutableSignal<T extends Array<unknown & {}>>(
69
- value: T[],
70
- ): Store<Record<string, T>>
71
- function toMutableSignal<T extends Record<keyof T, T[keyof T]>>(
72
- value: T,
73
- ): Store<T>
74
- function toMutableSignal<T extends State<T>>(value: State<T>): State<T>
75
- function toMutableSignal<T extends Store<T>>(value: Store<T>): Store<T>
76
- function toMutableSignal<T extends {}>(value: T): State<T>
82
+ function toMutableSignal<T extends {}>(value: T[]): Store<Record<string, T>>
77
83
  function toMutableSignal<T extends {}>(
78
- value: T | State<T> | Store<T> | T[],
79
- ): Signal<T> | Store<Record<string, T>> {
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> {
80
93
  if (isState<T>(value) || isStore<T>(value)) return value
81
- if (Array.isArray(value)) return store(arrayToRecord(value))
82
- if (isRecord(value)) return store(value as T)
83
- return state(value as T)
94
+ if (Array.isArray(value)) return store(value as T)
95
+ if (isRecord(value)) return store(value)
96
+ return state(value)
84
97
  }
85
98
 
86
99
  /* === Exports === */
@@ -88,6 +101,7 @@ function toMutableSignal<T extends {}>(
88
101
  export {
89
102
  type Signal,
90
103
  type MaybeSignal,
104
+ type UnknownSignalRecord,
91
105
  type SignalValues,
92
106
  UNSET,
93
107
  isSignal,