@zeix/cause-effect 0.9.7

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/.editorconfig ADDED
@@ -0,0 +1,7 @@
1
+ root = true
2
+
3
+ [*]
4
+ indent_style = tab
5
+ indent_size = 4
6
+ end_of_line = lf
7
+ charset = utf-8
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 - 2025 Zeix AG
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,122 @@
1
+ # Cause & Effect
2
+
3
+ Version 0.9.7
4
+
5
+ **Cause & Effect** - efficient state management with signals that sync instantly and reactively across your application.
6
+
7
+ ## Key Features
8
+
9
+ * **Efficient State Management**: Use lightweight signals for state updates that automatically notify dependents when needed.
10
+ * **Support for Asynchronous Operations**: Handle state updates smoothly, even when dealing with network requests or Promise-based libraries, without disrupting reactivity.
11
+ * **Memoized Computed Signals**: Optionally create derived values that are cached and automatically recalculated when source data changes.
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ # with npm
17
+ npm install @zeix/cause-effect
18
+
19
+ # or with bun
20
+ bun add @zeix/cause-effect
21
+ ```
22
+
23
+ ## Basic Usage
24
+
25
+ ### Single State Signal
26
+
27
+ `state()` creates a new state signal. To access the current value of the signal use the `.get()` method. To update the value of the signal use the `.set()` method with a new value or an updater function of the form `(v: T) => T`.
28
+
29
+ ```js
30
+ import { state, effect } from '@zeix/cause-effect'
31
+
32
+ const count = state(42)
33
+ effect(() => console.log(count.get())) // logs '42'
34
+ count.set(24) // logs '24'
35
+ document.querySelector('button.increment')
36
+ .addEventListener('click', () => count.set(v => ++v))
37
+ // Click on button logs '25', '26', and so on
38
+ ```
39
+
40
+ ### Sync Computed Signal
41
+
42
+ `computed()` creates a new computed signal. Computed signals are read-only and you can access the current resulting value using the `.get()` method.
43
+
44
+ ```js
45
+ import { state, computed, effect } from '@zeix/cause-effect'
46
+
47
+ const count = state(42)
48
+ const isOdd = computed(() => count.get() % 2)
49
+ effect(() => console.log(isOdd.get())) // logs 'false'
50
+ count.set(24) // logs nothing because 24 is also an even number
51
+ document.querySelector('button.increment')
52
+ .addEventListener('click', () => count.set(v => ++v))
53
+ // Click on button logs 'true', 'false', and so on
54
+ ```
55
+
56
+ If you want to derive a computed signal from a single other signal you can use the `.map()` method on either `State` or `Computed`. This does the same as the snippet above:
57
+
58
+ ```js
59
+ import { state, effect } from '@zeix/cause-effect'
60
+
61
+ const count = state(42)
62
+ const isOdd = count.map(v => v % 2)
63
+ effect(() => console.log(isOdd.get())) // logs 'false'
64
+ count.set(24) // logs nothing because 24 is also an even number
65
+ document.querySelector('button.increment')
66
+ .addEventListener('click', () => count.set(v => ++v))
67
+ // Click on button logs 'true', 'false', and so on
68
+ ```
69
+
70
+ ### Async Computed Signal
71
+
72
+ Async computed signals are as straight forward as their sync counterparts. Just create the computed signal with an async function.
73
+
74
+ **Caution**: You can't use the `.map()` method to create an async computed signal. And async computed signals will return `undefined` until the Promise is resolved.
75
+
76
+ ```js
77
+ import { state, computed, effect } from '@zeix/cause-effect'
78
+
79
+ const entryId = state(42)
80
+ const entryData = computed(async () => {
81
+ const response = await fetch(`/api/entry/${entryId.get()}`)
82
+ if (!response.ok) return new Error(`Failed to fetch data: ${response.statusText}`)
83
+ return response.json()
84
+ })
85
+ effect(() => {
86
+ let data
87
+ try {
88
+ data = entryData.get()
89
+ } catch (error) {
90
+ console.error(error.message) // logs the error message if an error ocurred
91
+ return
92
+ }
93
+ if (null == data) return // doesn't do anything while we are still waiting for the data
94
+ document.querySelector('.entry h2').textContent = data.title
95
+ document.querySelector('.entry p').textContent = data.description
96
+ })
97
+ // Updates h1 and p of the entry as soon as fetched data for entry becomes available
98
+ document.querySelector('button.next')
99
+ .addEventListener('click', () => entryId.set(v => ++v))
100
+ // Click on button updates h1 and p of the entry as soon as fetched data for the next entry is loaded
101
+ ```
102
+
103
+ ### Effects and Batching
104
+
105
+ Effects run synchronously as soon as source signals update. If you need to set multiple signals you can batch them together to ensure dependents are executed only once.
106
+
107
+ ```js
108
+ import { state, computed, effect, batch } from '@zeix/cause-effect'
109
+
110
+ const a = state(3)
111
+ const b = state(4)
112
+ const sum = computed(() => a.get() + b.get())
113
+ effect(() => console.log(sum.get())) // logs '7'
114
+ document.querySelector('button.double-all')
115
+ .addEventListener('click', () =>
116
+ batch(() => {
117
+ a.set(v => v * 2)
118
+ b.set(v => v * 2)
119
+ }
120
+ ))
121
+ // Click on button logs '14' only once (instead of first '10' and then '14' without batch)
122
+ ```
package/index.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @name Cause & Effect
3
+ * @version 0.9.7
4
+ * @author Esther Brunner
5
+ */
6
+ export { UNSET, State, state, isState } from './lib/state';
7
+ export { type Computed, computed, isComputed } from './lib/computed';
8
+ export { type Signal, isSignal, toSignal, batch } from './lib/signal';
9
+ export { effect } from './lib/effect';
package/index.js ADDED
@@ -0,0 +1 @@
1
+ var A=(y)=>typeof y==="function",D=(y)=>A(y)&&/^async\s+/.test(y.toString()),G=(y)=>(x)=>x instanceof y,X=G(Error),O=G(Promise);var R="Computed",q=(y,x)=>{x=x??D(y);let j=[],Q,B=null,W=!0,S=()=>{if(W=!0,x)I(j)},Y={[Symbol.toStringTag]:R,get:()=>{if(x)H(j);if(!x||W)J(()=>{let F=(L)=>{Q=L,W=!1,B=null},C=(L)=>{B=X(L)?L:new Error(`Computed function failed: ${L}`)};try{let L=y(Q);O(L)?L.then(F).catch(C):F(L)}catch(L){C(L)}},S);if(X(B))throw B;return Q},map:(F)=>q(()=>F(Y.get()))};return Y},Z=(y)=>!!y&&typeof y==="object"&&y[Symbol.toStringTag]===R;var z,$=!1,N=[],P=(y)=>M(y)||Z(y),U=(y,x=!1)=>P(y)?y:A(y)?q(y,x):k(y),H=(y)=>{if(z&&!y.includes(z))y.push(z)},I=(y)=>y.forEach((x)=>$?N.push(x):x()),J=(y,x)=>{let j=z;z=x,y(),z=j},T=(y)=>{$=!0,y(),$=!1,N.forEach((x)=>x()),N.length=0};var V=Symbol();class K{y;watchers=[];constructor(y){this.value=y}get(){return H(this.watchers),this.value}set(y){if(V!==y){let x=A(y)?y(this.value):y;if(Object.is(this.value,x))return;this.value=x}if(I(this.watchers),V===y)this.watchers=[]}map(y){return q(()=>y(this.get()))}}var k=(y)=>new K(y),M=G(K);var p=(y)=>{let x=()=>J(()=>{try{y()}catch(j){console.error(j)}},x);x()};export{U as toSignal,k as state,M as isState,P as isSignal,Z as isComputed,p as effect,q as computed,T as batch,V as UNSET,K as State};
package/index.ts ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @name Cause & Effect
3
+ * @version 0.9.7
4
+ * @author Esther Brunner
5
+ */
6
+ export { UNSET, State, state, isState } from './lib/state'
7
+ export { type Computed, computed, isComputed } from './lib/computed'
8
+ export { type Signal, isSignal, toSignal, batch } from './lib/signal'
9
+ export { effect } from './lib/effect'
@@ -0,0 +1,14 @@
1
+ export type Computed<T> = {
2
+ [Symbol.toStringTag]: "Computed";
3
+ get: () => T;
4
+ map: <U>(fn: (value: T) => U) => Computed<U>;
5
+ };
6
+ /**
7
+ * Create a derived state from existing states
8
+ *
9
+ * @since 0.9.0
10
+ * @param {() => T} fn - compute function to derive state
11
+ * @returns {Computed<T>} result of derived state
12
+ */
13
+ export declare const computed: <T>(fn: (v?: T) => T | Promise<T>, memo?: boolean) => Computed<T>;
14
+ export declare const isComputed: <T>(value: unknown) => value is Computed<T>;
@@ -0,0 +1,75 @@
1
+ import { type Watcher, subscribe, notify, watch } from "./signal"
2
+ import { isAsyncFunction, isError, isPromise } from "./util"
3
+
4
+ /* === Types === */
5
+
6
+ export type Computed<T> = {
7
+ [Symbol.toStringTag]: "Computed"
8
+ get: () => T
9
+ map: <U>(fn: (value: T) => U) => Computed<U>
10
+ }
11
+
12
+ /* === Constants === */
13
+
14
+ const TYPE_COMPUTED = 'Computed'
15
+
16
+ /* === Namespace Computed === */
17
+
18
+ /**
19
+ * Create a derived state from existing states
20
+ *
21
+ * @since 0.9.0
22
+ * @param {() => T} fn - compute function to derive state
23
+ * @returns {Computed<T>} result of derived state
24
+ */
25
+ export const computed = /*#__PURE__*/ <T>(
26
+ fn: (v?: T) => T | Promise<T>,
27
+ memo?: boolean
28
+ ): Computed<T> => {
29
+ memo = memo ?? isAsyncFunction(fn)
30
+ const watchers: Watcher[] = []
31
+ let value: T
32
+ let error: Error | null = null
33
+ let stale = true
34
+
35
+ const mark: Watcher = () => {
36
+ stale = true
37
+ if (memo) notify(watchers)
38
+ }
39
+
40
+ const c: Computed<T> = {
41
+ [Symbol.toStringTag]: TYPE_COMPUTED,
42
+ get: () => {
43
+ if (memo) subscribe(watchers)
44
+ if (!memo || stale) watch(() => {
45
+ const handleOk = (v: T) => {
46
+ value = v
47
+ stale = false
48
+ error = null
49
+ }
50
+ const handleErr = (e: unknown) => {
51
+ error = isError(e)
52
+ ? e
53
+ : new Error(`Computed function failed: ${e}`)
54
+ }
55
+ try {
56
+ const res = fn(value)
57
+ isPromise(res)
58
+ ? res.then(handleOk).catch(handleErr)
59
+ : handleOk(res)
60
+ } catch (e) {
61
+ handleErr(e)
62
+ }
63
+ }, mark)
64
+ if (isError(error)) throw error
65
+ return value
66
+ },
67
+ map: <U>(fn: (value: T) => U): Computed<U> =>
68
+ computed(() => fn(c.get())),
69
+ }
70
+ return c
71
+ }
72
+
73
+ export const isComputed = /*#__PURE__*/ <T>(value: unknown): value is Computed<T> =>
74
+ !!value && typeof value === 'object'
75
+ && (value as { [key in typeof Symbol.toStringTag]: string })[Symbol.toStringTag] === TYPE_COMPUTED
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Define what happens when a reactive state changes
3
+ *
4
+ * @since 0.1.0
5
+ * @param {() => void} fn - callback function to be executed when a state changes
6
+ */
7
+ export declare const effect: (fn: () => void) => void;
package/lib/effect.ts ADDED
@@ -0,0 +1,21 @@
1
+
2
+ import { type Watcher, watch } from "./signal"
3
+
4
+ /* === Exported Function === */
5
+
6
+ /**
7
+ * Define what happens when a reactive state changes
8
+ *
9
+ * @since 0.1.0
10
+ * @param {() => void} fn - callback function to be executed when a state changes
11
+ */
12
+ export const effect = (fn: () => void) => {
13
+ const run: Watcher = () => watch(() => {
14
+ try {
15
+ fn()
16
+ } catch (error) {
17
+ console.error(error)
18
+ }
19
+ }, run)
20
+ run()
21
+ }
@@ -0,0 +1,48 @@
1
+ import { type State } from "./state";
2
+ import { type Computed } from "./computed";
3
+ type Signal<T> = State<T> | Computed<T>;
4
+ type MaybeSignal<T> = State<T> | Computed<T> | T;
5
+ type Watcher = () => void;
6
+ /**
7
+ * Check whether a value is a Signal or not
8
+ *
9
+ * @since 0.9.0
10
+ * @param {any} value - value to check
11
+ * @returns {boolean} - true if value is a Signal, false otherwise
12
+ */
13
+ declare const isSignal: <T>(value: any) => value is Signal<T>;
14
+ /**
15
+ * Convert a value to a Signal if it's not already a Signal
16
+ *
17
+ * @since 0.9.6
18
+ * @param {MaybeSignal<T>} value - value to convert to a Signal
19
+ * @param memo
20
+ * @returns {Signal<T>} - converted Signal
21
+ */
22
+ declare const toSignal: <T>(value: MaybeSignal<T>, memo?: boolean) => Signal<T>;
23
+ /**
24
+ * Add notify function of active watchers to the set of watchers
25
+ *
26
+ * @param {Watcher[]} watchers - set of current watchers
27
+ */
28
+ declare const subscribe: (watchers: Watcher[]) => void;
29
+ /**
30
+ * Notify all subscribers of the state change or add to the pending set if batching is enabled
31
+ *
32
+ * @param {Watcher[]} watchers
33
+ */
34
+ declare const notify: (watchers: Watcher[]) => void;
35
+ /**
36
+ * Run a function in a reactive context
37
+ *
38
+ * @param {() => void} run - function to run the computation or effect
39
+ * @param {Watcher} mark - function to be called when the state changes
40
+ */
41
+ declare const watch: (run: () => void, mark: Watcher) => void;
42
+ /**
43
+ * Batch multiple state changes into a single update
44
+ *
45
+ * @param {() => void} run - function to run the batch of state changes
46
+ */
47
+ declare const batch: (run: () => void) => void;
48
+ export { type Signal, type Watcher, isSignal, toSignal, subscribe, notify, watch, batch };
package/lib/signal.ts ADDED
@@ -0,0 +1,98 @@
1
+ import { type State, isState, state } from "./state"
2
+ import { computed, type Computed, isComputed } from "./computed"
3
+ import { isFunction } from "./util"
4
+
5
+ /* === Types === */
6
+
7
+ type Signal<T> = State<T> | Computed<T>
8
+
9
+ type MaybeSignal<T> = State<T> | Computed<T> | T
10
+
11
+ type Watcher = () => void
12
+
13
+ /* === Internals === */
14
+
15
+ // Currently active watcher
16
+ let active: () => void | undefined
17
+
18
+ // Batching state
19
+ let batching = false
20
+
21
+ // Pending notifications
22
+ const pending: Watcher[] = []
23
+
24
+ /* === Exported Functions === */
25
+
26
+ /**
27
+ * Check whether a value is a Signal or not
28
+ *
29
+ * @since 0.9.0
30
+ * @param {any} value - value to check
31
+ * @returns {boolean} - true if value is a Signal, false otherwise
32
+ */
33
+ const isSignal = /*#__PURE__*/ <T>(value: any): value is Signal<T> =>
34
+ isState(value) || isComputed(value)
35
+
36
+ /**
37
+ * Convert a value to a Signal if it's not already a Signal
38
+ *
39
+ * @since 0.9.6
40
+ * @param {MaybeSignal<T>} value - value to convert to a Signal
41
+ * @param memo
42
+ * @returns {Signal<T>} - converted Signal
43
+ */
44
+ const toSignal = /*#__PURE__*/ <T>(
45
+ value: MaybeSignal<T>,
46
+ memo: boolean = false
47
+ ): Signal<T> =>
48
+ isSignal<T>(value) ? value
49
+ : isFunction(value) ? computed(value, memo)
50
+ : state(value)
51
+
52
+ /**
53
+ * Add notify function of active watchers to the set of watchers
54
+ *
55
+ * @param {Watcher[]} watchers - set of current watchers
56
+ */
57
+ const subscribe = (watchers: Watcher[]) => {
58
+ if (active && !watchers.includes(active)) watchers.push(active)
59
+ }
60
+
61
+ /**
62
+ * Notify all subscribers of the state change or add to the pending set if batching is enabled
63
+ *
64
+ * @param {Watcher[]} watchers
65
+ */
66
+ const notify = (watchers: Watcher[]) =>
67
+ watchers.forEach(n => batching ? pending.push(n) : n())
68
+
69
+ /**
70
+ * Run a function in a reactive context
71
+ *
72
+ * @param {() => void} run - function to run the computation or effect
73
+ * @param {Watcher} mark - function to be called when the state changes
74
+ */
75
+ const watch = (run: () => void, mark: Watcher): void => {
76
+ const prev = active
77
+ active = mark
78
+ run()
79
+ active = prev
80
+ }
81
+
82
+ /**
83
+ * Batch multiple state changes into a single update
84
+ *
85
+ * @param {() => void} run - function to run the batch of state changes
86
+ */
87
+ const batch = (run: () => void): void => {
88
+ batching = true
89
+ run()
90
+ batching = false
91
+ pending.forEach(n => n())
92
+ pending.length = 0
93
+ }
94
+
95
+ export {
96
+ type Signal, type Watcher,
97
+ isSignal, toSignal, subscribe, notify, watch, batch
98
+ }
package/lib/state.d.ts ADDED
@@ -0,0 +1,38 @@
1
+ import { type Computed } from "./computed";
2
+ export declare const UNSET: any;
3
+ /**
4
+ * Define a reactive state
5
+ *
6
+ * @since 0.9.0
7
+ * @class State
8
+ */
9
+ export declare class State<T> {
10
+ private value;
11
+ private watchers;
12
+ constructor(value: T);
13
+ /**
14
+ * Get the current value of the state
15
+ *
16
+ * @method of State<T>
17
+ * @returns {T} - current value of the state
18
+ */
19
+ get(): T;
20
+ /**
21
+ * Set a new value of the state
22
+ *
23
+ * @method of State<T>
24
+ * @param {T | ((v: T) => T)} value
25
+ * @returns {void}
26
+ */
27
+ set(value: T | ((v: T) => T)): void;
28
+ map<U>(fn: (value: T) => U): Computed<U>;
29
+ }
30
+ /**
31
+ * Create a new state signal
32
+ *
33
+ * @static method of State<T>
34
+ * @param {T} value - initial value of the state
35
+ * @returns {State<T>} - new state signal
36
+ */
37
+ export declare const state: <T>(value: T) => State<T>;
38
+ export declare const isState: (value: unknown) => value is State<any>;
package/lib/state.ts ADDED
@@ -0,0 +1,67 @@
1
+ import { isFunction, isInstanceOf } from "./util"
2
+ import { type Watcher, subscribe, notify } from "./signal"
3
+ import { type Computed, computed } from "./computed"
4
+
5
+ /* === Constants === */
6
+
7
+ export const UNSET: any = Symbol()
8
+
9
+ /* === Class State === */
10
+
11
+ /**
12
+ * Define a reactive state
13
+ *
14
+ * @since 0.9.0
15
+ * @class State
16
+ */
17
+ export class State<T> {
18
+ private watchers: Watcher[] = []
19
+
20
+ constructor(private value: T) {}
21
+
22
+ /**
23
+ * Get the current value of the state
24
+ *
25
+ * @method of State<T>
26
+ * @returns {T} - current value of the state
27
+ */
28
+ get(): T {
29
+ subscribe(this.watchers)
30
+ return this.value
31
+ }
32
+
33
+ /**
34
+ * Set a new value of the state
35
+ *
36
+ * @method of State<T>
37
+ * @param {T | ((v: T) => T)} value
38
+ * @returns {void}
39
+ */
40
+ set(value: T | ((v: T) => T)): void {
41
+ if (UNSET !== value) {
42
+ const newValue = isFunction(value) ? value(this.value) : value
43
+ if (Object.is(this.value, newValue)) return
44
+ this.value = newValue
45
+ }
46
+ notify(this.watchers)
47
+
48
+ // Setting to null clears the watchers so the signal can be garbage collected
49
+ if (UNSET === value) this.watchers = []
50
+ }
51
+
52
+ map<U>(fn: (value: T) => U): Computed<U> {
53
+ return computed<U>(() => fn(this.get()))
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Create a new state signal
59
+ *
60
+ * @static method of State<T>
61
+ * @param {T} value - initial value of the state
62
+ * @returns {State<T>} - new state signal
63
+ */
64
+ export const state = /*#__PURE__*/ <T>(value: T): State<T> =>
65
+ new State(value)
66
+
67
+ export const isState = /*#__PURE__*/ isInstanceOf(State)
package/lib/util.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ declare const isFunction: (value: unknown) => value is (...args: any[]) => any;
2
+ declare const isAsyncFunction: (value: unknown) => value is (...args: any[]) => Promise<any> | PromiseLike<any>;
3
+ declare const isInstanceOf: <T>(type: new (...args: any[]) => T) => (value: unknown) => value is T;
4
+ declare const isError: (value: unknown) => value is Error;
5
+ declare const isPromise: (value: unknown) => value is Promise<unknown>;
6
+ export { isFunction, isAsyncFunction, isInstanceOf, isError, isPromise };
package/lib/util.ts ADDED
@@ -0,0 +1,16 @@
1
+ /* === Utility Functions === */
2
+
3
+ const isFunction = /*#__PURE__*/ (value: unknown): value is (...args: any[]) => any =>
4
+ typeof value === 'function'
5
+
6
+ const isAsyncFunction = /*#__PURE__*/ (value: unknown): value is (...args: any[]) => Promise<any> | PromiseLike<any> =>
7
+ isFunction(value) && /^async\s+/.test(value.toString())
8
+
9
+ const isInstanceOf = /*#__PURE__*/ <T>(type: new (...args: any[]) => T) =>
10
+ (value: unknown): value is T =>
11
+ value instanceof type
12
+
13
+ const isError = /*#__PURE__*/ isInstanceOf(Error)
14
+ const isPromise = /*#__PURE__*/ isInstanceOf(Promise)
15
+
16
+ export { isFunction, isAsyncFunction, isInstanceOf, isError, isPromise }
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@zeix/cause-effect",
3
+ "version": "0.9.7",
4
+ "author": "Esther Brunner",
5
+ "main": "index.js",
6
+ "module": "index.ts",
7
+ "devDependencies": {
8
+ "@types/bun": "latest"
9
+ },
10
+ "peerDependencies": {
11
+ "typescript": "^5.6.3"
12
+ },
13
+ "description": "Cause & Effect - reactive state management with signals.",
14
+ "license": "MIT",
15
+ "keywords": [
16
+ "Cause & Effect",
17
+ "Reactivity",
18
+ "Signals",
19
+ "Effects"
20
+ ],
21
+ "publishConfig": {
22
+ "access": "public"
23
+ },
24
+ "scripts": {
25
+ "build": "bun build index.ts --outdir ./ --minify && bunx tsc",
26
+ "test": "bun test"
27
+ },
28
+ "type": "module",
29
+ "types": "index.d.ts"
30
+ }