import http, { IncomingMessage, ServerResponse, Server as Server$1, IncomingHttpHeaders } from 'node:http'; import http2, { Http2ServerRequest, Http2ServerResponse, Http2Server } from 'node:http2'; import { Readable } from 'node:stream'; import { z } from 'zod'; import * as vitest from 'vitest'; import { AsyncLocalStorage } from 'node:async_hooks'; import { EventEmitter } from 'node:events'; /** * Upload types for BlaizeJS * * Updated in Task [T1.2] to support file validation * - Added originalname field (standard in multipart parsers) * - Added encoding field * - Made buffer non-optional (always available with memory strategy) * * @packageDocumentation */ /** * Represents an uploaded file in a multipart/form-data request * * This interface matches the standard output from multipart parsers * like multer, busboy, and formidable. * */ interface UploadedFile { /** Form field name this file was uploaded under */ readonly fieldname: string; /** * Original filename from the client * * @example "photo.jpg", "document.pdf" */ readonly originalname: string; /** * File encoding (e.g., "7bit", "8bit", "binary") * */ readonly encoding: string; /** MIME type of the uploaded file */ readonly mimetype: string; /** Size of the file in bytes */ readonly size: number; /** * Stream containing the file data * * Available with 'stream' strategy. * For 'memory' strategy, create stream from buffer if needed. */ readonly stream?: Readable; /** * Buffer containing complete file data * * Optional with 'stream' or 'temp' strategies. */ readonly buffer?: Buffer; /** * Path to temporary file on disk * * Only available with 'temp' strategy. */ readonly tempPath?: string; /** * SHA-256 hash of file content * * Only computed if `computeHash: true` in parse options. */ readonly hash?: string; } /** * Complete multipart/form-data parsed content */ interface MultipartData { /** Form fields (non-file inputs) */ readonly fields: Record; /** Uploaded files */ readonly files: Record; } /** * Unified request type supporting both HTTP/1.1 and HTTP/2 */ type UnifiedRequest = IncomingMessage | Http2ServerRequest; /** * Unified response type supporting both HTTP/1.1 and HTTP/2 */ type UnifiedResponse = ServerResponse | Http2ServerResponse; /** * Request parameters extracted from URL path */ interface RequestParams { [key: string]: string; } /** * Query parameters from URL */ interface QueryParams { [key: string]: string | string[] | undefined; } /** * Options for streaming responses */ interface StreamOptions { contentType?: string; status?: number; headers?: Record; } /** * State container for storing request-scoped data * Allows for proper typing with generics */ interface State { [key: string]: unknown; } /** * Services container for storing injectable services/dependencies * Allows middleware to contribute services that are accessible throughout the request lifecycle */ interface Services { [key: string]: unknown; } interface ContextResponse { raw: UnifiedResponse; sent: boolean; statusCode: number; status: (code: number) => ContextResponse; header: (name: string, value: string) => ContextResponse; headers: (headers: Record) => ContextResponse; type: (contentType: string) => ContextResponse; json: (body: unknown, status?: number) => void; text: (body: string, status?: number) => void; html: (body: string, status?: number) => void; redirect: (url: string, status?: number) => void; stream: (readable: NodeJS.ReadableStream, options?: StreamOptions) => void; } interface ContextRequest { raw: UnifiedRequest; method: string; path: string; url: URL | null; query: QueryParams; params: RequestParams; protocol: string; isHttp2: boolean; body?: TBody; /** * Uploaded files from multipart/form-data requests * Available when Content-Type is multipart/form-data */ files?: Record; /** * Complete multipart data (files + fields) * Available when Content-Type is multipart/form-data */ multipart?: MultipartData; header: (name: string) => string | undefined; headers: (names?: string[]) => Record; } /** * Context object representing a request/response cycle * @template S - Type of the state object * @template Svc - Type of the services object * @template TBody - Type of the request body * @template TQuery - Type of the query parameters * @template TFiles - Type of the uploaded files */ interface Context { /** * Request information */ request: Omit & { body: TBody; query: TQuery; files: TFiles; }; /** * Response handling */ response: ContextResponse; /** * Request-scoped state for storing data during the request lifecycle */ state: S; /** * Services container for accessing injected services/dependencies * Populated by middleware that contribute services */ services: Svc; } interface BodyLimits { /** Maximum JSON body size in bytes (default: 512KB) */ json: number; /** Maximum form data size in bytes (default: 1MB) */ form: number; /** Maximum text body size in bytes (default: 5MB) */ text: number; /** Maximum raw/binary body size in bytes (default: 10MB) */ raw: number; /** Multipart/form-data limits */ multipart: MultipartLimits; } type MultipartLimits = { maxFileSize?: number; maxTotalSize?: number; maxFiles?: number; maxFieldSize?: number; }; /** * EventBus Type Definitions * * Foundational types for the BlaizeJS EventBus system. * These types define the contract for event-driven communication * within and across BlaizeJS server instances. * * @module @blaizejs/types/events * @since 0.4.0 */ /** * Event schema map for typed event validation * * Maps event type strings to Zod schemas for runtime validation. * Used by TypedEventBus to provide compile-time and runtime type safety. * * @example * ```typescript * import { z } from 'zod'; * * const schemas = { * 'user:created': z.object({ * userId: z.string().uuid(), * email: z.string().email(), * }), * 'order:placed': z.object({ * orderId: z.string(), * total: z.number().positive(), * }), * } satisfies EventSchemas; * ``` * * @see TypedEventBus */ type EventSchemas = Record>; /** * Core event structure for BlaizeJS EventBus * * Represents a single event with associated metadata. * All events follow this structure whether published locally * or distributed across servers via adapters. * * @template T - Type of the event data payload * * @example Basic event * ```typescript * const event: BlaizeEvent<{ userId: string }> = { * type: 'user:created', * data: { userId: '123' }, * timestamp: Date.now(), * serverId: 'server-1', * }; * ``` * * @example Event with correlation ID * ```typescript * const event: BlaizeEvent<{ orderId: string }> = { * type: 'order:placed', * data: { orderId: 'ord_123' }, * timestamp: Date.now(), * serverId: 'server-1', * correlationId: 'req_abc', * }; * ``` * * @example Event with no data * ```typescript * const event: BlaizeEvent = { * type: 'system:ready', * data: undefined, * timestamp: Date.now(), * serverId: 'server-1', * }; * ``` */ interface BlaizeEvent { /** * Event type identifier * * Typically follows namespace:action convention (e.g., 'user:created'). * Used for pattern matching and routing to subscribers. * * Must be non-empty. Very long type names (>256 chars) may cause * performance issues with pattern matching. * * @example * - 'user:created' * - 'order:placed' * - 'cache:invalidated' * - 'system:shutdown' */ type: string; /** * Event data payload * * Can be any JSON-serializable value, including: * - Objects: `{ userId: '123' }` * - Primitives: `'completed'`, `42`, `true` * - Arrays: `['item1', 'item2']` * - null/undefined: Indicates no data * * Type parameter T provides compile-time type safety. */ data: T; /** * Unix timestamp (milliseconds) when event was created * * Set automatically by EventBus.publish(). * Used for event ordering and debugging. */ timestamp: number; /** * ID of the server that published this event * * Automatically set by EventBus based on server configuration. * Used to: * - Prevent echo when using distributed adapters * - Track event origin for debugging * - Implement server-specific routing */ serverId: string; /** * Optional correlation ID for request tracing * * Links this event to a specific request or operation. * Propagates through the system for distributed tracing. * * @example * ```typescript * // In a route handler * const correlationId = ctx.correlationId; * await eventBus.publish('user:created', { userId }, correlationId); * ``` */ correlationId?: string; } /** * Event handler function signature * * Receives a BlaizeEvent and processes it. * Can be synchronous or asynchronous. * * @template T - Type of the event data payload * * @param event - The event to handle * @returns void or Promise * * @example Synchronous handler * ```typescript * const handler: EventHandler<{ userId: string }> = (event) => { * console.log('User created:', event.data.userId); * }; * ``` * * @example Async handler * ```typescript * const handler: EventHandler<{ userId: string }> = async (event) => { * await database.saveUser(event.data.userId); * await cache.invalidate(`user:${event.data.userId}`); * }; * ``` * * @example Handler with error handling * ```typescript * const handler: EventHandler<{ orderId: string }> = async (event) => { * try { * await processOrder(event.data.orderId); * } catch (error) { * logger.error('Order processing failed', { * orderId: event.data.orderId, * error, * correlationId: event.correlationId, * }); * } * }; * ``` */ type EventHandler = (event: BlaizeEvent) => void | Promise; /** * Function to unsubscribe from events * * Returned by EventBus.subscribe() and EventBusAdapter.subscribe(). * Calling this function removes the subscription. * * Must be idempotent - calling multiple times should be safe. * * @example * ```typescript * const unsubscribe = eventBus.subscribe('user:*', handler); * * // Later, remove subscription * unsubscribe(); * * // Safe to call again * unsubscribe(); // No-op * ``` * * @example Cleanup on component unmount * ```typescript * let unsubscribe: Unsubscribe; * * onMount(() => { * unsubscribe = eventBus.subscribe('data:updated', handleUpdate); * }); * * onUnmount(() => { * unsubscribe?.(); * }); * ``` */ type Unsubscribe = () => void; /** * Adapter interface for distributed event systems * * Adapters enable EventBus to work with external message brokers * like Redis Pub/Sub, RabbitMQ, or cloud messaging services. * * The adapter handles: * - Connection management * - Message serialization/deserialization * - Cross-server event propagation * - Health monitoring * * @example Redis adapter implementation * ```typescript * class RedisEventBusAdapter implements EventBusAdapter { * async connect(): Promise { * await this.redis.connect(); * } * * async disconnect(): Promise { * await this.redis.quit(); * } * * async publish(event: BlaizeEvent): Promise { * await this.redis.publish( * 'blaize:events', * JSON.stringify(event) * ); * } * * async subscribe( * pattern: string, * handler: EventHandler * ): Promise { * const channel = `blaize:events:${pattern}`; * await this.redis.subscribe(channel, handler); * * return () => { * this.redis.unsubscribe(channel); * }; * } * * async healthCheck(): Promise<{ healthy: boolean; message?: string }> { * try { * await this.redis.ping(); * return { healthy: true }; * } catch (error) { * return { * healthy: false, * message: 'Redis connection failed' * }; * } * } * } * ``` * * @see EventBus.setAdapter */ interface EventBusAdapter { /** * Establish connection to the underlying message broker * * Must be idempotent - safe to call multiple times. * Should throw if connection cannot be established. * * @throws {Error} If connection fails * * @example * ```typescript * await adapter.connect(); * console.log('Connected to message broker'); * ``` */ connect(): Promise; /** * Close connection to the underlying message broker * * Must be idempotent - safe to call multiple times. * Should gracefully handle cases where already disconnected. * * @example * ```typescript * await adapter.disconnect(); * console.log('Disconnected from message broker'); * ``` */ disconnect(): Promise; /** * Publish an event to the distributed system * * The adapter is responsible for: * - Serializing the event * - Publishing to appropriate channels/topics * - Handling transient failures with retries * * @param event - The event to publish * @throws {Error} If publish fails after retries * * @example * ```typescript * const event: BlaizeEvent = { * type: 'user:created', * data: { userId: '123' }, * timestamp: Date.now(), * serverId: 'server-1', * }; * * await adapter.publish(event); * ``` */ publish(event: BlaizeEvent): Promise; /** * Subscribe to events matching a pattern * * The pattern format is adapter-specific: * - Redis: 'user:*' matches 'user:created', 'user:updated' * - RabbitMQ: Routing key patterns * - Custom: Regex or glob patterns * * @param pattern - Event type pattern to match * @param handler - Function to call for matching events * @returns Unsubscribe function * * @example * ```typescript * const unsubscribe = await adapter.subscribe( * 'user:*', * async (event) => { * console.log('User event:', event.type); * } * ); * * // Later * unsubscribe(); * ``` */ subscribe(pattern: string, handler: EventHandler): Promise; /** * Check adapter health and connectivity * * Optional method to verify the adapter is functioning correctly. * Used by monitoring systems and health check endpoints. * * @returns Health status with optional message * * @example Healthy adapter * ```typescript * const health = await adapter.healthCheck?.(); * // { healthy: true } * ``` * * @example Unhealthy adapter * ```typescript * const health = await adapter.healthCheck?.(); * // { healthy: false, message: 'Connection timeout' } * ``` */ healthCheck?(): Promise<{ healthy: boolean; message?: string; }>; } /** * Main EventBus interface for event-driven communication * * EventBus provides a publish-subscribe pattern for decoupled * communication within a BlaizeJS application. * * Features: * - Type-safe event publishing and subscription * - Pattern-based routing (wildcards, regex) * - Optional distributed mode via adapters * - Automatic server identification * - Correlation ID propagation * * @example Basic usage * ```typescript * import { createEventBus } from '@blaizejs/core'; * * const eventBus = createEventBus({ serverId: 'server-1' }); * * // Subscribe to events * const unsubscribe = eventBus.subscribe('user:created', async (event) => { * console.log('New user:', event.data); * await sendWelcomeEmail(event.data.email); * }); * * // Publish an event * await eventBus.publish('user:created', { * userId: '123', * email: 'user@example.com', * }); * * // Cleanup * unsubscribe(); * ``` * * @example With adapter for distributed events * ```typescript * import { createEventBus } from '@blaizejs/core'; * import { RedisEventBusAdapter } from '@blaizejs/adapter-redis'; * * const eventBus = createEventBus({ serverId: 'server-1' }); * * // Enable distributed mode * const adapter = new RedisEventBusAdapter({ * host: 'localhost', * port: 6379, * }); * * await eventBus.setAdapter(adapter); * * // Events now propagate across all servers * await eventBus.publish('cache:invalidate', { key: 'users' }); * ``` * * @example Pattern matching * ```typescript * // Wildcard pattern * eventBus.subscribe('user:*', handler); * * // Regex pattern * eventBus.subscribe(/^(user|admin):/, handler); * * // Exact match * eventBus.subscribe('system:shutdown', handler); * ``` * * @see createEventBus * @see TypedEventBus */ interface EventBus { /** * Publish an event to all matching subscribers * * The event is delivered to: * - All local subscribers with matching patterns * - All remote subscribers (if adapter is set) * * Publishing is fire-and-forget. If a handler throws, * the error is logged but doesn't fail the publish operation. * * @param type - Event type identifier * @param data - Event data payload (optional) * @returns Promise that resolves when event is published * * @example Publish with data * ```typescript * await eventBus.publish('user:created', { * userId: '123', * email: 'user@example.com', * }); * ``` * * @example Publish without data * ```typescript * await eventBus.publish('system:ready'); * ``` * * @example Publish with correlation ID * ```typescript * // Inside a route handler * async ({ ctx, eventBus }) => { * await eventBus.publish('order:placed', * { orderId: '123' }, * ctx.correlationId * ); * } * ``` */ publish(type: string, data?: unknown): Promise; /** * Subscribe to events matching a pattern * * Patterns can be: * - Exact string: 'user:created' * - Wildcard: 'user:*' (matches user:created, user:updated, etc.) * - RegExp: /^user:/ (matches any event starting with 'user:') * * The subscription is active immediately. * Handlers are called in the order they were registered. * * @param pattern - Event type pattern (string or RegExp) * @param handler - Function to call for matching events * @returns Unsubscribe function * * @example String pattern * ```typescript * const unsubscribe = eventBus.subscribe( * 'user:created', * async (event) => { * await sendWelcomeEmail(event.data.email); * } * ); * ``` * * @example Wildcard pattern * ```typescript * eventBus.subscribe('user:*', (event) => { * console.log('User event:', event.type, event.data); * }); * ``` * * @example RegExp pattern * ```typescript * eventBus.subscribe(/^(user|admin):created$/, (event) => { * auditLog.record(event); * }); * ``` * * @example Cleanup * ```typescript * const unsubscribe = eventBus.subscribe('data:updated', handler); * * // Later * unsubscribe(); * ``` */ subscribe(pattern: string | RegExp, handler: EventHandler): Unsubscribe; /** * Set or replace the distributed adapter * * Enables cross-server event propagation. * Automatically connects the adapter. * * If an adapter is already set, it will be disconnected first. * * @param adapter - EventBusAdapter implementation * @returns Promise that resolves when adapter is connected * * @example * ```typescript * import { RedisEventBusAdapter } from '@blaizejs/adapter-redis'; * * const adapter = new RedisEventBusAdapter({ * host: 'localhost', * port: 6379, * }); * * await eventBus.setAdapter(adapter); * console.log('Distributed mode enabled'); * ``` */ setAdapter(adapter: EventBusAdapter): Promise; /** * Disconnect and cleanup * * Disconnects the adapter (if set) and clears all subscriptions. * Should be called during server shutdown. * * @returns Promise that resolves when cleanup is complete * * @example * ```typescript * // During server shutdown * await eventBus.disconnect(); * ``` */ disconnect(): Promise; /** * Server ID for this EventBus instance * * Used to: * - Identify event origin * - Prevent echo in distributed mode * - Debug event flow * * Automatically set during EventBus creation. * * @readonly * * @example * ```typescript * console.log('Server ID:', eventBus.serverId); * // Output: 'server-1' or auto-generated UUID * ``` */ readonly serverId: string; } /** * Helper to determine which events match a given pattern * * Performs compile-time pattern matching to extract event types * that match a subscription pattern. * * Pattern matching rules: * - `*` matches all events * - `prefix:*` matches all events starting with `prefix:` * - Exact string matches only that event * - Non-matching patterns return `never` * * @template TSchemas - Event schema map * @template TPattern - Pattern string to match * * @example Match all events * ```typescript * type Schemas = { * 'user:created': z.ZodObject<...>; * 'user:updated': z.ZodObject<...>; * 'order:placed': z.ZodObject<...>; * }; * * // Result: 'user:created' | 'user:updated' | 'order:placed' * type AllEvents = MatchingEvents; * ``` * * @example Match namespace wildcard * ```typescript * // Result: 'user:created' | 'user:updated' * type UserEvents = MatchingEvents; * ``` * * @example Match exact event * ```typescript * // Result: 'user:created' * type ExactMatch = MatchingEvents; * ``` * * @example Deep nesting * ```typescript * type Schemas = { * 'a:b:c': z.ZodObject<...>; * 'a:b:d': z.ZodObject<...>; * 'a:x:y': z.ZodObject<...>; * }; * * // Result: 'a:b:c' | 'a:b:d' * type DeepMatch = MatchingEvents; * ``` * * @example No match * ```typescript * // Result: never * type NoMatch = MatchingEvents; * ``` */ type MatchingEvents = TPattern extends '*' ? keyof TSchemas : TPattern extends `${infer Prefix}:*` ? Extract : TPattern extends keyof TSchemas ? TPattern : never; /** * Helper to extract a union of data types from matching events * * Takes a pattern and returns a union type of all event data types * that match that pattern. Uses Zod's type inference to extract * the TypeScript types from schemas. * * @template TSchemas - Event schema map * @template TPattern - Pattern string to match * * @example Single event type * ```typescript * type Schemas = { * 'user:created': z.ZodObject<{ userId: z.ZodString }>; * }; * * // Result: { userId: string } * type UserCreatedData = EventDataUnion; * ``` * * @example Union of multiple event types * ```typescript * type Schemas = { * 'user:created': z.ZodObject<{ userId: z.ZodString }>; * 'user:updated': z.ZodObject<{ userId: z.ZodString; email: z.ZodString }>; * }; * * // Result: { userId: string } | { userId: string; email: string } * type UserEventData = EventDataUnion; * ``` * * @example All events * ```typescript * type Schemas = { * 'user:created': z.ZodObject<{ userId: z.ZodString }>; * 'order:placed': z.ZodObject<{ orderId: z.ZodString }>; * }; * * // Result: { userId: string } | { orderId: string } * type AllEventData = EventDataUnion; * ``` * * @example No matching events * ```typescript * // Result: never * type NoData = EventDataUnion; * ``` */ type EventDataUnion = MatchingEvents extends never ? never : TSchemas[MatchingEvents] extends z.ZodType ? T : never; /** * Type-safe EventBus wrapper with Zod schema validation * * Provides compile-time and runtime type safety for event publishing * and subscription. All event types and data shapes are validated * against the provided Zod schemas. * * Features: * - Type-safe publish: Only known events with correct data types * - Type-safe subscribe: Handlers receive correctly typed event data * - Pattern matching: Wildcard support with type inference * - Runtime validation: Optional Zod validation on publish/receive * - Error handling: Configurable validation error behavior * * @template TSchemas - Map of event types to Zod schemas * * @example Basic usage * ```typescript * import { z } from 'zod'; * import { createTypedEventBus } from '@blaizejs/core'; * * const schemas = { * 'user:created': z.object({ * userId: z.string().uuid(), * email: z.string().email(), * }), * 'user:updated': z.object({ * userId: z.string().uuid(), * email: z.string().email(), * }), * 'order:placed': z.object({ * orderId: z.string(), * total: z.number().positive(), * }), * } satisfies EventSchemas; * * const typedBus = createTypedEventBus(baseBus, { schemas }); * * // Type-safe publish * await typedBus.publish('user:created', { * userId: '123', * email: 'user@example.com', * }); * * // TypeScript error: wrong data type * await typedBus.publish('user:created', { * userId: 123, // Error: should be string * }); * * // TypeScript error: unknown event * await typedBus.publish('unknown:event', {}); // Error * ``` * * @example Pattern-based subscriptions * ```typescript * // Subscribe to all user events * typedBus.subscribe('user:*', async (event) => { * // event.data is { userId: string; email: string } * // (union of user:created and user:updated) * console.log('User event:', event.type, event.data.userId); * }); * * // Subscribe to specific event * typedBus.subscribe('order:placed', async (event) => { * // event.data is { orderId: string; total: number } * await processOrder(event.data.orderId); * }); * * // Subscribe to all events * typedBus.subscribe('*', async (event) => { * // event.data is union of all event data types * auditLog.record(event); * }); * ``` * * @example With validation * ```typescript * const typedBus = createTypedEventBus(baseBus, { * schemas, * unknownEventBehavior: 'warn', * onValidationError: (error) => { * logger.error('Validation failed', { error }); * }, * }); * * // Validation always runs on publish * try { * await typedBus.publish('user:created', { * userId: 'invalid-uuid', // Fails UUID validation * }); * } catch (error) { * // EventValidationError thrown * } * * // Validation always runs on receive (invalid events dropped) * typedBus.subscribe('user:created', async (event) => { * // Only valid events reach this handler * // Extra fields are stripped automatically * }); * ``` * * @see createTypedEventBus * @see EventSchemas * @see TypedEventBusOptions */ interface TypedEventBus { /** * Publish a type-safe event * * Only allows publishing events that exist in the schema map, * and the data must match the schema's inferred type. * * **Validation**: The data is always validated against the Zod * schema at runtime. Extra fields are stripped automatically (Zod's * default `.strip()` behavior). * * @template K - Event type (must be a key in TSchemas) * @param type - Event type to publish * @param data - Event data matching the schema's type * @returns Promise that resolves when event is published * * @throws {EventValidationError} If validation fails * * @example * ```typescript * await typedBus.publish('user:created', { * userId: '123', * email: 'user@example.com', * }); * ``` * * @example Extra fields are stripped * ```typescript * await typedBus.publish('user:created', { * userId: '123', * email: 'user@example.com', * extraField: 'ignored', // Stripped automatically * }); * ``` */ publish(type: K, data: z.input): Promise; /** * Subscribe to events matching a pattern with type-safe handlers * * Supports exact matches, namespace wildcards (`prefix:*`), * and all events (`*`). The handler receives events with * correctly typed data based on the pattern. * * @template TPattern - Pattern string (event type, wildcard, or '*') * @param pattern - Event type pattern to match * @param handler - Handler function with typed event data * @returns Unsubscribe function * * @example Exact match * ```typescript * typedBus.subscribe('user:created', async (event) => { * // event.data is { userId: string; email: string } * await sendWelcomeEmail(event.data.email); * }); * ``` * * @example Wildcard pattern * ```typescript * typedBus.subscribe('user:*', async (event) => { * // event.data is union of all user:* event data types * if (event.type === 'user:created') { * // Type narrowing works here * } * }); * ``` * * @example All events * ```typescript * typedBus.subscribe('*', async (event) => { * // event.data is union of all event data types * auditLog.record(event); * }); * ``` */ subscribe(pattern: TPattern, handler: (event: BlaizeEvent>) => void | Promise): Unsubscribe; /** * Set or replace the distributed adapter * * @param adapter - EventBusAdapter implementation * @returns Promise that resolves when adapter is connected * * @see EventBus.setAdapter */ setAdapter(adapter: EventBusAdapter): Promise; /** * Disconnect and cleanup * * @returns Promise that resolves when cleanup is complete * * @see EventBus.disconnect */ disconnect(): Promise; /** * Server ID for this EventBus instance * * @readonly * @see EventBus.serverId */ readonly serverId: string; /** * Reference to the underlying EventBus * * Provides access to the base EventBus for advanced use cases * or when you need to bypass type safety temporarily. * * @readonly * * @example * ```typescript * // Access base bus for untyped operations * typedBus.base.publish('dynamic:event', dynamicData); * ``` */ readonly base: EventBus; } /** * Arbitrary metadata that can be attached to log entries * * Used to add structured context to logs. Keys are strings and values * can be any JSON-serializable data. The logger will handle Error objects * specially by extracting message, stack, and name properties. * * @example * ```typescript * const meta: LogMetadata = { * userId: '123', * action: 'login', * duration: 145, * tags: ['auth', 'success'] * }; * ``` */ interface LogMetadata { [key: string]: unknown; } /** * Core logger interface implemented by the Logger class * * Provides structured logging with automatic metadata enrichment, * child logger creation for request-scoped logging, and graceful * shutdown support via flush(). * * The logger is available in all route handlers via `ctx.services.log` * and automatically includes request context (correlationId, method, path). * * @example Basic Usage * ```typescript * // In route handler * export const GET = appRoute.get({ * handler: async (ctx) => { * ctx.services.log.info('User requested profile', { * userId: ctx.params.userId * }); * * try { * const user = await getUser(ctx.params.userId); * return user; * } catch (error) { * ctx.services.log.error('Failed to fetch user', { * userId: ctx.params.userId, * error * }); * throw error; * } * } * }); * ``` * * @example Child Loggers * ```typescript * // Create a child logger with additional context * const dbLogger = ctx.services.log.child({ * component: 'database', * operation: 'user-lookup' * }); * * dbLogger.debug('Executing query', { query: 'SELECT * FROM users' }); * // Output includes: { component: 'database', operation: 'user-lookup', ... } * ``` */ interface BlaizeLogger { /** * Log a debug message * * Use for detailed diagnostic information during development. * These logs are typically disabled in production environments. * * @param message - The log message * @param meta - Optional metadata to attach * * @example * ```typescript * ctx.services.log.debug('Cache lookup', { * key: 'user:123', * hit: true, * ttl: 3600 * }); * ``` */ debug(message: string, meta?: LogMetadata): void; /** * Log an info message * * Use for general informational messages about application flow. * This is the default log level for production environments. * * @param message - The log message * @param meta - Optional metadata to attach * * @example * ```typescript * ctx.services.log.info('User login successful', { * userId: '123', * method: 'oauth', * provider: 'google' * }); * ``` */ info(message: string, meta?: LogMetadata): void; /** * Log a warning message * * Use for potentially harmful situations that don't prevent * the application from functioning. * * @param message - The log message * @param meta - Optional metadata to attach * * @example * ```typescript * ctx.services.log.warn('Rate limit approaching threshold', { * userId: '123', * currentCount: 95, * limit: 100 * }); * ``` */ warn(message: string, meta?: LogMetadata): void; /** * Log an error message * * Use for error conditions that don't stop the application. * The logger automatically extracts stack traces from Error objects. * * @param message - The log message * @param meta - Optional metadata to attach * * @example * ```typescript * ctx.services.log.error('Database query failed', { * query: 'SELECT * FROM users', * error: err, // Error object - stack trace will be extracted * retryCount: 3 * }); * ``` */ error(message: string, meta?: LogMetadata): void; /** * Create a child logger with inherited metadata * * Child loggers inherit all metadata from their parent and add * their own. This is useful for adding component-specific context * without polluting the parent logger. * * Child metadata overrides parent metadata for the same keys. * * @param meta - Metadata to add to all logs from this child * @returns A new logger instance with merged metadata * * @example Component-Scoped Logging * ```typescript * // Create a child logger for a specific component * const authLogger = ctx.services.log.child({ * component: 'auth', * action: 'verify-token' * }); * * authLogger.debug('Verifying JWT token'); * // Output includes: { correlationId, method, path, component: 'auth', ... } * * authLogger.info('Token verified', { userId: '123' }); * // Output includes all parent + child metadata * ``` * * @example Nested Child Loggers * ```typescript * const dbLogger = ctx.services.log.child({ component: 'database' }); * const queryLogger = dbLogger.child({ operation: 'select' }); * * queryLogger.debug('Executing query'); * // Output includes: { correlationId, component: 'database', operation: 'select', ... } * ``` */ child(meta: LogMetadata): BlaizeLogger; /** * Flush any buffered logs and wait for completion * * Call this during graceful shutdown to ensure all logs are written * before the process exits. This is especially important for transports * that batch writes (e.g., sending logs to external services). * * If the transport doesn't implement flush(), this is a no-op. * * @returns Promise that resolves when all logs are flushed * * @example Graceful Shutdown * ```typescript * // In server shutdown handler * process.on('SIGTERM', async () => { * console.log('Shutting down gracefully...'); * * // Flush logs before exit * await server._logger.flush(); * * await server.close(); * process.exit(0); * }); * ``` */ flush(): Promise; } /** * Function to pass control to the next middleware */ type NextFunction = () => Promise | void; /** * Middleware function signature * * @param ctx - The Blaize context object * @param next - Function to invoke the next middleware in the chain * @param logger - Logger instance for logging within the middleware * * @example * const myMiddleware: MiddlewareFunction = async (ctx, next, logger) => { * logger.info('Executing my middleware'); * // Middleware logic here * await next(); * }; */ type MiddlewareFunction = (mc: MiddlewareContext) => Promise | void; /** * Middleware type with generic parameters for type-safe state and service contributions * @template TState - Type of state this middleware contributes to the context * @template TServices - Type of services this middleware contributes to the context */ interface Middleware { name: string; execute: MiddlewareFunction; skip?: ((ctx: Context) => boolean) | undefined; debug?: boolean | undefined; _services?: TServices; } /** * Handler Context Types * * Structured context objects for route handlers, middleware, and plugins. * These types replace positional parameters with named context objects, * improving type inference and developer experience. * * @module @blaizejs/types/handler-context * @since 0.4.0 */ /** * Context object for route handlers * * Provides structured access to request context, parameters, logging, * and event bus for standard HTTP route handlers. * * @template TState - Application state type (accumulated from middleware) * @template TServices - Application services type (accumulated from middleware) * @template TBody - Request body type (from schema validation) * @template TQuery - Query parameters type (from schema validation) * @template TParams - URL parameters type (from schema validation) * @template TEvents - Event schemas for typed event bus * @template TFiles - **NEW** Uploaded files type (e.g., { avatar: UploadedFile }) * * @example Basic usage with destructuring * ```typescript * export const GET = appRoute.get({ * handler: async ({ ctx, params, logger, eventBus }) => { * logger.info('Processing request', { userId: params.userId }); * * await eventBus.publish('user:viewed', { * userId: params.userId, * timestamp: Date.now(), * }); * * return { message: 'Success' }; * }, * }); * ``` * * @example With typed state and services * ```typescript * interface AppState extends State { * user: { id: string; role: string }; * } * * interface AppServices extends Services { * db: Database; * cache: Cache; * } * * export const GET = appRoute.get< * z.ZodObject<{ userId: z.ZodString }>, * never, * never, * z.ZodObject<{ message: z.ZodString }> * >({ * schema: { * params: z.object({ userId: z.string() }), * response: z.object({ message: z.string() }), * }, * handler: async ({ ctx, params, logger, eventBus }: HandlerContext) => { * // ctx.state.user is typed * // ctx.services.db is typed * // params.userId is string * * const user = await ctx.services.db.users.findById(params.userId); * * logger.info('User retrieved', { * userId: params.userId, * requestedBy: ctx.state.user.id * }); * * return { message: `Hello, ${user.name}` }; * }, * }); * ``` * * @example Partial destructuring * ```typescript * export const POST = appRoute.post({ * handler: async ({ ctx, logger }) => { * // Only destructure what you need * logger.info('Creating resource', { * body: ctx.body, * correlationId: ctx.correlationId * }); * * return { id: 'new-resource-id' }; * }, * }); * ``` * * @example Accessing request/response directly * ```typescript * export const GET = appRoute.get({ * handler: async ({ ctx, logger }) => { * const userAgent = ctx.request.headers.get('user-agent'); * const acceptLanguage = ctx.request.headers.get('accept-language'); * * logger.debug('Request headers', { userAgent, acceptLanguage }); * * // Set custom response headers * ctx.response.headers.set('X-Custom-Header', 'value'); * * return { message: 'Headers processed' }; * }, * }); * ``` */ interface HandlerContext, TEvents extends EventSchemas = EventSchemas, TFiles = unknown> { /** * The Blaize context object * * Contains request, response, state, services, and correlation ID. * State and services are accumulated from middleware execution. */ ctx: Context; /** * URL parameters extracted from the route path * * Type is inferred from route schema if provided, * otherwise defaults to Record. * * @example * ```typescript * // Route: /users/:userId/posts/:postId * // params = { userId: '123', postId: '456' } * ``` */ params: TParams; /** * Logger instance with automatic request context * * Pre-configured with correlation ID and request metadata. * Create child loggers for component-specific logging. */ logger: BlaizeLogger; /** * Typed event bus for publishing and subscribing to events * * Type-safe event publishing based on event schemas. * Supports both local and distributed event handling. */ eventBus: TypedEventBus; } /** * Context object for middleware functions * * Provides structured access to request context, next function, * logging, and event bus for middleware execution. * * @template TState - Application state type (accumulated from previous middleware) * @template TServices - Application services type (accumulated from previous middleware) * @template TEvents - Event schemas for typed event bus * * @example Basic authentication middleware * ```typescript * export const authMiddleware: MiddlewareFunction = async ({ * ctx, * next, * logger, * eventBus * }: MiddlewareContext) => { * const token = ctx.request.headers.get('authorization'); * * if (!token) { * logger.warn('Missing authentication token'); * throw new UnauthorizedError('Authentication required'); * } * * const user = await verifyToken(token); * * // Add user to context state * ctx.state.user = user; * * logger.info('User authenticated', { userId: user.id }); * * await eventBus.publish('user:authenticated', { * userId: user.id, * timestamp: Date.now(), * }); * * await next(); * }; * ``` * * @example Database connection middleware * ```typescript * export const dbMiddleware: MiddlewareFunction = async ({ * ctx, * next, * logger * }: MiddlewareContext) => { * const db = await createDatabaseConnection(); * * // Add database to context services * ctx.services.db = db; * * logger.debug('Database connection established'); * * try { * await next(); * } finally { * await db.close(); * logger.debug('Database connection closed'); * } * }; * ``` * * @example Request timing middleware * ```typescript * export const timingMiddleware: MiddlewareFunction = async ({ * ctx, * next, * logger * }: MiddlewareContext) => { * const startTime = Date.now(); * * try { * await next(); * } finally { * const duration = Date.now() - startTime; * * logger.info('Request completed', { * method: ctx.request.method, * path: ctx.request.url.pathname, * duration, * status: ctx.response.status, * }); * } * }; * ``` * * @example Conditional middleware execution * ```typescript * export const cacheMiddleware: MiddlewareFunction = async ({ * ctx, * next, * logger * }: MiddlewareContext) => { * // Skip caching for non-GET requests * if (ctx.request.method !== 'GET') { * return next(); * } * * const cacheKey = ctx.request.url.pathname; * const cached = await cache.get(cacheKey); * * if (cached) { * logger.debug('Cache hit', { cacheKey }); * ctx.response = cached; * return; // Don't call next() * } * * logger.debug('Cache miss', { cacheKey }); * await next(); * * // Cache successful responses * if (ctx.response.status === 200) { * await cache.set(cacheKey, ctx.response); * } * }; * ``` */ interface MiddlewareContext { /** * The Blaize context object * * Middleware can read and modify ctx.state and ctx.services * to pass data to subsequent middleware and route handlers. */ ctx: Context; /** * Function to invoke the next middleware in the chain * * Must be called to continue execution to the route handler. * Omitting next() will short-circuit the middleware chain. */ next: NextFunction; /** * Logger instance with automatic request context * * Pre-configured with correlation ID and request metadata. */ logger: BlaizeLogger; /** * Typed event bus for publishing and subscribing to events * * Useful for emitting middleware lifecycle events or * application-level events during request processing. */ eventBus: TypedEventBus; } /** * Helper type to extract TypeScript type from Zod schema */ type Infer = T extends z.ZodType ? z.output : unknown; /** * HTTP methods supported by the router */ type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS'; /** * Schema for route validation with generic type parameters */ interface RouteSchema

