@minejs/signals 0.0.2 → 0.0.4

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
@@ -8,8 +8,8 @@
8
8
  </div>
9
9
 
10
10
  <div align="center">
11
- <img src="https://img.shields.io/badge/v-0.0.2-black"/>
12
- <img src="https://img.shields.io/badge/🔥-@minjs-black"/>
11
+ <img src="https://img.shields.io/badge/v-0.0.4-black"/>
12
+ <img src="https://img.shields.io/badge/🔥-@minejs-black"/>
13
13
  <img src="https://img.shields.io/badge/zero-dependencies-black" alt="Test Coverage" />
14
14
  <br>
15
15
  <img src="https://img.shields.io/badge/coverage-99.14%25-brightgreen" alt="Test Coverage" />
@@ -1,2 +1,2 @@
1
- 'use strict';Object.defineProperty(exports,'__esModule',{value:true});var i=null,s=null,c=0,d=new Set,a=new Set;function v(n){let e=n,t=new Set;function r(){return i&&t.add(i),e}function o(u){Object.is(e,u)||(e=u,c>0?t.forEach(T=>d.add(T)):t.forEach(T=>T()));}function p(u){o(u(e));}function l(){return e}function x(u){return t.add(u),()=>t.delete(u)}let f=r;return f.set=o,f.update=p,f.peek=l,f.subscribe=x,f}function y(n){let e,t=false,r=()=>{if(t)return;e&&(e(),e=void 0);let p=i;i=r;try{let l=n();typeof l=="function"&&(e=l);}finally{i=p;}};r();let o=()=>{t||(t=true,e&&e());};return s&&s.push(o),o}function b(n){let e=v(void 0);y(()=>{e.set(n());});let t=e;return Object.defineProperty(t,"isComputed",{value:true,writable:false}),t}function g(n){c++;try{return n()}finally{if(c--,c===0){c++,a.clear();try{for(;d.size>0;){let e=Array.from(d);d.clear(),e.forEach(t=>{a.has(t)||(a.add(t),t());});}}finally{c--,a.clear();}}}}function h(n){let e=i;i=null;try{return n()}finally{i=e;}}function E(n,e){let t=n.peek();return y(()=>{let r=n(),o=h(()=>e(r,t));return t=r,o})}function C(n){let e={};for(let t in n)e[t]=v(n[t]);return e}function S(n){let e,t=false;return ()=>{if(t)return e;let r=n();return e=r,t=true,r}}function k(n){let e=[],t=s;s=e;try{return n(()=>{e.forEach(o=>o()),e.length=0,s=t;})}finally{s=t;}}var w={getCurrentEffect(){return i},getBatchDepth(){return c},getBatchedEffectsCount(){return d.size}};function m(n){return typeof n=="function"&&"set"in n&&"update"in n&&"peek"in n}function D(n){return m(n)&&"isComputed"in n}var R={signal:v,effect:y,computed:b,batch:g,untrack:h,on:E,store:C,memo:S,root:k,isSignal:m,isComputed:D,dev:w};exports.batch=g;exports.computed=b;exports.default=R;exports.dev=w;exports.effect=y;exports.isComputed=D;exports.isSignal=m;exports.memo=S;exports.on=E;exports.root=k;exports.signal=v;exports.store=C;exports.untrack=h;//# sourceMappingURL=main.cjs.map
2
- //# sourceMappingURL=main.cjs.map
1
+ 'use strict';Object.defineProperty(exports,'__esModule',{value:true});var i=null,s=null,c=0,d=new Set,a=new Set;function v(n){let e=n,t=new Set;function r(){return i&&t.add(i),e}function o(u){Object.is(e,u)||(e=u,c>0?t.forEach(T=>d.add(T)):t.forEach(T=>T()));}function p(u){o(u(e));}function l(){return e}function x(u){return t.add(u),()=>t.delete(u)}let f=r;return f.set=o,f.update=p,f.peek=l,f.subscribe=x,f}function y(n){let e,t=false,r=()=>{if(t)return;e&&(e(),e=void 0);let p=i;i=r;try{let l=n();typeof l=="function"&&(e=l);}finally{i=p;}};r();let o=()=>{t||(t=true,e&&e());};return s&&s.push(o),o}function b(n){let e=v(void 0);y(()=>{e.set(n());});let t=e;return Object.defineProperty(t,"isComputed",{value:true,writable:false}),t}function g(n){c++;try{return n()}finally{if(c--,c===0){c++,a.clear();try{for(;d.size>0;){let e=Array.from(d);d.clear(),e.forEach(t=>{a.has(t)||(a.add(t),t());});}}finally{c--,a.clear();}}}}function h(n){let e=i;i=null;try{return n()}finally{i=e;}}function E(n,e){let t=n.peek();return y(()=>{let r=n(),o=h(()=>e(r,t));return t=r,o})}function C(n){let e={};for(let t in n)e[t]=v(n[t]);return e}function S(n){let e,t=false;return ()=>{if(t)return e;let r=n();return e=r,t=true,r}}function k(n){let e=[],t=s;s=e;try{return n(()=>{e.forEach(o=>o()),e.length=0,s=t;})}finally{s=t;}}var w={getCurrentEffect(){return i},getBatchDepth(){return c},getBatchedEffectsCount(){return d.size}};function m(n){return typeof n=="function"&&"set"in n&&"update"in n&&"peek"in n}function D(n){return m(n)&&"isComputed"in n}var R={signal:v,effect:y,computed:b,batch:g,untrack:h,on:E,store:C,memo:S,root:k,isSignal:m,isComputed:D,dev:w};exports.batch=g;exports.computed=b;exports.default=R;exports.dev=w;exports.effect=y;exports.isComputed=D;exports.isSignal=m;exports.memo=S;exports.on=E;exports.root=k;exports.signal=v;exports.store=C;exports.untrack=h;//# sourceMappingURL=index.cjs.map
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"names":["currentEffect","currentRoot","batchDepth","batchedEffects","flushedEffects","signal","initialValue","value","subscribers","read","write","newValue","fn","update","peek","subscribe","sig","effect","cleanup","isDisposed","execute","prevEffect","result","disposer","computed","batch","effects","untrack","on","prevValue","store","initialState","key","memo","cachedValue","hasCachedValue","root","disposers","prevRoot","d","dev","isSignal","isComputed","index_default"],"mappings":"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","file":"index.cjs","sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\r\n// src/index.ts\r\n//\r\n// Made with ❤️ by Maysara.\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ PACK ════════════════════════════════════════╗\r\n\r\n import { Signal, EffectCleanup, Computed } from './types';\r\n export type * from './types';\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ INIT ════════════════════════════════════════╗\r\n\r\n let currentEffect : (() => void) | null = null;\r\n let currentRoot : (() => void)[] | null = null;\r\n let batchDepth : number = 0;\r\n const batchedEffects : Set<() => void> = new Set<() => void>();\r\n const flushedEffects : Set<() => void> = new Set<() => void>();\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ CORE ════════════════════════════════════════╗\r\n\r\n /**\r\n * Creates a reactive signal that can be read, written, and subscribed to.\r\n * @template T - The type of value stored in the signal\r\n * @param {T} initialValue - The initial value of the signal\r\n * @returns {Signal<T>} A signal object with read, set, update, peek, and subscribe methods\r\n * @example\r\n * const count = signal(0);\r\n * console.log(count()); // 0\r\n * count.set(5); // Update value\r\n */\r\n export function signal<T>(initialValue: T): Signal<T> {\r\n let value = initialValue;\r\n const subscribers = new Set<() => void>();\r\n\r\n function read(): T {\r\n // Track dependency if inside effect\r\n if (currentEffect) {\r\n subscribers.add(currentEffect);\r\n }\r\n return value;\r\n }\r\n\r\n function write(newValue: T): void {\r\n // Only update if value actually changed\r\n if (Object.is(value, newValue)) return;\r\n\r\n value = newValue;\r\n\r\n // Notify all subscribers\r\n if (batchDepth > 0) {\r\n // Batch mode: collect effects\r\n subscribers.forEach(fn => batchedEffects.add(fn));\r\n } else {\r\n // Immediate mode: run effects now\r\n subscribers.forEach(fn => fn());\r\n }\r\n }\r\n\r\n function update(fn: (prev: T) => T): void {\r\n write(fn(value));\r\n }\r\n\r\n function peek(): T {\r\n // Read without tracking\r\n return value;\r\n }\r\n\r\n function subscribe(fn: () => void): () => void {\r\n subscribers.add(fn);\r\n return () => subscribers.delete(fn);\r\n }\r\n\r\n // Create signal function with methods\r\n const sig = read as Signal<T>;\r\n sig.set = write;\r\n sig.update = update;\r\n sig.peek = peek;\r\n sig.subscribe = subscribe;\r\n\r\n return sig;\r\n }\r\n\r\n /**\r\n * Automatically runs a function when its signal dependencies change.\r\n * @param {() => EffectCleanup} fn - The effect function to run. Can optionally return a cleanup function.\r\n * @returns {() => void} A dispose function to stop the effect and clean up\r\n * @example\r\n * const count = signal(0);\r\n * effect(() => {\r\n * console.log('Count:', count());\r\n * return () => console.log('Cleaning up');\r\n * });\r\n */\r\n export function effect(fn: () => EffectCleanup): () => void {\r\n let cleanup: (() => void) | undefined;\r\n let isDisposed = false;\r\n\r\n const execute = () => {\r\n if (isDisposed) return;\r\n\r\n // Run cleanup from previous execution\r\n if (cleanup) {\r\n cleanup();\r\n cleanup = undefined;\r\n }\r\n\r\n // Set as current effect for dependency tracking\r\n const prevEffect = currentEffect;\r\n currentEffect = execute;\r\n\r\n try {\r\n // Run the effect function\r\n const result = fn();\r\n\r\n // Store cleanup if returned\r\n if (typeof result === 'function') {\r\n cleanup = result;\r\n }\r\n } finally {\r\n // Restore previous effect\r\n currentEffect = prevEffect;\r\n }\r\n };\r\n\r\n // Run immediately\r\n execute();\r\n\r\n // Create dispose function\r\n const disposer = () => {\r\n if (isDisposed) return;\r\n isDisposed = true;\r\n if (cleanup) cleanup();\r\n };\r\n\r\n // Register with current root if one exists\r\n if (currentRoot) {\r\n currentRoot.push(disposer);\r\n }\r\n\r\n // Return dispose function\r\n return disposer;\r\n }\r\n\r\n /**\r\n * Creates a computed signal that automatically updates when its dependencies change.\r\n * The computation result is cached and only recomputed when dependencies change.\r\n * @template T - The type of value computed\r\n * @param {() => T} fn - The computation function\r\n * @returns {Computed<T>} A read-only computed signal\r\n * @example\r\n * const count = signal(5);\r\n * const doubled = computed(() => count() * 2);\r\n * console.log(doubled()); // 10\r\n */\r\n export function computed<T>(fn: () => T): Computed<T> {\r\n const sig = signal<T>(undefined as T);\r\n\r\n // Create effect that updates the signal\r\n effect(() => {\r\n sig.set(fn());\r\n });\r\n\r\n // Mark as computed\r\n const computed = sig as Computed<T>;\r\n Object.defineProperty(computed, 'isComputed', {\r\n value: true,\r\n writable: false\r\n });\r\n\r\n return computed;\r\n }\r\n\r\n /**\r\n * Groups multiple signal updates together, deferring effect execution until all updates complete.\r\n * This improves performance by preventing cascading effect runs.\r\n * @template T - The return type of the function\r\n * @param {() => T} fn - A function that performs multiple signal updates\r\n * @returns {T} The return value of the function\r\n * @example\r\n * const a = signal(1);\r\n * const b = signal(2);\r\n * batch(() => {\r\n * a.set(10);\r\n * b.set(20);\r\n * }); // Effects only run once\r\n */\r\n export function batch<T>(fn: () => T): T {\r\n batchDepth++;\r\n\r\n try {\r\n return fn();\r\n } finally {\r\n batchDepth--;\r\n\r\n // If we're back at depth 0, flush batched effects\r\n if (batchDepth === 0) {\r\n // Keep batch mode active while flushing to prevent cascading effects\r\n batchDepth++;\r\n flushedEffects.clear();\r\n try {\r\n // Keep running effects until no more are queued\r\n while (batchedEffects.size > 0) {\r\n const effects = Array.from(batchedEffects);\r\n batchedEffects.clear();\r\n effects.forEach(fn => {\r\n // Only run if we haven't run it in this batch\r\n if (!flushedEffects.has(fn)) {\r\n flushedEffects.add(fn);\r\n fn();\r\n }\r\n });\r\n }\r\n } finally {\r\n batchDepth--;\r\n flushedEffects.clear();\r\n }\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Reads signals without creating dependencies on them.\r\n * Useful for accessing signal values without triggering effect re-runs.\r\n * @template T - The return type of the function\r\n * @param {() => T} fn - A function that accesses signals\r\n * @returns {T} The return value of the function\r\n * @example\r\n * const count = signal(0);\r\n * effect(() => {\r\n * const value = untrack(() => count()); // Won't trigger re-run\r\n * console.log(value);\r\n * });\r\n */\r\n export function untrack<T>(fn: () => T): T {\r\n const prevEffect = currentEffect;\r\n currentEffect = null;\r\n\r\n try {\r\n return fn();\r\n } finally {\r\n currentEffect = prevEffect;\r\n }\r\n }\r\n\r\n /**\r\n * Runs an effect only when a specific signal changes, providing both new and previous values.\r\n * @template T - The type of the signal\r\n * @param {Signal<T>} sig - The signal to watch\r\n * @param {(value: T, prevValue: T) => EffectCleanup} fn - Effect function called with new and previous values\r\n * @returns {() => void} A dispose function to stop watching\r\n * @example\r\n * const count = signal(0);\r\n * on(count, (newVal, oldVal) => {\r\n * console.log(`Changed from ${oldVal} to ${newVal}`);\r\n * });\r\n */\r\n export function on<T>(\r\n sig: Signal<T>,\r\n fn: (value: T, prevValue: T) => EffectCleanup\r\n ): () => void {\r\n let prevValue = sig.peek();\r\n\r\n return effect(() => {\r\n // Read the signal to create dependency\r\n const value = sig();\r\n\r\n // Run callback without tracking new dependencies\r\n const cleanup = untrack(() => fn(value, prevValue));\r\n prevValue = value;\r\n return cleanup;\r\n });\r\n }\r\n\r\n /**\r\n * Creates a store object where each property is a signal.\r\n * Provides a convenient way to manage multiple related reactive values.\r\n * @template T - The type of the initial state object\r\n * @param {T} initialState - An object with initial values\r\n * @returns {{ [K in keyof T]: Signal<T[K]> }} An object with signals for each property\r\n * @example\r\n * const state = store({ count: 0, name: 'John' });\r\n * console.log(state.count()); // 0\r\n * state.name.set('Jane');\r\n */\r\n export function store<T extends Record<string, any>>(\r\n initialState: T\r\n ): { [K in keyof T]: Signal<T[K]> } {\r\n const store = {} as any;\r\n\r\n for (const key in initialState) {\r\n store[key] = signal(initialState[key]);\r\n }\r\n\r\n return store;\r\n }\r\n\r\n /**\r\n * Memoizes the result of an expensive computation, caching it indefinitely.\r\n * Unlike computed, this doesn't depend on reactive signals.\r\n * @template T - The type of the memoized value\r\n * @param {() => T} fn - A function that performs the computation\r\n * @returns {() => T} A function that returns the cached result\r\n * @example\r\n * const expensiveCalc = memo(() => {\r\n * return Array.from({ length: 1000 }).map(expensiveOp);\r\n * });\r\n * const result = expensiveCalc(); // Computed only once\r\n */\r\n export function memo<T>(fn: () => T): () => T {\r\n let cachedValue: T | undefined;\r\n let hasCachedValue = false;\r\n\r\n return () => {\r\n if (hasCachedValue) {\r\n return cachedValue as T;\r\n }\r\n\r\n // Compute the value\r\n const value = fn();\r\n\r\n // Cache it\r\n cachedValue = value;\r\n hasCachedValue = true;\r\n\r\n return value;\r\n };\r\n }\r\n\r\n /**\r\n * Creates a root scope for managing effect and computed signal lifecycles.\r\n * All effects and disposers created within the function are collected and can be cleaned up together.\r\n * @template T - The return type of the function\r\n * @param {(dispose: () => void) => T} fn - A function that receives a dispose function\r\n * @returns {T} The return value of the function\r\n * @example\r\n * const dispose = root((dispose) => {\r\n * effect(() => console.log('Running'));\r\n * return 42;\r\n * });\r\n * dispose(); // Cleans up all effects created in the root\r\n */\r\n export function root<T>(fn: (dispose: () => void) => T): T {\r\n const disposers: (() => void)[] = [];\r\n const prevRoot = currentRoot;\r\n currentRoot = disposers;\r\n\r\n try {\r\n const dispose = () => {\r\n disposers.forEach(d => d());\r\n disposers.length = 0;\r\n currentRoot = prevRoot;\r\n };\r\n\r\n return fn(dispose);\r\n } finally {\r\n currentRoot = prevRoot;\r\n }\r\n }\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ HELP ════════════════════════════════════════╗\r\n\r\n /**\r\n * Development utilities for debugging signal reactivity\r\n */\r\n export const dev = {\r\n /**\r\n * Returns the currently executing effect, or null if no effect is running\r\n * @returns {(() => void) | null} The current effect function or null\r\n */\r\n getCurrentEffect(): (() => void) | null {\r\n return currentEffect;\r\n },\r\n\r\n /**\r\n * Returns the current batch depth (for debugging nested batch calls)\r\n * @returns {number} The current batch nesting level\r\n */\r\n getBatchDepth(): number {\r\n return batchDepth;\r\n },\r\n\r\n /**\r\n * Returns the count of effects currently pending in the batch queue\r\n * @returns {number} The number of batched effects waiting to run\r\n */\r\n getBatchedEffectsCount(): number {\r\n return batchedEffects.size;\r\n }\r\n };\r\n\r\n /**\r\n * Type guard to check if a value is a signal\r\n * @template T - The type of value the signal contains\r\n * @param {any} value - The value to check\r\n * @returns {boolean} True if the value is a signal\r\n * @example\r\n * if (isSignal(myValue)) {\r\n * console.log(myValue());\r\n * }\r\n */\r\n export function isSignal<T>(value: any): value is Signal<T> {\r\n return (\r\n typeof value === 'function' &&\r\n 'set' in value &&\r\n 'update' in value &&\r\n 'peek' in value\r\n );\r\n }\r\n\r\n /**\r\n * Type guard to check if a value is a computed signal\r\n * @template T - The type of value the computed signal contains\r\n * @param {any} value - The value to check\r\n * @returns {boolean} True if the value is a computed signal\r\n * @example\r\n * if (isComputed(myValue)) {\r\n * console.log('This is a computed signal');\r\n * }\r\n */\r\n export function isComputed<T>(value: any): value is Computed<T> {\r\n return isSignal(value) && 'isComputed' in value;\r\n }\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ ════ ════════════════════════════════════════╗\r\n\r\n export default {\r\n signal,\r\n effect,\r\n computed,\r\n batch,\r\n untrack,\r\n on,\r\n store,\r\n memo,\r\n root,\r\n isSignal,\r\n isComputed,\r\n dev\r\n };\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n"]}
@@ -1,63 +1,49 @@
1
- // src/types.d.ts
2
- //
3
- // Made with ❤️ by Maysara.
4
-
5
-
6
-
7
- // ╔════════════════════════════════════════ TYPE ════════════════════════════════════════╗
8
-
9
- /**
10
- * Represents a reactive signal that holds a value and notifies dependents when changed.
11
- * @template T - The type of value stored in the signal
12
- */
13
- interface Signal<T> {
14
- /**
15
- * Reads the current signal value and establishes a dependency if inside an effect.
16
- * @returns {T} The current value
17
- */
18
- (): T;
19
-
20
- /**
21
- * Sets the signal to a new value and notifies all subscribers.
22
- * @param {T} value - The new value to set
23
- */
24
-
25
- set(value: T): void;
26
- /**
27
- * Updates the signal by applying a function to its current value.
28
- * @param {(prev: T) => T} fn - Function that receives current value and returns new value
29
- */
30
-
31
- update(fn: (prev: T) => T): void;
32
- /**
33
- * Reads the signal value without creating a dependency relationship.
34
- * @returns {T} The current value
35
- */
36
-
37
- peek(): T;
38
- /**
39
- * Subscribes to signal changes.
40
- * @param {() => void} fn - Callback function to execute when signal changes
41
- * @returns {() => void} Unsubscribe function
42
- */
43
- subscribe(fn: () => void): () => void;
44
- }
45
-
46
- /**
47
- * Represents a computed (memoized) signal derived from other signals.
48
- * Automatically updates when dependencies change and is read-only.
49
- * @template T - The type of value computed
50
- */
51
- interface Computed<T> extends Signal<T> {
52
- /** Marks this signal as computed for type checking purposes */
53
- readonly isComputed: true;
54
- }
55
-
56
- /**
57
- * The return type of an effect function.
58
- * Can be void or a cleanup function that runs when the effect is disposed.
59
- */
60
- type EffectCleanup = void | (() => void);
1
+ /**
2
+ * Represents a reactive signal that holds a value and notifies dependents when changed.
3
+ * @template T - The type of value stored in the signal
4
+ */
5
+ interface Signal<T> {
6
+ /**
7
+ * Reads the current signal value and establishes a dependency if inside an effect.
8
+ * @returns {T} The current value
9
+ */
10
+ (): T;
11
+ /**
12
+ * Sets the signal to a new value and notifies all subscribers.
13
+ * @param {T} value - The new value to set
14
+ */
15
+ set(value: T): void;
16
+ /**
17
+ * Updates the signal by applying a function to its current value.
18
+ * @param {(prev: T) => T} fn - Function that receives current value and returns new value
19
+ */
20
+ update(fn: (prev: T) => T): void;
21
+ /**
22
+ * Reads the signal value without creating a dependency relationship.
23
+ * @returns {T} The current value
24
+ */
25
+ peek(): T;
26
+ /**
27
+ * Subscribes to signal changes.
28
+ * @param {() => void} fn - Callback function to execute when signal changes
29
+ * @returns {() => void} Unsubscribe function
30
+ */
31
+ subscribe(fn: () => void): () => void;
32
+ }
33
+ /**
34
+ * Represents a computed (memoized) signal derived from other signals.
35
+ * Automatically updates when dependencies change and is read-only.
36
+ * @template T - The type of value computed
37
+ */
38
+ interface Computed<T> extends Signal<T> {
39
+ /** Marks this signal as computed for type checking purposes */
40
+ readonly isComputed: true;
41
+ }
42
+ /**
43
+ * The return type of an effect function.
44
+ * Can be void or a cleanup function that runs when the effect is disposed.
45
+ */
46
+ type EffectCleanup = void | (() => void);
61
47
 
