/** * Typed worker-local events and frame lifecycle for Three Blocks applications. * * The dispatcher deliberately does not mirror events across threads. A worker bridge can * map its explicit protocol onto this local dispatcher without turning every local event * into a public, structured-clone boundary. * * @module three-blocks/runtime */ export type EventName = Extract; export type EventHandler = (payload: TPayload) => void; export interface TriggerOptions { /** Retain the latest payload and replay it to future listeners and components. */ readonly fireAtStart?: boolean; } export interface RafOptions { /** Lower priorities run first. Renderers normally use `Infinity`. */ readonly renderPriority?: number; /** `onThrottle` frequency. `Infinity` runs it every frame; values <= 0 disable it. */ readonly fps?: number; /** Optional component-specific delta cap in seconds. */ readonly maxDelta?: number; } export interface FrameTiming { readonly delta: number; readonly elapsedTime: number; readonly startTime: number; readonly throttleInterpolation: number; } type EmptyFrameValues = Readonly>; type FrameValues = Omit; /** * A readonly lifecycle view. One internal object is reused between handlers, so consumers * must read it synchronously and must not retain it. */ export type FrameContext = Readonly & FrameTiming>; export interface FrameLifecycle { readonly raf?: RafOptions; onBeforeRaf?(context: FrameContext): void | PromiseLike; onRaf?(context: FrameContext): void | PromiseLike; onAfterRaf?(context: FrameContext): void | PromiseLike; onThrottle?(context: FrameContext): void | PromiseLike; } /** Event `pointermove` maps to method `onPointermove`; casing after the first letter is retained. */ export type EventMethodMap = { [TName in EventName as `on${Capitalize}`]?: (payload: TEvents[TName]) => void; }; export type RuntimeComponent = FrameLifecycle & EventMethodMap; export interface DispatcherOptions { /** Shared renderer/scene/input values copied once into the reused lifecycle contexts. */ readonly context?: FrameValues; /** Global frame delta cap in seconds. Defaults to 0.1. */ readonly maxDelta?: number; /** Monotonic clock in seconds. Defaults to `Date.now() / 1000`. */ readonly clock?: () => number; } export interface FrameInput { /** Current monotonic time in seconds. */ readonly now?: number; /** Explicit raw delta in seconds. When omitted it is derived from the clock. */ readonly delta?: number; /** Explicit elapsed time in seconds. */ readonly elapsedTime?: number; /** Explicit lifecycle start time in seconds. */ readonly startTime?: number; /** Partial updates for shared lifecycle values. */ readonly context?: Partial>; } export interface FixedStepInput { readonly context?: Partial>; } export interface FixedStepReset { readonly startTime?: number; readonly elapsedTime?: number; } /** Typed local dispatcher with retained events and a deterministic frame scheduler. */ export declare class Dispatcher { #private; constructor(options?: DispatcherOptions); get paused(): boolean; on>(name: TName, handler: EventHandler): () => void; off>(name: TName, handler: EventHandler): void; trigger>(name: TName, payload: TEvents[TName], options?: TriggerOptions): void; clearRetained>(name: TName): void; register(component: RuntimeComponent): () => void; unregister(component: RuntimeComponent): void; isRegistered(component: RuntimeComponent): boolean; /** Call after changing a registered component's `raf.renderPriority`. */ invalidateRenderOrder(): void; setContext(values: Partial>): void; pause(): void; resume(): void; /** Run one clock-driven frame. The first frame after construction/resume receives delta 0. */ runFrame(input?: FrameInput): Promise; /** Advance without consulting wall time. Repeated calls are deterministic. */ fixedStep(delta: number, input?: FixedStepInput): Promise; resetFixedStep(reset?: FixedStepReset): void; } export declare function createDispatcher(options?: DispatcherOptions): Dispatcher; export {};