/** * @file Higher-Level Abstractions for @doeixd/machine * @description * This module provides a collection of powerful, pre-built patterns and primitives * on top of the core `@doeixd/machine` library. These utilities are designed to * solve common, recurring problems in state management, such as data fetching, * hierarchical state, and toggling boolean context properties. * * Think of this as the "standard library" of common machine patterns. */ import { MachineBase } from './base'; import { Machine, Transitions, Context } from './index'; /** * A type utility to infer the child machine type from a parent. */ type ChildMachine

= P extends MachineBase<{ child: infer C; }> ? C : never; type BooleanKey = { [K in keyof T]-?: T[K] extends boolean ? K : never; }[keyof T]; /** * Creates a transition method that delegates a call to a child machine. * * This is a higher-order function that reduces boilerplate when implementing * hierarchical state machines. It generates a method for the parent machine that: * 1. Checks if the specified action exists on the current child state. * 2. If it exists, calls the action on the child. * 3. Reconstructs the parent machine with the new child state returned by the action. * 4. If the action doesn't exist on the child, it returns the parent machine unchanged. * * @template P - The parent machine type, which must have a `child` property in its context. * @template K - The name of the action on the child machine to delegate to. * @param actionName - The string name of the child's transition method. * @param ...args - Any arguments to pass to the child's transition method. * @returns The parent machine instance, with its `child` state potentially updated. * * @example * ```typescript * class Parent extends MachineBase<{ child: ChildMachine }> { * // Instead of writing a manual delegation method... * // save = () => { * // if ('save' in this.context.child) { * // const newChild = this.context.child.save(); * // return setContext(this, { child: newChild }); * // } * // return this; * // } * * // ...you can just use the primitive. * save = delegateToChild('save'); * edit = delegateToChild('edit'); * } * ``` */ export declare function delegateToChild

; }>, K extends keyof ChildMachine