, // URL parameters schema Q extends z.ZodType = z.ZodType, // Query parameters schema B extends z.ZodType = z.ZodType, // Body schema F extends z.ZodType = z.ZodType, // Files schema (defaults to any for backward compatibility) R extends z.ZodType = z.ZodType, // Response schema ED extends z.ZodType = z.ZodType> { /** Parameter schema for validation */ params?: P; /** Query schema for validation */ query?: Q; /** Files schema for multipart/form-data validation */ files?: F; /** Body schema for validation */ body?: B; /** Response schema for validation */ response?: R; /** Error Response Details schema for validation */ errorResponseDetails?: ED; } /** * Route handler function with strongly typed params and response * * **NEW IN v1.0.0:** Handler now receives a single context object instead * of positional parameters. This provides better extensibility and DX. * * **DEPRECATED:** The old signature `(ctx, params, logger) => ...` is still * supported for backward compatibility but will be removed in v2.0.0. * A deprecation warning is shown at runtime for old handlers. * * **MIGRATION:** Update handlers to destructure the context object: * * ```typescript * // Before (deprecated): * handler: async (ctx, params, logger) => { * logger.info('Request', params); * return { data: params.userId }; * } * * // After (recommended): * handler: async ({ ctx, params, logger, eventBus }) => { * logger.info('Request', params); * await eventBus.publish('user:viewed', { userId: params.userId }); * return { data: params.userId }; * } * ``` * * @template TParams - URL parameters type (from route params like :id) * @template TQuery - Query parameters type (from ?key=value) * @template TBody - Request body type (validated via schema) * @template TResponse - Response data type (validated via schema if provided) * @template TState - Accumulated state from middleware (e.g., user, session) * @template TServices - Accumulated services from middleware and plugins (e.g., db, cache) * @template TEvents - Event schemas for typed EventBus * @template TFiles - Uploaded files type (validated via schema.files) * * @param hc - Handler context containing ctx, params, logger, eventBus * @returns Response data of type TResponse (or Promise) * * @example Basic GET handler * ```typescript * const handler: RouteHandler = async ({ ctx, params, logger }) => { * logger.info('Fetching user', { userId: params.userId }); * const user = await ctx.services.db.getUser(params.userId); * return user; * }; * ``` * * @example POST with events * ```typescript * const handler: RouteHandler = async ({ ctx, logger, eventBus }) => { * logger.info('Creating user', { email: ctx.body.email }); * * const user = await ctx.services.db.createUser(ctx.body); * * await eventBus.publish('user:created', { * userId: user.id, * email: user.email, * }); * * return user; * }; * ``` * * @example With typed events * ```typescript * type UserEvents = { * 'user:created': { userId: string; email: string }; * }; * * const handler: RouteHandler< * any, any, any, any, any, any, UserEvents * > = async ({ ctx, eventBus }) => { * const user = await createUser(ctx.body); * * // TypeScript enforces event data shape * await eventBus.publish('user:created', { * userId: user.id, * email: user.email, * }); * * return user; * }; * ``` */ type RouteHandler, TQuery = Record, TBody = unknown, TResponse = unknown, TState extends State = State, TServices extends Services = Services, TEvents extends EventSchemas = EventSchemas, TFiles = unknown> = (hc: HandlerContext) => Promise | TResponse; /** * Options for a route method with schema-based type inference */ interface RouteMethodOptions