62
48
  /**
63
49
  * Creates a reactive signal that can be read, written, and subscribed to.
@@ -1,63 +1,49 @@
1
- // src/types.d.ts
2
- //
3
- // Made with ❤️ by Maysara.
4
-
5
-
6
-
7
- // ╔════════════════════════════════════════ TYPE ════════════════════════════════════════╗
8
-
9
- /**
10
- * Represents a reactive signal that holds a value and notifies dependents when changed.
11
- * @template T - The type of value stored in the signal
12
- */
13
- interface Signal<T> {
14
- /**
15
- * Reads the current signal value and establishes a dependency if inside an effect.
16
- * @returns {T} The current value
17
- */
18
- (): T;
19
-
20
- /**
21
- * Sets the signal to a new value and notifies all subscribers.
22
- * @param {T} value - The new value to set
23
- */
24
-
25
- set(value: T): void;
26
- /**
27
- * Updates the signal by applying a function to its current value.
28
- * @param {(prev: T) => T} fn - Function that receives current value and returns new value
29
- */
30
-
31
- update(fn: (prev: T) => T): void;
32
- /**
33
- * Reads the signal value without creating a dependency relationship.
34
- * @returns {T} The current value
35
- */
36
-
37
- peek(): T;
38
- /**
39
- * Subscribes to signal changes.
40
- * @param {() => void} fn - Callback function to execute when signal changes
41
- * @returns {() => void} Unsubscribe function
42
- */
43
- subscribe(fn: () => void): () => void;
44
- }
45
-
46
- /**
47
- * Represents a computed (memoized) signal derived from other signals.
48
- * Automatically updates when dependencies change and is read-only.
49
- * @template T - The type of value computed
50
- */
51
- interface Computed<T> extends Signal<T> {
52
- /** Marks this signal as computed for type checking purposes */
53
- readonly isComputed: true;
54
- }
55
-
56
- /**
57
- * The return type of an effect function.
58
- * Can be void or a cleanup function that runs when the effect is disposed.
59
- */
60
- type EffectCleanup = void | (() => void);
1
+ /**
2
+ * Represents a reactive signal that holds a value and notifies dependents when changed.
3
+ * @template T - The type of value stored in the signal
4
+ */
5
+ interface Signal<T> {
6
+ /**
7
+ * Reads the current signal value and establishes a dependency if inside an effect.
8
+ * @returns {T} The current value
9
+ */
10
+ (): T;
11
+ /**
12
+ * Sets the signal to a new value and notifies all subscribers.
13
+ * @param {T} value - The new value to set
14
+ */
15
+ set(value: T): void;
16
+ /**
17
+ * Updates the signal by applying a function to its current value.
18
+ * @param {(prev: T) => T} fn - Function that receives current value and returns new value
19
+ */
20
+ update(fn: (prev: T) => T): void;
21
+ /**
22
+ * Reads the signal value without creating a dependency relationship.
23
+ * @returns {T} The current value
24
+ */
25
+ peek(): T;
26
+ /**
27
+ * Subscribes to signal changes.
28
+ * @param {() => void} fn - Callback function to execute when signal changes
29
+ * @returns {() => void} Unsubscribe function
30
+ */
31
+ subscribe(fn: () => void): () => void;
32
+ }
33
+ /**
34
+ * Represents a computed (memoized) signal derived from other signals.
35
+ * Automatically updates when dependencies change and is read-only.
36
+ * @template T - The type of value computed
37
+ */
38
+ interface Computed<T> extends Signal<T> {
39
+ /** Marks this signal as computed for type checking purposes */
40
+ readonly isComputed: true;
41
+ }
42
+ /**
43
+ * The return type of an effect function.
44
+ * Can be void or a cleanup function that runs when the effect is disposed.
45
+ */
46
+ type EffectCleanup = void | (() => void);
61
47
 
