import type { ReactNode } from "react"; import type { NavigationLocation, NavigateOptions, TrackedActionState, InflightAction, RscMetadata, HandleData } from "./types.js"; /** * Phase of a navigation operation */ export type NavigationPhase = "fetching" | "streaming"; /** * Phase of an action operation */ export type ActionPhase = "fetching" | "streaming" | "settling"; /** * Entry tracking an in-flight navigation */ export interface NavigationEntry { url: string; abort: AbortController; phase: NavigationPhase; startedAt: number; options?: NavigateOptions & { skipLoadingState?: boolean; }; } /** * Entry tracking an in-flight action */ export interface ActionEntry { /** Unique instance ID for this action invocation */ id: string; /** Server action function ID (normalized name like "addToCart") */ actionId: string; /** Abort controller for this action */ abort: AbortController; /** Current phase of the action */ phase: ActionPhase; /** Action arguments */ payload: unknown[]; /** Result from action (set on completion) */ result?: unknown; /** Error from action (set on failure) */ error?: unknown; /** Segment IDs that were revalidated by this action */ revalidatedSegments: string[]; /** Timestamp when action started */ startedAt: number; /** Whether action processing is complete (may still be streaming) */ completed?: boolean; } /** * Derived navigation state (computed from source of truth) */ export interface DerivedNavigationState { /** Navigation lifecycle state */ state: "idle" | "loading"; /** Whether any operation is streaming */ isStreaming: boolean; /** Current committed location */ location: NavigationLocation; /** URL being navigated to (null if idle) */ pendingUrl: string | null; /** List of inflight actions (for compatibility) */ inflightActions: InflightAction[]; } /** * Callback for UI updates when root should re-render */ export type UpdateCallback = (update: { root: ReactNode | Promise; metadata: RscMetadata; }) => void; /** * State change listener */ export type StateListener = () => void; /** * Action state listener */ export type ActionStateListener = (state: TrackedActionState) => void; /** * Handle state listener */ export type HandleListener = () => void; /** * Internal handle state stored in controller */ export interface HandleState { data: HandleData; segmentOrder: string[]; } /** * Token for tracking an active stream * Call end() when the stream completes */ export interface StreamingToken { /** End this streaming operation */ end(): void; } /** * Result from starting a navigation * Implements Disposable for use with `using` keyword */ export interface NavigationHandle extends Disposable { /** Abort controller for this navigation */ abort: AbortController; /** Signal for this navigation */ signal: AbortSignal; /** Start streaming and get a token to end it later */ startStreaming(): StreamingToken; /** Complete the navigation successfully */ complete(location: NavigationLocation): void; /** Whether navigation was completed successfully */ readonly completed: boolean; } /** * Result from starting an action * Implements Disposable for use with `using` keyword */ export interface ActionHandle extends Disposable { /** Unique instance ID */ id: string; /** Abort controller for this action */ abort: AbortController; /** Signal for this action */ signal: AbortSignal; /** Start streaming and get a token to end it later */ startStreaming(): StreamingToken; /** Record segments that were revalidated */ recordRevalidatedSegments(segmentIds: string[]): void; /** Complete the action with result */ complete(result?: unknown): void; /** Fail the action with error */ fail(error: unknown): void; /** Whether action was completed (success or failure) */ readonly settled: boolean; /** Check if any concurrent actions were started */ hadConcurrentActions: boolean; /** Get segments to consolidate (only valid when this is the last action) */ getConsolidationSegments(): string[] | null; /** Clear consolidation tracking */ clearConsolidation(): void; } /** * Event controller interface */ export interface EventController { startNavigation(url: string, options?: NavigateOptions & { skipLoadingState?: boolean; }): NavigationHandle; abortNavigation(): void; startAction(actionId: string, args: unknown[]): ActionHandle; abortAllActions(): void; getState(): DerivedNavigationState; getActionState(actionId: string): TrackedActionState; setLocation(location: NavigationLocation): void; subscribe(listener: StateListener): () => void; subscribeToAction(actionId: string, listener: ActionStateListener): () => void; subscribeToHandles(listener: HandleListener): () => void; setHandleData(data: HandleData, matched?: string[], isPartial?: boolean): void; getHandleState(): HandleState; getCurrentNavigation(): NavigationEntry | null; getInflightActions(): Map; } /** * Configuration for creating an event controller */ export interface EventControllerConfig { initialLocation?: NavigationLocation; } /** * Create an event controller for managing navigation and action state * * The controller uses a reactive model where: * - Source of truth: currentNavigation, inflightActions, location * - Derived state: navState, isStreaming computed from source * * Navigation uses switchMap semantics (new nav cancels previous). * Actions use mergeMap semantics (all run concurrently, consolidate at end). */ export declare function createEventController(config?: EventControllerConfig): EventController; /** * Initialize the global event controller */ export declare function initEventController(config?: EventControllerConfig): EventController; /** * Get the global event controller */ export declare function getEventController(): EventController; /** * Reset the controller instance (for testing) */ export declare function resetEventController(): void; //# sourceMappingURL=event-controller.d.ts.map