, Q extends z.ZodType = z.ZodType, B extends z.ZodType = z.ZodType, F extends z.ZodType = z.ZodType, R extends z.ZodType = z.ZodType, ED extends z.ZodType = z.ZodType> { /** Schema for request/response validation */ schema?: RouteSchema; /** Handler function for the route */ handler: RouteHandler

: Record, Q extends z.ZodType ? Infer : QueryParams, B extends z.ZodType ? Infer : unknown, R extends z.ZodType ? Infer : unknown, State, Services, EventSchemas, F extends z.ZodType ? Infer : unknown>; /** Middleware to apply to this route */ middleware?: Middleware[]; /** Route-specific options */ options?: Record; } /** * Route definition mapping HTTP methods to handlers */ interface RouteDefinition { GET?: RouteMethodOptions; POST?: RouteMethodOptions; PUT?: RouteMethodOptions; DELETE?: RouteMethodOptions; PATCH?: RouteMethodOptions; HEAD?: RouteMethodOptions; OPTIONS?: RouteMethodOptions; } /** * Route object with path */ interface Route extends RouteDefinition { /** Path of the route */ path: string; } /** * Router interface */ interface Router { ready: Promise; /** Handle an incoming request */ handleRequest: (ctx: Context, logger: BlaizeLogger, eventBus: TypedEventBus) => Promise; /** Get all registered routes */ getRoutes: () => Route[]; /** Add a route programmatically */ addRoute: (route: Route) => void; /** Add multiple routes programmatically with batch processing */ addRoutes: (routes: Route[]) => { added: Route[]; removed: string[]; changed: Route[]; }; /** Add a route directory for plugins */ addRouteDirectory(directory: string, options?: { prefix?: string; }): Promise; /** Get route conflicts */ getRouteConflicts(): Array<{ path: string; sources: string[]; }>; /** Close watchers and cleanup resources */ close?: () => Promise; } /** * Configuration for route methods that don't accept a request body * Used by: GET, HEAD, DELETE, OPTIONS */ type RouteConfigWithoutBody

