/** * Integration DSL for composing child reducers. * * The `integrate()` function provides a fluent builder API that reduces * boilerplate when integrating multiple child reducers into a parent reducer. * * Key benefits over manual ifLet() calls: * - 87% less integration boilerplate * - Chainable API for multiple children * - Type-safe field access * - Automatic PresentationAction handling * * @packageDocumentation */ import type { Reducer } from '../types.js'; import { type IdentifiedItem } from '../composition/for-each.js'; /** * Create a fluent builder for integrating child reducers. * * This function starts a builder chain that composes child reducers into a * parent reducer using the ifLetPresentation pattern. Each `.with()` call * adds another child integration, and `.build()` produces the final reducer. * * **What it does:** * - Wraps core reducer logic * - Provides fluent `.with(field, childReducer)` API * - Automatically handles PresentationAction unwrapping * - Batches effects from core + all child reducers * - Type-safe field access via keyof * * **Performance:** * - Zero overhead at runtime (compiles to manual ifLet calls) * - No additional function calls vs manual integration * - Tree-shakeable if unused * * @template State - Parent state type * @template Action - Parent action type * @param coreReducer - The core parent reducer (without child integration) * @returns IntegrationBuilder for chaining `.with()` calls * * @example * ```typescript * // Manual pattern (before): * const reducer: Reducer = (state, action, deps) => { * // Core logic * const [s1, e1] = coreReducer(state, action, deps); * * // Child integration (verbose!) * const [s2, e2] = ifLetPresentation( * (s) => s.destination, * (s, d) => ({ ...s, destination: d }), * 'destination', * (ca) => ({ type: 'destination', action: ca }), * destinationReducer * )(s1, action, deps); * * return [s2, Effect.batch(e1, e2)]; * }; * * // DSL pattern (after): * const reducer = integrate(coreReducer) * .with('destination', destinationReducer) * .build(); * ``` * * @example * ```typescript * // Multiple child integrations: * const reducer = integrate(coreReducer) * .with('destination', destinationReducer) * .with('alert', alertReducer) * .with('sheet', sheetReducer) * .build(); * ``` */ export declare function integrate(coreReducer?: Reducer): IntegrationBuilder; /** * Fluent builder for composing child reducers. * * This class provides the `.with()` and `.build()` methods for the integration DSL. * Each `.with()` call adds a child integration, and `.build()` produces the final reducer. * * **Implementation details:** * - Uses ifLetPresentation() for each child integration * - Batches effects from parent and all children * - Type-safe field access via `keyof State` * - Supports method chaining * * @template State - Parent state type * @template Action - Parent action type * @template Dependencies - Dependency injection type */ declare class IntegrationBuilder { private integrations; private fields; private coreReducer; constructor(coreReducer?: Reducer); /** * Integrate a child reducer at a specific state field. * * This method adds a child reducer integration for an optional field in the parent state. * The field must be nullable (to support dismissal), and the parent action type must * have a corresponding case with a PresentationAction wrapper. * * **Assumptions:** * - Parent state has field `K` with type `ChildState | null` * - Parent action has case `{ type: K; action: PresentationAction }` * - Child reducer operates on non-null `ChildState` * * **What it handles:** * - PresentationAction unwrapping (dismiss + presented) * - State extraction and update * - Effect mapping from child to parent * - Effect batching with core reducer * * @template K - The state field key (must be keyof State) * @template ChildAction - The child action type * @param field - The state field name (e.g., 'destination', 'alert') * @param childReducer - The child reducer to integrate * @returns this (for method chaining) * * @example * ```typescript * interface ParentState { * items: Item[]; * destination: DestinationState | null; // Optional child state * } * * type ParentAction = * | { type: 'addButtonTapped' } * | { type: 'destination'; action: PresentationAction }; * * const reducer = integrate(coreReducer) * .with('destination', destinationReducer) // Type-safe! * .build(); * ``` */ with(field: K, childReducer: Reducer, ChildAction, Dependencies>): this; /** * Set the core reducer logic. * * This method sets the base reducer that will be composed with child integrations. * Useful when you want to define integrations before the core logic. * * @param reducer - The core reducer function * @returns this (for method chaining) * * @example * ```typescript * const reducer = integrate() * .forEach('counter', s => s.counters, (s, c) => ({ ...s, counters: c }), counterReducer) * .reduce((state, action) => { * // Core logic here * return [state, Effect.none()]; * }) * .build(); * ``` */ reduce(reducer: Reducer): this; /** * Integrate a collection of child reducers. * * This method eliminates boilerplate for managing dynamic arrays of child features. * It automatically handles action routing by ID, array updates, and effect mapping. * * **Assumptions:** * - Parent state has an array field of type `Array<{ id: ID; state: ChildState }>` * - Parent action has case `{ type: actionType; id: ID; action: ChildAction }` * - IDs are unique within the array * * **What it handles:** * - Finding items by ID (O(n) lookup) * - Running child reducer on matching item * - Immutable array updates * - Effect mapping from child to parent * - Silent ignore for missing IDs (handles removed items gracefully) * * @template ChildState - The child state type * @template ChildAction - The child action type * @template ID - The ID type (string | number) * @param actionType - The action type string to match (e.g., 'counter') * @param getArray - Extract the array from parent state * @param setArray - Update parent state with modified array * @param childReducer - The child reducer to run for each item * @returns this (for method chaining) * * @example * ```typescript * interface ParentState { * counters: Array<{ id: string; state: CounterState }>; * nextId: number; * } * * type ParentAction = * | { type: 'addCounter' } * | { type: 'removeCounter'; id: string } * | { type: 'counter'; id: string; action: CounterAction }; * * const reducer = integrate() * .forEach( * 'counter', * s => s.counters, * (s, counters) => ({ ...s, counters }), * counterReducer * ) * .reduce((state, action) => { * // Handle add/remove * switch (action.type) { * case 'addCounter': * return [ * { * ...state, * counters: [...state.counters, { id: `counter-${state.nextId}`, state: initialState }], * nextId: state.nextId + 1 * }, * Effect.none() * ]; * case 'removeCounter': * return [ * { ...state, counters: state.counters.filter(c => c.id !== action.id) }, * Effect.none() * ]; * default: * return [state, Effect.none()]; * } * }) * .build(); * ``` */ forEach(actionType: string, getArray: (state: State) => Array>, setArray: (state: State, array: Array>) => State, childReducer: Reducer): this; /** * Build the final integrated reducer. * * This method composes all child integrations added via `.with()` and `.forEach()` into * a single reducer function. The integrations are applied left-to-right * (first `.with()` or `.forEach()` integrates first). * * **Execution order:** * 1. Core reducer runs first (if set via constructor or .reduce()) * 2. Each child integration runs in order * 3. Effects from all stages are batched * * @returns The final composed reducer * * @example * ```typescript * const reducer = integrate(coreReducer) * .with('destination', destinationReducer) // Runs second * .forEach('items', s => s.items, (s, i) => ({ ...s, items: i }), itemReducer) // Runs third * .build(); * ``` */ build(): Reducer; } export {}; //# sourceMappingURL=integrate.d.ts.map