import { ActionsBase, Factory, StateBase, StoreSpec, StoreTuple } from '../types'; /** * Async data mode: * - "fresh": data is undefined during loading/error (only show fresh data) * - "stale": data is preserved during loading/error (stale-while-revalidate) */ export type AsyncMode = "fresh" | "stale"; export type AsyncStatus = "idle" | "pending" | "success" | "error"; /** * Serialized async state for persistence/hydration. * Only success states are persisted. */ export type SerializedAsyncState = { status: "success"; mode: AsyncMode; data: T; } | null; /** * Async state with mode support. * - Fresh mode: data is undefined during idle/pending/error * - Stale mode: data is preserved (T) during pending/error after first load */ export type AsyncState = AsyncIdleState | AsyncPendingState | AsyncSuccessState | AsyncErrorState; export interface AsyncIdleStateFresh { status: "idle"; mode: "fresh"; data: undefined; error: undefined; timestamp: undefined; /** @internal Request ID for concurrency control */ __requestId?: AsyncRequestId; toJSON?(): SerializedAsyncState; } export interface AsyncPendingStateFresh { status: "pending"; mode: "fresh"; data: undefined; error: undefined; timestamp: undefined; /** @internal Key for Suspense promise tracking */ __key?: AsyncKey; /** @internal Request ID for concurrency control */ __requestId?: AsyncRequestId; toJSON?(): SerializedAsyncState; } export interface AsyncErrorStateFresh { status: "error"; mode: "fresh"; data: undefined; error: Error; timestamp: undefined; /** @internal Request ID for concurrency control */ __requestId?: AsyncRequestId; toJSON?(): SerializedAsyncState; } export interface AsyncIdleStateStale { status: "idle"; mode: "stale"; data: T; error: undefined; timestamp: undefined; /** @internal Request ID for concurrency control */ __requestId?: AsyncRequestId; toJSON?(): SerializedAsyncState; } export interface AsyncPendingStateStale { status: "pending"; mode: "stale"; data: T; error: undefined; timestamp: undefined; /** @internal Key for Suspense promise tracking */ __key?: AsyncKey; /** @internal Request ID for concurrency control */ __requestId?: AsyncRequestId; toJSON?(): SerializedAsyncState; } export interface AsyncErrorStateStale { status: "error"; mode: "stale"; data: T; error: Error; timestamp: undefined; /** @internal Request ID for concurrency control */ __requestId?: AsyncRequestId; toJSON?(): SerializedAsyncState; } export type AsyncIdleState = M extends "fresh" ? AsyncIdleStateFresh : M extends "stale" ? AsyncIdleStateStale : AsyncIdleStateFresh | AsyncIdleStateStale; export type AsyncPendingState = M extends "fresh" ? AsyncPendingStateFresh : M extends "stale" ? AsyncPendingStateStale : AsyncPendingStateFresh | AsyncPendingStateStale; export interface AsyncSuccessState { status: "success"; mode: "fresh" | "stale"; data: T; error: undefined; timestamp: number; /** @internal Request ID for concurrency control */ __requestId?: AsyncRequestId; toJSON?(): SerializedAsyncState; } export type AsyncErrorState = M extends "fresh" ? AsyncErrorStateFresh : M extends "stale" ? AsyncErrorStateStale : AsyncErrorStateFresh | AsyncErrorStateStale; /** * Opaque key type for linking state to pending promise. * Used internally for React Suspense support. */ export type AsyncKey = object & { __brand?: T; }; /** * Request ID for detecting external state modifications. * Used to prevent stale async updates from overwriting rolled-back state. */ export type AsyncRequestId = object; /** * Context passed to async handlers. */ export interface AsyncContext { /** AbortSignal for cancellation */ signal: AbortSignal; /** * Safely execute operations that should be cancelled together. * * Overloads: * 1. `safe(promise)` - Wrap promise, never resolve/reject if cancelled * 2. `safe(fn, ...args)` - Call function, wrap result if promise * 3. `safe(Abortable, ...args)` - Call with signal, wrap result if promise * * @example * ```ts * // Wrap a promise * const data = await ctx.safe(fetch('/api/data')); * * // Call a normal function * const result = await ctx.safe(myAsyncFn, arg1, arg2); * * // Call an abortable function (auto-injects signal) * const user = await ctx.safe(getUser, userId); * ``` */ safe(promise: Promise): Promise; safe(fn: (...args: TArgs) => TResult, ...args: TArgs): TResult extends Promise ? Promise : TResult; /** * Cancel the current async operation. * Useful for implementing timeouts. * * @example * async.action(focus, async (ctx) => { * // Timeout after 5 seconds * setTimeout(ctx.cancel, 5000); * * const data = await ctx.safe(fetch('/api/slow')); * return data; * }); */ cancel(): void; /** * Get another store's state and actions. * * @example * // Array destructuring * const [state, actions] = get(counterSpec); * * // Named properties * const tuple = get(counterSpec); * tuple.state.count; */ get(spec: StoreSpec): StoreTuple; /** * Get a service or factory instance. * * @example * const db = get(() => new IndexedDBService()); * await db.users.getAll(); */ get(factory: Factory, ...args: A): T; } /** * Async handler function signature. * Receives AsyncContext as first arg, then user-defined args. */ export type AsyncHandler = (context: AsyncContext, ...args: TArgs) => T | PromiseLike; /** * Built-in retry delay strategies. */ export declare const retryStrategy: { /** Exponential backoff: 1s, 2s, 4s, 8s... (max 30s) */ readonly backoff: (attempt: number) => number; /** Linear: 1s, 2s, 3s, 4s... (max 30s) */ readonly linear: (attempt: number) => number; /** Fixed 1 second delay */ readonly fixed: () => number; /** Fibonacci: 1s, 1s, 2s, 3s, 5s, 8s... (max 30s) */ readonly fibonacci: (attempt: number) => number; /** Immediate retry (no delay) */ readonly immediate: () => number; /** Add jitter (±30%) to any strategy */ readonly withJitter: (strategy: (n: number) => number) => (attempt: number) => number; }; /** Built-in retry strategy names */ export type RetryStrategyName = Exclude; export type AsyncRetryDelayFn = (attempt: number, error: Error) => number | Promise; export type AsyncRetryDelay = number | AsyncRetryDelayFn; export interface AsyncRetryOptions { /** Number of retry attempts */ count: number; /** Delay between retries: ms, strategy name, or custom function */ delay?: AsyncRetryDelay | RetryStrategyName; } /** * Options for async state management. * * For retry, error handling, and other cross-cutting concerns, * use the `use()` pattern with wrapper utilities: * * ```ts * import { retry, catchError } from "storion/async"; * * const userQuery = async.action( * focus("user"), * userService.getUser.use(retry(3)).use(catchError(console.error)) * ); * ``` */ export interface AsyncOptions { /** Auto-cancel previous request on new dispatch (default: true) */ autoCancel?: boolean; } export type CancellablePromise = Promise & { cancel(): void; }; /** * Last invocation info for async action (for typing asyncAction.last()) */ export interface AsyncLastInvocation { /** Arguments passed to the last dispatch */ readonly args: TArgs; /** Invocation count (1-indexed) */ readonly nth: number; /** Current async state (reactive) */ readonly state: AsyncState; } /** * API returned from async.action() or async.mixin(). */ export interface AsyncActions { /** Dispatch the async operation */ dispatch(...args: TArgs): CancellablePromise; /** Re-dispatch with last args. Returns undefined if no previous dispatch. */ refresh(): CancellablePromise | undefined; /** Cancel ongoing operation */ cancel(): void; /** Reset to idle state */ reset(): void; /** * Get the last invocation info including current async state. * Reactive - reads from the async state, triggers re-render when state changes. * * @returns Last invocation info with state, or undefined if never dispatched */ last(): AsyncLastInvocation | undefined; /** * Set state to success with the given data. * Useful for optimistic updates, websocket data, SSR hydration, or testing. * * Respects `autoCancel` option: * - If `autoCancel: true` (default): cancels any in-flight request * - If `autoCancel: false`: lets in-flight request complete but prevents it from overwriting state * * @param data - The data to set as success value */ success(data: T): void; } /** * Infer the data type from an AsyncState */ export type InferAsyncData = T extends AsyncState ? D : never; /** * Infer the mode from an AsyncState */ export type InferAsyncMode = T extends AsyncState ? M : never; /** * Settled result for a single async state */ export type SettledResult = { status: "success"; data: T; } | (M extends "stale" ? { status: "error"; error: Error; data: T; } : { status: "error"; error: Error; data: undefined; }) | (M extends "stale" ? { status: "pending"; data: T; } : { status: "pending"; data: undefined; }) | (M extends "stale" ? { status: "idle"; data: T; } : { status: "idle"; data: undefined; }); /** * Map a tuple of AsyncState to their data types */ export type MapAsyncData[]> = { -readonly [K in keyof T]: InferAsyncData; }; /** * Map a tuple of AsyncState to SettledResult */ export type MapSettledResult[]> = { -readonly [K in keyof T]: SettledResult, InferAsyncMode>; }; /** * Race result type - tuple of [key, value] for AsyncState */ export type RaceResult>> = { [K in keyof T]: [K, InferAsyncData]; }[keyof T]; /** * Infer the data type from a PromiseWithState */ export type InferPromiseData = T extends PromiseWithState ? D : never; /** * Map a tuple of PromiseWithState to their data types */ export type MapPromiseData[]> = { -readonly [K in keyof T]: InferPromiseData; }; /** * Settled result for a PromiseWithState */ export type PromiseSettledResult = { status: "fulfilled"; value: T; } | { status: "rejected"; reason: any; } | { status: "pending"; }; /** * Map a tuple of PromiseWithState to PromiseSettledResult */ export type MapPromiseSettledResult[]> = { -readonly [K in keyof T]: PromiseSettledResult>; }; /** * Race result type - tuple of [key, value] for PromiseWithState */ export type PromiseRaceResult>> = { [K in keyof T]: [K, InferPromiseData]; }[keyof T]; /** * Union type for either AsyncState or PromiseLike */ export type AsyncOrPromise = AsyncState | PromiseLike; /** * Infer data type from either AsyncState or PromiseLike */ export type InferData = T extends AsyncState ? D : T extends PromiseLike ? D : never; /** * Map a tuple of AsyncOrPromise to their data types */ export type MapData = { -readonly [K in keyof T]: InferData; }; /** * Map a record of AsyncOrPromise to their data types */ export type MapRecordData> = { [K in keyof T]: InferData; }; /** * Combined settled result for either AsyncState or PromiseWithState */ export type CombinedSettledResult = { status: "fulfilled" | "success"; value: T; } | { status: "rejected" | "error"; reason: any; } | { status: "pending"; } | { status: "idle"; }; /** * Map a tuple of AsyncOrPromise to CombinedSettledResult */ export type MapCombinedSettledResult = { -readonly [K in keyof T]: CombinedSettledResult>; }; /** * Race result type - tuple of [key, value] for either type */ export type CombinedRaceResult> = { [K in keyof T]: [K, InferData]; }[keyof T]; export declare class AsyncNotReadyError extends Error { readonly status: AsyncStatus; constructor(message: string, status: AsyncStatus); } export declare class AsyncAggregateError extends Error { readonly errors: Error[]; constructor(message: string, errors: Error[]); } export interface PromiseWithState extends PromiseLike { state: NoInfer>; } export type PromiseState = { status: "pending"; value: undefined; reason: any; } | { status: "fulfilled"; value: T; reason: any; } | { status: "rejected"; value: undefined; reason: any; }; //# sourceMappingURL=types.d.ts.map