= { schema?: { params?: P extends never ? never : P; query?: Q extends never ? never : Q; response?: R extends never ? never : R; }; handler: RouteHandler

: Record, Q extends z.ZodType ? Infer : QueryParams, never, // No body [ R ] extends [never] ? void : R extends z.ZodType ? Infer : void, TState, TServices, TEvents, never>; middleware?: Middleware[]; options?: Record; }; /** * Configuration for route methods that accept a request body * Used by: POST, PUT, PATCH */ type RouteConfigWithBody

= { schema?: { params?: P extends never ? never : P; query?: Q extends never ? never : Q; body?: B extends never ? never : B; files?: F extends never ? never : F; response?: R extends never ? never : R; }; handler: RouteHandler

: Record, Q extends z.ZodType ? Infer : QueryParams, B extends z.ZodType ? Infer : unknown, [ R ] extends [never] ? void : R extends z.ZodType ? Infer : void, TState, TServices, TEvents, F extends z.ZodType ? Infer : unknown>; middleware?: Middleware[]; options?: Record; }; /** * GET route creator with state, services, and event schemas support * * **NEW IN v1.0.0:** Added TEvents generic for typed EventBus * * @template TState - Accumulated state from middleware * @template TServices - Accumulated services from middleware and plugins * @template TEvents - Event schemas for typed EventBus (NEW) * * @example With typed events * ```typescript * type UserEvents = { * 'user:viewed': { userId: string; timestamp: number }; * }; * * export const GET = createGetRoute()({ * handler: async ({ params, eventBus }) => { * await eventBus.publish('user:viewed', { * userId: params.userId, * timestamp: Date.now(), * }); * * return { userId: params.userId }; * }, * }); * ``` */ type CreateGetRoute = () =>

(config: RouteConfigWithoutBody) => { GET: RouteMethodOptions

; path: string; }; /** * POST route creator with state, services, and event schemas support * * @template TState - Accumulated state from middleware * @template TServices - Accumulated services from middleware and plugins * @template TEvents - Event schemas for typed EventBus (NEW) */ type CreatePostRoute = () =>

(config: RouteConfigWithBody) => { POST: RouteMethodOptions

; path: string; }; /** * PUT route creator with state and services support */ type CreatePutRoute = () =>

(config: RouteConfigWithBody) => { PUT: RouteMethodOptions

; path: string; }; /** * DELETE route creator with state and services support */ type CreateDeleteRoute = () =>

(config: RouteConfigWithoutBody) => { DELETE: RouteMethodOptions

; path: string; }; /** * PATCH route creator with state and services support */ type CreatePatchRoute = () =>

(config: RouteConfigWithBody) => { PATCH: RouteMethodOptions

; path: string; }; /** * CORS Types for BlaizeJS Framework * * Comprehensive type definitions for W3C-compliant CORS middleware * with support for string, regex, and async function origin validation. * * @module @blaizejs/types/cors */ /** * Origin configuration type supporting multiple validation methods * * @example * ```typescript * // String origin (exact match) * const origin: CorsOrigin = 'https://example.com'; * * // RegExp pattern * const origin: CorsOrigin = /^https:\/\/.*\.example\.com$/; * * // Dynamic validation function * const origin: CorsOrigin = async (origin, ctx) => { * return await checkOriginAllowed(origin, ctx?.state.user); * }; * * // Array of mixed types * const origin: CorsOrigin = [ * 'https://localhost:3000', * /^https:\/\/.*\.example\.com$/, * (origin) => origin.endsWith('.trusted.com') * ]; * ``` */ type CorsOrigin = string | RegExp | ((origin: string, ctx?: Context) => boolean | Promise) | Array) => boolean | Promise)>; /** * HTTP methods that can be allowed in CORS * Based on W3C CORS specification */ type CorsHttpMethod = HttpMethod | 'CONNECT' | 'TRACE'; /** * Main CORS configuration options * * @example * ```typescript * const corsOptions: CorsOptions = { * origin: 'https://example.com', * methods: ['GET', 'POST'], * credentials: true, * maxAge: 86400 * }; * ``` */ interface CorsOptions { /** * Configures the Access-Control-Allow-Origin header * * Possible values: * - `true`: Allow all origins (sets to '*' unless credentials is true, then reflects origin) * - `false`: Disable CORS (no headers set) * - `string`: Specific origin to allow * - `RegExp`: Pattern to match origins * - `function`: Custom validation logic * - `array`: Multiple origin configurations * * @default false */ origin?: boolean | CorsOrigin; /** * Configures the Access-Control-Allow-Methods header * * @default ['GET', 'HEAD', 'PUT', 'PATCH', 'POST', 'DELETE'] * @example ['GET', 'POST'] */ methods?: CorsHttpMethod[] | string; /** * Configures the Access-Control-Allow-Headers header * * Pass an array of allowed headers or a comma-delimited string. * * @default Request's Access-Control-Request-Headers header value * @example ['Content-Type', 'Authorization'] */ allowedHeaders?: string[] | string; /** * Configures the Access-Control-Expose-Headers header * * Headers that the browser is allowed to access. * * @default [] * @example ['Content-Range', 'X-Content-Range'] */ exposedHeaders?: string[] | string; /** * Configures the Access-Control-Allow-Credentials header * * Set to true to allow credentials (cookies, authorization headers, TLS client certificates). * Note: Cannot be used with origin: '*' for security reasons. * * @default false */ credentials?: boolean; /** * Configures the Access-Control-Max-Age header in seconds * * Indicates how long browsers can cache preflight response. * Set to -1 to disable caching. * * @default undefined (browser decides) * @example 86400 // 24 hours */ maxAge?: number; /** * Whether to pass the CORS preflight response to the next handler * * When false, the preflight response is sent immediately. * When true, control passes to the next middleware/handler. * * @default false */ preflightContinue?: boolean; /** * HTTP status code for successful OPTIONS requests * * Some legacy browsers require 200, while 204 is more correct. * * @default 204 */ optionsSuccessStatus?: number; } /** * BlaizeJS Server Module - Enhanced with Correlation Configuration * * Provides the core HTTP/2 server implementation with HTTP/1.1 fallback * and correlation ID tracking configuration. */ interface StopOptions { timeout?: number; plugins?: Plugin[]; onStopping?: () => Promise | void; onStopped?: () => Promise | void; } /** * BlaizeJS Server instance with generic type accumulation * * @template TState - The accumulated state type from middleware * @template TServices - The accumulated services type from middleware and plugins * */ interface Server { /** The underlying HTTP or HTTP/2 server */ server: http.Server | http2.Http2Server | undefined; /** The port the server is configured to listen on */ port: number; /** CORS configuration for this server */ corsOptions?: CorsOptions | boolean; /** Body size limits for incoming requests */ bodyLimits: BodyLimits; /** The host the server is bound to */ host: string; events: EventEmitter; /** Direct access to registered plugins */ plugins: Plugin[]; /** Direct access to registered plugins */ middleware: Middleware[]; /** Internal property for signal handlers */ _signalHandlers?: { unregister: () => void; }; /** Internal logger instance (for server use only) */ _logger: BlaizeLogger; /** * Server ID for multi-server coordination * @readonly */ readonly serverId: string; /** * EventBus for server-wide event communication * @readonly */ readonly eventBus: TypedEventBus; /** Start the server and listen for connections */ listen: (port?: number, host?: string) => Promise>; /** Stop the server */ close: (stopOptions?: StopOptions) => Promise; /** * Add global middleware to the server * * @param middleware - Single middleware or array of middleware to add * @returns New Server instance with accumulated types from the middleware * * @example * ```typescript * // Single middleware * const serverWithAuth = server.use(authMiddleware); * // serverWithAuth has type Server<{user: User}, {auth: AuthService}> * * // Array of middleware * const serverWithMiddleware = server.use([authMiddleware, loggerMiddleware]); * // serverWithMiddleware has type Server<{user, requestId}, {auth, logger}> * ``` */ use(middleware: Middleware): Server; use[]>(middleware: MW): Server>, TServices & UnionToIntersection>, TEvents>; /** * Register a plugin with the server * * @param plugin - Single plugin or array of plugins to register * @returns Promise resolving to new Server instance with accumulated types * * @example * ```typescript * // Single plugin * const serverWithDb = await server.register(databasePlugin); * // serverWithDb has type Server<{}, {db: DatabaseService}> * * // Array of plugins * const serverWithPlugins = await server.register([dbPlugin, cachePlugin]); * // serverWithPlugins has type Server<{}, {db, cache}> * ``` */ register(plugin: Plugin): Promise>; register

[]>(plugin: P): Promise>, TServices & UnionToIntersection>, TEvents>>; /** Access to the routing system */ router: Router; /** Context storage system */ context: AsyncLocalStorage; pluginManager: PluginLifecycleManager; } /** * BlaizeJS Plugin Module * * Provides the plugin system for extending framework functionality. */ /** * Plugin lifecycle hooks with full type safety * * Plugins execute in this order: * 1. register() - Add middleware, routes * 2. initialize() - Create resources * 3. onServerStart() - Start background work * 4. onServerStop() - Stop background work * 5. terminate() - Cleanup resources */ interface PluginHooks { /** * Called when plugin is registered to server * * Use this hook to: * - Add middleware via server.use() * - Add routes via server.router.addRoute() * - Subscribe to server events * * @param server - BlaizeJS server instance * @example * ```typescript * register: async (server) => { * server.use(createMiddleware({ * handler: async (ctx, next) => { * ctx.services.db = db; * await next(); * }, * })); * } * ``` */ register?: (server: Server) => void | Promise; /** * Called during server initialization * * Use this hook to: * - Create database connections * - Initialize services * - Allocate resources * * @example * ```typescript * initialize: async () => { * db = await Database.connect(config); * } * ``` */ initialize?: (server: Server) => void | Promise; /** * Called when server starts listening * * Use this hook to: * - Start background workers * - Start cron jobs * - Begin health checks * * @example * ```typescript * onServerStart: async () => { * worker = new BackgroundWorker(); * await worker.start(); * } * ``` */ onServerStart?: (server: Http2Server | Server$1) => void | Promise; /** * Called when server stops listening * * Use this hook to: * - Stop background workers * - Flush buffers * - Complete in-flight work * * @example * ```typescript * onServerStop: async () => { * await worker.stop({ graceful: true }); * } * ``` */ onServerStop?: (server: Http2Server | Server$1) => void | Promise; /** * Called during server termination * * Use this hook to: * - Close database connections * - Release file handles * - Free memory * * @example * ```typescript * terminate: async () => { * await db?.close(); * } * ``` */ terminate?: (server: Server) => void | Promise; } /** * Plugin interface */ interface Plugin extends PluginHooks { /** Plugin name */ name: string; /** Plugin version */ version: string; /** * Called when plugin is registered to server * * This hook is always present - createPlugin provides a default empty async function * if not specified by the plugin author. * * @override Makes register required (not optional like in PluginHooks) */ register: (server: Server) => void | Promise; /** * Type carriers for compile-time type information * These are never used at runtime but allow TypeScript to track types */ _state?: TState; _services?: TServices; } interface PluginLifecycleManager { registerPlugins(server: Server): Promise; initializePlugins(server: Server): Promise; terminatePlugins(server: Server): Promise; onServerStart(server: Server, httpServer: any): Promise; onServerStop(server: Server, httpServer: any): Promise; } /** * Type composition utilities for extracting and composing middleware type contributions * @module composition * @since v0.4.0 */ /** * Extracts the State type contribution from a middleware * @template T - The middleware type to extract from * @returns The state type if present, empty object otherwise */ type ExtractMiddlewareState = T extends Middleware ? S : {}; /** * Extracts the State type contribution from a plugin * @template T - The plugin type to extract from * @returns The state type if present, empty object otherwise */ type ExtractPluginState = T extends Plugin ? S : {}; /** * Extracts the Services type contribution from a middleware * @template T - The middleware type to extract from * @returns The services type if present, empty object otherwise */ type ExtractMiddlewareServices = T extends Middleware ? S : {}; /** * Extracts the Services type contribution from a plugin * @template T - The plugin type to extract from * @returns The services type if present, empty object otherwise */ type ExtractPluginServices = T extends Plugin ? S : {}; /** * Utility type to convert a union type to an intersection type * This is the magic that allows us to compose multiple middleware contributions * * @example * type U = { a: string } | { b: number } * type I = UnionToIntersection // { a: string } & { b: number } * */ type UnionToIntersection = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never; /** * Configuration options for creating test contexts */ interface TestContextConfig { method?: string; path?: string; query?: Record; params?: Record; headers?: Record; files?: unknown; body?: unknown; initialState?: Record; initialServices?: Record; withEventEmitter?: boolean; } /** * Create a test context for BlaizeJS testing * * Creates a complete Context object suitable for testing route handlers and middleware. * * The function uses a generic approach that allows the context to be used with * handlers that expect different query types (including `never` for SSE routes). * * @param config Configuration options for the context * @returns Complete BlaizeJS Context object * * @example * ```typescript * // Basic usage * const ctx = createMockContext(); * * // With configuration * const ctx = createMockContext({ * method: 'POST', * path: '/api/users', * body: { name: 'John Doe' }, * headers: { authorization: 'Bearer token' } * }); * * // For SSE testing * const ctx = createMockContext({ * withEventEmitter: true, * headers: { accept: 'text/event-stream' } * }); * ``` */ declare function createMockContext>(config?: TestContextConfig): Context; /** * Create a mock context specifically for SSE routes * This version ensures compatibility with SSE route handlers */ declare function createSSEMockContext(config?: TestContextConfig): Context; /** * Mock EventBus for Testing * * Provides mock implementations of EventBus for testing with assertion helpers * that reduce test boilerplate by ~70%. * * @packageDocumentation */ /** * Assertion helpers for MockEventBus * * These methods make testing event publishing much cleaner and more readable. */ interface MockEventBusHelpers { /** * Assert that an event with the given type was published * * Optionally validates event data using partial matching (toMatchObject). * This means you only need to specify the fields you care about. * * @param eventType - The expected event type * @param data - Optional data to validate (partial match) * @throws {Error} If the event was not published, with helpful error message * * @example * ```typescript * await eventBus.publish('user:created', { userId: '123', email: 'test@example.com' }); * * // Pass - exact match * eventBus.assertPublished('user:created', { userId: '123', email: 'test@example.com' }); * * // Pass - partial match (only check userId) * eventBus.assertPublished('user:created', { userId: '123' }); * * // Throw - event not found * eventBus.assertPublished('user:deleted'); // Error with actual events listed * * // Throw - data doesn't match * eventBus.assertPublished('user:created', { userId: '456' }); // Error * ``` */ assertPublished(eventType: string, data?: unknown): void; /** * Assert that an event with the given type was NOT published * * @param eventType - The event type that should not have been published * @throws {Error} If the event was published * * @example * ```typescript * await eventBus.publish('user:created', { userId: '123' }); * * // Pass - event was not published * eventBus.assertNotPublished('user:deleted'); * * // Throw - event was published * eventBus.assertNotPublished('user:created'); // Error * ``` */ assertNotPublished(eventType: string): void; /** * Get all published events, optionally filtered by type * * Returns a copy of the events array to prevent external mutation. * * @param eventType - Optional event type to filter by * @returns Array of published events * * @example * ```typescript * await eventBus.publish('user:created', { userId: '123' }); * await eventBus.publish('user:updated', { userId: '123' }); * await eventBus.publish('user:created', { userId: '456' }); * * // Get all events * const allEvents = eventBus.getPublishedEvents(); * console.log(allEvents.length); // 3 * * // Get only user:created events * const createdEvents = eventBus.getPublishedEvents('user:created'); * console.log(createdEvents.length); // 2 * ``` */ getPublishedEvents(eventType?: string): Array<{ type: string; data: unknown; timestamp: number; }>; /** * Clear all tracked events and reset mock state * * Clears: * - All published events * - Vitest mock call history * * @example * ```typescript * await eventBus.publish('user:created', { userId: '123' }); * eventBus.clear(); * * eventBus.getPublishedEvents(); // [] * expect(eventBus.publish).not.toHaveBeenCalled(); // ✅ Pass * ``` */ clear(): void; } /** * Create a mock EventBus for testing with assertion helpers * * NEW in 0.6.0: Assertion helpers reduce test boilerplate by ~70%: * - eventBus.assertPublished('event:type', { data }) * - eventBus.assertNotPublished('event:type') * - eventBus.getPublishedEvents('event:type') * - eventBus.clear() * * @param overrides - Optional overrides for specific EventBus methods * @returns Mock EventBus with assertion helpers * * @example Basic usage * ```typescript * import { createMockEventBus } from '@blaizejs/testing-utils'; * * const eventBus = createMockEventBus(); * await eventBus.publish('user:created', { userId: '123' }); * * // Old way - verbose * expect(eventBus.publish).toHaveBeenCalledWith('user:created', { userId: '123' }); * * // New way - concise * eventBus.assertPublished('user:created', { userId: '123' }); * ``` * * @example With TypeScript event schemas * ```typescript * type MyEvents = { * 'user:created': { userId: string; email: string }; * 'user:deleted': { userId: string }; * }; * * const eventBus = createMockEventBus(); * await eventBus.publish('user:created', { userId: '123', email: 'test@example.com' }); * * eventBus.assertPublished('user:created', { userId: '123' }); * ``` */ declare function createMockEventBus(overrides?: Partial): TypedEventBus & MockEventBusHelpers; /** * Create a working mock EventBus with in-memory pub/sub for integration tests * * This implementation actually handles subscriptions and calls handlers when * events are published, making it useful for integration tests that need * real event flow. * * @param serverId - Optional server ID (defaults to 'mock-server') * @returns Working EventBus with real pub/sub behavior * * @example * ```typescript * const eventBus = createWorkingMockEventBus('test-server'); * * const handler = vi.fn(); * eventBus.subscribe('user:*', handler); * * await eventBus.publish('user:created', { userId: '123' }); * * expect(handler).toHaveBeenCalledWith( * expect.objectContaining({ * type: 'user:created', * data: { userId: '123' }, * }) * ); * ``` */ declare function createWorkingMockEventBus(serverId?: string): EventBus; interface MockRequestBase { url: string; method: string; headers: IncomingHttpHeaders; socket: { encrypted: boolean; }; } interface MockHttp2Request extends MockRequestBase { stream: object; httpVersionMajor: number; } declare const createMockHttp1Request: (overrides?: Partial) => MockRequestBase; declare const createMockHttp2Request: (overrides?: Partial) => MockHttp2Request; declare const createMockResponse: () => { statusCode: number; setHeader: vitest.Mock<(...args: any[]) => any>; getHeader: vitest.Mock<(...args: any[]) => any>; removeHeader: vitest.Mock<(...args: any[]) => any>; end: vitest.Mock<(...args: any[]) => any>; write: vitest.Mock<(...args: any[]) => any>; on: vitest.Mock<(...args: any[]) => any>; }; /** * Log entry for internal tracking */ type LogEntry = { level: 'info' | 'debug' | 'warn' | 'error'; message: string; meta?: LogMetadata; }; /** * Mock logger for testing with tracking capabilities and assertion helpers * * Tracks all log calls and child logger creations for assertion in tests. * All log methods are Vitest spies, so you can use assertions like: * - expect(logger.info).toHaveBeenCalled() * - expect(logger.warn).toHaveBeenCalledWith('message', { meta }) * * NEW in 0.6.0: Assertion helpers reduce test boilerplate by ~70%: * - logger.assertInfoCalled('message', { meta }) * - logger.assertErrorCalled('Error occurred') * - logger.getLogsByLevel('warn') * - logger.clear() * * @example * ```typescript * import { createMockLogger } from '@blaizejs/testing-utils'; * * const logger = createMockLogger(); * await middleware.execute(ctx, next, logger); * * // Old way - verbose * expect(logger.info).toHaveBeenCalledWith('Processing request'); * * // New way - concise * logger.assertInfoCalled('Processing request'); * ``` * * @example Partial meta matching * ```typescript * logger.info('User created', { userId: '123', timestamp: 1234567890 }); * * // Only check userId, ignore timestamp * logger.assertInfoCalled('User created', { userId: '123' }); * ``` */ declare class MockLogger implements BlaizeLogger { /** * All log entries captured by this logger * * Public for backwards compatibility with existing tests. * Prefer using getLogsByLevel() for cleaner tests. */ logs: LogEntry[]; /** * Child logger contexts created */ childContexts: LogMetadata[]; /** * Child logger instances created * * When child() is called, the new logger instance is stored here. * Useful for tests that need to access child logger logs. */ childLoggers: MockLogger[]; /** * Constructor for MockLogger * * @param sharedLogs - Optional shared logs array (for child loggers) * @param sharedChildContexts - Optional shared child contexts array * @param sharedChildLoggers - Optional shared child loggers array */ constructor(sharedLogs?: LogEntry[], sharedChildContexts?: LogMetadata[], sharedChildLoggers?: MockLogger[]); /** * Log a debug message */ debug: vitest.Mock<(message: string, meta?: LogMetadata) => void>; /** * Log an info message */ info: vitest.Mock<(message: string, meta?: LogMetadata) => void>; /** * Log a warning message */ warn: vitest.Mock<(message: string, meta?: LogMetadata) => void>; /** * Log an error message */ error: vitest.Mock<(message: string, meta?: LogMetadata) => void>; /** * Create a child logger with additional context * * Child loggers share the parent's logs array, so all logs from * children appear in the parent's logs for easier testing. */ child: vitest.Mock<(context: LogMetadata) => BlaizeLogger>; /** * Flush any pending logs (no-op for mock) */ flush: vitest.Mock<() => Promise>; /** * Assert that an info log with the given message was called * * Optionally validates metadata using partial matching (toMatchObject). * This means you only need to specify the fields you care about. * * @param message - The expected log message * @param meta - Optional metadata to validate (partial match) * @throws {Error} If the log was not called, with helpful error message listing actual logs * * @example * ```typescript * logger.info('User created', { userId: '123', extra: 'data' }); * * // Pass - exact match * logger.assertInfoCalled('User created', { userId: '123', extra: 'data' }); * * // Pass - partial match (only check userId) * logger.assertInfoCalled('User created', { userId: '123' }); * * // Throw - message not found * logger.assertInfoCalled('Missing message'); // Error with actual logs listed * * // Throw - meta doesn't match * logger.assertInfoCalled('User created', { userId: '456' }); // Error * ``` */ assertInfoCalled(message: string, meta?: Record): void; /** * Assert that a debug log with the given message was called * * @param message - The expected log message * @param meta - Optional metadata to validate (partial match) * @throws {Error} If the log was not called * * @example * ```typescript * logger.debug('Query executed', { duration: 42 }); * logger.assertDebugCalled('Query executed', { duration: 42 }); * ``` */ assertDebugCalled(message: string, meta?: Record): void; /** * Assert that a warn log with the given message was called * * @param message - The expected log message * @param meta - Optional metadata to validate (partial match) * @throws {Error} If the log was not called * * @example * ```typescript * logger.warn('Rate limit approaching', { remaining: 10 }); * logger.assertWarnCalled('Rate limit approaching', { remaining: 10 }); * ``` */ assertWarnCalled(message: string, meta?: Record): void; /** * Assert that an error log with the given message was called * * @param message - The expected log message * @param meta - Optional metadata to validate (partial match) * @throws {Error} If the log was not called * * @example * ```typescript * logger.error('Database connection failed', { error: dbError }); * logger.assertErrorCalled('Database connection failed'); * ``` */ assertErrorCalled(message: string, meta?: Record): void; /** * Get all logs for a specific level * * Returns a copy of the logs array to prevent external mutation. * * @param level - The log level to filter by * @returns Array of log entries (message and meta) * * @example * ```typescript * logger.info('First'); * logger.debug('Debug'); * logger.info('Second'); * * const infoLogs = logger.getLogsByLevel('info'); * console.log(infoLogs); * // [ * // { message: 'First', meta: undefined }, * // { message: 'Second', meta: undefined } * // ] * ``` */ getLogsByLevel(level: 'info' | 'debug' | 'warn' | 'error'): Array<{ message: string; meta?: LogMetadata; }>; /** * Check if a log with the given message and level exists * * Helper method for backwards compatibility with existing tests. * * @param message - The log message to search for * @param level - The log level (defaults to 'info') * @returns True if a matching log entry exists * * @example * ```typescript * logger.info('Server started'); * logger.hasLog('Server started', 'info'); // true * logger.hasLog('Not logged', 'info'); // false * ``` */ hasLog(message: string, level?: 'info' | 'debug' | 'warn' | 'error'): boolean; /** * Get all logs from this logger and all child loggers * * NOTE: With the shared logs implementation, this method now returns * the same array as `this.logs` since child loggers share the parent's * logs array. Kept for backwards compatibility. * * @returns Array of all log entries * * @example * ```typescript * const parentLogger = createMockLogger(); * const childLogger = parentLogger.child({ requestId: '123' }); * * parentLogger.info('Parent log'); * childLogger.info('Child log'); * * const allLogs = parentLogger.getAllLogs(); * // Same as parentLogger.logs (child logs already included) * ``` */ getAllLogs(): LogEntry[]; /** * Clear all tracked data and reset mock state * * Clears: * - All log entries * - Child logger contexts * - Child logger instances * - Vitest mock call history * * @example * ```typescript * logger.info('Test'); * logger.clear(); * * logger.getLogsByLevel('info'); // [] * expect(logger.info).not.toHaveBeenCalled(); // ✅ Pass * ``` */ clear(): void; /** * Get last log entry (legacy method, kept for backwards compatibility) * @deprecated Use getLogsByLevel() instead */ getLastLog(): LogEntry | undefined; } /** * Factory function for creating mock loggers * * @returns A new MockLogger instance with assertion helpers * * @example * ```typescript * import { createMockLogger } from '@blaizejs/testing-utils'; * * const logger = createMockLogger(); * logger.info('Test message', { userId: '123' }); * logger.assertInfoCalled('Test message', { userId: '123' }); * ``` */ declare function createMockLogger(): MockLogger; /** * Middleware Testing Utilities * * Helper functions for testing middleware with context object signature */ /** * Enhanced createMockMiddleware that covers common test scenarios */ declare function createMockMiddleware(config?: { name?: string; execute?: (mc: { ctx: Context; next: NextFunction; logger: MockLogger; eventBus: TypedEventBus; }) => Promise | void; behavior?: 'pass' | 'block' | 'error'; errorMessage?: string; stateChanges?: Record; skip?: (ctx: Context) => boolean; debug?: boolean; }): Middleware; /** * Create a mock plugin for testing */ declare function createMockPlugin(overrides?: Partial): Plugin; /** * Create multiple mock plugins for testing */ declare function createMockPlugins(count: number, baseOverrides?: Partial): Plugin[]; /** * Create a mock router for testing */ declare function createMockRouter(overrides?: Partial): Router; /** * Mock implementation of createGetRoute for testing * Now returns a function that accepts state/services/events generics */ declare const mockGetRoute: CreateGetRoute; /** * Mock implementation of createPostRoute for testing * Now returns a function that accepts state/services/events generics */ declare const mockPostRoute: CreatePostRoute; /** * Mock implementation of createPutRoute for testing * Now returns a function that accepts state/services/events generics */ declare const mockPutRoute: CreatePutRoute; /** * Mock implementation of createDeleteRoute for testing * Now returns a function that accepts state/services/events generics */ declare const mockDeleteRoute: CreateDeleteRoute; /** * Mock implementation of createPatchRoute for testing * Now returns a function that accepts state/services/events generics */ declare const mockPatchRoute: CreatePatchRoute; /** * Create a set of commonly used mock routes for testing * Now uses the updated mock route creators with state/services/events support * IMPORTANT: All handlers now use NEW signature ({ ctx, params, logger, eventBus }) */ declare function createMockRoutesSet(): { readonly healthCheck: { GET: RouteMethodOptions, z.ZodType>; path: string; }; readonly getUser: { GET: RouteMethodOptions, never, never, never, z.ZodObject<{ user: z.ZodObject<{ id: z.ZodString; name: z.ZodString; email: z.ZodString; }, "strip", z.ZodTypeAny, { name: string; id: string; email: string; }, { name: string; id: string; email: string; }>; }, "strip", z.ZodTypeAny, { user: { name: string; id: string; email: string; }; }, { user: { name: string; id: string; email: string; }; }>, z.ZodType>; path: string; }; readonly createUser: { POST: RouteMethodOptions, never, z.ZodObject<{ user: z.ZodObject<{ id: z.ZodString; name: z.ZodString; email: z.ZodString; }, "strip", z.ZodTypeAny, { name: string; id: string; email: string; }, { name: string; id: string; email: string; }>; }, "strip", z.ZodTypeAny, { user: { name: string; id: string; email: string; }; }, { user: { name: string; id: string; email: string; }; }>, z.ZodType>; path: string; }; readonly listUsers: { GET: RouteMethodOptions; limit: z.ZodOptional; sort: z.ZodOptional>; }, "strip", z.ZodTypeAny, { limit?: number | undefined; sort?: "asc" | "desc" | undefined; page?: number | undefined; }, { limit?: number | undefined; sort?: "asc" | "desc" | undefined; page?: number | undefined; }>, never, never, z.ZodObject<{ users: z.ZodArray, "many">; total: z.ZodNumber; }, "strip", z.ZodTypeAny, { users: { name: string; id: string; }[]; total: number; }, { users: { name: string; id: string; }[]; total: number; }>, z.ZodType>; path: string; }; readonly updateUser: { PUT: RouteMethodOptions, z.ZodObject<{ notify: z.ZodOptional; }, "strip", z.ZodTypeAny, { notify?: boolean | undefined; }, { notify?: boolean | undefined; }>, z.ZodObject<{ name: z.ZodOptional; email: z.ZodOptional; }, "strip", z.ZodTypeAny, { name?: string | undefined; email?: string | undefined; }, { name?: string | undefined; email?: string | undefined; }>, never, z.ZodObject<{ user: z.ZodObject<{ id: z.ZodString; name: z.ZodString; email: z.ZodString; }, "strip", z.ZodTypeAny, { name: string; id: string; email: string; }, { name: string; id: string; email: string; }>; }, "strip", z.ZodTypeAny, { user: { name: string; id: string; email: string; }; }, { user: { name: string; id: string; email: string; }; }>, z.ZodType>; path: string; }; }; /** * Create a mock server instance for testing */ declare function createMockServer(overrides?: Partial>, pluginManagerOverrides?: Partial): Server; /** * Create a mock server with plugins for testing */ declare function createMockServerWithPlugins(pluginCount?: number, serverOverrides?: Partial>, pluginOverrides?: Partial, pluginManagerOverrides?: Partial): { server: Server; plugins: Plugin[]; }; /** * Create a mock HTTP server for testing */ declare function createMockHttpServer(overrides?: any): any; /** * Reset all mocks in a server instance */ declare function resetServerMocks(server: Server): void; /** * Test Helper Utilities * * Common test patterns and helper functions for BlaizeJS route testing. * Reduces boilerplate and provides consistent testing patterns. * * @packageDocumentation */ /** * Route test context with logger, eventBus, and cleanup * * Provides all the common dependencies needed for route handler testing * in a single convenient object. * * @template TSchemas - Optional event schemas for typed EventBus */ interface RouteTestContext { /** * Mock logger with assertion helpers * * @example * ```typescript * logger.info('User created', { userId: '123' }); * logger.assertInfoCalled('User created', { userId: '123' }); * ``` */ logger: MockLogger; /** * Mock EventBus with assertion helpers * * @example * ```typescript * await eventBus.publish('user:created', { userId: '123' }); * eventBus.assertPublished('user:created', { userId: '123' }); * ``` */ eventBus: TypedEventBus & MockEventBusHelpers; /** * Cleanup function to reset all mocks * * Clears: * - Logger state and assertions * - EventBus state and assertions * - All Vitest mock call history * * @example * ```typescript * afterEach(() => { * cleanup(); * }); * ``` */ cleanup: () => void; } /** * Create a complete test context for route handler testing * * Provides pre-configured logger and eventBus with assertion helpers, * plus a cleanup function to reset state between tests. * * This is the primary testing utility - use it in every route test to * reduce boilerplate from ~10 lines to ~1 line. * * @template TSchemas - Optional event schemas for typed EventBus * @returns Test context with logger, eventBus, and cleanup * * @example Basic usage * ```typescript * import { createRouteTestContext } from '@blaizejs/testing-utils'; * * describe('GET /users/:userId', () => { * it('should fetch user and publish event', async () => { * const { logger, eventBus, cleanup } = createRouteTestContext(); * * const result = await getUserById.handler({ * params: { userId: 'test-123' }, * logger, * eventBus, * }); * * expect(result.id).toBe('test-123'); * logger.assertInfoCalled('Fetching user', { userId: 'test-123' }); * eventBus.assertPublished('user:viewed', { userId: 'test-123' }); * * cleanup(); * }); * }); * ``` * * @example With cleanup in afterEach * ```typescript * describe('User routes', () => { * const { logger, eventBus, cleanup } = createRouteTestContext(); * * afterEach(() => { * cleanup(); // Reset state between tests * }); * * it('test 1', async () => { * // Use logger and eventBus * }); * * it('test 2', async () => { * // Fresh state from cleanup * }); * }); * ``` * * @example With typed event schemas * ```typescript * import { z } from 'zod'; * * type MyEvents = { * 'user:created': z.ZodObject<{ userId: z.ZodString }>; * 'order:placed': z.ZodObject<{ orderId: z.ZodString }>; * }; * * const { logger, eventBus, cleanup } = createRouteTestContext(); * * // eventBus now has type hints for event names and data * await eventBus.publish('user:created', { userId: '123' }); * ``` */ declare function createRouteTestContext(): RouteTestContext; export { type MockEventBusHelpers, MockLogger, type RouteTestContext, createMockContext, createMockEventBus, createMockHttp1Request, createMockHttp2Request, createMockHttpServer, createMockLogger, createMockMiddleware, createMockPlugin, createMockPlugins, createMockResponse, createMockRouter, createMockRoutesSet, createMockServer, createMockServerWithPlugins, createRouteTestContext, createSSEMockContext, createWorkingMockEventBus, mockDeleteRoute, mockGetRoute, mockPatchRoute, mockPostRoute, mockPutRoute, resetServerMocks };