@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 +2 -10
- package/index.dev.js +50 -49
- package/index.js +1 -1
- package/index.ts +12 -2
- package/package.json +1 -1
- package/src/diff.ts +15 -3
- package/src/effect.ts +1 -2
- package/src/match.ts +13 -18
- package/src/resolve.ts +7 -17
- package/src/signal.ts +44 -30
- package/src/store.ts +62 -63
- package/src/util.ts +15 -17
- package/test/signal.test.ts +451 -0
- package/test/store.test.ts +27 -0
- package/types/index.d.ts +4 -4
- package/types/src/diff.d.ts +6 -3
- package/types/src/match.d.ts +3 -3
- package/types/src/resolve.d.ts +3 -3
- package/types/src/signal.d.ts +17 -13
- package/types/src/store.d.ts +20 -15
- package/types/src/util.d.ts +3 -4
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Cause & Effect
|
|
2
2
|
|
|
3
|
-
Version 0.15.
|
|
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
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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,
|
|
194
|
-
record[key] =
|
|
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
|
|
202
|
+
const addProperty = (key, value) => {
|
|
203
|
+
const stringKey = String(key);
|
|
200
204
|
const signal = toMutableSignal(value);
|
|
201
|
-
signals.set(
|
|
205
|
+
signals.set(stringKey, signal);
|
|
202
206
|
const cleanup = effect(() => {
|
|
203
|
-
const
|
|
204
|
-
if (
|
|
205
|
-
emit("store-change", { [key]:
|
|
207
|
+
const currentValue = signal.get();
|
|
208
|
+
if (currentValue != null)
|
|
209
|
+
emit("store-change", { [key]: currentValue });
|
|
206
210
|
});
|
|
207
|
-
cleanups.set(
|
|
211
|
+
cleanups.set(stringKey, cleanup);
|
|
208
212
|
};
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 [
|
|
325
|
-
yield [
|
|
328
|
+
for (const [key, signal] of signals) {
|
|
329
|
+
yield [key, signal];
|
|
326
330
|
}
|
|
327
331
|
};
|
|
328
332
|
}
|
|
329
|
-
return signals.get(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
563
|
+
else if (result.errors)
|
|
560
564
|
handlers.err?.(result.errors);
|
|
561
|
-
|
|
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
|
-
|
|
567
|
-
|
|
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
|
-
|
|
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,
|
|
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.
|
|
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 {
|
|
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
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
|
|
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
|
|
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 {
|
|
142
|
+
export {
|
|
143
|
+
type DiffResult,
|
|
144
|
+
diff,
|
|
145
|
+
isEqual,
|
|
146
|
+
type UnknownRecord,
|
|
147
|
+
type UnknownRecordOrArray,
|
|
148
|
+
}
|
package/src/effect.ts
CHANGED
package/src/match.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { ResolveResult } from './resolve'
|
|
2
|
-
import type {
|
|
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
|
|
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
|
|
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
|
-
|
|
34
|
-
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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
|
|
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
|
|
47
|
-
|
|
48
|
-
):
|
|
49
|
-
|
|
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:
|
|
55
|
-
):
|
|
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
|
|
58
|
-
if (Array.isArray(value)) return store(
|
|
59
|
-
if (isRecord(value)) return store(value
|
|
60
|
-
return state(value
|
|
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.
|
|
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
|
|
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
|
|
79
|
-
):
|
|
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(
|
|
82
|
-
if (isRecord(value)) return store(value
|
|
83
|
-
return state(value
|
|
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,
|