@liteforge/core 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 SchildW3rk
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,173 @@
1
+ # @liteforge/core
2
+
3
+ Fine-grained reactive primitives for LiteForge.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @liteforge/core
9
+ ```
10
+
11
+ ## Overview
12
+
13
+ `@liteforge/core` provides the reactive foundation for LiteForge applications. It includes signals for reactive state, computed values for derived state, effects for side effects, and batching for performance optimization.
14
+
15
+ ## API
16
+
17
+ ### signal
18
+
19
+ Creates a reactive value that notifies subscribers when it changes.
20
+
21
+ ```ts
22
+ import { signal } from '@liteforge/core'
23
+
24
+ const count = signal(0)
25
+
26
+ // Read the value
27
+ count() // 0
28
+
29
+ // Set a new value
30
+ count.set(5)
31
+
32
+ // Update based on previous value
33
+ count.update(n => n + 1) // 6
34
+
35
+ // Peek without subscribing
36
+ count.peek() // 6
37
+ ```
38
+
39
+ **Options:**
40
+
41
+ | Option | Type | Description |
42
+ |--------|------|-------------|
43
+ | `name` | `string` | Debug name for devtools |
44
+ | `equals` | `(a, b) => boolean` | Custom equality function |
45
+
46
+ ```ts
47
+ const user = signal({ name: 'Alice' }, {
48
+ name: 'currentUser',
49
+ equals: (a, b) => a.name === b.name
50
+ })
51
+ ```
52
+
53
+ ### computed
54
+
55
+ Creates a derived value that automatically tracks dependencies.
56
+
57
+ ```ts
58
+ import { signal, computed } from '@liteforge/core'
59
+
60
+ const firstName = signal('John')
61
+ const lastName = signal('Doe')
62
+
63
+ const fullName = computed(() => `${firstName()} ${lastName()}`)
64
+
65
+ fullName() // "John Doe"
66
+
67
+ firstName.set('Jane')
68
+ fullName() // "Jane Doe"
69
+ ```
70
+
71
+ Computed values are lazy and cached — they only recalculate when dependencies change and only when read.
72
+
73
+ ### effect
74
+
75
+ Runs a function whenever its dependencies change.
76
+
77
+ ```ts
78
+ import { signal, effect } from '@liteforge/core'
79
+
80
+ const count = signal(0)
81
+
82
+ const dispose = effect(() => {
83
+ console.log(`Count is now: ${count()}`)
84
+ })
85
+ // Logs: "Count is now: 0"
86
+
87
+ count.set(1)
88
+ // Logs: "Count is now: 1"
89
+
90
+ // Stop the effect
91
+ dispose()
92
+ ```
93
+
94
+ ### batch
95
+
96
+ Groups multiple signal updates into a single notification cycle.
97
+
98
+ ```ts
99
+ import { signal, effect, batch } from '@liteforge/core'
100
+
101
+ const a = signal(1)
102
+ const b = signal(2)
103
+
104
+ effect(() => {
105
+ console.log(`a=${a()}, b=${b()}`)
106
+ })
107
+ // Logs once: "a=1, b=2"
108
+
109
+ batch(() => {
110
+ a.set(10)
111
+ b.set(20)
112
+ })
113
+ // Logs once: "a=10, b=20"
114
+ ```
115
+
116
+ Without batch, the effect would run twice (once for each signal update).
117
+
118
+ ### onCleanup
119
+
120
+ Registers a cleanup function to run before an effect re-executes or is disposed.
121
+
122
+ ```ts
123
+ import { signal, effect, onCleanup } from '@liteforge/core'
124
+
125
+ const userId = signal(1)
126
+
127
+ effect(() => {
128
+ const id = userId()
129
+ const controller = new AbortController()
130
+
131
+ fetch(`/api/users/${id}`, { signal: controller.signal })
132
+ .then(r => r.json())
133
+ .then(console.log)
134
+
135
+ onCleanup(() => {
136
+ controller.abort()
137
+ })
138
+ })
139
+ ```
140
+
141
+ ## Types
142
+
143
+ ```ts
144
+ import type {
145
+ Signal,
146
+ ReadonlySignal,
147
+ SignalOptions,
148
+ EffectFn,
149
+ DisposeFn,
150
+ EffectOptions,
151
+ ComputeFn,
152
+ ComputedOptions
153
+ } from '@liteforge/core'
154
+ ```
155
+
156
+ ## Debug Utilities
157
+
158
+ For integration with devtools:
159
+
160
+ ```ts
161
+ import { enableDebug, disableDebug, createDebugBus } from '@liteforge/core'
162
+
163
+ enableDebug()
164
+
165
+ const bus = createDebugBus()
166
+ bus.on('signal:update', (payload) => {
167
+ console.log(`Signal ${payload.name} changed to ${payload.value}`)
168
+ })
169
+ ```
170
+
171
+ ## License
172
+
173
+ MIT
@@ -0,0 +1,32 @@
1
+ /**
2
+ * LiteForge Batch
3
+ *
4
+ * Batch multiple signal updates together to avoid redundant effect executions.
5
+ */
6
+ /**
7
+ * Batch multiple signal updates together.
8
+ *
9
+ * Effects will only be notified once after the batch completes,
10
+ * even if multiple signals are updated. Supports nested batches.
11
+ *
12
+ * @param fn - Function containing signal updates to batch
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * const firstName = signal('John');
17
+ * const lastName = signal('Doe');
18
+ *
19
+ * effect(() => {
20
+ * console.log(firstName(), lastName());
21
+ * });
22
+ * // logs: "John Doe"
23
+ *
24
+ * batch(() => {
25
+ * firstName.set('Jane');
26
+ * lastName.set('Smith');
27
+ * });
28
+ * // logs: "Jane Smith" (only once, not twice)
29
+ * ```
30
+ */
31
+ export declare function batch(fn: () => void): void;
32
+ //# sourceMappingURL=batch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batch.d.ts","sourceRoot":"","sources":["../src/batch.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI,CAO1C"}
package/dist/batch.js ADDED
@@ -0,0 +1,41 @@
1
+ /**
2
+ * LiteForge Batch
3
+ *
4
+ * Batch multiple signal updates together to avoid redundant effect executions.
5
+ */
6
+ import { startBatch, endBatch } from './internals.js';
7
+ /**
8
+ * Batch multiple signal updates together.
9
+ *
10
+ * Effects will only be notified once after the batch completes,
11
+ * even if multiple signals are updated. Supports nested batches.
12
+ *
13
+ * @param fn - Function containing signal updates to batch
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * const firstName = signal('John');
18
+ * const lastName = signal('Doe');
19
+ *
20
+ * effect(() => {
21
+ * console.log(firstName(), lastName());
22
+ * });
23
+ * // logs: "John Doe"
24
+ *
25
+ * batch(() => {
26
+ * firstName.set('Jane');
27
+ * lastName.set('Smith');
28
+ * });
29
+ * // logs: "Jane Smith" (only once, not twice)
30
+ * ```
31
+ */
32
+ export function batch(fn) {
33
+ startBatch();
34
+ try {
35
+ fn();
36
+ }
37
+ finally {
38
+ endBatch();
39
+ }
40
+ }
41
+ //# sourceMappingURL=batch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batch.js","sourceRoot":"","sources":["../src/batch.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAEtD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,UAAU,KAAK,CAAC,EAAc;IAClC,UAAU,EAAE,CAAC;IACb,IAAI,CAAC;QACH,EAAE,EAAE,CAAC;IACP,CAAC;YAAS,CAAC;QACT,QAAQ,EAAE,CAAC;IACb,CAAC;AACH,CAAC"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * LiteForge Cleanup
3
+ *
4
+ * Register cleanup functions to run before an effect re-executes
5
+ * or when the effect is disposed.
6
+ */
7
+ /**
8
+ * Register a cleanup function for the current effect.
9
+ *
10
+ * The cleanup function will be called:
11
+ * - Before the effect re-runs (when dependencies change)
12
+ * - When the effect is disposed
13
+ *
14
+ * @param fn - The cleanup function to register
15
+ * @throws Error if called outside of an effect
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * effect(() => {
20
+ * const handler = () => console.log(count());
21
+ * window.addEventListener('resize', handler);
22
+ *
23
+ * onCleanup(() => {
24
+ * window.removeEventListener('resize', handler);
25
+ * });
26
+ * });
27
+ * ```
28
+ */
29
+ export declare function onCleanup(fn: () => void): void;
30
+ //# sourceMappingURL=cleanup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cleanup.d.ts","sourceRoot":"","sources":["../src/cleanup.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI,CAE9C"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * LiteForge Cleanup
3
+ *
4
+ * Register cleanup functions to run before an effect re-executes
5
+ * or when the effect is disposed.
6
+ */
7
+ import { registerCleanup } from './internals.js';
8
+ /**
9
+ * Register a cleanup function for the current effect.
10
+ *
11
+ * The cleanup function will be called:
12
+ * - Before the effect re-runs (when dependencies change)
13
+ * - When the effect is disposed
14
+ *
15
+ * @param fn - The cleanup function to register
16
+ * @throws Error if called outside of an effect
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * effect(() => {
21
+ * const handler = () => console.log(count());
22
+ * window.addEventListener('resize', handler);
23
+ *
24
+ * onCleanup(() => {
25
+ * window.removeEventListener('resize', handler);
26
+ * });
27
+ * });
28
+ * ```
29
+ */
30
+ export function onCleanup(fn) {
31
+ registerCleanup(fn);
32
+ }
33
+ //# sourceMappingURL=cleanup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cleanup.js","sourceRoot":"","sources":["../src/cleanup.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,SAAS,CAAC,EAAc;IACtC,eAAe,CAAC,EAAE,CAAC,CAAC;AACtB,CAAC"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * LiteForge Computed
3
+ *
4
+ * A Computed is a read-only signal derived from other signals.
5
+ * It lazily evaluates and caches its value until dependencies change.
6
+ */
7
+ import type { ReadonlySignal } from './signal.js';
8
+ /** Function that computes the derived value */
9
+ export type ComputeFn<T> = () => T;
10
+ /** Options for creating a computed value. */
11
+ export interface ComputedOptions {
12
+ /** Debug label for DevTools. If not provided, an auto-generated ID is used. */
13
+ label?: string;
14
+ /**
15
+ * Mark computed as internal (DevTools-owned).
16
+ * Internal computeds don't emit debug events to avoid infinite loops.
17
+ * @internal
18
+ */
19
+ __internal?: boolean;
20
+ }
21
+ /**
22
+ * Create a computed (derived) signal.
23
+ *
24
+ * The computation only runs when the value is read AND dependencies have changed.
25
+ * The result is cached until a dependency changes.
26
+ *
27
+ * @param fn - Function that computes the derived value
28
+ * @param options - Optional configuration including debug label
29
+ * @returns A read-only signal
30
+ *
31
+ * @example
32
+ * ```ts
33
+ * const count = signal(0);
34
+ * const doubled = computed(() => count() * 2);
35
+ *
36
+ * doubled(); // → 0 (computed on first read)
37
+ * count.set(5);
38
+ * doubled(); // → 10 (recomputed because count changed)
39
+ * doubled(); // → 10 (cached, not recomputed)
40
+ *
41
+ * // With debug label
42
+ * const tripled = computed(() => count() * 3, { label: 'tripled' });
43
+ * ```
44
+ */
45
+ export declare function computed<T>(fn: ComputeFn<T>, options?: ComputedOptions): ReadonlySignal<T>;
46
+ //# sourceMappingURL=computed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"computed.d.ts","sourceRoot":"","sources":["../src/computed.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAUlD,+CAA+C;AAC/C,MAAM,MAAM,SAAS,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC;AAEnC,6CAA6C;AAC7C,MAAM,WAAW,eAAe;IAC9B,+EAA+E;IAC/E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAMD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EACxB,EAAE,EAAE,SAAS,CAAC,CAAC,CAAC,EAChB,OAAO,CAAC,EAAE,eAAe,GACxB,cAAc,CAAC,CAAC,CAAC,CA0HnB"}
@@ -0,0 +1,144 @@
1
+ /**
2
+ * LiteForge Computed
3
+ *
4
+ * A Computed is a read-only signal derived from other signals.
5
+ * It lazily evaluates and caches its value until dependencies change.
6
+ */
7
+ import { observerStack, getCurrentObserver, notifySubscribers, } from './internals.js';
8
+ import { generateDebugId, emitComputedRecalc, } from './debug.js';
9
+ // ============================================================================
10
+ // Implementation
11
+ // ============================================================================
12
+ /**
13
+ * Create a computed (derived) signal.
14
+ *
15
+ * The computation only runs when the value is read AND dependencies have changed.
16
+ * The result is cached until a dependency changes.
17
+ *
18
+ * @param fn - Function that computes the derived value
19
+ * @param options - Optional configuration including debug label
20
+ * @returns A read-only signal
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * const count = signal(0);
25
+ * const doubled = computed(() => count() * 2);
26
+ *
27
+ * doubled(); // → 0 (computed on first read)
28
+ * count.set(5);
29
+ * doubled(); // → 10 (recomputed because count changed)
30
+ * doubled(); // → 10 (cached, not recomputed)
31
+ *
32
+ * // With debug label
33
+ * const tripled = computed(() => count() * 3, { label: 'tripled' });
34
+ * ```
35
+ */
36
+ export function computed(fn, options) {
37
+ let value;
38
+ let dirty = true; // Start dirty so first read computes
39
+ // Debug info
40
+ const debugLabel = options?.label;
41
+ const debugId = debugLabel ?? generateDebugId('computed');
42
+ const isInternal = options?.__internal === true;
43
+ // Subscribers to this computed (other effects/computeds that read us)
44
+ const subscribers = new Set();
45
+ // Map from execute function to tagged subscriber (for efficient removal)
46
+ const subscriberMap = new Map();
47
+ // Track our own dependencies
48
+ const observer = {
49
+ execute: markDirty,
50
+ cleanup: () => { }, // Computeds don't need cleanup
51
+ dependencies: new Set(),
52
+ isEffect: false, // Mark as computed for synchronous dirty propagation
53
+ };
54
+ /**
55
+ * Mark as dirty and notify downstream subscribers.
56
+ * Called when any upstream dependency changes.
57
+ */
58
+ function markDirty() {
59
+ if (!dirty) {
60
+ dirty = true;
61
+ // Notify our subscribers that we might have changed
62
+ // Computeds are notified synchronously, effects are scheduled
63
+ notifySubscribers(subscribers);
64
+ }
65
+ }
66
+ /**
67
+ * Recompute the value if dirty.
68
+ */
69
+ function recompute() {
70
+ // Clear old dependencies
71
+ for (const depSet of observer.dependencies) {
72
+ for (const sub of depSet) {
73
+ if (sub.fn === observer.execute) {
74
+ depSet.delete(sub);
75
+ break;
76
+ }
77
+ }
78
+ }
79
+ observer.dependencies.clear();
80
+ // Track new dependencies during computation
81
+ observerStack.push(observer);
82
+ const startTime = performance.now();
83
+ try {
84
+ value = fn();
85
+ }
86
+ finally {
87
+ observerStack.pop();
88
+ }
89
+ const duration = performance.now() - startTime;
90
+ // Emit recalc event (zero cost if debug not enabled, skip for internal)
91
+ if (!isInternal) {
92
+ emitComputedRecalc(debugId, debugLabel, value, duration);
93
+ }
94
+ dirty = false;
95
+ }
96
+ /**
97
+ * Read the computed value.
98
+ */
99
+ function read() {
100
+ // Subscribe the current observer to this computed
101
+ const currentObserver = getCurrentObserver();
102
+ if (currentObserver) {
103
+ let taggedSub = subscriberMap.get(currentObserver.execute);
104
+ if (!taggedSub) {
105
+ taggedSub = {
106
+ fn: currentObserver.execute,
107
+ isEffect: currentObserver.isEffect,
108
+ };
109
+ subscriberMap.set(currentObserver.execute, taggedSub);
110
+ }
111
+ subscribers.add(taggedSub);
112
+ currentObserver.dependencies.add(subscribers);
113
+ }
114
+ // Recompute if necessary
115
+ if (dirty) {
116
+ recompute();
117
+ }
118
+ return value;
119
+ }
120
+ /**
121
+ * Peek at the value without subscribing.
122
+ */
123
+ function peek() {
124
+ if (dirty) {
125
+ recompute();
126
+ }
127
+ return value;
128
+ }
129
+ // Attach peek method
130
+ read.peek = peek;
131
+ // Attach debug info (read-only)
132
+ Object.defineProperty(read, '__debugId', {
133
+ value: debugId,
134
+ writable: false,
135
+ enumerable: false,
136
+ });
137
+ Object.defineProperty(read, '__debugLabel', {
138
+ value: debugLabel,
139
+ writable: false,
140
+ enumerable: false,
141
+ });
142
+ return read;
143
+ }
144
+ //# sourceMappingURL=computed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"computed.js","sourceRoot":"","sources":["../src/computed.ts"],"names":[],"mappings":"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"}