/** * @fileoverview Streaming Contracts - WebSocket/SSE Standardization * v0.18.0 Enhancement: Consistent streaming envelope and event types * Purpose: Real-time data streaming with standardized message format */ import { z } from 'zod'; // Streaming event types (Management requirement #7 - Finalized) export const StreamEventType = z.enum([ // ML streaming events 'signal_update', 'consensus_update', 'model_performance_update', 'model_deployment_update', 'ensemble_weights_update', // Trading streaming events 'position_update', 'trade_executed', 'account_balance_update', 'trading_account_update', // Market streaming events 'price_update', 'indicator_update', 'market_status_update', 'ohlcv_update', // Job streaming events 'job_status_update', 'job_progress_update', 'job_log_entry', 'job_completed', 'job_failed', // Auth streaming events (Frontend requirement) 'auth_session_update', 'auth_permission_change', 'auth_role_change', // Marketplace streaming events 'marketplace_vendor_update', 'marketplace_model_update', 'marketplace_deployment_update', 'marketplace_revenue_update', // System streaming events 'health_update', 'system_alert', 'performance_alert', // Connection management 'connection_established', 'heartbeat', 'connection_error' ]); // Standard streaming message envelope export const StreamMessage = z.object({ // Message identification id: z.string().uuid(), type: StreamEventType, timestamp: z.string().datetime(), // Event data (flexible payload) data: z.record(z.any()), // Metadata source: z.string(), // 'infra-api', 'ml-service', etc. channel: z.string().optional(), // Channel/room identifier user_id: z.string().uuid().optional(), // User-specific events // Reliability sequence: z.number().int().nonnegative().optional(), // Message ordering retry_count: z.number().int().nonnegative().default(0) }).strict(); // Connection handshake export const StreamHandshake = z.object({ type: z.literal('handshake'), client_id: z.string().uuid(), supported_events: z.array(StreamEventType), auth_token: z.string().optional(), // JWT for authenticated streams subscriptions: z.array(z.string()).optional() // Initial subscriptions }).strict(); // Subscription management export const StreamSubscription = z.object({ type: z.literal('subscribe'), events: z.array(StreamEventType), filters: z.object({ symbol: z.string().optional(), user_id: z.string().uuid().optional(), job_id: z.string().uuid().optional() }).strict().optional() }).strict(); export const StreamUnsubscription = z.object({ type: z.literal('unsubscribe'), events: z.array(StreamEventType).optional(), // Empty = unsubscribe all filters: z.object({ symbol: z.string().optional(), user_id: z.string().uuid().optional(), job_id: z.string().uuid().optional() }).strict().optional() }).strict(); // Specific streaming event schemas export const SignalStreamEvent = z.object({ type: z.literal('signal_update'), data: z.object({ id: z.string().uuid(), symbol: z.string(), decision: z.enum(['BUY', 'SELL', 'HOLD']), confidence: z.number().min(0).max(1), model_id: z.string(), timeframe: z.string(), created_at: z.string().datetime() }).strict() }).strict(); export const PositionStreamEvent = z.object({ type: z.literal('position_update'), data: z.object({ id: z.string().uuid(), symbol: z.string(), direction: z.enum(['BUY', 'SELL']), size: z.number(), current_price: z.number(), unrealized_pnl: z.number(), updated_at: z.string().datetime() }).strict() }).strict(); export const JobStreamEvent = z.object({ type: z.literal('job_progress_update'), data: z.object({ job_id: z.string().uuid(), progress_percent: z.number().min(0).max(100), status: z.enum(['pending', 'queued', 'running', 'succeeded', 'failed', 'cancelled']), message: z.string().optional(), updated_at: z.string().datetime() }).strict() }).strict(); // Auth streaming events (Frontend requirement) export const AuthStreamEvent = z.object({ type: z.enum(['auth_session_update', 'auth_permission_change', 'auth_role_change']), data: z.object({ user_id: z.string().uuid(), session_id: z.string().uuid().optional(), new_role: z.string().optional(), new_permissions: z.array(z.string()).optional(), expires_at: z.number().int().positive().optional(), updated_at: z.string().datetime() }).strict() }).strict(); // Marketplace streaming events export const MarketplaceStreamEvent = z.object({ type: z.enum(['marketplace_vendor_update', 'marketplace_model_update', 'marketplace_deployment_update']), data: z.object({ vendor_id: z.string().uuid().optional(), model_id: z.string().uuid().optional(), deployment_id: z.string().uuid().optional(), status: z.string(), updated_at: z.string().datetime() }).strict() }).strict(); // Streaming error handling export const StreamError = z.object({ type: z.literal('error'), error: z.object({ code: z.string(), message: z.string(), details: z.record(z.any()).optional() }).strict(), timestamp: z.string().datetime() }).strict(); // WebSocket connection configuration export const WebSocketConfig = z.object({ url: z.string().url(), protocols: z.array(z.string()).optional(), heartbeat_interval_ms: z.number().int().positive().default(30000), reconnect_attempts: z.number().int().nonnegative().default(5), reconnect_delay_ms: z.number().int().positive().default(1000), max_message_size: z.number().int().positive().default(1048576) // 1MB }).strict(); // SSE configuration export const SSEConfig = z.object({ url: z.string().url(), retry_ms: z.number().int().positive().default(3000), timeout_ms: z.number().int().positive().default(300000), // 5 minutes with_credentials: z.boolean().default(false) }).strict(); // Type exports export type StreamEventTypeEnum = z.infer; export type StreamMessageType = z.infer; export type StreamHandshakeType = z.infer; export type StreamSubscriptionType = z.infer; export type StreamUnsubscriptionType = z.infer; export type SignalStreamEventType = z.infer; export type PositionStreamEventType = z.infer; export type JobStreamEventType = z.infer; export type StreamErrorType = z.infer; export type WebSocketConfigType = z.infer; export type SSEConfigType = z.infer;