& string>(actionName: K): (...args: ChildMachine

[K] extends (...a: infer A) => any ? A : never) => P; /** * Creates a transition method that toggles a boolean property within the machine's context. * * This is a simple utility to reduce boilerplate for managing boolean flags. * * @template M - The machine type. * @template K - The key of the boolean property in the machine's context. * @param prop - The string name of the context property to toggle. * @returns A new machine instance with the toggled property. * * @example * ```typescript * class SettingsMachine extends MachineBase<{ notifications: boolean; darkMode: boolean }> { * toggleNotifications = toggle('notifications'); * toggleDarkMode = toggle('darkMode'); * } * ``` */ export declare function toggle, K extends BooleanKey>>(prop: K): (this: M) => M; /** * A fully-featured, pre-built state machine for data fetching. * It handles loading, success, error states, cancellation, and retry logic out of the box. * * This machine is highly customizable through its configuration options. */ /** * Abort-aware request function consumed by {@link createFetchMachine}. * * @typeParam T - Successful data type. * @typeParam P - Request parameter type. */ export type Fetcher = (params: P, options: { signal: AbortSignal; }) => Promise; /** * Success callback invoked once when a fetch attempt resolves. * @typeParam T - Successful data type. */ export type OnSuccess = (data: T) => void; /** * Final-failure callback invoked after the retry budget is exhausted. * @typeParam E - Normalized error type. */ export type OnError = (error: E) => void; /** * Configuration for {@link createFetchMachine}. * * @typeParam T - Successful data type. * @typeParam E - Normalized error type exposed by error typestates. * @typeParam P - Parameters accepted by `fetch`, `retry`, and `refetch`. */ export interface FetchMachineConfig { /** Performs one request and should honor the supplied abort signal. */ fetcher: Fetcher; /** Used when a transition does not supply explicit parameters. */ initialParams?: P; /** Retries available after the first failed attempt. Defaults to `3`. */ maxRetries?: number; /** Observes successful data before the success snapshot is returned. */ onSuccess?: OnSuccess; /** Observes the normalized error after no retries remain. */ onError?: OnError; /** Converts unknown thrown values into the declared error type. */ mapError?: (error: unknown) => E; } type IdleContext = { status: 'idle'; }; type LoadingContext = { status: 'loading'; abortController: AbortController; attempts: number; }; type RetryingContext = { status: 'retrying'; error: E; attempts: number; }; type SuccessContext = { status: 'success'; data: T; }; type ErrorContext = { status: 'error'; error: E; }; type CanceledContext = { status: 'canceled'; }; declare class IdleMachine extends MachineBase { private config; constructor(config: FetchMachineConfig); fetch: (params?: P) => LoadingMachine; } type LoadingResult = SuccessMachine | RetryingMachine | ErrorMachine | CanceledMachine; declare class LoadingMachine extends MachineBase { private config; private params; private readonly completion; constructor(config: FetchMachineConfig, params: P, attempts: number); /** Resolves to the typestate produced by the configured fetch operation. */ done: () => Promise>; private execute; succeed: (data: T) => SuccessMachine; fail: (error: E) => RetryingMachine | ErrorMachine; cancel: () => CanceledMachine; } declare class RetryingMachine extends MachineBase> { private config; private params; constructor(config: FetchMachineConfig, params: P, error: E, attempts: number); retry: (params?: P) => LoadingMachine; } declare class SuccessMachine extends MachineBase> { private config; constructor(config: FetchMachineConfig, context: SuccessContext); refetch: (params?: P) => LoadingMachine; } declare class ErrorMachine extends MachineBase> { private config; constructor(config: FetchMachineConfig, context: ErrorContext); retry: (params?: P) => LoadingMachine; } declare class CanceledMachine extends MachineBase { private config; constructor(config: FetchMachineConfig); refetch: (params?: P) => LoadingMachine; } /** * Complete typestate union returned by {@link createFetchMachine} transitions. * * Narrow `context.status` before calling state-specific operations such as * `done`, `retry`, `cancel`, or `refetch`. * * @typeParam T - Successful data type. * @typeParam E - Normalized error type. * @typeParam P - Request parameter type. */ export type FetchMachine = IdleMachine | LoadingMachine | RetryingMachine | SuccessMachine | ErrorMachine | CanceledMachine; /** * Creates a pre-built, highly configurable async data-fetching machine. * * This factory function returns a state machine that handles the entire lifecycle * of a data request, including loading, success, error, cancellation, and retries. * * @template T - The type of the data to be fetched. * @template E - The type of the error. * @template P - The type of parameters accepted by fetch operations. * @param config - Configuration object. * @param config.fetcher - An async function that takes params and returns the data. * @param [config.maxRetries=3] - The number of times to retry on failure. * @param [config.onSuccess] - Optional callback fired with the data on success. * @param [config.onError] - Optional callback fired with the error on final failure. * @param [config.mapError] - Converts an unknown thrown value to `E`. * @returns An `IdleMachine` instance, ready to start fetching. * @throws {TypeError} If `config.fetcher` is not a function. * @throws {RangeError} If `maxRetries` is negative or not an integer. * * @example * ```typescript * // 1. Define your data fetching logic * async function fetchUser(id: number): Promise<{ id: number; name: string }> { * const res = await fetch(`/api/users/${id}`); * if (!res.ok) throw new Error('User not found'); * return res.json(); * } * * // 2. Create the machine * const userMachine = createFetchMachine({ * fetcher: fetchUser, * onSuccess: (user) => console.log(`Fetched: ${user.name}`), * }); * * // 3. Use it (e.g., in a React hook) * if (userMachine.context.status === 'idle') { * const loading = userMachine.fetch(123); * const result = await loading.done(); * } * ``` * * @note This is a simplified example. For a real-world implementation, you would * typically use this machine with a runner (like `runMachine` or `useMachine`) to * manage the async transitions and state updates automatically. */ export declare function createFetchMachine(config: FetchMachineConfig): FetchMachine; /** * The core type for a Parallel Machine. * It combines two machines, M1 and M2, into a single, unified type. * @template M1 - The first machine in the parallel composition. * @template M2 - The second machine in the parallel composition. */ export type ParallelMachine, M2 extends Machine> = Machine & Context> & { [K in keyof Transitions]: Transitions[K] extends (...args: infer A) => infer R ? R extends Machine ? (...args: A) => ParallelMachine : never : never; } & { [K in keyof Transitions]: Transitions[K] extends (...args: infer A) => infer R ? R extends Machine ? (...args: A) => ParallelMachine : never : never; }; /** * Creates a parallel machine by composing two independent machines. * * This function takes two machines and merges them into a single machine entity. * Transitions from either machine can be called, and they will only affect * their respective part of the combined state. * * Transition names must be unique across the two machines. A collision throws * instead of silently choosing one implementation. * * @param m1 The first machine instance. * @param m2 The second machine instance. * @returns A new ParallelMachine instance. * @throws {Error} If the inputs share a context key or transition name. * @typeParam M1 - First machine type. * @typeParam M2 - Second machine type. * * @example * ```ts * const combined = createParallelMachine(counter, panel); * const updated = combined.increment().toggle(); * ``` */ export declare function createParallelMachine, M2 extends Machine>(m1: M1, m2: M2): ParallelMachine; /** * Rewrites every transition return type while retaining its name and parameters. * * @typeParam M - Machine whose transitions are inspected. * @typeParam T - Replacement return type for every transition. * @example * ```ts * type Chained = RemapTransitions; * ``` */ export type RemapTransitions, T> = { [K in keyof Transitions]: Transitions[K] extends (...args: infer A) => any ? (...args: A) => T : never; }; export {}; //# sourceMappingURL=higher-order.d.ts.map