### Configure basic signals Source: https://github.com/maverick-js/signals/blob/main/_autodocs/configuration.md Examples of initializing signals with debugging IDs and custom change detection logic. ```typescript import { signal } from '@maverick-js/signals'; // Simple signal with debugging ID const $count = signal(0, { id: 'counter' }); // Signal with custom change detection const $email = signal('user@example.com', { id: 'email', dirty: (prev, next) => { // Only consider changed if length changed significantly return Math.abs(prev.length - next.length) > 2; } }); ``` -------------------------------- ### Install Maverick JS Signals Source: https://github.com/maverick-js/signals/blob/main/README.md Commands to install the package using common JavaScript package managers. ```bash $: npm i @maverick-js/signals $: pnpm i @maverick-js/signals $: yarn add @maverick-js/signals ``` -------------------------------- ### Signal Usage Example Source: https://github.com/maverick-js/signals/blob/main/_autodocs/api-signals.md Demonstrates creating a signal, updating its value, creating computed signals, and using effects with synchronous flushing. ```typescript import { signal, computed, effect, tick } from '@maverick-js/signals'; // Create a signal const $count = signal(0); // Read the value console.log($count()); // 0 // Update with a direct value $count.set(5); // Update with a function (receives previous value) $count.set((prev) => prev + 1); // Create a computed signal that depends on $count const $doubled = computed(() => $count() * 2); // Create an effect that runs whenever $count changes const stop = effect(() => { console.log('Count is:', $count()); }); tick(); // Flush batched updates synchronously // Stop watching stop(); ``` -------------------------------- ### selector() Usage Example Source: https://github.com/maverick-js/signals/blob/main/_autodocs/api-map.md Demonstrates creating boolean signals for specific modes and observing their changes using effects. ```typescript import { signal, effect, tick } from '@maverick-js/signals'; import { selector } from '@maverick-js/signals/map'; const $mode = signal('light'); // Create selector for light/dark/auto modes const $isLight = selector($mode)('light'); const $isDark = selector($mode)('dark'); const $isAuto = selector($mode)('auto'); // Each boolean signal only notifies on enter/exit effect(() => { if ($isLight()) { console.log('Switched to light mode'); } }); effect(() => { if ($isDark()) { console.log('Switched to dark mode'); } }); tick(); $mode.set('dark'); tick(); // Logs: "Switched to dark mode" $mode.set('auto'); tick(); // Logs nothing (no effect for auto key yet) // Entering auto mode registers it effect(() => { if ($isAuto()) { console.log('Using auto mode'); } }); tick(); // Logs: "Using auto mode" $mode.set('light'); tick(); // Logs: "Switched to light mode" ``` -------------------------------- ### Configure a computed signal with initial value Source: https://github.com/maverick-js/signals/blob/main/_autodocs/types.md Example of providing an initial value to a computed signal to handle potential errors during the first computation. ```typescript const $dangerous = computed(() => { if (Math.random() > 0.5) { throw new Error('Random error'); } return 42; }, { initial: 0, id: 'risky' }); ``` -------------------------------- ### Apply custom comparison strategies Source: https://github.com/maverick-js/signals/blob/main/_autodocs/configuration.md Examples of various custom dirty functions for case-insensitive strings, numeric tolerance, array length, and forced notification states. ```typescript // Case-insensitive string comparison const $email = signal('alice@example.com', { dirty: (prev, next) => prev.toLowerCase() !== next.toLowerCase() }); // Number tolerance check const $temperature = signal(20, { dirty: (prev, next) => Math.abs(next - prev) >= 1 }); // Array length-based comparison const $items = signal([1, 2, 3], { dirty: (prev, next) => prev.length !== next.length }); // Always notify (even if same value) const $always = signal(0, { dirty: () => true }); // Never notify (even if different value) const $never = signal(0, { dirty: () => false }); ``` -------------------------------- ### Configure a signal with custom options Source: https://github.com/maverick-js/signals/blob/main/_autodocs/types.md Example of using a custom dirty check to notify observers only when the value changes by a specific threshold. ```typescript const $count = signal(0, { id: 'counter', dirty: (prev, next) => { // Only notify if changed by more than 5 return Math.abs(next - prev) >= 5; } }); ``` -------------------------------- ### Build Project Source: https://github.com/maverick-js/signals/blob/main/_autodocs/README.md Command to build the project. ```bash npm run build ``` -------------------------------- ### Run Benchmarks Source: https://github.com/maverick-js/signals/blob/main/_autodocs/README.md Command to execute performance benchmarks. ```bash npm run bench:layers # Performance benchmarks ``` -------------------------------- ### Setting Production Environment Variables Source: https://github.com/maverick-js/signals/blob/main/_autodocs/configuration.md Configure the build environment to production to strip development features and optimize bundle size. ```bash # Using build tools like Vite, webpack, etc. NODE_ENV=production npm run build # Or in your build configuration process.env.NODE_ENV = 'production'; ``` -------------------------------- ### Run Tests Source: https://github.com/maverick-js/signals/blob/main/_autodocs/README.md Commands for running standard, watch-mode, and garbage collection tests. ```bash npm run test npm run test:watch npm run test:gc # Memory/garbage collection tests ``` -------------------------------- ### signal(initial, options) Source: https://github.com/maverick-js/signals/blob/main/_autodocs/quick-reference.md Creates a new reactive signal with an initial value and optional configuration. ```APIDOC ## signal(initial, options) ### Description Creates a new reactive signal. The signal can be updated using the .set() method. ### Parameters - **initial** (any) - Required - The initial value of the signal. - **options** (object) - Optional - Configuration object. - **id** (string) - Optional - Debug identifier. - **dirty** (function) - Optional - Custom change detection function (prev, next) => boolean. ``` -------------------------------- ### Observe Changes with Effect Source: https://github.com/maverick-js/signals/blob/main/README.md Shows how to run side effects when dependencies update and how to use cleanup functions within an effect. ```js import { signal, computed, effect } from '@maverick-js/signals'; const $a = signal(10); const $b = signal(20); const $c = computed(() => $a() + $b()); // This effect will run each time `$a` or `$b` is updated. const stop = effect(() => console.log($c())); // Stop observing. stop(); ``` ```js effect(() => { return () => { // Called each time effect re-runs and when disposed of. }; }); ``` -------------------------------- ### View Source Code Directory Structure Source: https://github.com/maverick-js/signals/blob/main/_autodocs/INDEX.md Displays the file organization of the project source code. ```text src/ ├── signals.ts - signal(), computed(), effect(), readonly() ├── core.ts - root(), tick(), peek(), untrack(), scope, context ├── map.ts - computedMap(), computedKeyedMap() ├── selector.ts - selector() ├── types.ts - Type definitions ├── symbols.ts - Internal symbols └── index.ts - Main exports ``` -------------------------------- ### Create and Read a Signal Source: https://github.com/maverick-js/signals/blob/main/_autodocs/README.md Demonstrates initializing a signal, reading its value, and updating it using direct values or functional updates. ```typescript import { signal } from '@maverick-js/signals'; const $count = signal(0); console.log($count()); // 0 $count.set(5); console.log($count()); // 5 $count.set(prev => prev + 1); console.log($count()); // 6 ``` -------------------------------- ### Batch Updates and Timing Source: https://github.com/maverick-js/signals/blob/main/_autodocs/quick-reference.md Explains default microtask batching and how to force synchronous execution using tick. ```typescript // Batched updates (default) $a.set(1); $b.set(2); // Effects run on next microtask // Force synchronous tick(); // Flush pending effects immediately ``` -------------------------------- ### Use WriteSignal Source: https://github.com/maverick-js/signals/blob/main/_autodocs/types.md Demonstrates reading, updating, and checking the writability of a signal. ```typescript const $count = signal(0); // Read const current = $count(); // 0 // Write with direct value $count.set(5); // Write with function $count.set((prev) => prev + 1); // Check if writable if (isWriteSignal($count)) { $count.set(10); } ``` -------------------------------- ### Manage context with getContext and setContext Source: https://github.com/maverick-js/signals/blob/main/README.md Low-level utilities for setting and retrieving context values across the computation tree. ```js import { root, getContext, setContext } from '@maverick-js/signals'; const key = Symbol(); root(() => { setContext(key, 100); // ... root(() => { const value = getContext(key); // 100 }); }); ``` -------------------------------- ### Reactive Signals Usage Source: https://github.com/maverick-js/signals/blob/main/README.md Demonstrates the core API including signal creation, computed properties, effect subscriptions, and manual tick flushing within a root scope. ```js import { root, signal, computed, effect, tick } from '@maverick-js/signals'; root((dispose) => { // Create - all types supported (string, array, object, etc.) const $m = signal(1); const $x = signal(1); const $b = signal(0); // Compute - only re-computed when `$m`, `$x`, or `$b` changes. const $y = computed(() => $m() * $x() + $b()); // Effect - this will run whenever `$y` is updated. const stop = effect(() => { console.log($y()); // Called each time `effect` ends and when finally disposed. return () => {}; }); $m.set(10); // logs `10` inside effect // Flush queue synchronously so effect is run. // Otherwise, effects will be batched and run on the microtask queue. tick(); $b.set((prev) => prev + 5); // logs `15` inside effect tick(); // Nothing has changed - no re-compute. $y(); // Stop running effect. stop(); // ... // Dispose of all signals inside `root`. dispose(); }); ``` -------------------------------- ### Create a computation root in TypeScript Source: https://github.com/maverick-js/signals/blob/main/_autodocs/api-core.md Demonstrates initializing a root scope with and without a dispose function to manage reactive computations. ```typescript import { root, signal, computed, effect } from '@maverick-js/signals'; // Create a root scope const result = root((dispose) => { const $count = signal(0); const $doubled = computed(() => $count() * 2); effect(() => { console.log($doubled()); }); $count.set(5); // triggers effect // Return value from root return $doubled(); }); console.log(result); // 10 // Or without needing dispose parameter const value = root(() => { const $x = signal(42); return $x(); // 42 }); ``` -------------------------------- ### root() Source: https://github.com/maverick-js/signals/blob/main/_autodocs/api-core.md Creates a computation root with a dispose function to clean up all child computations. ```APIDOC ## root(init) ### Description Creates a computation root with a dispose function to clean up all child computations. This is used to manage reactive scopes and prevent memory leaks by ensuring descendant computations are cleaned up. ### Signature `function root(init: (dispose: Dispose) => T): T` ### Parameters - **init** ((dispose: Dispose) => T) - Required - Initialization function that receives a dispose function. ### Return Type - **T** - The return value of the init function. ### Behavior - Stores all inner computations as children of the root scope. - Calling the dispose function cleans up all descendant computations. - If init.length is 0, the function is called directly. - If init.length > 0, the function is bound with the dispose function as the first argument. ### Example ```typescript import { root, signal, computed, effect } from '@maverick-js/signals'; const result = root((dispose) => { const $count = signal(0); const $doubled = computed(() => $count() * 2); effect(() => { console.log($doubled()); }); $count.set(5); return $doubled(); }); ``` ``` -------------------------------- ### Define and Update Signals Source: https://github.com/maverick-js/signals/blob/main/README.md Shows how to initialize a signal and update its value using direct assignment or a functional update. ```js import { signal } from '@maverick-js/signals'; const $a = signal(10); $a(); // read $a.set(20); // write (1) $a.set((prev) => prev + 10); // write (2) ``` -------------------------------- ### Access Signal Values without Tracking Source: https://github.com/maverick-js/signals/blob/main/README.md Demonstrates using peek to read a signal without triggering dependency tracking. ```js import { signal, computed, peek } from '@maverick-js/signals'; const $a = signal(10); const $b = computed(() => { // `$a` will not trigger updates on `$b`. const value = peek($a); }); ``` -------------------------------- ### Synchronize Signal Updates with tick Source: https://github.com/maverick-js/signals/blob/main/README.md Demonstrates the difference between batched asynchronous updates and synchronous flushing using the tick function. ```js import { signal } from '@maverick-js/signals'; const $a = signal(10); $a.set(10); $a.set(20); $a.set(30); // only this write is applied ``` ```js import { signal, tick } from '@maverick-js/signals'; const $a = signal(10); // All writes are applied. $a.set(10); tick(); $a.set(20); tick(); $a.set(30); ``` -------------------------------- ### Usage of setContext and getContext Source: https://github.com/maverick-js/signals/blob/main/_autodocs/api-core.md Demonstrates setting context values within a root scope and retrieving them inside effects. ```typescript import { root, signal, effect, getScope, setContext, getContext } from '@maverick-js/signals'; const AppConfigKey = Symbol('appConfig'); const RequestKey = Symbol('request'); root(() => { const appScope = getScope(); // Set context in the current scope setContext(AppConfigKey, { apiUrl: 'https://api.example.com', timeout: 5000 }); effect(() => { const config = getContext(AppConfigKey); console.log(config?.apiUrl); // https://api.example.com }); // Outer scope can set context before inner scope inherits setContext(RequestKey, { id: 'req-123' }); effect(() => { const request = getContext(RequestKey); console.log(request?.id); // req-123 }); }); ``` -------------------------------- ### Create a reactive effect with cleanup Source: https://github.com/maverick-js/signals/blob/main/_autodocs/api-signals.md Demonstrates initializing an effect that tracks signal dependencies and provides a cleanup function to handle state changes or disposal. ```typescript import { signal, computed, effect, tick } from '@maverick-js/signals'; const $name = signal('Alice'); const $age = signal(30); const $greeting = computed(() => `Hello ${$name()}`); // Effect runs immediately and on each dependency change const stop = effect(() => { console.log($greeting()); console.log(`Age: ${$age()}`); // Return a cleanup function return () => { console.log('Cleaning up before next effect run'); }; }); tick(); // logs "Hello Alice", "Age: 30", then "Cleaning up..." $name.set('Bob'); tick(); // logs "Hello Bob", "Age: 30", then "Cleaning up..." // Stop the effect stop(); // logs "Cleaning up..." one final time ``` -------------------------------- ### Create Computed Signals Source: https://github.com/maverick-js/signals/blob/main/README.md Demonstrates creating derived state that only re-computes when dependencies change, and shows nesting capabilities. ```js import { signal, computed, tick } from '@maverick-js/signals'; const $a = signal(10); const $b = signal(10); const $c = computed(() => $a() + $b()); console.log($c()); // logs 20 $a.set(20); tick(); console.log($c()); // logs 30 $b.set(20); tick(); console.log($c()); // logs 40 // Nothing changed - no re-compute. console.log($c()); // logs 40 ``` ```js import { signal, computed } from '@maverick-js/signals'; const $a = signal(10); const $b = signal(10); const $c = computed(() => $a() + $b()); // Computed signals can be deeply nested. const $d = computed(() => $a() + $b() + $c()); const $e = computed(() => $d()); ``` -------------------------------- ### Dependency Injection with Context Source: https://github.com/maverick-js/signals/blob/main/_autodocs/patterns-and-examples.md Uses root to define context providers that are accessible to child effects via getContext. ```typescript import { root, signal, effect, setContext, getContext } from '@maverick-js/signals'; interface AppConfig { apiUrl: string; timeout: number; } const ConfigKey = Symbol('AppConfig'); const LoggerKey = Symbol('Logger'); root(() => { // Set up context at root setContext(ConfigKey, { apiUrl: 'https://api.example.com', timeout: 5000 }); setContext(LoggerKey, { log: (msg: string) => console.log('[APP]', msg), error: (msg: string) => console.error('[ERROR]', msg) }); // Child computations inherit context effect(() => { const config = getContext(ConfigKey); const logger = getContext(LoggerKey); logger?.log(`Using API: ${config?.apiUrl}`); }); }); ``` -------------------------------- ### Implement reactive effects Source: https://github.com/maverick-js/signals/blob/main/_autodocs/types.md Shows how to define an effect with an optional cleanup function that runs before re-execution or disposal. ```typescript effect(() => { // This runs immediately and whenever dependencies change // Optionally return cleanup function return () => { // This runs before next execution and on disposal }; }); ``` -------------------------------- ### Import Map Utilities Source: https://github.com/maverick-js/signals/blob/main/_autodocs/README.md Use these imports to access specialized mapping utilities for signals. ```typescript import { computedMap, computedKeyedMap, selector } from '@maverick-js/signals/map'; ``` -------------------------------- ### Manage Conditional Dependencies Source: https://github.com/maverick-js/signals/blob/main/_autodocs/patterns-and-examples.md Use peek to access signal values without creating reactive dependencies, useful for conditional logic. ```typescript import { signal, computed, peek } from '@maverick-js/signals'; const $mode = signal<'light' | 'dark'>('light'); const $systemTheme = signal<'light' | 'dark'>('light'); const $theme = computed(() => { const mode = $mode(); // Only depend on system theme if mode is 'auto' if (mode === 'auto') { return $systemTheme(); } // Peek at system theme without creating dependency return mode; }); // When mode changes, $theme updates // When systemTheme changes, $theme only updates if mode is 'auto' ``` -------------------------------- ### Import Core Signal Primitives Source: https://github.com/maverick-js/signals/blob/main/_autodocs/README.md Use these imports to access the primary signal, computed, effect, and root functions. ```typescript import { signal, computed, effect, root } from '@maverick-js/signals'; ``` -------------------------------- ### Handle List Items Source: https://github.com/maverick-js/signals/blob/main/_autodocs/quick-reference.md Render, add, and filter items within a signal-backed list. ```typescript const $items = signal([1, 2, 3]); // Render const $elements = computedKeyedMap($items, (item) => { const el = document.createElement('div'); el.textContent = item; return el; }); // Add $items.set(prev => [...prev, 4]); // Filter $items.set(prev => prev.filter(x => x > 1)); ``` -------------------------------- ### Importing Maverick.js Signals Source: https://github.com/maverick-js/signals/blob/main/_autodocs/quick-reference.md Imports for core signals, array utilities, and TypeScript types from the library. ```typescript // Main signals import { signal, computed, effect, readonly, root, peek, untrack, tick, getScope, scoped, getContext, setContext, onError, onDispose, isReadSignal, isWriteSignal, } from '@maverick-js/signals'; // Array utilities import { computedMap, computedKeyedMap, selector, } from '@maverick-js/signals/map'; // Types only import type { ReadSignal, WriteSignal, Effect, StopEffect, Scope, SignalOptions, ComputedSignalOptions, } from '@maverick-js/signals'; ``` -------------------------------- ### Scopes and Context Source: https://github.com/maverick-js/signals/blob/main/_autodocs/quick-reference.md Methods for managing execution scopes and context storage. ```APIDOC ## getScope() Returns the current execution scope. ## scoped(fn, scopeRef) Executes a function within a specific scope. ## setContext(key, value) Stores a value in the current context. ## getContext(key) Retrieves a value from the current context. ``` -------------------------------- ### Type Checking Utilities Source: https://github.com/maverick-js/signals/blob/main/_autodocs/quick-reference.md Functions to verify signal types at runtime. ```APIDOC ## Type Checking ### Methods - **isReadSignal(value)** - Returns true if the value is a readable signal. - **isWriteSignal(value)** - Returns true if the value is a writable signal. ``` -------------------------------- ### Select appropriate map functions Source: https://github.com/maverick-js/signals/blob/main/_autodocs/configuration.md Use computedMap for primitives and computedKeyedMap for objects to ensure efficient reactivity. ```typescript // Primitives -> computedMap const $numbers = signal([1, 2, 3]); const $squared = computedMap($numbers, (n) => n() * n()); // Objects -> computedKeyedMap const $users = signal([{ id: 1, name: 'Alice' }]); const $elements = computedKeyedMap($users, (user) => document.createElement('div') ); ``` -------------------------------- ### Main Package Exports Source: https://github.com/maverick-js/signals/blob/main/_autodocs/README.md Lists the primary API exports available from the @maverick-js/signals package. ```typescript import { // Signal creation signal, computed, readonly, // Effects effect, // Scope management root, getScope, scoped, // Tracking control peek, untrack, tick, // Context getContext, setContext, // Lifecycle onError, onDispose, // Type guards isReadSignal, isWriteSignal, // Low-level createScope, createComputation, isFunction, isNotEqual, // Types type ReadSignal, type WriteSignal, type Effect, type StopEffect, type Scope, type Computation, type SignalOptions, type ComputedSignalOptions, type MaybeSignal, type InferSignalValue, } from '@maverick-js/signals'; ``` -------------------------------- ### Using Compile-Time Environment Flags Source: https://github.com/maverick-js/signals/blob/main/_autodocs/configuration.md Utilize __DEV__ and __TEST__ flags to conditionally execute code based on the current environment. ```typescript if (__DEV__) { // This code only exists in development builds signal.node = createComputation(...); } if (__TEST__) { // This code only exists in test builds } ``` -------------------------------- ### Using readonly() to restrict signal access Source: https://github.com/maverick-js/signals/blob/main/_autodocs/api-signals.md Demonstrates creating a read-only signal and verifying that it cannot be updated directly while still tracking the original signal's state. ```typescript import { signal, readonly, isWriteSignal } from '@maverick-js/signals'; const $internalCount = signal(0); const $count = readonly($internalCount); // Read the value (works) console.log($count()); // 0 // Cannot write through the readonly signal console.log(isWriteSignal($count)); // false // But we can still update via the original signal $internalCount.set(5); console.log($count()); // 5 (reflects the change) ``` -------------------------------- ### Retrieve context values in TypeScript Source: https://github.com/maverick-js/signals/blob/main/_autodocs/api-core.md Demonstrates setting and retrieving context values using symbols within a root computation scope. ```typescript import { root, signal, effect, getScope, setContext, getContext } from '@maverick-js/signals'; const ThemeKey = Symbol('theme'); const UserKey = Symbol('user'); root(() => { setContext(ThemeKey, 'dark'); setContext(UserKey, { id: 1, name: 'Alice' }); effect(() => { const theme = getContext(ThemeKey); // 'dark' const user = getContext<{id: number, name: string}>(UserKey); console.log(`User ${user?.name} using ${theme} theme`); }); // Nested scope inherits parent context root(() => { const inherited = getContext(ThemeKey); // 'dark' (inherited) const notFound = getContext(Symbol('unknown')); // undefined }); }); ``` -------------------------------- ### Create Orphan Computations Source: https://github.com/maverick-js/signals/blob/main/README.md Demonstrates creating a computation without a parent scope, which relies on garbage collection for disposal. ```js import { computed } from '@maverick-js/signals'; const obj = {}; // This is an orphan - GC'd when `obj` is. const $b = computed(() => obj); ``` -------------------------------- ### effect(effect, options) Source: https://github.com/maverick-js/signals/blob/main/_autodocs/configuration.md Creates a reactive effect with optional configuration for debugging. ```APIDOC ## effect(effect, options) ### Description Registers a reactive effect that runs when dependencies change. Supports an optional debugging identifier. ### Parameters - **effect** (Function) - Required - The effect function to execute. - **options** (Object) - Optional - Configuration object. - **id** (string) - Optional - Debugging identifier, only available in development/test environments. ### Returns - **StopEffect** (Function) - A function to stop the effect. ``` -------------------------------- ### signal() Source: https://github.com/maverick-js/signals/blob/main/_autodocs/api-signals.md Creates a writable signal that holds a reactive value. The signal returns the current value when invoked and provides a set() method for updates. ```APIDOC ## signal(initialValue, options) ### Description Creates a writable signal that holds a reactive value. The signal returns the current value when invoked, and provides a set() method for updates. ### Signature function signal(initialValue: T, options?: SignalOptions): WriteSignal ### Parameters - **initialValue** (T) - Required - The initial value of the signal - **options** (SignalOptions) - Optional - Configuration object with optional id and dirty properties - **options.id** (string) - Optional - Debugging identifier (development/test environments only) - **options.dirty** ((prev: T, next: T) => boolean) - Optional - Custom function to determine if value has changed ### Return Type WriteSignal — A function that reads the signal value when invoked, with a set() method for writes. ### Behavior - Signal reads track dependencies automatically in computations and effects - Updates are batched by default on the microtask queue; use tick() to flush synchronously - Only notifies observers when the value actually changes (shallow equality check by default) - Can hold any JavaScript type: primitives, arrays, objects, etc. ### Example import { signal, computed, effect, tick } from '@maverick-js/signals'; // Create a signal const $count = signal(0); // Read the value console.log($count()); // 0 // Update with a direct value $count.set(5); // Update with a function (receives previous value) $count.set((prev) => prev + 1); // Create a computed signal that depends on $count const $doubled = computed(() => $count() * 2); // Create an effect that runs whenever $count changes const stop = effect(() => { console.log('Count is:', $count()); }); tick(); // Flush batched updates synchronously // Stop watching stop(); ``` -------------------------------- ### SignalOptions Source: https://github.com/maverick-js/signals/blob/main/_autodocs/types.md Configuration options for the signal() function, including debugging identifiers and custom change detection. ```APIDOC ## SignalOptions ### Description Configuration options passed to the signal() function to control debugging and change detection behavior. ### Properties - **id** (string) - Optional - Debugging identifier (dev/test only). - **dirty** ((prev: T, next: T) => boolean) - Optional - Custom change detection function that determines whether observers are notified. ### Example ```typescript const $count = signal(0, { id: 'counter', dirty: (prev, next) => { return Math.abs(next - prev) >= 5; } }); ``` ``` -------------------------------- ### effect() Source: https://github.com/maverick-js/signals/blob/main/_autodocs/api-signals.md Creates a side effect that runs immediately and re-runs whenever any accessed signals change. ```APIDOC ## effect() ### Description Creates a side effect that runs whenever any of its accessed signals change. The effect runs immediately on creation and automatically tracks dependencies. ### Signature `function effect(effect: Effect, options?: { id?: string }): StopEffect` ### Parameters - **effect** (Effect) - Required - Function to run whenever dependencies change. - **options** ({ id?: string }) - Optional - Configuration options. - **id** (string) - Optional - Debugging identifier (development/test environments only). ### Return Type - **StopEffect** - A function that stops the effect when called. ### Behavior - Runs immediately on initialization. - Automatically tracks signals read during execution as dependencies. - Re-runs whenever any dependency changes. - Updates are batched via microtask queue. - Supports returning a cleanup function that runs before each re-execution and on disposal. ### Example ```typescript import { signal, computed, effect, tick } from '@maverick-js/signals'; const $name = signal('Alice'); const $age = signal(30); const $greeting = computed(() => `Hello ${$name()}`); const stop = effect(() => { console.log($greeting()); console.log(`Age: ${$age()}`); return () => { console.log('Cleaning up before next effect run'); }; }); tick(); $name.set('Bob'); tick(); stop(); ``` ``` -------------------------------- ### Configure Signal Options Source: https://github.com/maverick-js/signals/blob/main/_autodocs/quick-reference.md Apply debug IDs, custom change detection, or initial values to signals, computed properties, effects, and maps. ```typescript // Signal options signal(initial, { id: 'debug-id', // Dev/test only dirty: (prev, next) => prev !== next // Change detection }); // Computed options computed(() => { /* ... */ }, { id: 'debug-id', initial: fallbackValue, // Before first run dirty: (prev, next) => prev !== next }); // Effect options effect(() => { /* ... */ }, { id: 'debug-id' }); // Map options computedMap($array, map, { id: 'debug-id' }); ``` -------------------------------- ### Implement State Machines Source: https://github.com/maverick-js/signals/blob/main/_autodocs/patterns-and-examples.md Manage complex state transitions using signals and effects. This pattern is useful for tracking application lifecycle states like loading, success, or error. ```typescript import { signal, effect } from '@maverick-js/signals'; type State = 'idle' | 'loading' | 'success' | 'error'; const $state = signal('idle', { id: 'appState' }); const $data = signal(null); const $error = signal(null); effect(() => { const state = $state(); if (state === 'idle') { console.log('Ready to start'); } else if (state === 'loading') { console.log('Loading data...'); } else if (state === 'success') { console.log('Data loaded:', $data()); } else if (state === 'error') { console.log('Error:', $error()?.message); } }); // State transitions async function loadData() { $state.set('loading'); try { const response = await fetch('/api/data'); $data.set(await response.json()); $state.set('success'); } catch (err) { $error.set(err as Error); $state.set('error'); } } ``` -------------------------------- ### Create a Type-Safe Signal Factory Source: https://github.com/maverick-js/signals/blob/main/_autodocs/patterns-and-examples.md Defines a factory interface to standardize signal creation with specific types. Useful for enforcing consistent signal initialization across an application. ```typescript import { signal, computed } from '@maverick-js/signals'; import type { WriteSignal, ReadSignal } from '@maverick-js/signals'; interface SignalFactory { createString(initial: string, id?: string): WriteSignal; createNumber(initial: number, id?: string): WriteSignal; createBoolean(initial: boolean, id?: string): WriteSignal; } const createSignalFactory = (): SignalFactory => ({ createString: (initial, id) => signal(initial, { id }), createNumber: (initial, id) => signal(initial, { id }), createBoolean: (initial, id) => signal(initial, { id }) }); // Usage const factory = createSignalFactory(); const $name = factory.createString('John', 'name'); const $age = factory.createNumber(30, 'age'); const $active = factory.createBoolean(true, 'active'); ``` -------------------------------- ### Signal Creation Source: https://github.com/maverick-js/signals/blob/main/_autodocs/quick-reference.md Methods for creating writable and computed signals, as well as converting signals to readonly. ```APIDOC ## signal(initialValue, options?) Creates a writable signal. ## computed(fn, options?) Creates a readonly computed signal. ## readonly(signal) Converts a writable signal into a readonly signal. ``` -------------------------------- ### computedKeyedMap(list, map, options) Source: https://github.com/maverick-js/signals/blob/main/_autodocs/api-map.md Creates a reactive map that tracks items by reference, allowing for efficient updates and reordering without re-mapping existing items. ```APIDOC ## computedKeyedMap(list, map, options) ### Description Reactive map helper that caches array items by reference. The item value is fixed but the index changes. When items move in the array, their mapped representations move too. ### Signature `function computedKeyedMap(list: ReadSignal, map: (value: Item, index: ReadSignal) => MappedItem, options?: { id?: string }): ReadSignal` ### Parameters - **list** (ReadSignal) - Required - Signal containing the array to map - **map** ((value: Item, index: ReadSignal) => MappedItem) - Required - Mapping function receives the item value directly and an index signal - **options** ({ id?: string }) - Optional - Debugging identifier (development/test environments only) ### Return Type `ReadSignal` — A readonly signal of mapped items. ### Example ```typescript import { signal, tick } from '@maverick-js/signals'; import { computedKeyedMap } from '@maverick-js/signals/map'; const users = [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]; const $users = signal(users); const $elements = computedKeyedMap($users, (user, $index) => { const div = document.createElement('div'); div.textContent = user.name; return div; }); ``` ``` -------------------------------- ### Using computed() for Derived State Source: https://github.com/maverick-js/signals/blob/main/_autodocs/api-signals.md Demonstrates creating computed signals, dependency tracking, and handling errors with initial values. ```typescript import { signal, computed, tick } from '@maverick-js/signals'; const $width = signal(10); const $height = signal(20); // Computed signals auto-track their dependencies const $area = computed(() => { return $width() * $height(); }); console.log($area()); // 200 $width.set(15); tick(); console.log($area()); // 300 (automatically recomputed) // Deeply nested computed signals work fine const $doubled = computed(() => $area() * 2); const $tripled = computed(() => $doubled() * 1.5); // With initial value to handle errors const $safe = computed(() => { if ($width() < 0) throw new Error('Invalid width'); return $width() * $height(); }, { initial: 0 }); ``` -------------------------------- ### getContext Source: https://github.com/maverick-js/signals/blob/main/README.md Retrieves a context value for a given key by traversing the computation tree. ```APIDOC ## getContext(key) ### Description Attempts to get a context value for the given key by walking up the computation tree. Returns undefined if no value is found. ### Parameters - **key** (any) - The key associated with the context value. ``` -------------------------------- ### Cleanup with Root Source: https://github.com/maverick-js/signals/blob/main/_autodocs/README.md Wraps reactive primitives in a root scope to allow for manual disposal of all contained signals and effects. ```typescript import { root, signal, computed, effect } from '@maverick-js/signals'; root((dispose) => { const $count = signal(0); const $doubled = computed(() => $count() * 2); effect(() => console.log($doubled())); // ...later... dispose(); // Cleans up everything }); ``` -------------------------------- ### Handle Scopes and Context Source: https://github.com/maverick-js/signals/blob/main/_autodocs/quick-reference.md Provides utilities for retrieving the current scope, executing code within a specific scope, and managing context storage. ```typescript // Get current scope const scope = getScope(); // Run in a specific scope scoped(() => { // Code here runs in the given scope }, scopeRef); // Context storage setContext(key, value); const value = getContext(key); ``` -------------------------------- ### ComputedSignalOptions Source: https://github.com/maverick-js/signals/blob/main/_autodocs/types.md Configuration options for the computed() function, extending SignalOptions with initial value support. ```APIDOC ## ComputedSignalOptions ### Description Configuration options used by the computed() function. Extends SignalOptions to provide an initial value until the first computation succeeds. ### Properties - **id** (string) - Optional - Debugging identifier (dev/test only). - **dirty** ((prev: T | R, next: T | R) => boolean) - Optional - Custom change detection function. - **initial** (R) - Optional - Initial value returned until the first successful computation. ### Example ```typescript const $dangerous = computed(() => { if (Math.random() > 0.5) { throw new Error('Random error'); } return 42; }, { initial: 0, id: 'risky' }); ``` ``` -------------------------------- ### Manage signal batching and updates Source: https://github.com/maverick-js/signals/blob/main/_autodocs/configuration.md Rely on default batching for performance, using tick only when synchronous updates are strictly required. ```typescript // Good: Let batching happen $count.set(1); $count.set(2); $count.set(3); // Only if you need synchronous updates import { tick } from '@maverick-js/signals'; tick(); // Force synchronous flush ``` -------------------------------- ### effect(fn, options) Source: https://github.com/maverick-js/signals/blob/main/_autodocs/quick-reference.md Creates a side-effect that runs when dependencies change. ```APIDOC ## effect(fn, options) ### Description Registers a side-effect function that re-runs whenever any accessed signals change. ### Parameters - **fn** (function) - Required - The effect function. - **options** (object) - Optional - Configuration object. - **id** (string) - Optional - Debug identifier. ``` -------------------------------- ### peek() Source: https://github.com/maverick-js/signals/blob/main/_autodocs/api-core.md Reads a signal value without creating a dependency in the current computation. ```APIDOC ## peek(fn: () => T) ### Description Reads a signal value without creating a dependency in the current computation. This disables observer tracking while maintaining scope tracking. ### Signature `function peek(fn: () => T): T` ### Parameters - **fn** (() => T) - Required - Function to execute without tracking. ### Return Type `T` - The return value of the function. ### Example ```typescript import { signal, computed, peek } from '@maverick-js/signals'; const $a = signal(10); const $b = signal(20); const $sum = computed(() => { const aValue = $a(); const bValue = peek(() => $b()); return aValue + bValue; }); ``` ``` -------------------------------- ### Effects and Lifecycle Source: https://github.com/maverick-js/signals/blob/main/_autodocs/quick-reference.md Methods for managing side effects, cleanup, and error handling within signal scopes. ```APIDOC ## effect(fn) Subscribes to signal changes. Returns a stop function. ## root(fn) Creates a root scope for signals and computations. Returns a dispose function. ## onDispose(fn) Registers a callback to run when the current scope is disposed. ## onError(fn) Registers a callback to handle errors in child computations. ``` -------------------------------- ### Batching and Timing Source: https://github.com/maverick-js/signals/blob/main/_autodocs/quick-reference.md Methods for controlling the timing of signal updates. ```APIDOC ## tick() Flushes pending effects immediately to force synchronous updates. ``` -------------------------------- ### root Source: https://github.com/maverick-js/signals/blob/main/README.md Creates a reactive scope for child computations. When the provided dispose function is called, all computations created within the scope are disposed. ```APIDOC ## root(fn: (dispose: () => void) => T): T ### Description Creates a reactive scope. Computations created within this scope are treated as children and are disposed when the scope's dispose function is invoked. ### Parameters - **fn** (function) - Required - A function that receives a dispose callback and returns a value. ``` -------------------------------- ### Define ContextRecord Source: https://github.com/maverick-js/signals/blob/main/_autodocs/types.md Represents a plain object mapping context keys to values, supporting both strings and symbols. ```typescript type ContextRecord = Record; ``` -------------------------------- ### getContext(key, scope?) Source: https://github.com/maverick-js/signals/blob/main/_autodocs/api-core.md Retrieves a context value from the parent or ancestor scopes within the signal computation tree. ```APIDOC ## getContext(key, scope?) ### Description Retrieves a context value from the parent scope or ancestor scopes. It walks up the computation tree and returns the first matching value found, or undefined if not found. ### Signature `function getContext(key: string | symbol, scope?: Scope | null): T | undefined` ### Parameters - **key** (string | symbol) - Required - The context key to retrieve. - **scope** (Scope | null) - Optional - The scope to start searching from (defaults to currentScope). ### Return Type `T | undefined` - The context value, or undefined if not found. ### Example ```typescript import { root, effect, setContext, getContext } from '@maverick-js/signals'; const ThemeKey = Symbol('theme'); root(() => { setContext(ThemeKey, 'dark'); effect(() => { const theme = getContext(ThemeKey); // 'dark' }); }); ``` ``` -------------------------------- ### Handling Microtask Batching Source: https://github.com/maverick-js/signals/blob/main/_autodocs/quick-reference.md Signal updates are batched by default; use tick() to flush pending effects synchronously. ```typescript const $count = signal(0); effect(() => console.log($count())); $count.set(1); console.log('After set'); tick(); console.log('After tick'); // Output: // "After set" // 0 (initial effect from setup) // 1 (after tick flush) // "After tick" ``` -------------------------------- ### Reading Values Source: https://github.com/maverick-js/signals/blob/main/_autodocs/quick-reference.md Methods for reading signal values with or without dependency tracking. ```APIDOC ## $signal() Reads the signal value and tracks it as a dependency. ## peek($signal) Reads the signal value without tracking it as a dependency. ## untrack(fn) Executes a function without tracking any signal dependencies. ``` -------------------------------- ### computedMap(list, map, options) Source: https://github.com/maverick-js/signals/blob/main/_autodocs/configuration.md Maps a signal list to a new signal list with optional debugging configuration. ```APIDOC ## computedMap(list, map, options) ### Description Transforms a signal containing an array into a new signal containing a mapped array. ### Parameters - **list** (ReadSignal) - Required - The source signal list. - **map** (Function) - Required - Mapping function receiving (value: ReadSignal, index: number). - **options** (Object) - Optional - Configuration object. - **id** (string) - Optional - Debugging identifier, only available in development/test environments. ### Returns - **ReadSignal** - A signal containing the mapped items. ``` -------------------------------- ### peek Source: https://github.com/maverick-js/signals/blob/main/README.md Reads a signal value without tracking it as a dependency. ```APIDOC ## peek(fn: () => T): T ### Description Returns the current value of a signal or computation without triggering observer tracking. ### Parameters - **fn** (function) - Required - The signal or computation to read. ``` -------------------------------- ### Create Signals and Computed Values Source: https://github.com/maverick-js/signals/blob/main/_autodocs/quick-reference.md Defines writable signals, computed read-only signals, and how to convert writable signals to read-only. ```typescript // Writable signal const $count = signal(initialValue, options?); $count(); // Read $count.set(value); // Write // Readonly signal const $computed = computed(() => { /* ... */ }, options?); $computed(); // Read only // Make readonly const $readonly = readonly($writable); ``` -------------------------------- ### Observe default shallow inequality behavior Source: https://github.com/maverick-js/signals/blob/main/_autodocs/configuration.md Demonstrates how shallow inequality affects notification triggers for primitive and object values. ```typescript const $count = signal(10); $count.set(10); // No notification (10 === 10) $count.set(10); // No notification (10 === 10) const $user = signal({ id: 1, name: 'Alice' }); const user1 = { id: 1, name: 'Alice' }; const user2 = { id: 1, name: 'Alice' }; $user.set(user1); $user.set(user2); // Notifies! (user1 !== user2, different objects) ``` -------------------------------- ### Provider Pattern for State Management Source: https://github.com/maverick-js/signals/blob/main/_autodocs/patterns-and-examples.md Encapsulates store logic within a provider function and exposes it via context for child components. ```typescript import { root, signal, effect, setContext, getContext, onDispose } from '@maverick-js/signals'; interface UserStore { user: () => any; login: (email: string, password: string) => Promise; logout: () => void; } const UserStoreKey = Symbol('UserStore'); function createUserStore(): UserStore { const $user = signal(null); return { user: () => $user(), login: async (email, password) => { // Fetch user... $user.set({ email, authenticated: true }); }, logout: () => { $user.set(null); } }; } root(() => { const userStore = createUserStore(); setContext(UserStoreKey, userStore); // Use in child effects effect(() => { const store = getContext(UserStoreKey); if (store?.user()) { console.log('User logged in:', store.user().email); } }); }); ``` -------------------------------- ### Implement custom change detection Source: https://github.com/maverick-js/signals/blob/main/_autodocs/configuration.md Use the dirty function for semantic comparisons, but avoid expensive deep comparisons that impact performance. ```typescript // Good: Comparisons that make semantic sense const $celsius = signal(20, { dirty: (prev, next) => Math.abs(next - prev) >= 0.1 }); // Avoid: Expensive comparisons on every set const $data = signal(largeObject, { dirty: (prev, next) => complexDeepComparison(prev, next) // Too expensive! }); ```