### Install Alwatr Signal Package Source: https://github.com/alwatr/flux/blob/next/packages/signal/README.md Installs the Alwatr Signal package using npm. This is the first step to using the library in your project. Ensure you have Node.js and npm installed. ```bash npm i @alwatr/signal ``` -------------------------------- ### Finite State Machine (FSM) - Simple Example (TypeScript) Source: https://context7.com/alwatr/flux/llms.txt Implements a simple finite state machine using Alwatr's FSM service. This example defines states, events, and context for a light switch, demonstrating state transitions and context updates. It utilizes `createFsmService` to manage the machine's lifecycle and signal emissions for state changes. ```typescript import {createFsmService} from '@alwatr/fsm'; import type {StateMachineConfig} from '@alwatr/fsm'; type LightState = 'on' | 'off'; type LightEvent = {type: 'TOGGLE'}; type LightContext = {brightness: number}; const lightConfig: StateMachineConfig = { name: 'light-switch', initial: 'off', context: {brightness: 0}, states: { off: { on: { TOGGLE: { target: 'on', assigners: [() => ({brightness: 100})], }, }, }, on: { on: { TOGGLE: { target: 'off', assigners: [() => ({brightness: 0})], }, }, }, }, }; const lightService = createFsmService(lightConfig); lightService.stateSignal.subscribe((state) => { console.log(`Light is ${state.name}, brightness: ${state.context.brightness}`); }); // Output: Light is off, brightness: 0 lightService.eventSignal.dispatch({type: 'TOGGLE'}); // Output: Light is on, brightness: 100 lightService.eventSignal.dispatch({type: 'TOGGLE'}); // Output: Light is off, brightness: 0 ``` -------------------------------- ### Install Alwatr Flux Packages Source: https://context7.com/alwatr/flux/llms.txt Installs the core Alwatr Flux packages using npm. Users can install individual packages like @alwatr/signal or @alwatr/fsm, or the combined @alwatr/flux package. ```bash npm install @alwatr/signal npm install @alwatr/fsm # Or install both at once npm install @alwatr/flux ``` -------------------------------- ### StateSignal API Source: https://github.com/alwatr/flux/blob/next/packages/signal/README.md Documentation for the StateSignal class, including its constructor, methods for getting and setting values, and subscription capabilities. ```APIDOC ## StateSignal ### Description Manages a state value and notifies listeners when the value changes. ### Constructor - **`constructor(config)`**: Creates a new state signal. - **`config.name`** (`string`) - The name of the signal. - **`config.initialValue`** (`T`) - The initial value of the signal. ### Methods - **`.get()`**: `T` - Gets the current value of the signal. - **`.set(newValue: T)`**: Sets a new value for the signal and notifies all listeners. - **`.subscribe(callback, options?)`**: Subscribes a listener function to the signal. Returns an object with an `unsubscribe` method. - `options.once` (`boolean`, optional) - If true, the listener is called only once and then automatically removed. - `options.priority` (`boolean`, optional) - If true, the listener is moved to the front of the queue and executed before other listeners. - `options.receivePrevious` (`boolean`, optional, default: `true`) - If false, prevents the listener from being called immediately with the current value upon subscription. - **`.untilNext()`**: Returns a `Promise` that resolves with the next value emitted by the signal. ``` -------------------------------- ### FSM Service Creation API Source: https://github.com/alwatr/flux/blob/next/README.md API for creating a new Finite State Machine (FSM) service using the `createFsmService` factory function. ```APIDOC ## `createFsmService(config)` ### Description The main factory function to create a new FSM service. ### Method `createFsmService` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **`config`** (StateMachineConfig) - Required - The declarative configuration object for the state machine. ### Request Example ```json { "config": { ... state machine configuration ... } } ``` ### Response #### Success Response (200) - **`FsmService`** - An instance of the FSM service with two main properties: `stateSignal` and `eventSignal`. #### Response Example ```json { "stateSignal": { ... readable signal object ... }, "eventSignal": { ... signal object to dispatch events ... } } ``` ``` -------------------------------- ### Key Types Source: https://github.com/alwatr/flux/blob/next/packages/fsm/README.md Defines the core types used in the Alwatr Flux library for state machine configuration and operation. ```APIDOC ## Key Types ### `MachineState` - **Description**: Represents the complete state of the machine, including the current state name and its associated context. - **Properties**: - **name** (S) - The name of the current state. - **context** (C) - The context data associated with the current state. ### `MachineEvent` - **Description**: The base interface for all events dispatched to the state machine. - **Properties**: - **type** (T) - A unique identifier for the event type. ### `StateMachineConfig` - **Description**: The main configuration object used to define the state machine's behavior. - **Properties**: - **initial** (TState) - The initial state of the machine. - **context** (TContext) - The initial context object for the machine. - **states** (object) - An object mapping state names to their configurations (transitions, entry/exit actions, etc.). ### `Transition` - **Description**: Defines the configuration for a transition from one state to another. - **Properties**: - **target** (TState) - Optional. The target state for this transition. If not provided, the machine stays in the current state. - **condition** (function) - Optional. A predicate function that must return true for the transition to occur. - **actions** (array) - Optional. An array of action functions to execute during the transition. ``` -------------------------------- ### Key Types in Alwatr Flux Source: https://github.com/alwatr/flux/blob/next/README.md Defines the core types used within Alwatr Flux for state machine configuration and operation. ```APIDOC ## Key Types / انواع کلیدی ### `MachineState` Represents the complete state, containing `name: S` and `context: C`. ### `MachineEvent` The base interface for events. Must have a `type: T` property. ### `StateMachineConfig` The main configuration object defining `initial`, `context`, and `states`. ### `Transition` Defines a transition with an optional `target`, `condition`, and `assigners`. ``` -------------------------------- ### Configure Light Switch State Machine Logic (TypeScript) Source: https://github.com/alwatr/flux/blob/next/README.md Configures the Alwatr Flux state machine for a light switch. It defines the initial state, context, and the transitions between 'on' and 'off' states triggered by 'TOGGLE' and 'SET_BRIGHTNESS' events. This includes setting initial context values and updating context during transitions. ```typescript const lightMachineConfig: StateMachineConfig = { name: 'light-switch', initial: 'off', context: {brightness: 0}, states: { off: { on: { // When in the 'off' state and a 'TOGGLE' event occurs... TOGGLE: { target: 'on', // ...transition to the 'on' state. assigners: [() => ({brightness: 100})], // ...and set brightness to 100. }, }, }, on: { on: { // When in the 'on' state and a 'TOGGLE' event occurs... TOGGLE: { target: 'off', // ...transition to 'off'. assigners: [() => ({brightness: 0})], // ...and reset brightness. }, // An internal transition that only updates context without changing the state. SET_BRIGHTNESS: { // No 'target' means it's an internal transition. assigners: [(event) => ({brightness: event.level})], }, }, }, }, }; ``` -------------------------------- ### Factory Function: createFsmService Source: https://github.com/alwatr/flux/blob/next/packages/fsm/README.md The main factory function used to create a new FSM service. It takes a configuration object and returns an FSM service instance. ```APIDOC ## POST /api/fsm/create ### Description Creates a new Finite State Machine (FSM) service instance. ### Method POST ### Endpoint /api/fsm/create ### Parameters #### Request Body - **config** (StateMachineConfig) - Required - The declarative configuration object for the state machine. ### Request Example ```json { "config": { "initial": "idle", "context": { "count": 0 }, "states": { "idle": { "on": { "INCREMENT": { "target": "counting", "actions": ["incrementCounter"] } } }, "counting": { "on": { "RESET": { "target": "idle", "actions": ["resetCounter"] } } } } } } ``` ### Response #### Success Response (200) - **service** (FsmService) - An instance of the FSM service with `stateSignal` and `eventSignal`. - **stateSignal** (ReadableSignal>) - Emits the current machine state. - **eventSignal** (Signal>) - Signal to dispatch new events. #### Response Example ```json { "service": { "stateSignal": { "value": { "name": "idle", "context": { "count": 0 } } }, "eventSignal": {} } } ``` ``` -------------------------------- ### Define Light Switch State Machine Types (TypeScript) Source: https://github.com/alwatr/flux/blob/next/README.md Defines the necessary TypeScript types for the light switch state machine, including context, states, and events. This setup ensures type safety for the state machine's configuration and operations. ```typescript import type {StateMachineConfig} from '@alwatr/fsm'; // The context stores the brightness level. type LightContext = {brightness: number}; // The machine can only be in one of these two states. type LightState = 'on' | 'off'; // Define the events that can be sent to the machine. type LightEvent = {type: 'TOGGLE'} | {type: 'SET_BRIGHTNESS'; level: number}; ``` -------------------------------- ### FSM Persistent State with LocalStorage in TypeScript Source: https://context7.com/alwatr/flux/llms.txt This example shows how to create a state machine that persists its state in localStorage using the @alwatr/fsm library. It defines states, events, and context for an onboarding flow. The `persistent` configuration option with `storageKey` enables automatic saving and restoring of the FSM state across page reloads. ```typescript import {createFsmService} from '@alwatr/fsm'; import type {StateMachineConfig} from '@alwatr/fsm'; type OnboardingState = 'welcome' | 'profile' | 'preferences' | 'completed'; type OnboardingEvent = {type: 'NEXT'} | {type: 'BACK'}; type OnboardingContext = {step: number}; const onboardingConfig: StateMachineConfig = { name: 'onboarding', initial: 'welcome', context: {step: 1}, persistent: { schemaVersion: 1, storageKey: 'app-onboarding-state', }, states: { welcome: { on: { NEXT: { target: 'profile', assigners: [() => ({step: 2})], }, }, }, profile: { on: { NEXT: { target: 'preferences', assigners: [() => ({step: 3})], }, BACK: { target: 'welcome', assigners: [() => ({step: 1})], }, }, }, preferences: { on: { NEXT: { target: 'completed', assigners: [() => ({step: 4})], }, BACK: { target: 'profile', assigners: [() => ({step: 2})], }, }, }, completed: {}, }, }; // State is automatically saved to localStorage const onboardingService = createFsmService(onboardingConfig); // On page reload, state is restored console.log('Current state:', onboardingService.stateSignal.get()); onboardingService.eventSignal.dispatch({type: 'NEXT'}); // State persists across page refreshes ``` -------------------------------- ### Combine Signals and Observe Updates in TypeScript Source: https://github.com/alwatr/flux/blob/next/packages/signal/README.md Demonstrates how changes in StateSignals propagate through ComputedSignals and trigger EffectSignals. This example includes subscribing to signal updates to observe the reactive flow and the final output. ```typescript // Subscribe to changes for demonstration fullName.subscribe((newFullName) => { console.log(`Full name signal updated to: ${newFullName}`); }); // Let's change the first name. firstName.set('Jane'); // This will trigger: // 1. `fullName` to recalculate its value. // 2. The `fullName.subscribe` callback to run. // 3. The `loggerEffect` to run. // Let's increment the counter. counter.set(1); // This will trigger: // 1. The `loggerEffect` to run again. /* The output would be: User: John Full name signal updated to: User: Jane User: Jane has clicked 0 times. User: Jane has clicked 1 times. */ ``` -------------------------------- ### Define Core Signals with StateSignal and ComputedSignal Source: https://github.com/alwatr/flux/blob/next/packages/flux/README.md This example demonstrates defining a StateSignal to hold user input and a ComputedSignal to derive a boolean value based on the input's length. It uses the @alwatr/signal library. ```typescript import {StateSignal, ComputedSignal, createDebouncedSignal} from '@alwatr/signal'; // 1. A StateSignal to hold the raw input from the user. // ۱. یک StateSignal برای نگهداری ورودی خام کاربر. const searchInput = new StateSignal({ name: 'search-input', initialValue: '', }); // 2. A ComputedSignal that derives a boolean value. // It's true only if the input is long enough. // ۲. یک ComputedSignal که یک مقدار boolean را استخراج می‌کند. // این سیگنال تنها زمانی true است که طول ورودی کافی باشد. const isSearchValid = new ComputedSignal({ name: 'is-search-valid', deps: [searchInput], get: () => searchInput.get().length >= 3, }); ``` -------------------------------- ### Guarded Transitions with Conditions (TypeScript) Source: https://context7.com/alwatr/flux/llms.txt Illustrates how to implement guarded transitions in a state machine using conditions. This example simulates an ATM machine, where transitions are only allowed if specific criteria (e.g., correct PIN, sufficient balance) are met. It showcases conditional logic for authentication and withdrawal operations. ```typescript import {createFsmService} from '@alwatr/fsm'; import type {StateMachineConfig} from '@alwatr/fsm'; type ATMState = 'idle' | 'authenticated' | 'processing'; type ATMEvent = | {type: 'INSERT_CARD'; pin: string} | {type: 'WITHDRAW'; amount: number} | {type: 'COMPLETE'} | {type: 'CANCEL'}; type ATMContext = {balance: number; pin: string}; const atmConfig: StateMachineConfig = { name: 'atm-machine', initial: 'idle', context: {balance: 1000, pin: '1234'}, states: { idle: { on: { INSERT_CARD: { target: 'authenticated', condition: (event, context) => event.pin === context.pin, }, }, }, authenticated: { entry: [() => console.log('Authentication successful')], on: { WITHDRAW: { target: 'processing', condition: (event, context) => event.amount <= context.balance, assigners: [(event, context) => ({balance: context.balance - event.amount})], }, CANCEL: {target: 'idle'}, }, }, processing: { entry: [(event, context) => console.log(`Dispensing cash. Balance: ${context.balance}`)], on: { COMPLETE: {target: 'idle'}, }, }, }, }; const atmService = createFsmService(atmConfig); atmService.eventSignal.dispatch({type: 'INSERT_CARD', pin: '1234'}); // Output: Authentication successful atmService.eventSignal.dispatch({type: 'WITHDRAW', amount: 100}); // Output: Dispensing cash. Balance: 900 atmService.eventSignal.dispatch({type: 'COMPLETE'}); ``` -------------------------------- ### FSM Entry and Exit Effects in TypeScript Source: https://context7.com/alwatr/flux/llms.txt This example illustrates how to define entry and exit effects for states in a state machine using @alwatr/fsm. The `entry` property allows executing code when a state is entered, and the `exit` property allows executing code when a state is exited. This is useful for side effects like starting/stopping timers or logging. ```typescript import {createFsmService} from '@alwatr/fsm'; import type {StateMachineConfig} from '@alwatr/fsm'; type TimerState = 'idle' | 'running' | 'paused'; type TimerEvent = {type: 'START'} | {type: 'PAUSE'} | {type: 'RESET'}; type TimerContext = {elapsed: number; intervalId: number | null}; const timerConfig: StateMachineConfig = { name: 'timer', initial: 'idle', context: {elapsed: 0, intervalId: null}, states: { idle: { on: { START: {target: 'running'}, }, }, running: { entry: [ () => { console.log('Timer started'); // Note: In real app, store intervalId in context return; }, ], exit: [ () => { console.log('Timer stopped'); }, ], on: { PAUSE: {target: 'paused'}, RESET: { target: 'idle', assigners: [() => ({elapsed: 0})], }, }, }, paused: { entry: [() => console.log('Timer paused')], on: { START: {target: 'running'}, RESET: { target: 'idle', assigners: [() => ({elapsed: 0})], }, }, }, }, }; const timerService = createFsmService(timerConfig); timerService.eventSignal.dispatch({type: 'START'}); // Output: Timer started timerService.eventSignal.dispatch({type: 'PAUSE'}); // Output: Timer stopped // Output: Timer paused timerService.eventSignal.dispatch({type: 'RESET'}); ``` -------------------------------- ### Create a Debounced Signal with createDebouncedSignal Source: https://github.com/alwatr/flux/blob/next/packages/flux/README.md This example shows how to create a debounced signal using the `createDebouncedSignal` function from @alwatr/signal. It delays updates to the debounced signal until a specified period of inactivity has passed on the source signal, preventing excessive event firing. ```typescript // 3. A debounced signal that waits for 300ms of inactivity on the searchInput. // ۳. یک سیگنال debounce شده که ۳۰۰ میلی‌ثانیه پس از توقف فعالیت در searchInput به‌روز می‌شود. const debouncedSearch = createDebouncedSignal(searchInput, { delay: 300, }); ``` -------------------------------- ### FSM Internal Transitions (No State Change) in TypeScript Source: https://context7.com/alwatr/flux/llms.txt This example demonstrates internal transitions within a state machine using @alwatr/fsm. Internal transitions allow for updating the FSM's context without changing the current state. This is useful for actions like volume adjustments where the core state remains 'playing'. ```typescript import {createFsmService} from '@alwatr/fsm'; import type {StateMachineConfig} from '@alwatr/fsm'; type PlayerState = 'playing'; type PlayerEvent = | {type: 'VOLUME_UP'} | {type: 'VOLUME_DOWN'} | {type: 'SET_VOLUME'; level: number}; type PlayerContext = {volume: number}; const playerConfig: StateMachineConfig = { name: 'music-player', initial: 'playing', context: {volume: 50}, states: { playing: { on: { // Internal transitions - no target, only context updates VOLUME_UP: { assigners: [(event, context) => ({volume: Math.min(100, context.volume + 10)})], }, VOLUME_DOWN: { assigners: [(event, context) => ({volume: Math.max(0, context.volume - 10)})], }, SET_VOLUME: { assigners: [(event) => ({volume: event.level})], }, }, }, }, }; const playerService = createFsmService(playerConfig); playerService.stateSignal.subscribe((state) => { console.log(`Volume: ${state.context.volume}`); }); // Output: Volume: 50 playerService.eventSignal.dispatch({type: 'VOLUME_UP'}); // Output: Volume: 60 playerService.eventSignal.dispatch({type: 'SET_VOLUME', level: 75}); // Output: Volume: 75 ``` -------------------------------- ### Signal Subscription Options (TypeScript) Source: https://context7.com/alwatr/flux/llms.txt Demonstrates advanced subscription options for Alwatr signals, including one-time subscriptions, priority subscriptions that execute first, and options to control immediate receipt of the current value. These options allow fine-grained control over how listeners react to signal emissions. ```typescript import {StateSignal} from '@alwatr/signal'; const notifications = new StateSignal({ name: 'notifications', initialValue: 'Welcome', }); // One-time subscription notifications.subscribe( (msg) => console.log('Once:', msg), {once: true} ); // Priority subscription (runs first) notifications.subscribe( (msg) => console.log('Priority:', msg), {priority: true} ); // Don't receive current value immediately notifications.subscribe( (msg) => console.log('Future only:', msg), {receivePrevious: false} ); notifications.set('New notification'); // Output: Priority: New notification // Output: Once: New notification // Output: Future only: New notification notifications.set('Another notification'); // Output: Priority: Another notification // Output: Future only: Another notification // (Once listener is gone) ``` -------------------------------- ### ComputedSignal API Source: https://github.com/alwatr/flux/blob/next/packages/signal/README.md Documentation for the ComputedSignal class, including its constructor, memoized getter, and crucial destroy method for cleanup. ```APIDOC ## ComputedSignal ### Description Creates a signal whose value is computed based on other signals. The value is memoized and recalculated only when dependencies change. ### Constructor - **`constructor(config)`**: Creates a new computed signal. - **`config.name`** (`string`) - The name of the signal. - **`config.deps`** (`IReadonlySignal[]`) - An array of dependency signals that this computed signal depends on. - **`config.get`** (`() => T`) - The function that computes the value of the signal. This function will be called when dependencies change. ### Methods - **`.get()`**: `T` - Gets the current (memoized) value of the computed signal. - **`.destroy()`**: Cleans up the signal's internal subscriptions to its dependencies. **This is crucial to prevent memory leaks.** - **`.subscribe(callback, options?)`**: Subscribes a listener function to the signal. Returns an object with an `unsubscribe` method. - **`.untilNext()`**: Returns a `Promise` that resolves with the next value emitted by the signal. ``` -------------------------------- ### Subscribe to FSM State Changes and Dispatch Events (TypeScript) Source: https://github.com/alwatr/flux/blob/next/packages/flux/README.md This snippet shows how to create an FSM service, subscribe to its state changes to update the UI, and dispatch events to drive the machine. It utilizes Alwatr's FSM capabilities for managing application logic. ```typescript const fetchService = createFsmService(fetchMachineConfig); // Subscribe to state changes to update the UI. fetchService.stateSignal.subscribe((state) => { console.log(`Current State: ${state.name}`, state.context); // In a real app: // if (state.name === 'pending') showSpinner(); // if (state.name === 'success') showUserData(state.context.user); // if (state.name === 'error') showError(state.context.error); }); // Dispatch events to drive the machine. fetchService.eventSignal.dispatch({type: 'FETCH', id: '1'}); // If it fails, you could dispatch a retry event. // setTimeout(() => { // if (fetchService.stateSignal.get().name === 'error') { // fetchService.eventSignal.dispatch({type: 'RETRY'}); // } // }, 1000); ``` -------------------------------- ### EffectSignal API Source: https://github.com/alwatr/flux/blob/next/packages/signal/README.md Documentation for the EffectSignal class, detailing its constructor for running side effects based on signal dependencies and its destroy method. ```APIDOC ## EffectSignal ### Description Creates a signal that runs a side effect function whenever its dependencies change. ### Constructor - **`constructor(config)`**: Creates a new effect signal. - **`config.name`** (`string`) - The name of the signal. - **`config.deps`** (`IReadonlySignal[]`) - An array of dependency signals that trigger the effect. - **`config.run`** (`() => void | Promise`) - The side effect function to execute when dependencies change. - **`config.runImmediately`** (`boolean`, optional, default: `false`) - If true, the effect function will be executed immediately after the signal is created. ### Methods - **`.destroy()`**: Cleans up the signal's internal subscriptions to its dependencies. **This is crucial to prevent memory leaks.** - **`.subscribe(callback, options?)`**: Subscribes a listener function to the signal. Returns an object with an `unsubscribe` method. - **`.untilNext()`**: Returns a `Promise` that resolves when the effect runs next. ``` -------------------------------- ### Common Signal Methods Source: https://github.com/alwatr/flux/blob/next/packages/signal/README.md Details on methods common across different Alwatr Signal types, such as subscribing, waiting for the next event, and destroying signals. ```APIDOC ## Common Methods These methods are available on most Alwatr Signal types (`ComputedSignal`, `EffectSignal`, `EventSignal`, and `StateSignal`). - **`.subscribe(callback, options?)`**: - **Description**: Subscribes a listener function to the signal. The listener will be called when the signal's value changes or an event is dispatched. - **Returns**: `{ unsubscribe: () => void }` - An object containing a function to unsubscribe the listener. - **`options`** (optional): - `once: true`: The listener is called only once and then automatically removed. - `priority: true`: The listener is moved to the front of the queue and is executed before other listeners. - **`.untilNext()`**: - **Description**: Returns a `Promise` that resolves with the next value or payload emitted by the signal. - **`.destroy()`**: - **Availability**: Available on `ComputedSignal`, `EffectSignal`, and `EventSignal` (not on `StateSignal`). - **Description**: Cleans up the signal's internal subscriptions and resources. **It is essential to call this method when a signal is no longer needed to prevent memory leaks.** ``` -------------------------------- ### EventSignal API Source: https://github.com/alwatr/flux/blob/next/packages/signal/README.md Documentation for the EventSignal class, used for dispatching events and subscribing to them. ```APIDOC ## EventSignal ### Description Manages and dispatches events to multiple listeners. ### Constructor - **`constructor(config)`**: Creates a new event signal. - **`config.name`** (`string`) - The name of the event signal. ### Methods - **`.dispatch(payload: T)`**: Dispatches an event with the given payload to all subscribed listeners. - **`.subscribe(callback, options?)`**: Subscribes a listener function to the event signal. Returns an object with an `unsubscribe` method. - `options.once` (`boolean`, optional) - If true, the listener is called only once and then automatically removed. - `options.priority` (`boolean`, optional) - If true, the listener is moved to the front of the queue and executed before other listeners. - **`.untilNext()`**: Returns a `Promise` that resolves with the next payload dispatched by the event signal. ``` -------------------------------- ### EffectSignal: Handling Side Effects in TypeScript Source: https://context7.com/alwatr/flux/llms.txt Demonstrates the use of EffectSignal for managing side effects triggered by state changes. It shows how to define dependencies, a `run` function for asynchronous operations, and control initial execution. Includes cleanup. ```typescript import {StateSignal, EffectSignal} from '@alwatr/signal'; const userId = new StateSignal({ name: 'current-user-id', initialValue: '', }); const apiCaller = new EffectSignal({ name: 'fetch-user-effect', deps: [userId], run: async () => { const id = userId.get(); if (!id) return; console.log(`Fetching user ${id}...`); const response = await fetch(`https://api.example.com/users/${id}`); const user = await response.json(); console.log('User data:', user); }, runImmediately: false, // Don't run on creation }); userId.set('123'); // Output: Fetching user 123... // Output: User data: {id: '123', name: '...'} // Cleanup apiCaller.destroy(); ``` -------------------------------- ### StateSignal: Mutable Reactive State in TypeScript Source: https://context7.com/alwatr/flux/llms.txt Demonstrates the creation and usage of StateSignal for managing mutable reactive state. It shows how to initialize a state, subscribe to its changes, update its value, and unsubscribe. ```typescript import {StateSignal} from '@alwatr/signal'; // Create a mutable state container const counter = new StateSignal({ name: 'app-counter', initialValue: 0, }); // Subscribe to changes (called immediately with current value) const subscription = counter.subscribe((value) => { console.log(`Counter: ${value}`); }); // Output: Counter: 0 // Update the state counter.set(5); // Output: Counter: 5 counter.set(counter.get() + 1); // Output: Counter: 6 // Unsubscribe when done subscription.unsubscribe(); ``` -------------------------------- ### Create State Signals with TypeScript Source: https://github.com/alwatr/flux/blob/next/packages/signal/README.md Demonstrates the creation of StateSignal instances in TypeScript. StateSignals hold mutable values and notify dependents upon changes. Each signal is initialized with a name and an initial value. ```typescript import {StateSignal} from '@alwatr/signal'; // A signal to hold the user's first name. const firstName = new StateSignal({ name: 'user-firstName', initialValue: 'John', }); // A signal to hold a simple counter. const counter = new StateSignal({ name: 'app-counter', initialValue: 0, }); ``` -------------------------------- ### Async Data Fetching with Effects (TypeScript) Source: https://context7.com/alwatr/flux/llms.txt Demonstrates how to perform asynchronous data fetching within a state machine. It handles pending states, resolves successful fetches, and manages errors. The FSM uses `fetch` API for data retrieval and dispatches events to transition between states. ```typescript import {createFsmService} from '@alwatr/fsm'; import type {StateMachineConfig} from '@alwatr/fsm'; type FetchState = 'idle' | 'pending' | 'success' | 'error'; type User = {id: string; name: string}; type FetchEvent = | {type: 'FETCH'; id: string} | {type: 'RESOLVE'; user: User} | {type: 'REJECT'; error: string} | {type: 'RETRY'}; type FetchContext = {user: User | null; error: string | null; retries: number}; const fetchConfig: StateMachineConfig = { name: 'user-fetcher', initial: 'idle', context: {user: null, error: null, retries: 0}, states: { idle: { on: { FETCH: {target: 'pending'}, }, }, pending: { entry: [ async (event) => { if (event.type !== 'FETCH') return; try { const response = await fetch(`/api/users/${event.id}`); if (!response.ok) throw new Error('Not found'); const user = await response.json(); return {type: 'RESOLVE', user}; } catch (err) { return {type: 'REJECT', error: (err as Error).message}; } }, ], on: { RESOLVE: { target: 'success', assigners: [(event) => ({user: event.user, error: null})], }, REJECT: { target: 'error', assigners: [(event) => ({error: event.error})], }, }, }, success: { entry: [(event, context) => console.log('Loaded user:', context.user)], }, error: { on: { RETRY: { target: 'pending', condition: (event, context) => context.retries < 3, assigners: [(event, context) => ({retries: context.retries + 1})], }, }, }, }, }; const fetchService = createFsmService(fetchConfig); fetchService.stateSignal.subscribe((state) => { console.log('State:', state.name, 'Context:', state.context); }); fetchService.eventSignal.dispatch({type: 'FETCH', id: '123'}); // Output: State: pending ... // Output: Loaded user: {id: '123', name: '...'} // Output: State: success ... ``` -------------------------------- ### EventSignal: Managing Stateless Events in TypeScript Source: https://context7.com/alwatr/flux/llms.txt Explains how to use EventSignal for managing stateless events. It covers defining event interfaces, subscribing to events, dispatching events, and waiting for the next event using `untilNext()`. ```typescript import {EventSignal} from '@alwatr/signal'; interface ClickEvent { type: 'click'; x: number; y: number; button: 'left' | 'right'; } const clickEvents = new EventSignal({ name: 'mouse-clicks', }); // Subscribe to events clickEvents.subscribe((event) => { console.log(`Clicked at (${event.x}, ${event.y}) with ${event.button} button`); }); // Dispatch events clickEvents.dispatch({type: 'click', x: 100, y: 200, button: 'left'}); // Output: Clicked at (100, 200) with left button // Wait for next event const nextClick = await clickEvents.untilNext(); console.log('Next click:', nextClick); ``` -------------------------------- ### ComputedSignal: Derived State in TypeScript Source: https://context7.com/alwatr/flux/llms.txt Illustrates how to create derived state using ComputedSignal. It shows how to define dependencies and a getter function that automatically recalculates the derived state when any of its dependencies change. Includes subscription and cleanup. ```typescript import {StateSignal, ComputedSignal} from '@alwatr/signal'; const firstName = new StateSignal({ name: 'firstName', initialValue: 'John', }); const lastName = new StateSignal({ name: 'lastName', initialValue: 'Doe', }); // Automatically recalculates when dependencies change const fullName = new ComputedSignal({ name: 'fullName', deps: [firstName, lastName], get: () => `${firstName.get()} ${lastName.get()}`, }); fullName.subscribe((name) => console.log(`Full name: ${name}`)); // Output: Full name: John Doe firstName.set('Jane'); // Output: Full name: Jane Doe // Clean up when no longer needed fullName.destroy(); ``` -------------------------------- ### Resilient Data Fetcher with State Machine - TypeScript Source: https://github.com/alwatr/flux/blob/next/packages/flux/README.md Defines a Finite State Machine (FSM) for a resilient data fetching workflow. It handles 'idle', 'pending', 'success', and 'error' states, including context for user data, errors, and retry counts. Transitions are defined with entry effects, assigners, and conditions. ```typescript import {createFsmService} from '@alwatr/fsm'; import type {StateMachineConfig} from '@alwatr/fsm'; // Types for our machine type User = {id: string; name: string}; type FetchContext = {user: User | null; error: Error | null; retries: number}; type FetchState = 'idle' | 'pending' | 'success' | 'error'; type FetchEvent = {type: 'FETCH'; id: string} | {type: 'RESOLVE'; user: User} | {type: 'REJECT'; error: Error} | {type: 'RETRY'}; // The entire logic is declared in this single configuration object. // تمام منطق در این آبجکت پیکربندی واحد تعریف می‌شود. const fetchMachineConfig: StateMachineConfig = { name: 'user-fetcher', initial: 'idle', context: {user: null, error: null, retries: 0}, states: { idle: { on: { FETCH: {target: 'pending'}, }, }, pending: { // On entering 'pending', this effect runs automatically. // با ورود به وضعیت 'pending'، این effect به صورت خودکار اجرا می‌شود. entry: [ async (event) => { if (event.type !== 'FETCH') return; // Type guard try { const response = await fetch(`https://api.example.com/users/${event.id}`); if (!response.ok) throw new Error('User not found'); const user = (await response.json()) as User; // An effect can dispatch a new event back to the machine. // یک effect می‌تواند یک رویداد جدید را به خود ماشین ارسال کند. return {type: 'RESOLVE', user}; } catch (err) { return {type: 'REJECT', error: err as Error}; } }, ], on: { RESOLVE: { target: 'success', assigners: [(event) => ({user: event.user})], // Update context }, REJECT: { target: 'error', assigners: [(event) => ({error: event.error})], // Update context }, }, }, success: { on: { FETCH: {target: 'pending'}, // Allow re-fetching }, }, error: { on: { RETRY: { target: 'pending', // A transition can be protected by a condition. // یک گذار می‌تواند توسط یک شرط محافظت شود. condition: (event, context) => context.retries < 3, assigners: [(event, context) => ({retries: context.retries + 1})], }, }, }, }, }; ``` -------------------------------- ### Create a Computed Signal with TypeScript Source: https://github.com/alwatr/flux/blob/next/packages/signal/README.md Illustrates the creation of a ComputedSignal in TypeScript. This read-only signal derives its value from other signals. It automatically recalculates when its dependencies change and memoizes the result. ```typescript import {ComputedSignal} from '@alwatr/signal'; const fullName = new ComputedSignal({ name: 'user-fullName', deps: [firstName], // This computed signal depends on firstName. get: () => `User: ${firstName.get()}`, }); console.log(fullName.get()); // Outputs: "User: John" ``` -------------------------------- ### Preventing Memory Leaks with ComputedSignal.destroy() Source: https://github.com/alwatr/flux/blob/next/packages/signal/README.md Demonstrates how to create a ComputedSignal and correctly call its .destroy() method when it's no longer needed. This is crucial for unsubscribing dependencies and enabling garbage collection, thus preventing memory leaks. ```typescript const isEven = new ComputedSignal({ deps: [counter], get: () => counter.get() % 2 === 0, }); // ... use it for a while ... // When the component/logic using it is about to be removed: isEven.destroy(); ``` -------------------------------- ### Create an Effect Signal with TypeScript Source: https://github.com/alwatr/flux/blob/next/packages/signal/README.md Shows how to create an EffectSignal in TypeScript. This signal executes a side effect (e.g., logging, DOM updates) when its dependent signals change. It acts as a bridge to the outside world. ```typescript import {EffectSignal} from '@alwatr/signal'; const loggerEffect = new EffectSignal({ deps: [fullName, counter], // This effect depends on fullName and counter. run: () => { console.log(`${fullName.get()} has clicked ${counter.get()} times.`); }, }); ``` -------------------------------- ### Persistent State Signal with LocalStorage (TypeScript) Source: https://context7.com/alwatr/flux/llms.txt Manages state that is automatically persisted to localStorage. Changes to the signal are debounced before saving to prevent excessive writes. Restores the last saved state on initialization. Requires an interface defining the state shape, a name, storage key, and schema version. ```typescript import {PersistentStateSignal} from '@alwatr/signal'; interface UserPreferences { theme: 'light' | 'dark'; language: string; } // Automatically saves to localStorage const preferences = new PersistentStateSignal({ name: 'user-preferences', storageKey: 'app-preferences', schemaVersion: 1, initialValue: { theme: 'light', language: 'en', }, saveDebounceDelay: 500, // Debounce writes }); preferences.subscribe((prefs) => { console.log('Preferences updated:', prefs); }); // Changes are saved to localStorage automatically preferences.set({theme: 'dark', language: 'en'}); // On page reload, previous value is restored const restored = preferences.get(); console.log('Restored preferences:', restored); ``` -------------------------------- ### Map Signal Operator (TypeScript) Source: https://context7.com/alwatr/flux/llms.txt Creates a new signal by applying a mapping function to each value emitted by a source signal. This is useful for transforming data into a different format or unit. It requires a source signal and a map configuration. ```typescript import {StateSignal, createMappedSignal} from '@alwatr/signal'; const temperature = new StateSignal({ name: 'celsius', initialValue: 0, }); // Convert Celsius to Fahrenheit const fahrenheit = createMappedSignal(temperature, { map: (celsius) => (celsius * 9 / 5) + 32, name: 'fahrenheit', }); fahrenheit.subscribe((f) => { console.log(`Temperature: ${f}°F`); }); // Output: Temperature: 32°F temperature.set(100); // Output: Temperature: 212°F fahrenheit.destroy(); ``` -------------------------------- ### Debounced Signal Operator in TypeScript Source: https://context7.com/alwatr/flux/llms.txt Demonstrates how to use the `createDebouncedSignal` operator to limit the rate at which a signal's value is processed. This is useful for reducing API calls or expensive computations triggered by rapid input changes. ```typescript import {StateSignal, createDebouncedSignal, EffectSignal} from '@alwatr/signal'; // Search input that updates frequently const searchInput = new StateSignal({ name: 'search-input', initialValue: '', }); // Debounce to reduce API calls const debouncedSearch = createDebouncedSignal(searchInput, { delay: 300, name: 'debounced-search', }); // Only trigger search after user stops typing new EffectSignal({ deps: [debouncedSearch], run: () => { const query = debouncedSearch.get(); if (query.length < 3) return; console.log(`Searching for: ${query}`); // fetch(`/api/search?q=${query}`); }, }); // Rapid updates searchInput.set('a'); searchInput.set('ab'); searchInput.set('abc'); searchInput.set('abcd'); // Only after 300ms: Searching for: abcd // Cleanup debouncedSearch.destroy(); ```