62
48
  /**
63
49
  * Creates a reactive signal that can be read, written, and subscribed to.
@@ -1,2 +1,2 @@
1
- var i=null,s=null,c=0,d=new Set,a=new Set;function v(n){let e=n,t=new Set;function r(){return i&&t.add(i),e}function o(u){Object.is(e,u)||(e=u,c>0?t.forEach(T=>d.add(T)):t.forEach(T=>T()));}function p(u){o(u(e));}function l(){return e}function x(u){return t.add(u),()=>t.delete(u)}let f=r;return f.set=o,f.update=p,f.peek=l,f.subscribe=x,f}function y(n){let e,t=false,r=()=>{if(t)return;e&&(e(),e=void 0);let p=i;i=r;try{let l=n();typeof l=="function"&&(e=l);}finally{i=p;}};r();let o=()=>{t||(t=true,e&&e());};return s&&s.push(o),o}function b(n){let e=v(void 0);y(()=>{e.set(n());});let t=e;return Object.defineProperty(t,"isComputed",{value:true,writable:false}),t}function g(n){c++;try{return n()}finally{if(c--,c===0){c++,a.clear();try{for(;d.size>0;){let e=Array.from(d);d.clear(),e.forEach(t=>{a.has(t)||(a.add(t),t());});}}finally{c--,a.clear();}}}}function h(n){let e=i;i=null;try{return n()}finally{i=e;}}function E(n,e){let t=n.peek();return y(()=>{let r=n(),o=h(()=>e(r,t));return t=r,o})}function C(n){let e={};for(let t in n)e[t]=v(n[t]);return e}function S(n){let e,t=false;return ()=>{if(t)return e;let r=n();return e=r,t=true,r}}function k(n){let e=[],t=s;s=e;try{return n(()=>{e.forEach(o=>o()),e.length=0,s=t;})}finally{s=t;}}var w={getCurrentEffect(){return i},getBatchDepth(){return c},getBatchedEffectsCount(){return d.size}};function m(n){return typeof n=="function"&&"set"in n&&"update"in n&&"peek"in n}function D(n){return m(n)&&"isComputed"in n}var R={signal:v,effect:y,computed:b,batch:g,untrack:h,on:E,store:C,memo:S,root:k,isSignal:m,isComputed:D,dev:w};export{g as batch,b as computed,R as default,w as dev,y as effect,D as isComputed,m as isSignal,S as memo,E as on,k as root,v as signal,C as store,h as untrack};//# sourceMappingURL=main.js.map
2
- //# sourceMappingURL=main.js.map
1
+ var i=null,s=null,c=0,d=new Set,a=new Set;function v(n){let e=n,t=new Set;function r(){return i&&t.add(i),e}function o(u){Object.is(e,u)||(e=u,c>0?t.forEach(T=>d.add(T)):t.forEach(T=>T()));}function p(u){o(u(e));}function l(){return e}function x(u){return t.add(u),()=>t.delete(u)}let f=r;return f.set=o,f.update=p,f.peek=l,f.subscribe=x,f}function y(n){let e,t=false,r=()=>{if(t)return;e&&(e(),e=void 0);let p=i;i=r;try{let l=n();typeof l=="function"&&(e=l);}finally{i=p;}};r();let o=()=>{t||(t=true,e&&e());};return s&&s.push(o),o}function b(n){let e=v(void 0);y(()=>{e.set(n());});let t=e;return Object.defineProperty(t,"isComputed",{value:true,writable:false}),t}function g(n){c++;try{return n()}finally{if(c--,c===0){c++,a.clear();try{for(;d.size>0;){let e=Array.from(d);d.clear(),e.forEach(t=>{a.has(t)||(a.add(t),t());});}}finally{c--,a.clear();}}}}function h(n){let e=i;i=null;try{return n()}finally{i=e;}}function E(n,e){let t=n.peek();return y(()=>{let r=n(),o=h(()=>e(r,t));return t=r,o})}function C(n){let e={};for(let t in n)e[t]=v(n[t]);return e}function S(n){let e,t=false;return ()=>{if(t)return e;let r=n();return e=r,t=true,r}}function k(n){let e=[],t=s;s=e;try{return n(()=>{e.forEach(o=>o()),e.length=0,s=t;})}finally{s=t;}}var w={getCurrentEffect(){return i},getBatchDepth(){return c},getBatchedEffectsCount(){return d.size}};function m(n){return typeof n=="function"&&"set"in n&&"update"in n&&"peek"in n}function D(n){return m(n)&&"isComputed"in n}var R={signal:v,effect:y,computed:b,batch:g,untrack:h,on:E,store:C,memo:S,root:k,isSignal:m,isComputed:D,dev:w};export{g as batch,b as computed,R as default,w as dev,y as effect,D as isComputed,m as isSignal,S as memo,E as on,k as root,v as signal,C as store,h as untrack};//# sourceMappingURL=index.js.map
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"names":["currentEffect","currentRoot","batchDepth","batchedEffects","flushedEffects","signal","initialValue","value","subscribers","read","write","newValue","fn","update","peek","subscribe","sig","effect","cleanup","isDisposed","execute","prevEffect","result","disposer","computed","batch","effects","untrack","on","prevValue","store","initialState","key","memo","cachedValue","hasCachedValue","root","disposers","prevRoot","d","dev","isSignal","isComputed","index_default"],"mappings":"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","file":"index.js","sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\r\n// src/index.ts\r\n//\r\n// Made with ❤️ by Maysara.\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ PACK ════════════════════════════════════════╗\r\n\r\n import { Signal, EffectCleanup, Computed } from './types';\r\n export type * from './types';\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ INIT ════════════════════════════════════════╗\r\n\r\n let currentEffect : (() => void) | null = null;\r\n let currentRoot : (() => void)[] | null = null;\r\n let batchDepth : number = 0;\r\n const batchedEffects : Set<() => void> = new Set<() => void>();\r\n const flushedEffects : Set<() => void> = new Set<() => void>();\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ CORE ════════════════════════════════════════╗\r\n\r\n /**\r\n * Creates a reactive signal that can be read, written, and subscribed to.\r\n * @template T - The type of value stored in the signal\r\n * @param {T} initialValue - The initial value of the signal\r\n * @returns {Signal<T>} A signal object with read, set, update, peek, and subscribe methods\r\n * @example\r\n * const count = signal(0);\r\n * console.log(count()); // 0\r\n * count.set(5); // Update value\r\n */\r\n export function signal<T>(initialValue: T): Signal<T> {\r\n let value = initialValue;\r\n const subscribers = new Set<() => void>();\r\n\r\n function read(): T {\r\n // Track dependency if inside effect\r\n if (currentEffect) {\r\n subscribers.add(currentEffect);\r\n }\r\n return value;\r\n }\r\n\r\n function write(newValue: T): void {\r\n // Only update if value actually changed\r\n if (Object.is(value, newValue)) return;\r\n\r\n value = newValue;\r\n\r\n // Notify all subscribers\r\n if (batchDepth > 0) {\r\n // Batch mode: collect effects\r\n subscribers.forEach(fn => batchedEffects.add(fn));\r\n } else {\r\n // Immediate mode: run effects now\r\n subscribers.forEach(fn => fn());\r\n }\r\n }\r\n\r\n function update(fn: (prev: T) => T): void {\r\n write(fn(value));\r\n }\r\n\r\n function peek(): T {\r\n // Read without tracking\r\n return value;\r\n }\r\n\r\n function subscribe(fn: () => void): () => void {\r\n subscribers.add(fn);\r\n return () => subscribers.delete(fn);\r\n }\r\n\r\n // Create signal function with methods\r\n const sig = read as Signal<T>;\r\n sig.set = write;\r\n sig.update = update;\r\n sig.peek = peek;\r\n sig.subscribe = subscribe;\r\n\r\n return sig;\r\n }\r\n\r\n /**\r\n * Automatically runs a function when its signal dependencies change.\r\n * @param {() => EffectCleanup} fn - The effect function to run. Can optionally return a cleanup function.\r\n * @returns {() => void} A dispose function to stop the effect and clean up\r\n * @example\r\n * const count = signal(0);\r\n * effect(() => {\r\n * console.log('Count:', count());\r\n * return () => console.log('Cleaning up');\r\n * });\r\n */\r\n export function effect(fn: () => EffectCleanup): () => void {\r\n let cleanup: (() => void) | undefined;\r\n let isDisposed = false;\r\n\r\n const execute = () => {\r\n if (isDisposed) return;\r\n\r\n // Run cleanup from previous execution\r\n if (cleanup) {\r\n cleanup();\r\n cleanup = undefined;\r\n }\r\n\r\n // Set as current effect for dependency tracking\r\n const prevEffect = currentEffect;\r\n currentEffect = execute;\r\n\r\n try {\r\n // Run the effect function\r\n const result = fn();\r\n\r\n // Store cleanup if returned\r\n if (typeof result === 'function') {\r\n cleanup = result;\r\n }\r\n } finally {\r\n // Restore previous effect\r\n currentEffect = prevEffect;\r\n }\r\n };\r\n\r\n // Run immediately\r\n execute();\r\n\r\n // Create dispose function\r\n const disposer = () => {\r\n if (isDisposed) return;\r\n isDisposed = true;\r\n if (cleanup) cleanup();\r\n };\r\n\r\n // Register with current root if one exists\r\n if (currentRoot) {\r\n currentRoot.push(disposer);\r\n }\r\n\r\n // Return dispose function\r\n return disposer;\r\n }\r\n\r\n /**\r\n * Creates a computed signal that automatically updates when its dependencies change.\r\n * The computation result is cached and only recomputed when dependencies change.\r\n * @template T - The type of value computed\r\n * @param {() => T} fn - The computation function\r\n * @returns {Computed<T>} A read-only computed signal\r\n * @example\r\n * const count = signal(5);\r\n * const doubled = computed(() => count() * 2);\r\n * console.log(doubled()); // 10\r\n */\r\n export function computed<T>(fn: () => T): Computed<T> {\r\n const sig = signal<T>(undefined as T);\r\n\r\n // Create effect that updates the signal\r\n effect(() => {\r\n sig.set(fn());\r\n });\r\n\r\n // Mark as computed\r\n const computed = sig as Computed<T>;\r\n Object.defineProperty(computed, 'isComputed', {\r\n value: true,\r\n writable: false\r\n });\r\n\r\n return computed;\r\n }\r\n\r\n /**\r\n * Groups multiple signal updates together, deferring effect execution until all updates complete.\r\n * This improves performance by preventing cascading effect runs.\r\n * @template T - The return type of the function\r\n * @param {() => T} fn - A function that performs multiple signal updates\r\n * @returns {T} The return value of the function\r\n * @example\r\n * const a = signal(1);\r\n * const b = signal(2);\r\n * batch(() => {\r\n * a.set(10);\r\n * b.set(20);\r\n * }); // Effects only run once\r\n */\r\n export function batch<T>(fn: () => T): T {\r\n batchDepth++;\r\n\r\n try {\r\n return fn();\r\n } finally {\r\n batchDepth--;\r\n\r\n // If we're back at depth 0, flush batched effects\r\n if (batchDepth === 0) {\r\n // Keep batch mode active while flushing to prevent cascading effects\r\n batchDepth++;\r\n flushedEffects.clear();\r\n try {\r\n // Keep running effects until no more are queued\r\n while (batchedEffects.size > 0) {\r\n const effects = Array.from(batchedEffects);\r\n batchedEffects.clear();\r\n effects.forEach(fn => {\r\n // Only run if we haven't run it in this batch\r\n if (!flushedEffects.has(fn)) {\r\n flushedEffects.add(fn);\r\n fn();\r\n }\r\n });\r\n }\r\n } finally {\r\n batchDepth--;\r\n flushedEffects.clear();\r\n }\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Reads signals without creating dependencies on them.\r\n * Useful for accessing signal values without triggering effect re-runs.\r\n * @template T - The return type of the function\r\n * @param {() => T} fn - A function that accesses signals\r\n * @returns {T} The return value of the function\r\n * @example\r\n * const count = signal(0);\r\n * effect(() => {\r\n * const value = untrack(() => count()); // Won't trigger re-run\r\n * console.log(value);\r\n * });\r\n */\r\n export function untrack<T>(fn: () => T): T {\r\n const prevEffect = currentEffect;\r\n currentEffect = null;\r\n\r\n try {\r\n return fn();\r\n } finally {\r\n currentEffect = prevEffect;\r\n }\r\n }\r\n\r\n /**\r\n * Runs an effect only when a specific signal changes, providing both new and previous values.\r\n * @template T - The type of the signal\r\n * @param {Signal<T>} sig - The signal to watch\r\n * @param {(value: T, prevValue: T) => EffectCleanup} fn - Effect function called with new and previous values\r\n * @returns {() => void} A dispose function to stop watching\r\n * @example\r\n * const count = signal(0);\r\n * on(count, (newVal, oldVal) => {\r\n * console.log(`Changed from ${oldVal} to ${newVal}`);\r\n * });\r\n */\r\n export function on<T>(\r\n sig: Signal<T>,\r\n fn: (value: T, prevValue: T) => EffectCleanup\r\n ): () => void {\r\n let prevValue = sig.peek();\r\n\r\n return effect(() => {\r\n // Read the signal to create dependency\r\n const value = sig();\r\n\r\n // Run callback without tracking new dependencies\r\n const cleanup = untrack(() => fn(value, prevValue));\r\n prevValue = value;\r\n return cleanup;\r\n });\r\n }\r\n\r\n /**\r\n * Creates a store object where each property is a signal.\r\n * Provides a convenient way to manage multiple related reactive values.\r\n * @template T - The type of the initial state object\r\n * @param {T} initialState - An object with initial values\r\n * @returns {{ [K in keyof T]: Signal<T[K]> }} An object with signals for each property\r\n * @example\r\n * const state = store({ count: 0, name: 'John' });\r\n * console.log(state.count()); // 0\r\n * state.name.set('Jane');\r\n */\r\n export function store<T extends Record<string, any>>(\r\n initialState: T\r\n ): { [K in keyof T]: Signal<T[K]> } {\r\n const store = {} as any;\r\n\r\n for (const key in initialState) {\r\n store[key] = signal(initialState[key]);\r\n }\r\n\r\n return store;\r\n }\r\n\r\n /**\r\n * Memoizes the result of an expensive computation, caching it indefinitely.\r\n * Unlike computed, this doesn't depend on reactive signals.\r\n * @template T - The type of the memoized value\r\n * @param {() => T} fn - A function that performs the computation\r\n * @returns {() => T} A function that returns the cached result\r\n * @example\r\n * const expensiveCalc = memo(() => {\r\n * return Array.from({ length: 1000 }).map(expensiveOp);\r\n * });\r\n * const result = expensiveCalc(); // Computed only once\r\n */\r\n export function memo<T>(fn: () => T): () => T {\r\n let cachedValue: T | undefined;\r\n let hasCachedValue = false;\r\n\r\n return () => {\r\n if (hasCachedValue) {\r\n return cachedValue as T;\r\n }\r\n\r\n // Compute the value\r\n const value = fn();\r\n\r\n // Cache it\r\n cachedValue = value;\r\n hasCachedValue = true;\r\n\r\n return value;\r\n };\r\n }\r\n\r\n /**\r\n * Creates a root scope for managing effect and computed signal lifecycles.\r\n * All effects and disposers created within the function are collected and can be cleaned up together.\r\n * @template T - The return type of the function\r\n * @param {(dispose: () => void) => T} fn - A function that receives a dispose function\r\n * @returns {T} The return value of the function\r\n * @example\r\n * const dispose = root((dispose) => {\r\n * effect(() => console.log('Running'));\r\n * return 42;\r\n * });\r\n * dispose(); // Cleans up all effects created in the root\r\n */\r\n export function root<T>(fn: (dispose: () => void) => T): T {\r\n const disposers: (() => void)[] = [];\r\n const prevRoot = currentRoot;\r\n currentRoot = disposers;\r\n\r\n try {\r\n const dispose = () => {\r\n disposers.forEach(d => d());\r\n disposers.length = 0;\r\n currentRoot = prevRoot;\r\n };\r\n\r\n return fn(dispose);\r\n } finally {\r\n currentRoot = prevRoot;\r\n }\r\n }\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ HELP ════════════════════════════════════════╗\r\n\r\n /**\r\n * Development utilities for debugging signal reactivity\r\n */\r\n export const dev = {\r\n /**\r\n * Returns the currently executing effect, or null if no effect is running\r\n * @returns {(() => void) | null} The current effect function or null\r\n */\r\n getCurrentEffect(): (() => void) | null {\r\n return currentEffect;\r\n },\r\n\r\n /**\r\n * Returns the current batch depth (for debugging nested batch calls)\r\n * @returns {number} The current batch nesting level\r\n */\r\n getBatchDepth(): number {\r\n return batchDepth;\r\n },\r\n\r\n /**\r\n * Returns the count of effects currently pending in the batch queue\r\n * @returns {number} The number of batched effects waiting to run\r\n */\r\n getBatchedEffectsCount(): number {\r\n return batchedEffects.size;\r\n }\r\n };\r\n\r\n /**\r\n * Type guard to check if a value is a signal\r\n * @template T - The type of value the signal contains\r\n * @param {any} value - The value to check\r\n * @returns {boolean} True if the value is a signal\r\n * @example\r\n * if (isSignal(myValue)) {\r\n * console.log(myValue());\r\n * }\r\n */\r\n export function isSignal<T>(value: any): value is Signal<T> {\r\n return (\r\n typeof value === 'function' &&\r\n 'set' in value &&\r\n 'update' in value &&\r\n 'peek' in value\r\n );\r\n }\r\n\r\n /**\r\n * Type guard to check if a value is a computed signal\r\n * @template T - The type of value the computed signal contains\r\n * @param {any} value - The value to check\r\n * @returns {boolean} True if the value is a computed signal\r\n * @example\r\n * if (isComputed(myValue)) {\r\n * console.log('This is a computed signal');\r\n * }\r\n */\r\n export function isComputed<T>(value: any): value is Computed<T> {\r\n return isSignal(value) && 'isComputed' in value;\r\n }\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ ════ ════════════════════════════════════════╗\r\n\r\n export default {\r\n signal,\r\n effect,\r\n computed,\r\n batch,\r\n untrack,\r\n on,\r\n store,\r\n memo,\r\n root,\r\n isSignal,\r\n isComputed,\r\n dev\r\n };\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@minejs/signals",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "A lightweight, zero-dependency signals library for reactive JavaScript applications",
5
5
  "keywords": ["minejs", "signals"],
6
6
  "license": "MIT",
@@ -18,14 +18,14 @@
18
18
  "url": "git+https://github.com/minejs-org/signals.git"
19
19
  },
20
20
  "type": "module",
21
- "main": "./dist/main.js",
22
- "types": "./dist/main.d.ts",
21
+ "main": "./dist/index.js",
22
+ "types": "./dist/index.d.ts",
23
23
  "files": ["dist"],
24
24
  "exports": {
25
25
  ".": {
26
- "types": "./dist/main.d.ts",
27
- "import": "./dist/main.js",
28
- "require": "./dist/main.js"
26
+ "types": "./dist/index.d.ts",
27
+ "import": "./dist/index.js",
28
+ "require": "./dist/index.js"
29
29
  }
30
30
  },
31
31
  "scripts": {
package/dist/main.cjs.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/main.ts"],"names":["currentEffect","currentRoot","batchDepth","batchedEffects","flushedEffects","signal","initialValue","value","subscribers","read","write","newValue","fn","update","peek","subscribe","sig","effect","cleanup","isDisposed","execute","prevEffect","result","disposer","computed","batch","effects","untrack","on","prevValue","store","initialState","key","memo","cachedValue","hasCachedValue","root","disposers","prevRoot","d","dev","isSignal","isComputed","main_default"],"mappings":"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","file":"main.cjs","sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\r\n// src/main.ts\r\n//\r\n// Made with ❤️ by Maysara.\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ PACK ════════════════════════════════════════╗\r\n\r\n import { Signal, EffectCleanup, Computed } from './types';\r\n export type * from './types';\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ INIT ════════════════════════════════════════╗\r\n\r\n let currentEffect : (() => void) | null = null;\r\n let currentRoot : (() => void)[] | null = null;\r\n let batchDepth : number = 0;\r\n const batchedEffects : Set<() => void> = new Set<() => void>();\r\n const flushedEffects : Set<() => void> = new Set<() => void>();\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ CORE ════════════════════════════════════════╗\r\n\r\n /**\r\n * Creates a reactive signal that can be read, written, and subscribed to.\r\n * @template T - The type of value stored in the signal\r\n * @param {T} initialValue - The initial value of the signal\r\n * @returns {Signal<T>} A signal object with read, set, update, peek, and subscribe methods\r\n * @example\r\n * const count = signal(0);\r\n * console.log(count()); // 0\r\n * count.set(5); // Update value\r\n */\r\n export function signal<T>(initialValue: T): Signal<T> {\r\n let value = initialValue;\r\n const subscribers = new Set<() => void>();\r\n\r\n function read(): T {\r\n // Track dependency if inside effect\r\n if (currentEffect) {\r\n subscribers.add(currentEffect);\r\n }\r\n return value;\r\n }\r\n\r\n function write(newValue: T): void {\r\n // Only update if value actually changed\r\n if (Object.is(value, newValue)) return;\r\n\r\n value = newValue;\r\n\r\n // Notify all subscribers\r\n if (batchDepth > 0) {\r\n // Batch mode: collect effects\r\n subscribers.forEach(fn => batchedEffects.add(fn));\r\n } else {\r\n // Immediate mode: run effects now\r\n subscribers.forEach(fn => fn());\r\n }\r\n }\r\n\r\n function update(fn: (prev: T) => T): void {\r\n write(fn(value));\r\n }\r\n\r\n function peek(): T {\r\n // Read without tracking\r\n return value;\r\n }\r\n\r\n function subscribe(fn: () => void): () => void {\r\n subscribers.add(fn);\r\n return () => subscribers.delete(fn);\r\n }\r\n\r\n // Create signal function with methods\r\n const sig = read as Signal<T>;\r\n sig.set = write;\r\n sig.update = update;\r\n sig.peek = peek;\r\n sig.subscribe = subscribe;\r\n\r\n return sig;\r\n }\r\n\r\n /**\r\n * Automatically runs a function when its signal dependencies change.\r\n * @param {() => EffectCleanup} fn - The effect function to run. Can optionally return a cleanup function.\r\n * @returns {() => void} A dispose function to stop the effect and clean up\r\n * @example\r\n * const count = signal(0);\r\n * effect(() => {\r\n * console.log('Count:', count());\r\n * return () => console.log('Cleaning up');\r\n * });\r\n */\r\n export function effect(fn: () => EffectCleanup): () => void {\r\n let cleanup: (() => void) | undefined;\r\n let isDisposed = false;\r\n\r\n const execute = () => {\r\n if (isDisposed) return;\r\n\r\n // Run cleanup from previous execution\r\n if (cleanup) {\r\n cleanup();\r\n cleanup = undefined;\r\n }\r\n\r\n // Set as current effect for dependency tracking\r\n const prevEffect = currentEffect;\r\n currentEffect = execute;\r\n\r\n try {\r\n // Run the effect function\r\n const result = fn();\r\n\r\n // Store cleanup if returned\r\n if (typeof result === 'function') {\r\n cleanup = result;\r\n }\r\n } finally {\r\n // Restore previous effect\r\n currentEffect = prevEffect;\r\n }\r\n };\r\n\r\n // Run immediately\r\n execute();\r\n\r\n // Create dispose function\r\n const disposer = () => {\r\n if (isDisposed) return;\r\n isDisposed = true;\r\n if (cleanup) cleanup();\r\n };\r\n\r\n // Register with current root if one exists\r\n if (currentRoot) {\r\n currentRoot.push(disposer);\r\n }\r\n\r\n // Return dispose function\r\n return disposer;\r\n }\r\n\r\n /**\r\n * Creates a computed signal that automatically updates when its dependencies change.\r\n * The computation result is cached and only recomputed when dependencies change.\r\n * @template T - The type of value computed\r\n * @param {() => T} fn - The computation function\r\n * @returns {Computed<T>} A read-only computed signal\r\n * @example\r\n * const count = signal(5);\r\n * const doubled = computed(() => count() * 2);\r\n * console.log(doubled()); // 10\r\n */\r\n export function computed<T>(fn: () => T): Computed<T> {\r\n const sig = signal<T>(undefined as T);\r\n\r\n // Create effect that updates the signal\r\n effect(() => {\r\n sig.set(fn());\r\n });\r\n\r\n // Mark as computed\r\n const computed = sig as Computed<T>;\r\n Object.defineProperty(computed, 'isComputed', {\r\n value: true,\r\n writable: false\r\n });\r\n\r\n return computed;\r\n }\r\n\r\n /**\r\n * Groups multiple signal updates together, deferring effect execution until all updates complete.\r\n * This improves performance by preventing cascading effect runs.\r\n * @template T - The return type of the function\r\n * @param {() => T} fn - A function that performs multiple signal updates\r\n * @returns {T} The return value of the function\r\n * @example\r\n * const a = signal(1);\r\n * const b = signal(2);\r\n * batch(() => {\r\n * a.set(10);\r\n * b.set(20);\r\n * }); // Effects only run once\r\n */\r\n export function batch<T>(fn: () => T): T {\r\n batchDepth++;\r\n\r\n try {\r\n return fn();\r\n } finally {\r\n batchDepth--;\r\n\r\n // If we're back at depth 0, flush batched effects\r\n if (batchDepth === 0) {\r\n // Keep batch mode active while flushing to prevent cascading effects\r\n batchDepth++;\r\n flushedEffects.clear();\r\n try {\r\n // Keep running effects until no more are queued\r\n while (batchedEffects.size > 0) {\r\n const effects = Array.from(batchedEffects);\r\n batchedEffects.clear();\r\n effects.forEach(fn => {\r\n // Only run if we haven't run it in this batch\r\n if (!flushedEffects.has(fn)) {\r\n flushedEffects.add(fn);\r\n fn();\r\n }\r\n });\r\n }\r\n } finally {\r\n batchDepth--;\r\n flushedEffects.clear();\r\n }\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Reads signals without creating dependencies on them.\r\n * Useful for accessing signal values without triggering effect re-runs.\r\n * @template T - The return type of the function\r\n * @param {() => T} fn - A function that accesses signals\r\n * @returns {T} The return value of the function\r\n * @example\r\n * const count = signal(0);\r\n * effect(() => {\r\n * const value = untrack(() => count()); // Won't trigger re-run\r\n * console.log(value);\r\n * });\r\n */\r\n export function untrack<T>(fn: () => T): T {\r\n const prevEffect = currentEffect;\r\n currentEffect = null;\r\n\r\n try {\r\n return fn();\r\n } finally {\r\n currentEffect = prevEffect;\r\n }\r\n }\r\n\r\n /**\r\n * Runs an effect only when a specific signal changes, providing both new and previous values.\r\n * @template T - The type of the signal\r\n * @param {Signal<T>} sig - The signal to watch\r\n * @param {(value: T, prevValue: T) => EffectCleanup} fn - Effect function called with new and previous values\r\n * @returns {() => void} A dispose function to stop watching\r\n * @example\r\n * const count = signal(0);\r\n * on(count, (newVal, oldVal) => {\r\n * console.log(`Changed from ${oldVal} to ${newVal}`);\r\n * });\r\n */\r\n export function on<T>(\r\n sig: Signal<T>,\r\n fn: (value: T, prevValue: T) => EffectCleanup\r\n ): () => void {\r\n let prevValue = sig.peek();\r\n\r\n return effect(() => {\r\n // Read the signal to create dependency\r\n const value = sig();\r\n\r\n // Run callback without tracking new dependencies\r\n const cleanup = untrack(() => fn(value, prevValue));\r\n prevValue = value;\r\n return cleanup;\r\n });\r\n }\r\n\r\n /**\r\n * Creates a store object where each property is a signal.\r\n * Provides a convenient way to manage multiple related reactive values.\r\n * @template T - The type of the initial state object\r\n * @param {T} initialState - An object with initial values\r\n * @returns {{ [K in keyof T]: Signal<T[K]> }} An object with signals for each property\r\n * @example\r\n * const state = store({ count: 0, name: 'John' });\r\n * console.log(state.count()); // 0\r\n * state.name.set('Jane');\r\n */\r\n export function store<T extends Record<string, any>>(\r\n initialState: T\r\n ): { [K in keyof T]: Signal<T[K]> } {\r\n const store = {} as any;\r\n\r\n for (const key in initialState) {\r\n store[key] = signal(initialState[key]);\r\n }\r\n\r\n return store;\r\n }\r\n\r\n /**\r\n * Memoizes the result of an expensive computation, caching it indefinitely.\r\n * Unlike computed, this doesn't depend on reactive signals.\r\n * @template T - The type of the memoized value\r\n * @param {() => T} fn - A function that performs the computation\r\n * @returns {() => T} A function that returns the cached result\r\n * @example\r\n * const expensiveCalc = memo(() => {\r\n * return Array.from({ length: 1000 }).map(expensiveOp);\r\n * });\r\n * const result = expensiveCalc(); // Computed only once\r\n */\r\n export function memo<T>(fn: () => T): () => T {\r\n let cachedValue: T | undefined;\r\n let hasCachedValue = false;\r\n\r\n return () => {\r\n if (hasCachedValue) {\r\n return cachedValue as T;\r\n }\r\n\r\n // Compute the value\r\n const value = fn();\r\n\r\n // Cache it\r\n cachedValue = value;\r\n hasCachedValue = true;\r\n\r\n return value;\r\n };\r\n }\r\n\r\n /**\r\n * Creates a root scope for managing effect and computed signal lifecycles.\r\n * All effects and disposers created within the function are collected and can be cleaned up together.\r\n * @template T - The return type of the function\r\n * @param {(dispose: () => void) => T} fn - A function that receives a dispose function\r\n * @returns {T} The return value of the function\r\n * @example\r\n * const dispose = root((dispose) => {\r\n * effect(() => console.log('Running'));\r\n * return 42;\r\n * });\r\n * dispose(); // Cleans up all effects created in the root\r\n */\r\n export function root<T>(fn: (dispose: () => void) => T): T {\r\n const disposers: (() => void)[] = [];\r\n const prevRoot = currentRoot;\r\n currentRoot = disposers;\r\n\r\n try {\r\n const dispose = () => {\r\n disposers.forEach(d => d());\r\n disposers.length = 0;\r\n currentRoot = prevRoot;\r\n };\r\n\r\n return fn(dispose);\r\n } finally {\r\n currentRoot = prevRoot;\r\n }\r\n }\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ HELP ════════════════════════════════════════╗\r\n\r\n /**\r\n * Development utilities for debugging signal reactivity\r\n */\r\n export const dev = {\r\n /**\r\n * Returns the currently executing effect, or null if no effect is running\r\n * @returns {(() => void) | null} The current effect function or null\r\n */\r\n getCurrentEffect(): (() => void) | null {\r\n return currentEffect;\r\n },\r\n\r\n /**\r\n * Returns the current batch depth (for debugging nested batch calls)\r\n * @returns {number} The current batch nesting level\r\n */\r\n getBatchDepth(): number {\r\n return batchDepth;\r\n },\r\n\r\n /**\r\n * Returns the count of effects currently pending in the batch queue\r\n * @returns {number} The number of batched effects waiting to run\r\n */\r\n getBatchedEffectsCount(): number {\r\n return batchedEffects.size;\r\n }\r\n };\r\n\r\n /**\r\n * Type guard to check if a value is a signal\r\n * @template T - The type of value the signal contains\r\n * @param {any} value - The value to check\r\n * @returns {boolean} True if the value is a signal\r\n * @example\r\n * if (isSignal(myValue)) {\r\n * console.log(myValue());\r\n * }\r\n */\r\n export function isSignal<T>(value: any): value is Signal<T> {\r\n return (\r\n typeof value === 'function' &&\r\n 'set' in value &&\r\n 'update' in value &&\r\n 'peek' in value\r\n );\r\n }\r\n\r\n /**\r\n * Type guard to check if a value is a computed signal\r\n * @template T - The type of value the computed signal contains\r\n * @param {any} value - The value to check\r\n * @returns {boolean} True if the value is a computed signal\r\n * @example\r\n * if (isComputed(myValue)) {\r\n * console.log('This is a computed signal');\r\n * }\r\n */\r\n export function isComputed<T>(value: any): value is Computed<T> {\r\n return isSignal(value) && 'isComputed' in value;\r\n }\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ ════ ════════════════════════════════════════╗\r\n\r\n export default {\r\n signal,\r\n effect,\r\n computed,\r\n batch,\r\n untrack,\r\n on,\r\n store,\r\n memo,\r\n root,\r\n isSignal,\r\n isComputed,\r\n dev\r\n };\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n"]}
package/dist/main.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/main.ts"],"names":["currentEffect","currentRoot","batchDepth","batchedEffects","flushedEffects","signal","initialValue","value","subscribers","read","write","newValue","fn","update","peek","subscribe","sig","effect","cleanup","isDisposed","execute","prevEffect","result","disposer","computed","batch","effects","untrack","on","prevValue","store","initialState","key","memo","cachedValue","hasCachedValue","root","disposers","prevRoot","d","dev","isSignal","isComputed","main_default"],"mappings":"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","file":"main.js","sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\r\n// src/main.ts\r\n//\r\n// Made with ❤️ by Maysara.\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ PACK ════════════════════════════════════════╗\r\n\r\n import { Signal, EffectCleanup, Computed } from './types';\r\n export type * from './types';\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ INIT ════════════════════════════════════════╗\r\n\r\n let currentEffect : (() => void) | null = null;\r\n let currentRoot : (() => void)[] | null = null;\r\n let batchDepth : number = 0;\r\n const batchedEffects : Set<() => void> = new Set<() => void>();\r\n const flushedEffects : Set<() => void> = new Set<() => void>();\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ CORE ════════════════════════════════════════╗\r\n\r\n /**\r\n * Creates a reactive signal that can be read, written, and subscribed to.\r\n * @template T - The type of value stored in the signal\r\n * @param {T} initialValue - The initial value of the signal\r\n * @returns {Signal<T>} A signal object with read, set, update, peek, and subscribe methods\r\n * @example\r\n * const count = signal(0);\r\n * console.log(count()); // 0\r\n * count.set(5); // Update value\r\n */\r\n export function signal<T>(initialValue: T): Signal<T> {\r\n let value = initialValue;\r\n const subscribers = new Set<() => void>();\r\n\r\n function read(): T {\r\n // Track dependency if inside effect\r\n if (currentEffect) {\r\n subscribers.add(currentEffect);\r\n }\r\n return value;\r\n }\r\n\r\n function write(newValue: T): void {\r\n // Only update if value actually changed\r\n if (Object.is(value, newValue)) return;\r\n\r\n value = newValue;\r\n\r\n // Notify all subscribers\r\n if (batchDepth > 0) {\r\n // Batch mode: collect effects\r\n subscribers.forEach(fn => batchedEffects.add(fn));\r\n } else {\r\n // Immediate mode: run effects now\r\n subscribers.forEach(fn => fn());\r\n }\r\n }\r\n\r\n function update(fn: (prev: T) => T): void {\r\n write(fn(value));\r\n }\r\n\r\n function peek(): T {\r\n // Read without tracking\r\n return value;\r\n }\r\n\r\n function subscribe(fn: () => void): () => void {\r\n subscribers.add(fn);\r\n return () => subscribers.delete(fn);\r\n }\r\n\r\n // Create signal function with methods\r\n const sig = read as Signal<T>;\r\n sig.set = write;\r\n sig.update = update;\r\n sig.peek = peek;\r\n sig.subscribe = subscribe;\r\n\r\n return sig;\r\n }\r\n\r\n /**\r\n * Automatically runs a function when its signal dependencies change.\r\n * @param {() => EffectCleanup} fn - The effect function to run. Can optionally return a cleanup function.\r\n * @returns {() => void} A dispose function to stop the effect and clean up\r\n * @example\r\n * const count = signal(0);\r\n * effect(() => {\r\n * console.log('Count:', count());\r\n * return () => console.log('Cleaning up');\r\n * });\r\n */\r\n export function effect(fn: () => EffectCleanup): () => void {\r\n let cleanup: (() => void) | undefined;\r\n let isDisposed = false;\r\n\r\n const execute = () => {\r\n if (isDisposed) return;\r\n\r\n // Run cleanup from previous execution\r\n if (cleanup) {\r\n cleanup();\r\n cleanup = undefined;\r\n }\r\n\r\n // Set as current effect for dependency tracking\r\n const prevEffect = currentEffect;\r\n currentEffect = execute;\r\n\r\n try {\r\n // Run the effect function\r\n const result = fn();\r\n\r\n // Store cleanup if returned\r\n if (typeof result === 'function') {\r\n cleanup = result;\r\n }\r\n } finally {\r\n // Restore previous effect\r\n currentEffect = prevEffect;\r\n }\r\n };\r\n\r\n // Run immediately\r\n execute();\r\n\r\n // Create dispose function\r\n const disposer = () => {\r\n if (isDisposed) return;\r\n isDisposed = true;\r\n if (cleanup) cleanup();\r\n };\r\n\r\n // Register with current root if one exists\r\n if (currentRoot) {\r\n currentRoot.push(disposer);\r\n }\r\n\r\n // Return dispose function\r\n return disposer;\r\n }\r\n\r\n /**\r\n * Creates a computed signal that automatically updates when its dependencies change.\r\n * The computation result is cached and only recomputed when dependencies change.\r\n * @template T - The type of value computed\r\n * @param {() => T} fn - The computation function\r\n * @returns {Computed<T>} A read-only computed signal\r\n * @example\r\n * const count = signal(5);\r\n * const doubled = computed(() => count() * 2);\r\n * console.log(doubled()); // 10\r\n */\r\n export function computed<T>(fn: () => T): Computed<T> {\r\n const sig = signal<T>(undefined as T);\r\n\r\n // Create effect that updates the signal\r\n effect(() => {\r\n sig.set(fn());\r\n });\r\n\r\n // Mark as computed\r\n const computed = sig as Computed<T>;\r\n Object.defineProperty(computed, 'isComputed', {\r\n value: true,\r\n writable: false\r\n });\r\n\r\n return computed;\r\n }\r\n\r\n /**\r\n * Groups multiple signal updates together, deferring effect execution until all updates complete.\r\n * This improves performance by preventing cascading effect runs.\r\n * @template T - The return type of the function\r\n * @param {() => T} fn - A function that performs multiple signal updates\r\n * @returns {T} The return value of the function\r\n * @example\r\n * const a = signal(1);\r\n * const b = signal(2);\r\n * batch(() => {\r\n * a.set(10);\r\n * b.set(20);\r\n * }); // Effects only run once\r\n */\r\n export function batch<T>(fn: () => T): T {\r\n batchDepth++;\r\n\r\n try {\r\n return fn();\r\n } finally {\r\n batchDepth--;\r\n\r\n // If we're back at depth 0, flush batched effects\r\n if (batchDepth === 0) {\r\n // Keep batch mode active while flushing to prevent cascading effects\r\n batchDepth++;\r\n flushedEffects.clear();\r\n try {\r\n // Keep running effects until no more are queued\r\n while (batchedEffects.size > 0) {\r\n const effects = Array.from(batchedEffects);\r\n batchedEffects.clear();\r\n effects.forEach(fn => {\r\n // Only run if we haven't run it in this batch\r\n if (!flushedEffects.has(fn)) {\r\n flushedEffects.add(fn);\r\n fn();\r\n }\r\n });\r\n }\r\n } finally {\r\n batchDepth--;\r\n flushedEffects.clear();\r\n }\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Reads signals without creating dependencies on them.\r\n * Useful for accessing signal values without triggering effect re-runs.\r\n * @template T - The return type of the function\r\n * @param {() => T} fn - A function that accesses signals\r\n * @returns {T} The return value of the function\r\n * @example\r\n * const count = signal(0);\r\n * effect(() => {\r\n * const value = untrack(() => count()); // Won't trigger re-run\r\n * console.log(value);\r\n * });\r\n */\r\n export function untrack<T>(fn: () => T): T {\r\n const prevEffect = currentEffect;\r\n currentEffect = null;\r\n\r\n try {\r\n return fn();\r\n } finally {\r\n currentEffect = prevEffect;\r\n }\r\n }\r\n\r\n /**\r\n * Runs an effect only when a specific signal changes, providing both new and previous values.\r\n * @template T - The type of the signal\r\n * @param {Signal<T>} sig - The signal to watch\r\n * @param {(value: T, prevValue: T) => EffectCleanup} fn - Effect function called with new and previous values\r\n * @returns {() => void} A dispose function to stop watching\r\n * @example\r\n * const count = signal(0);\r\n * on(count, (newVal, oldVal) => {\r\n * console.log(`Changed from ${oldVal} to ${newVal}`);\r\n * });\r\n */\r\n export function on<T>(\r\n sig: Signal<T>,\r\n fn: (value: T, prevValue: T) => EffectCleanup\r\n ): () => void {\r\n let prevValue = sig.peek();\r\n\r\n return effect(() => {\r\n // Read the signal to create dependency\r\n const value = sig();\r\n\r\n // Run callback without tracking new dependencies\r\n const cleanup = untrack(() => fn(value, prevValue));\r\n prevValue = value;\r\n return cleanup;\r\n });\r\n }\r\n\r\n /**\r\n * Creates a store object where each property is a signal.\r\n * Provides a convenient way to manage multiple related reactive values.\r\n * @template T - The type of the initial state object\r\n * @param {T} initialState - An object with initial values\r\n * @returns {{ [K in keyof T]: Signal<T[K]> }} An object with signals for each property\r\n * @example\r\n * const state = store({ count: 0, name: 'John' });\r\n * console.log(state.count()); // 0\r\n * state.name.set('Jane');\r\n */\r\n export function store<T extends Record<string, any>>(\r\n initialState: T\r\n ): { [K in keyof T]: Signal<T[K]> } {\r\n const store = {} as any;\r\n\r\n for (const key in initialState) {\r\n store[key] = signal(initialState[key]);\r\n }\r\n\r\n return store;\r\n }\r\n\r\n /**\r\n * Memoizes the result of an expensive computation, caching it indefinitely.\r\n * Unlike computed, this doesn't depend on reactive signals.\r\n * @template T - The type of the memoized value\r\n * @param {() => T} fn - A function that performs the computation\r\n * @returns {() => T} A function that returns the cached result\r\n * @example\r\n * const expensiveCalc = memo(() => {\r\n * return Array.from({ length: 1000 }).map(expensiveOp);\r\n * });\r\n * const result = expensiveCalc(); // Computed only once\r\n */\r\n export function memo<T>(fn: () => T): () => T {\r\n let cachedValue: T | undefined;\r\n let hasCachedValue = false;\r\n\r\n return () => {\r\n if (hasCachedValue) {\r\n return cachedValue as T;\r\n }\r\n\r\n // Compute the value\r\n const value = fn();\r\n\r\n // Cache it\r\n cachedValue = value;\r\n hasCachedValue = true;\r\n\r\n return value;\r\n };\r\n }\r\n\r\n /**\r\n * Creates a root scope for managing effect and computed signal lifecycles.\r\n * All effects and disposers created within the function are collected and can be cleaned up together.\r\n * @template T - The return type of the function\r\n * @param {(dispose: () => void) => T} fn - A function that receives a dispose function\r\n * @returns {T} The return value of the function\r\n * @example\r\n * const dispose = root((dispose) => {\r\n * effect(() => console.log('Running'));\r\n * return 42;\r\n * });\r\n * dispose(); // Cleans up all effects created in the root\r\n */\r\n export function root<T>(fn: (dispose: () => void) => T): T {\r\n const disposers: (() => void)[] = [];\r\n const prevRoot = currentRoot;\r\n currentRoot = disposers;\r\n\r\n try {\r\n const dispose = () => {\r\n disposers.forEach(d => d());\r\n disposers.length = 0;\r\n currentRoot = prevRoot;\r\n };\r\n\r\n return fn(dispose);\r\n } finally {\r\n currentRoot = prevRoot;\r\n }\r\n }\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ HELP ════════════════════════════════════════╗\r\n\r\n /**\r\n * Development utilities for debugging signal reactivity\r\n */\r\n export const dev = {\r\n /**\r\n * Returns the currently executing effect, or null if no effect is running\r\n * @returns {(() => void) | null} The current effect function or null\r\n */\r\n getCurrentEffect(): (() => void) | null {\r\n return currentEffect;\r\n },\r\n\r\n /**\r\n * Returns the current batch depth (for debugging nested batch calls)\r\n * @returns {number} The current batch nesting level\r\n */\r\n getBatchDepth(): number {\r\n return batchDepth;\r\n },\r\n\r\n /**\r\n * Returns the count of effects currently pending in the batch queue\r\n * @returns {number} The number of batched effects waiting to run\r\n */\r\n getBatchedEffectsCount(): number {\r\n return batchedEffects.size;\r\n }\r\n };\r\n\r\n /**\r\n * Type guard to check if a value is a signal\r\n * @template T - The type of value the signal contains\r\n * @param {any} value - The value to check\r\n * @returns {boolean} True if the value is a signal\r\n * @example\r\n * if (isSignal(myValue)) {\r\n * console.log(myValue());\r\n * }\r\n */\r\n export function isSignal<T>(value: any): value is Signal<T> {\r\n return (\r\n typeof value === 'function' &&\r\n 'set' in value &&\r\n 'update' in value &&\r\n 'peek' in value\r\n );\r\n }\r\n\r\n /**\r\n * Type guard to check if a value is a computed signal\r\n * @template T - The type of value the computed signal contains\r\n * @param {any} value - The value to check\r\n * @returns {boolean} True if the value is a computed signal\r\n * @example\r\n * if (isComputed(myValue)) {\r\n * console.log('This is a computed signal');\r\n * }\r\n */\r\n export function isComputed<T>(value: any): value is Computed<T> {\r\n return isSignal(value) && 'isComputed' in value;\r\n }\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ ════ ════════════════════════════════════════╗\r\n\r\n export default {\r\n signal,\r\n effect,\r\n computed,\r\n batch,\r\n untrack,\r\n on,\r\n store,\r\n memo,\r\n root,\r\n isSignal,\r\n isComputed,\r\n dev\r\n };\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n"]}