/** * Pulse Runtime 2.0 - TypeScript Definitions * * Type definitions for the public API of Pulse Runtime 2.0. * * @module pulse-runtime */ import * as http from 'http'; import { EventEmitter } from 'events'; // ============================================================================= // Core Types // ============================================================================= /** * Task priority levels */ export type TaskPriority = 0 | 1 | 2; // 0 = high, 1 = normal, 2 = low /** * Task state */ export type TaskState = 'pending' | 'running' | 'sleeping' | 'completed' | 'cancelled'; /** * Options for spawning a task */ export interface SpawnOptions { /** * Task name for debugging */ name?: string; /** * Task priority (0=high, 1=normal, 2=low) * @default 1 */ priority?: TaskPriority; } /** * Task handle returned by spawn() */ export interface Task { /** * Unique task ID */ id: number; /** * Task name (if provided) */ name?: string; /** * Current task state */ state: TaskState; /** * Task priority */ priority: TaskPriority; /** * Promise that resolves when task completes */ completionPromise: Promise; /** * Cancel this task and all child tasks */ cancel(): void; } /** * Request context for distributed tracing */ export interface RequestContext { /** * Trace ID for distributed tracing */ traceId?: string; /** * Request ID for this specific request */ requestId?: string; /** * Custom metadata (user-defined) */ [key: string]: any; } // ============================================================================= // Core Primitives // ============================================================================= /** * Spawn a new task in the current scheduler context. * * @param fn - Async function to run as a task * @param options - Task options * @returns Task handle with .completionPromise and .cancel() * * @example * ```typescript * const task = spawn(async () => { * await sleep(100); * return 'done'; * }); * * const result = await task.completionPromise; // result: string * ``` */ export function spawn( fn: () => Promise, options?: SpawnOptions ): Task; /** * Sleep for a specified number of milliseconds. * Respects cancellation from parent tasks. * * @param ms - Milliseconds to sleep * @returns Promise that resolves after sleep duration * * @example * ```typescript * await sleep(1000); // Sleep for 1 second * ``` */ export function sleep(ms: number): Promise; /** * Get the current request context (trace ID, request ID, custom metadata). * Returns undefined if called outside a request context. * * @returns Request context object or undefined * * @example * ```typescript * const ctx = getRequestContext(); * if (ctx) { * console.log(ctx.traceId, ctx.requestId); * } * ``` */ export function getRequestContext(): RequestContext | undefined; // ============================================================================= // Channel Communication // ============================================================================= /** * FIFO channel for task communication. * Supports buffered and unbuffered (rendezvous) modes. * * @example * ```typescript * const ch = new Channel(5); // Buffered channel with capacity 5 * * // Sender * await ch.send(42); * ch.close(); * * // Receiver * while (true) { * const [data, ok] = await ch.recv(); * if (!ok) break; // Channel closed * console.log(data); // data: number * } * ``` */ export class Channel { /** * Create a new channel * * @param capacity - Buffer capacity (0 = unbuffered/rendezvous) */ constructor(capacity?: number); /** * Send a value to the channel * Blocks if channel is full (buffered) or no receiver ready (unbuffered) * * @param value - Value to send * @throws {Error} If channel is closed */ send(value: T): Promise; /** * Receive a value from the channel * Blocks if channel is empty * * @returns Tuple of [value, ok] where ok=false if channel is closed and empty */ recv(): Promise<[T, boolean]>; /** * Close the channel * Pending senders will throw, pending receivers will get [undefined, false] */ close(): void; /** * Check if channel is closed */ get closed(): boolean; /** * Get current buffer size */ get size(): number; /** * Get channel capacity */ get capacity(): number; /** * Async iterator support */ [Symbol.asyncIterator](): AsyncIterableIterator; } // ============================================================================= // Select Statement // ============================================================================= /** * Channel operation type */ export type SelectOp = 'send' | 'recv'; /** * Select case configuration */ export interface SelectCaseConfig { /** * Channel to operate on */ channel: Channel; /** * Operation type ('send' or 'recv') */ op: SelectOp; /** * Value to send (required for 'send' operations) */ value?: T; /** * Handler function called if this case wins * - For 'recv': receives the value from the channel * - For 'send': receives undefined */ handler: (data: T | undefined) => Promise | R; } /** * Select case for channel operations */ export class SelectCase { constructor(config: SelectCaseConfig); } /** * Select options */ export interface SelectOptions { /** * Abort signal for cancellation */ signal?: AbortSignal; } /** * Multiplex channel operations. When multiple cases are ready, * chooses by declaration order (first wins). * * @param cases - Array of select cases * @param options - Select options * @returns Result from the winning case handler * * @example * ```typescript * const ch1 = new Channel(1); * const ch2 = new Channel(1); * * const result = await select([ * selectCase({ channel: ch1, op: 'recv', handler: async (data) => `str: ${data}` }), * selectCase({ channel: ch2, op: 'recv', handler: async (data) => `num: ${data}` }) * ]); * ``` */ export function select( cases: SelectCase[], options?: SelectOptions ): Promise; /** * Create a select case for channel operations. * * @param config - Case configuration * @returns SelectCase instance * * @example * ```typescript * selectCase({ * channel: ch, * op: 'recv', * handler: async (data) => { * console.log('Received:', data); * return data; * } * }) * ``` */ export function selectCase( config: SelectCaseConfig ): SelectCase; // ============================================================================= // HTTP Integration // ============================================================================= /** * HTTP request handler */ export type RequestHandler = ( req: http.IncomingMessage, res: http.ServerResponse ) => Promise | void; /** * Request context extractor */ export type ContextExtractor = (req: http.IncomingMessage) => RequestContext; /** * Options for createServerWithScheduler */ export interface ServerOptions { /** * Max concurrent requests (pool size) * @default 100 */ maxPoolSize?: number; /** * Max queued requests when pool is full * @default 50 */ maxQueueSize?: number; /** * Request timeout in milliseconds * @default 30000 */ timeout?: number; /** * Request context extractor function */ context?: ContextExtractor | RequestContext; } /** * HTTP server with attached pool */ export interface PulseServer extends http.Server { /** * Attached scheduler pool */ pool: SchedulerPool; } /** * Create an HTTP server with integrated scheduler pool. * Each request runs in an isolated RequestScheduler with automatic cleanup. * * @param handler - Request handler * @param options - Server options * @returns HTTP server instance with attached pool * * @example * ```typescript * const server = createServerWithScheduler(async (req, res) => { * const result = await spawn(async () => { * await sleep(100); * return 'Hello World'; * }).completionPromise; * * res.writeHead(200, { 'Content-Type': 'text/plain' }); * res.end(result); * }); * * server.listen(3000); * ``` */ export function createServerWithScheduler( handler: RequestHandler, options?: ServerOptions ): PulseServer; /** * Graceful shutdown options */ export interface ShutdownOptions { /** * Shutdown timeout in milliseconds * @default 30000 */ timeout?: number; /** * Callback when shutdown starts */ onShutdown?: (signal: string) => void; /** * Callback when shutdown completes */ onComplete?: (result: ShutdownResult) => void; } /** * Shutdown result */ export interface ShutdownResult { /** * Whether shutdown completed successfully */ success: boolean; /** * Shutdown duration in milliseconds */ duration: number; /** * Number of active requests waited for */ activeWaitedFor: number; } /** * Setup graceful shutdown for an HTTP server. * Handles SIGTERM and SIGINT signals. * * @param server - HTTP server instance * @param options - Shutdown options * * @example * ```typescript * setupGracefulShutdown(server, { * timeout: 30000, * onShutdown: (signal) => console.log(`Shutting down: ${signal}`), * onComplete: (result) => console.log(`Shutdown: ${result.success}`) * }); * ``` */ export function setupGracefulShutdown( server: http.Server | PulseServer, options?: ShutdownOptions ): void; /** * Create a health check handler for load balancers and Kubernetes probes. * Returns 200 if healthy, 503 if degraded or shutting down. * * @param server - HTTP server instance (uses default pool if not provided) * @returns Request handler * * @example * ```typescript * const healthHandler = createHealthCheckHandler(server); * * // In your router: * if (req.url === '/health') { * return healthHandler(req, res); * } * ``` */ export function createHealthCheckHandler( server?: http.Server | PulseServer ): RequestHandler; /** * Pool statistics */ export interface PoolStats { /** * Currently active schedulers (requests in flight) */ currentActive: number; /** * Currently available schedulers in pool */ currentAvailable: number; /** * Currently queued requests */ currentQueue: number; /** * Maximum pool size */ maxPoolSize: number; /** * Maximum queue size */ maxQueueSize: number; /** * Pool utilization percentage (0-100) */ utilizationPercent: number; } /** * Get pool statistics for monitoring. * * @param server - HTTP server instance (uses default pool if not provided) * @returns Pool statistics * * @example * ```typescript * const stats = getPoolStats(server); * console.log(`Active: ${stats.currentActive}, Queue: ${stats.currentQueue}`); * ``` */ export function getPoolStats(server?: http.Server | PulseServer): PoolStats; /** * Health status */ export interface HealthStatus { /** * Overall health status */ healthy: boolean; /** * Status string */ status: 'healthy' | 'degraded' | 'shutting_down'; /** * Server uptime in milliseconds */ uptime: number; /** * Pool statistics */ pool: PoolStats; } /** * Get health status for monitoring. * * @param server - HTTP server instance * @returns Health status * * @example * ```typescript * const health = getHealth(server); * if (!health.healthy) { * console.warn(`Server unhealthy: ${health.status}`); * } * ``` */ export function getHealth(server?: http.Server | PulseServer): HealthStatus; // ============================================================================= // Advanced: Pool Management // ============================================================================= /** * Scheduler options */ export interface SchedulerOptions { /** * Batch size for task execution * @default 10 */ batchSize?: number; /** * Maximum number of tasks * @default 10000 */ maxTasks?: number; /** * Request timeout in milliseconds * @default 30000 */ timeout?: number; } /** * Pool options */ export interface PoolOptions { /** * Max concurrent schedulers * @default 100 */ maxPoolSize?: number; /** * Max queued requests when pool is full * @default 50 */ maxQueueSize?: number; /** * Options for each scheduler instance */ schedulerOptions?: SchedulerOptions; } /** * Pool events */ export interface PoolEvents { 'scheduler:acquired': (scheduler: any, meta: { reused: boolean; queueTime: number }) => void; 'scheduler:released': (scheduler: any, meta: { duration: number }) => void; 'scheduler:created': (scheduler: any) => void; 'request:start': () => void; 'request:complete': (meta: { statusCode: number; duration: number }) => void; 'request:error': (meta: { error: Error; statusCode: number }) => void; 'request:timeout': () => void; 'request:abort': () => void; 'request:queued': (meta: { queueSize: number }) => void; 'request:rejected': (meta: { reason: string }) => void; 'pool:exhausted': () => void; } /** * Scheduler pool for managing RequestScheduler instances. * Most users should use createServerWithScheduler() instead. * * This is exposed for advanced users who need manual pool management. * * @example * ```typescript * const pool = new SchedulerPool({ * maxPoolSize: 200, * maxQueueSize: 100 * }); * * const scheduler = await pool.acquire(); * try { * await scheduler.runHandler(async () => { * // Your code here * }); * } finally { * pool.release(scheduler); * } * ``` */ export class SchedulerPool extends EventEmitter { constructor(options?: PoolOptions); /** * Acquire a scheduler from the pool * Blocks if pool is full and queue is not full * Throws PoolExhaustedError if queue is full */ acquire(): Promise; /** * Release a scheduler back to the pool */ release(scheduler: any): void; /** * Get pool statistics */ getStats(): PoolStats; /** * Shutdown the pool gracefully */ shutdown(timeout?: number): Promise; /** * Event emitter for pool events */ on(event: K, listener: PoolEvents[K]): this; once(event: K, listener: PoolEvents[K]): this; off(event: K, listener: PoolEvents[K]): this; } // ============================================================================= // Error Types // ============================================================================= /** * Error thrown when a task is cancelled. * Catch this to handle cancellation gracefully. * * @example * ```typescript * try { * await sleep(1000); * } catch (error) { * if (error instanceof CancelledError) { * console.log('Task was cancelled'); * } * } * ``` */ export class CancelledError extends Error { constructor(message?: string); } /** * Error thrown when the scheduler pool is exhausted. * This typically results in a 503 Service Unavailable response. * * @example * ```typescript * try { * const scheduler = await pool.acquire(); * } catch (error) { * if (error instanceof PoolExhaustedError) { * console.log('Pool exhausted, queue full'); * } * } * ``` */ export class PoolExhaustedError extends Error { constructor(message?: string); }