@esengine/server 1.1.4 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/ratelimit/strategies/TokenBucket.ts","../../src/ratelimit/strategies/SlidingWindow.ts","../../src/ratelimit/strategies/FixedWindow.ts","../../src/ratelimit/context.ts","../../src/ratelimit/decorators/rateLimit.ts","../../src/ratelimit/mixin/withRateLimit.ts"],"names":["TokenBucketStrategy","config","name","_rate","_capacity","_buckets","Map","rate","capacity","consume","key","cost","now","Date","bucket","_getOrCreateBucket","_refillBucket","tokens","allowed","remaining","Math","floor","resetAt","ceil","tokensNeeded","retryAfter","getStatus","get","reset","delete","cleanup","expireThreshold","lastUpdate","set","elapsed","tokensToAdd","min","createTokenBucketStrategy","SlidingWindowStrategy","_windowMs","_windows","window","_getOrCreateWindow","_cleanExpiredTimestamps","currentCount","timestamps","length","i","push","_getResetAt","oldestTimestamp","max","cutoff","filter","ts","createSlidingWindowStrategy","FixedWindowStrategy","_maybeResetWindow","count","windowStart","_getWindowStart","currentWindowStart","createFixedWindowStrategy","RateLimitContext","globalStrategy","_key","_globalStrategy","_messageStrategies","_consecutiveLimitCount","consecutiveLimitCount","check","messageType","has","result","strategy","values","resetConsecutiveCount","setMessageStrategy","removeMessageStrategy","hasMessageStrategy","RATE_LIMIT_METADATA_KEY","getRateLimitMetadata","target","metadataMap","setRateLimitMetadata","metadata","existing","enabled","getMessageTypeFromMethod","methodName","sym","Object","getOwnPropertySymbols","desc","getOwnPropertyDescriptor","value","Array","isArray","handler","method","type","handlers","Symbol","for","undefined","rateLimit","propertyKey","descriptor","String","queueMicrotask","msgType","hasOwnProperty","defineProperty","writable","enumerable","noRateLimit","exempt","rateLimitMessage","noRateLimitMessage","PLAYER_RATE_LIMIT_CONTEXT","createStrategy","messagesPerSecond","burstSize","getPlayerRateLimitContext","player","data","setPlayerRateLimitContext","context","configurable","withRateLimit","Base","onLimited","disconnectOnLimit","maxConsecutiveLimits","getKey","id","cleanupInterval","RateLimitRoom","args","_rateLimitStrategy","_playerContexts","WeakMap","_cleanupTimer","_startCleanup","_initMessageStrategies","rateLimitStrategy","getRateLimitContext","_handleMessage","playerId","getPlayer","_createPlayerContext","_getMessageMetadata","msgStrategy","_handleRateLimited","_addPlayer","conn","_removePlayer","reason","dispose","_stopCleanup","onRateLimited","kick","prototype","setInterval","clearInterval"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @zh 令牌桶速率限制策略\n * @en Token bucket rate limit strategy\n */\n\nimport type { IRateLimitStrategy, RateLimitResult, StrategyConfig } from '../types.js';\n\n/**\n * @zh 令牌桶状态\n * @en Token bucket state\n */\ninterface BucketState {\n /**\n * @zh 当前令牌数\n * @en Current token count\n */\n tokens: number;\n\n /**\n * @zh 上次更新时间\n * @en Last update time\n */\n lastUpdate: number;\n}\n\n/**\n * @zh 令牌桶速率限制策略\n * @en Token bucket rate limit strategy\n *\n * @zh 令牌桶算法允许突发流量,同时保持长期速率限制。\n * 令牌以固定速率添加到桶中,每个请求消耗一个或多个令牌。\n * @en Token bucket algorithm allows burst traffic while maintaining long-term rate limit.\n * Tokens are added to the bucket at a fixed rate, each request consumes one or more tokens.\n *\n * @example\n * ```typescript\n * const strategy = new TokenBucketStrategy({\n * rate: 10, // 10 tokens per second\n * capacity: 20 // bucket can hold 20 tokens max\n * });\n *\n * const result = strategy.consume('player-123');\n * if (result.allowed) {\n * // Process message\n * }\n * ```\n */\nexport class TokenBucketStrategy implements IRateLimitStrategy {\n readonly name = 'token-bucket';\n\n private _rate: number;\n private _capacity: number;\n private _buckets: Map<string, BucketState> = new Map();\n\n /**\n * @zh 创建令牌桶策略\n * @en Create token bucket strategy\n *\n * @param config - @zh 配置 @en Configuration\n * @param config.rate - @zh 每秒添加的令牌数 @en Tokens added per second\n * @param config.capacity - @zh 桶容量(最大令牌数)@en Bucket capacity (max tokens)\n */\n constructor(config: StrategyConfig) {\n this._rate = config.rate;\n this._capacity = config.capacity;\n }\n\n /**\n * @zh 尝试消费令牌\n * @en Try to consume tokens\n */\n consume(key: string, cost: number = 1): RateLimitResult {\n const now = Date.now();\n const bucket = this._getOrCreateBucket(key, now);\n\n this._refillBucket(bucket, now);\n\n if (bucket.tokens >= cost) {\n bucket.tokens -= cost;\n return {\n allowed: true,\n remaining: Math.floor(bucket.tokens),\n resetAt: now + Math.ceil((this._capacity - bucket.tokens) / this._rate * 1000)\n };\n }\n\n const tokensNeeded = cost - bucket.tokens;\n const retryAfter = Math.ceil(tokensNeeded / this._rate * 1000);\n\n return {\n allowed: false,\n remaining: 0,\n resetAt: now + retryAfter,\n retryAfter\n };\n }\n\n /**\n * @zh 获取当前状态\n * @en Get current status\n */\n getStatus(key: string): RateLimitResult {\n const now = Date.now();\n const bucket = this._buckets.get(key);\n\n if (!bucket) {\n return {\n allowed: true,\n remaining: this._capacity,\n resetAt: now\n };\n }\n\n this._refillBucket(bucket, now);\n\n return {\n allowed: bucket.tokens >= 1,\n remaining: Math.floor(bucket.tokens),\n resetAt: now + Math.ceil((this._capacity - bucket.tokens) / this._rate * 1000)\n };\n }\n\n /**\n * @zh 重置指定键\n * @en Reset specified key\n */\n reset(key: string): void {\n this._buckets.delete(key);\n }\n\n /**\n * @zh 清理所有记录\n * @en Clean up all records\n */\n cleanup(): void {\n const now = Date.now();\n const expireThreshold = 60000;\n\n for (const [key, bucket] of this._buckets) {\n if (now - bucket.lastUpdate > expireThreshold && bucket.tokens >= this._capacity) {\n this._buckets.delete(key);\n }\n }\n }\n\n /**\n * @zh 获取或创建桶\n * @en Get or create bucket\n */\n private _getOrCreateBucket(key: string, now: number): BucketState {\n let bucket = this._buckets.get(key);\n\n if (!bucket) {\n bucket = {\n tokens: this._capacity,\n lastUpdate: now\n };\n this._buckets.set(key, bucket);\n }\n\n return bucket;\n }\n\n /**\n * @zh 补充令牌\n * @en Refill tokens\n */\n private _refillBucket(bucket: BucketState, now: number): void {\n const elapsed = now - bucket.lastUpdate;\n const tokensToAdd = (elapsed / 1000) * this._rate;\n\n bucket.tokens = Math.min(this._capacity, bucket.tokens + tokensToAdd);\n bucket.lastUpdate = now;\n }\n}\n\n/**\n * @zh 创建令牌桶策略\n * @en Create token bucket strategy\n *\n * @example\n * ```typescript\n * const strategy = createTokenBucketStrategy({\n * rate: 10,\n * capacity: 20\n * });\n * ```\n */\nexport function createTokenBucketStrategy(config: StrategyConfig): TokenBucketStrategy {\n return new TokenBucketStrategy(config);\n}\n","/**\n * @zh 滑动窗口速率限制策略\n * @en Sliding window rate limit strategy\n */\n\nimport type { IRateLimitStrategy, RateLimitResult, StrategyConfig } from '../types.js';\n\n/**\n * @zh 滑动窗口状态\n * @en Sliding window state\n */\ninterface WindowState {\n /**\n * @zh 请求时间戳列表\n * @en Request timestamp list\n */\n timestamps: number[];\n}\n\n/**\n * @zh 滑动窗口速率限制策略\n * @en Sliding window rate limit strategy\n *\n * @zh 滑动窗口算法精确跟踪时间窗口内的请求数。\n * 比固定窗口更精确,但内存开销稍大。\n * @en Sliding window algorithm precisely tracks requests within a time window.\n * More accurate than fixed window, but with slightly higher memory overhead.\n *\n * @example\n * ```typescript\n * const strategy = new SlidingWindowStrategy({\n * rate: 10, // 10 requests per second\n * capacity: 10 // window size (same as rate for 1-second window)\n * });\n *\n * const result = strategy.consume('player-123');\n * if (result.allowed) {\n * // Process message\n * }\n * ```\n */\nexport class SlidingWindowStrategy implements IRateLimitStrategy {\n readonly name = 'sliding-window';\n\n private _rate: number;\n private _capacity: number;\n private _windowMs: number;\n private _windows: Map<string, WindowState> = new Map();\n\n /**\n * @zh 创建滑动窗口策略\n * @en Create sliding window strategy\n *\n * @param config - @zh 配置 @en Configuration\n * @param config.rate - @zh 每秒允许的请求数 @en Requests allowed per second\n * @param config.capacity - @zh 窗口容量 @en Window capacity\n */\n constructor(config: StrategyConfig) {\n this._rate = config.rate;\n this._capacity = config.capacity;\n this._windowMs = 1000;\n }\n\n /**\n * @zh 尝试消费配额\n * @en Try to consume quota\n */\n consume(key: string, cost: number = 1): RateLimitResult {\n const now = Date.now();\n const window = this._getOrCreateWindow(key);\n\n this._cleanExpiredTimestamps(window, now);\n\n const currentCount = window.timestamps.length;\n\n if (currentCount + cost <= this._capacity) {\n for (let i = 0; i < cost; i++) {\n window.timestamps.push(now);\n }\n\n return {\n allowed: true,\n remaining: this._capacity - window.timestamps.length,\n resetAt: this._getResetAt(window, now)\n };\n }\n\n const oldestTimestamp = window.timestamps[0] || now;\n const retryAfter = Math.max(0, oldestTimestamp + this._windowMs - now);\n\n return {\n allowed: false,\n remaining: 0,\n resetAt: oldestTimestamp + this._windowMs,\n retryAfter\n };\n }\n\n /**\n * @zh 获取当前状态\n * @en Get current status\n */\n getStatus(key: string): RateLimitResult {\n const now = Date.now();\n const window = this._windows.get(key);\n\n if (!window) {\n return {\n allowed: true,\n remaining: this._capacity,\n resetAt: now + this._windowMs\n };\n }\n\n this._cleanExpiredTimestamps(window, now);\n\n const remaining = Math.max(0, this._capacity - window.timestamps.length);\n\n return {\n allowed: remaining > 0,\n remaining,\n resetAt: this._getResetAt(window, now)\n };\n }\n\n /**\n * @zh 重置指定键\n * @en Reset specified key\n */\n reset(key: string): void {\n this._windows.delete(key);\n }\n\n /**\n * @zh 清理所有过期记录\n * @en Clean up all expired records\n */\n cleanup(): void {\n const now = Date.now();\n\n for (const [key, window] of this._windows) {\n this._cleanExpiredTimestamps(window, now);\n\n if (window.timestamps.length === 0) {\n this._windows.delete(key);\n }\n }\n }\n\n /**\n * @zh 获取或创建窗口\n * @en Get or create window\n */\n private _getOrCreateWindow(key: string): WindowState {\n let window = this._windows.get(key);\n\n if (!window) {\n window = { timestamps: [] };\n this._windows.set(key, window);\n }\n\n return window;\n }\n\n /**\n * @zh 清理过期的时间戳\n * @en Clean expired timestamps\n */\n private _cleanExpiredTimestamps(window: WindowState, now: number): void {\n const cutoff = now - this._windowMs;\n window.timestamps = window.timestamps.filter(ts => ts > cutoff);\n }\n\n /**\n * @zh 获取重置时间\n * @en Get reset time\n */\n private _getResetAt(window: WindowState, now: number): number {\n if (window.timestamps.length === 0) {\n return now + this._windowMs;\n }\n\n return window.timestamps[0] + this._windowMs;\n }\n}\n\n/**\n * @zh 创建滑动窗口策略\n * @en Create sliding window strategy\n *\n * @example\n * ```typescript\n * const strategy = createSlidingWindowStrategy({\n * rate: 10,\n * capacity: 10\n * });\n * ```\n */\nexport function createSlidingWindowStrategy(config: StrategyConfig): SlidingWindowStrategy {\n return new SlidingWindowStrategy(config);\n}\n","/**\n * @zh 固定窗口速率限制策略\n * @en Fixed window rate limit strategy\n */\n\nimport type { IRateLimitStrategy, RateLimitResult, StrategyConfig } from '../types.js';\n\n/**\n * @zh 固定窗口状态\n * @en Fixed window state\n */\ninterface WindowState {\n /**\n * @zh 当前窗口计数\n * @en Current window count\n */\n count: number;\n\n /**\n * @zh 窗口开始时间\n * @en Window start time\n */\n windowStart: number;\n}\n\n/**\n * @zh 固定窗口速率限制策略\n * @en Fixed window rate limit strategy\n *\n * @zh 固定窗口算法将时间划分为固定长度的窗口,在每个窗口内计数请求。\n * 实现简单,内存开销小,但在窗口边界可能有两倍突发的问题。\n * @en Fixed window algorithm divides time into fixed-length windows and counts requests in each window.\n * Simple to implement with low memory overhead, but may have 2x burst issue at window boundaries.\n *\n * @example\n * ```typescript\n * const strategy = new FixedWindowStrategy({\n * rate: 10, // 10 requests per second\n * capacity: 10 // same as rate for 1-second window\n * });\n *\n * const result = strategy.consume('player-123');\n * if (result.allowed) {\n * // Process message\n * }\n * ```\n */\nexport class FixedWindowStrategy implements IRateLimitStrategy {\n readonly name = 'fixed-window';\n\n private _rate: number;\n private _capacity: number;\n private _windowMs: number;\n private _windows: Map<string, WindowState> = new Map();\n\n /**\n * @zh 创建固定窗口策略\n * @en Create fixed window strategy\n *\n * @param config - @zh 配置 @en Configuration\n * @param config.rate - @zh 每秒允许的请求数 @en Requests allowed per second\n * @param config.capacity - @zh 窗口容量 @en Window capacity\n */\n constructor(config: StrategyConfig) {\n this._rate = config.rate;\n this._capacity = config.capacity;\n this._windowMs = 1000;\n }\n\n /**\n * @zh 尝试消费配额\n * @en Try to consume quota\n */\n consume(key: string, cost: number = 1): RateLimitResult {\n const now = Date.now();\n const window = this._getOrCreateWindow(key, now);\n\n this._maybeResetWindow(window, now);\n\n if (window.count + cost <= this._capacity) {\n window.count += cost;\n\n return {\n allowed: true,\n remaining: this._capacity - window.count,\n resetAt: window.windowStart + this._windowMs\n };\n }\n\n const retryAfter = window.windowStart + this._windowMs - now;\n\n return {\n allowed: false,\n remaining: 0,\n resetAt: window.windowStart + this._windowMs,\n retryAfter: Math.max(0, retryAfter)\n };\n }\n\n /**\n * @zh 获取当前状态\n * @en Get current status\n */\n getStatus(key: string): RateLimitResult {\n const now = Date.now();\n const window = this._windows.get(key);\n\n if (!window) {\n return {\n allowed: true,\n remaining: this._capacity,\n resetAt: this._getWindowStart(now) + this._windowMs\n };\n }\n\n this._maybeResetWindow(window, now);\n\n const remaining = Math.max(0, this._capacity - window.count);\n\n return {\n allowed: remaining > 0,\n remaining,\n resetAt: window.windowStart + this._windowMs\n };\n }\n\n /**\n * @zh 重置指定键\n * @en Reset specified key\n */\n reset(key: string): void {\n this._windows.delete(key);\n }\n\n /**\n * @zh 清理所有过期记录\n * @en Clean up all expired records\n */\n cleanup(): void {\n const now = Date.now();\n const currentWindowStart = this._getWindowStart(now);\n\n for (const [key, window] of this._windows) {\n if (window.windowStart < currentWindowStart - this._windowMs) {\n this._windows.delete(key);\n }\n }\n }\n\n /**\n * @zh 获取或创建窗口\n * @en Get or create window\n */\n private _getOrCreateWindow(key: string, now: number): WindowState {\n let window = this._windows.get(key);\n\n if (!window) {\n window = {\n count: 0,\n windowStart: this._getWindowStart(now)\n };\n this._windows.set(key, window);\n }\n\n return window;\n }\n\n /**\n * @zh 如果需要则重置窗口\n * @en Reset window if needed\n */\n private _maybeResetWindow(window: WindowState, now: number): void {\n const currentWindowStart = this._getWindowStart(now);\n\n if (window.windowStart < currentWindowStart) {\n window.count = 0;\n window.windowStart = currentWindowStart;\n }\n }\n\n /**\n * @zh 获取窗口开始时间\n * @en Get window start time\n */\n private _getWindowStart(now: number): number {\n return Math.floor(now / this._windowMs) * this._windowMs;\n }\n}\n\n/**\n * @zh 创建固定窗口策略\n * @en Create fixed window strategy\n *\n * @example\n * ```typescript\n * const strategy = createFixedWindowStrategy({\n * rate: 10,\n * capacity: 10\n * });\n * ```\n */\nexport function createFixedWindowStrategy(config: StrategyConfig): FixedWindowStrategy {\n return new FixedWindowStrategy(config);\n}\n","/**\n * @zh 速率限制上下文\n * @en Rate limit context\n */\n\nimport type {\n IRateLimitContext,\n IRateLimitStrategy,\n RateLimitResult,\n MessageRateLimitConfig\n} from './types.js';\n\n/**\n * @zh 速率限制上下文\n * @en Rate limit context\n *\n * @zh 管理单个玩家的速率限制状态,支持全局限制和按消息类型限制\n * @en Manages rate limit status for a single player, supports global and per-message-type limits\n *\n * @example\n * ```typescript\n * const context = new RateLimitContext('player-123', globalStrategy);\n *\n * // Check global rate limit\n * const result = context.consume();\n *\n * // Check per-message rate limit\n * const tradeResult = context.consume('Trade', 1);\n * ```\n */\nexport class RateLimitContext implements IRateLimitContext {\n private _key: string;\n private _globalStrategy: IRateLimitStrategy;\n private _messageStrategies: Map<string, IRateLimitStrategy> = new Map();\n private _consecutiveLimitCount: number = 0;\n\n /**\n * @zh 创建速率限制上下文\n * @en Create rate limit context\n *\n * @param key - @zh 限流键(通常是玩家ID)@en Rate limit key (usually player ID)\n * @param globalStrategy - @zh 全局限流策略 @en Global rate limit strategy\n */\n constructor(key: string, globalStrategy: IRateLimitStrategy) {\n this._key = key;\n this._globalStrategy = globalStrategy;\n }\n\n /**\n * @zh 获取连续被限流次数\n * @en Get consecutive limit count\n */\n get consecutiveLimitCount(): number {\n return this._consecutiveLimitCount;\n }\n\n /**\n * @zh 检查是否允许(不消费)\n * @en Check if allowed (without consuming)\n */\n check(messageType?: string): RateLimitResult {\n if (messageType && this._messageStrategies.has(messageType)) {\n return this._messageStrategies.get(messageType)!.getStatus(this._key);\n }\n\n return this._globalStrategy.getStatus(this._key);\n }\n\n /**\n * @zh 消费配额\n * @en Consume quota\n */\n consume(messageType?: string, cost: number = 1): RateLimitResult {\n let result: RateLimitResult;\n\n if (messageType && this._messageStrategies.has(messageType)) {\n result = this._messageStrategies.get(messageType)!.consume(this._key, cost);\n } else {\n result = this._globalStrategy.consume(this._key, cost);\n }\n\n if (result.allowed) {\n this._consecutiveLimitCount = 0;\n } else {\n this._consecutiveLimitCount++;\n }\n\n return result;\n }\n\n /**\n * @zh 重置限流状态\n * @en Reset rate limit status\n */\n reset(messageType?: string): void {\n if (messageType) {\n if (this._messageStrategies.has(messageType)) {\n this._messageStrategies.get(messageType)!.reset(this._key);\n }\n } else {\n this._globalStrategy.reset(this._key);\n for (const strategy of this._messageStrategies.values()) {\n strategy.reset(this._key);\n }\n }\n }\n\n /**\n * @zh 重置连续限流计数\n * @en Reset consecutive limit count\n */\n resetConsecutiveCount(): void {\n this._consecutiveLimitCount = 0;\n }\n\n /**\n * @zh 为特定消息类型设置独立的限流策略\n * @en Set independent rate limit strategy for specific message type\n *\n * @param messageType - @zh 消息类型 @en Message type\n * @param strategy - @zh 限流策略 @en Rate limit strategy\n */\n setMessageStrategy(messageType: string, strategy: IRateLimitStrategy): void {\n this._messageStrategies.set(messageType, strategy);\n }\n\n /**\n * @zh 移除特定消息类型的限流策略\n * @en Remove rate limit strategy for specific message type\n *\n * @param messageType - @zh 消息类型 @en Message type\n */\n removeMessageStrategy(messageType: string): void {\n this._messageStrategies.delete(messageType);\n }\n\n /**\n * @zh 检查是否有特定消息类型的限流策略\n * @en Check if has rate limit strategy for specific message type\n *\n * @param messageType - @zh 消息类型 @en Message type\n */\n hasMessageStrategy(messageType: string): boolean {\n return this._messageStrategies.has(messageType);\n }\n}\n","/**\n * @zh 速率限制装饰器\n * @en Rate limit decorators\n */\n\nimport type { MessageRateLimitConfig, RateLimitMetadata } from '../types.js';\n\n/**\n * @zh 速率限制元数据存储键\n * @en Rate limit metadata storage key\n */\nexport const RATE_LIMIT_METADATA_KEY = Symbol('rateLimitMetadata');\n\n/**\n * @zh 获取速率限制元数据\n * @en Get rate limit metadata\n *\n * @param target - @zh 目标对象 @en Target object\n * @param messageType - @zh 消息类型 @en Message type\n * @returns @zh 元数据 @en Metadata\n */\nexport function getRateLimitMetadata(target: any, messageType: string): RateLimitMetadata | undefined {\n const metadataMap = target[RATE_LIMIT_METADATA_KEY] as Map<string, RateLimitMetadata> | undefined;\n return metadataMap?.get(messageType);\n}\n\n/**\n * @zh 设置速率限制元数据\n * @en Set rate limit metadata\n *\n * @param target - @zh 目标对象 @en Target object\n * @param messageType - @zh 消息类型 @en Message type\n * @param metadata - @zh 元数据 @en Metadata\n */\nfunction setRateLimitMetadata(target: any, messageType: string, metadata: RateLimitMetadata): void {\n if (!target[RATE_LIMIT_METADATA_KEY]) {\n target[RATE_LIMIT_METADATA_KEY] = new Map<string, RateLimitMetadata>();\n }\n const metadataMap = target[RATE_LIMIT_METADATA_KEY] as Map<string, RateLimitMetadata>;\n const existing = metadataMap.get(messageType) ?? { enabled: true };\n metadataMap.set(messageType, { ...existing, ...metadata });\n}\n\n/**\n * @zh 从方法获取消息类型\n * @en Get message type from method\n *\n * @zh 通过查找 onMessage 装饰器设置的元数据来获取消息类型\n * @en Gets message type by looking up metadata set by onMessage decorator\n */\nfunction getMessageTypeFromMethod(target: any, methodName: string): string | undefined {\n const messageHandlers = Symbol.for('messageHandlers');\n\n for (const sym of Object.getOwnPropertySymbols(target.constructor)) {\n const desc = Object.getOwnPropertyDescriptor(target.constructor, sym);\n if (desc?.value && Array.isArray(desc.value)) {\n for (const handler of desc.value) {\n if (handler.method === methodName) {\n return handler.type;\n }\n }\n }\n }\n\n const handlers = target.constructor[Symbol.for('messageHandlers')] as { type: string; method: string }[] | undefined;\n if (handlers) {\n for (const handler of handlers) {\n if (handler.method === methodName) {\n return handler.type;\n }\n }\n }\n\n return undefined;\n}\n\n/**\n * @zh 速率限制装饰器\n * @en Rate limit decorator\n *\n * @zh 为消息处理器设置独立的速率限制配置\n * @en Set independent rate limit configuration for message handler\n *\n * @example\n * ```typescript\n * class GameRoom extends withRateLimit(Room) {\n * @rateLimit({ messagesPerSecond: 1, burstSize: 2 })\n * @onMessage('Trade')\n * handleTrade(data: TradeData, player: Player) {\n * // This message has stricter rate limit\n * }\n *\n * @rateLimit({ cost: 5 })\n * @onMessage('ExpensiveAction')\n * handleExpensiveAction(data: any, player: Player) {\n * // This message consumes 5 tokens\n * }\n * }\n * ```\n */\nexport function rateLimit(config?: MessageRateLimitConfig): MethodDecorator {\n return function (\n target: Object,\n propertyKey: string | symbol,\n descriptor: PropertyDescriptor\n ): PropertyDescriptor {\n const methodName = String(propertyKey);\n\n queueMicrotask(() => {\n const msgType = getMessageTypeFromMethod(target, methodName);\n if (msgType) {\n setRateLimitMetadata(target, msgType, {\n enabled: true,\n config\n });\n }\n });\n\n const metadata: RateLimitMetadata = {\n enabled: true,\n config\n };\n\n if (!target.hasOwnProperty(RATE_LIMIT_METADATA_KEY)) {\n Object.defineProperty(target, RATE_LIMIT_METADATA_KEY, {\n value: new Map<string, RateLimitMetadata>(),\n writable: false,\n enumerable: false\n });\n }\n\n return descriptor;\n };\n}\n\n/**\n * @zh 豁免速率限制装饰器\n * @en Exempt from rate limit decorator\n *\n * @zh 标记消息处理器不受速率限制\n * @en Mark message handler as exempt from rate limit\n *\n * @example\n * ```typescript\n * class GameRoom extends withRateLimit(Room) {\n * @noRateLimit()\n * @onMessage('Heartbeat')\n * handleHeartbeat(data: any, player: Player) {\n * // This message is not rate limited\n * }\n *\n * @noRateLimit()\n * @onMessage('Ping')\n * handlePing(data: any, player: Player) {\n * player.send('Pong', {});\n * }\n * }\n * ```\n */\nexport function noRateLimit(): MethodDecorator {\n return function (\n target: Object,\n propertyKey: string | symbol,\n descriptor: PropertyDescriptor\n ): PropertyDescriptor {\n const methodName = String(propertyKey);\n\n queueMicrotask(() => {\n const msgType = getMessageTypeFromMethod(target, methodName);\n if (msgType) {\n setRateLimitMetadata(target, msgType, {\n enabled: false,\n exempt: true\n });\n }\n });\n\n return descriptor;\n };\n}\n\n/**\n * @zh 速率限制消息装饰器(直接指定消息类型)\n * @en Rate limit message decorator (directly specify message type)\n *\n * @zh 当无法自动获取消息类型时使用此装饰器\n * @en Use this decorator when message type cannot be obtained automatically\n *\n * @example\n * ```typescript\n * class GameRoom extends withRateLimit(Room) {\n * @rateLimitMessage('Trade', { messagesPerSecond: 1 })\n * @onMessage('Trade')\n * handleTrade(data: TradeData, player: Player) {\n * // Explicitly rate limited\n * }\n * }\n * ```\n */\nexport function rateLimitMessage(\n messageType: string,\n config?: MessageRateLimitConfig\n): MethodDecorator {\n return function (\n target: Object,\n propertyKey: string | symbol,\n descriptor: PropertyDescriptor\n ): PropertyDescriptor {\n setRateLimitMetadata(target, messageType, {\n enabled: true,\n config\n });\n\n return descriptor;\n };\n}\n\n/**\n * @zh 豁免速率限制消息装饰器(直接指定消息类型)\n * @en Exempt rate limit message decorator (directly specify message type)\n *\n * @example\n * ```typescript\n * class GameRoom extends withRateLimit(Room) {\n * @noRateLimitMessage('Heartbeat')\n * @onMessage('Heartbeat')\n * handleHeartbeat(data: any, player: Player) {\n * // Explicitly exempted\n * }\n * }\n * ```\n */\nexport function noRateLimitMessage(messageType: string): MethodDecorator {\n return function (\n target: Object,\n propertyKey: string | symbol,\n descriptor: PropertyDescriptor\n ): PropertyDescriptor {\n setRateLimitMetadata(target, messageType, {\n enabled: false,\n exempt: true\n });\n\n return descriptor;\n };\n}\n","/**\n * @zh 房间速率限制 Mixin\n * @en Room rate limit mixin\n */\n\nimport type { Player, Room } from '../../room/index.js';\nimport { RateLimitContext } from '../context.js';\nimport { getRateLimitMetadata, RATE_LIMIT_METADATA_KEY } from '../decorators/rateLimit.js';\nimport { FixedWindowStrategy } from '../strategies/FixedWindow.js';\nimport { SlidingWindowStrategy } from '../strategies/SlidingWindow.js';\nimport { TokenBucketStrategy } from '../strategies/TokenBucket.js';\nimport type {\n IRateLimitContext,\n IRateLimitStrategy,\n RateLimitConfig,\n RateLimitMetadata,\n RateLimitResult\n} from '../types.js';\n\n/**\n * @zh 玩家速率限制上下文存储\n * @en Player rate limit context storage\n */\nconst PLAYER_RATE_LIMIT_CONTEXT = Symbol('playerRateLimitContext');\n\n/**\n * @zh 带速率限制的玩家\n * @en Player with rate limit\n */\nexport interface RateLimitedPlayer<TData = Record<string, unknown>> extends Player<TData> {\n /**\n * @zh 速率限制上下文\n * @en Rate limit context\n */\n readonly rateLimit: IRateLimitContext;\n}\n\n/**\n * @zh 带速率限制的房间接口\n * @en Room with rate limit interface\n */\nexport interface IRateLimitRoom {\n /**\n * @zh 获取玩家的速率限制上下文\n * @en Get rate limit context for player\n */\n getRateLimitContext(player: Player): IRateLimitContext | null;\n\n /**\n * @zh 全局速率限制策略\n * @en Global rate limit strategy\n */\n readonly rateLimitStrategy: IRateLimitStrategy;\n\n /**\n * @zh 速率限制钩子(被限流时调用)\n * @en Rate limit hook (called when rate limited)\n */\n onRateLimited?(player: Player, messageType: string, result: RateLimitResult): void;\n}\n\n/**\n * @zh 速率限制房间构造器类型\n * @en Rate limit room constructor type\n */\nexport type RateLimitRoomClass = new (...args: any[]) => Room & IRateLimitRoom;\n\n/**\n * @zh 创建策略实例\n * @en Create strategy instance\n */\nfunction createStrategy(config: RateLimitConfig): IRateLimitStrategy {\n const rate = config.messagesPerSecond ?? 10;\n const capacity = config.burstSize ?? rate * 2;\n\n switch (config.strategy) {\n case 'sliding-window':\n return new SlidingWindowStrategy({ rate, capacity });\n case 'fixed-window':\n return new FixedWindowStrategy({ rate, capacity });\n case 'token-bucket':\n default:\n return new TokenBucketStrategy({ rate, capacity });\n }\n}\n\n/**\n * @zh 获取玩家的速率限制上下文\n * @en Get rate limit context for player\n */\nexport function getPlayerRateLimitContext(player: Player): IRateLimitContext | null {\n const data = player as unknown as Record<symbol, unknown>;\n return (data[PLAYER_RATE_LIMIT_CONTEXT] as IRateLimitContext) ?? null;\n}\n\n/**\n * @zh 设置玩家的速率限制上下文\n * @en Set rate limit context for player\n */\nfunction setPlayerRateLimitContext(player: Player, context: IRateLimitContext): void {\n const data = player as unknown as Record<symbol, unknown>;\n data[PLAYER_RATE_LIMIT_CONTEXT] = context;\n\n Object.defineProperty(player, 'rateLimit', {\n get: () => data[PLAYER_RATE_LIMIT_CONTEXT],\n enumerable: true,\n configurable: false\n });\n}\n\n/**\n * @zh 包装房间类添加速率限制功能\n * @en Wrap room class with rate limit functionality\n *\n * @zh 使用 mixin 模式为房间添加速率限制,在消息处理前验证速率限制\n * @en Uses mixin pattern to add rate limit to room, validates rate before processing messages\n *\n * @example\n * ```typescript\n * import { Room, onMessage } from '@esengine/server';\n * import { withRateLimit } from '@esengine/server/ratelimit';\n *\n * class GameRoom extends withRateLimit(Room, {\n * messagesPerSecond: 10,\n * burstSize: 20,\n * onLimited: (player, type, result) => {\n * player.send('Error', {\n * code: 'RATE_LIMITED',\n * retryAfter: result.retryAfter\n * });\n * }\n * }) {\n * @onMessage('Move')\n * handleMove(data: { x: number, y: number }, player: Player) {\n * // Protected by rate limit\n * }\n * }\n * ```\n *\n * @example\n * // Combine with auth\n * ```typescript\n * class GameRoom extends withRateLimit(\n * withRoomAuth(Room, { requireAuth: true }),\n * { messagesPerSecond: 10 }\n * ) {\n * // Both auth and rate limit active\n * }\n * ```\n */\nexport function withRateLimit<TBase extends new (...args: any[]) => Room = new (...args: any[]) => Room>(\n Base: TBase,\n config: RateLimitConfig = {}\n): TBase & (new (...args: any[]) => IRateLimitRoom) {\n const {\n messagesPerSecond = 10,\n burstSize = 20,\n strategy = 'token-bucket',\n onLimited,\n disconnectOnLimit = false,\n maxConsecutiveLimits = 0,\n getKey = (player: Player) => player.id,\n cleanupInterval = 60000\n } = config;\n\n abstract class RateLimitRoom extends (Base as new (...args: any[]) => Room) implements IRateLimitRoom {\n private _rateLimitStrategy: IRateLimitStrategy;\n private _playerContexts: WeakMap<Player, RateLimitContext> = new WeakMap();\n private _cleanupTimer: ReturnType<typeof setInterval> | null = null;\n private _messageStrategies: Map<string, IRateLimitStrategy> = new Map();\n\n constructor(...args: any[]) {\n super(...args);\n this._rateLimitStrategy = createStrategy({\n messagesPerSecond,\n burstSize,\n strategy\n });\n\n this._startCleanup();\n this._initMessageStrategies();\n }\n\n /**\n * @zh 全局速率限制策略\n * @en Global rate limit strategy\n */\n get rateLimitStrategy(): IRateLimitStrategy {\n return this._rateLimitStrategy;\n }\n\n /**\n * @zh 速率限制钩子(可覆盖)\n * @en Rate limit hook (can be overridden)\n */\n onRateLimited?(player: Player, messageType: string, result: RateLimitResult): void;\n\n /**\n * @zh 获取玩家的速率限制上下文\n * @en Get rate limit context for player\n */\n getRateLimitContext(player: Player): IRateLimitContext | null {\n return this._playerContexts.get(player) ?? null;\n }\n\n /**\n * @internal\n * @zh 重写消息处理以添加速率限制检查\n * @en Override message handling to add rate limit check\n */\n _handleMessage(type: string, data: unknown, playerId: string): void {\n const player = this.getPlayer(playerId);\n if (!player) return;\n\n let context = this._playerContexts.get(player);\n if (!context) {\n context = this._createPlayerContext(player);\n }\n\n const metadata = this._getMessageMetadata(type);\n\n if (metadata?.exempt) {\n super._handleMessage(type, data, playerId);\n return;\n }\n\n const cost = metadata?.config?.cost ?? 1;\n let result: RateLimitResult;\n\n if (metadata?.config && (metadata.config.messagesPerSecond || metadata.config.burstSize)) {\n if (!context.hasMessageStrategy(type)) {\n const msgStrategy = createStrategy({\n messagesPerSecond: metadata.config.messagesPerSecond ?? messagesPerSecond,\n burstSize: metadata.config.burstSize ?? burstSize,\n strategy\n });\n context.setMessageStrategy(type, msgStrategy);\n }\n result = context.consume(type, cost);\n } else {\n result = context.consume(undefined, cost);\n }\n\n if (!result.allowed) {\n this._handleRateLimited(player, type, result, context);\n return;\n }\n\n super._handleMessage(type, data, playerId);\n }\n\n /**\n * @internal\n * @zh 重写 _addPlayer 以初始化速率限制上下文\n * @en Override _addPlayer to initialize rate limit context\n */\n async _addPlayer(id: string, conn: any): Promise<Player | null> {\n const player = await super._addPlayer(id, conn);\n if (player) {\n this._createPlayerContext(player);\n }\n return player;\n }\n\n /**\n * @internal\n * @zh 重写 _removePlayer 以清理速率限制上下文\n * @en Override _removePlayer to cleanup rate limit context\n */\n async _removePlayer(id: string, reason?: string): Promise<void> {\n const player = this.getPlayer(id);\n if (player) {\n const context = this._playerContexts.get(player);\n if (context) {\n context.reset();\n }\n this._playerContexts.delete(player);\n }\n await super._removePlayer(id, reason);\n }\n\n /**\n * @zh 重写 dispose 以清理定时器\n * @en Override dispose to cleanup timer\n */\n dispose(): void {\n this._stopCleanup();\n super.dispose();\n }\n\n /**\n * @zh 创建玩家的速率限制上下文\n * @en Create rate limit context for player\n */\n private _createPlayerContext(player: Player): RateLimitContext {\n const key = getKey(player);\n const context = new RateLimitContext(key, this._rateLimitStrategy);\n this._playerContexts.set(player, context);\n setPlayerRateLimitContext(player, context);\n return context;\n }\n\n /**\n * @zh 处理被限流的情况\n * @en Handle rate limited situation\n */\n private _handleRateLimited(\n player: Player,\n messageType: string,\n result: RateLimitResult,\n context: RateLimitContext\n ): void {\n if (this.onRateLimited) {\n this.onRateLimited(player, messageType, result);\n }\n\n onLimited?.(player, messageType, result);\n\n if (disconnectOnLimit) {\n this.kick(player as any, 'rate_limited');\n return;\n }\n\n if (maxConsecutiveLimits > 0 && context.consecutiveLimitCount >= maxConsecutiveLimits) {\n this.kick(player as any, 'too_many_rate_limits');\n }\n }\n\n /**\n * @zh 获取消息的元数据\n * @en Get message metadata\n */\n private _getMessageMetadata(type: string): RateLimitMetadata | undefined {\n return getRateLimitMetadata(this.constructor.prototype, type);\n }\n\n /**\n * @zh 初始化消息策略(从装饰器元数据)\n * @en Initialize message strategies (from decorator metadata)\n */\n private _initMessageStrategies(): void {\n const metadataMap = (this.constructor.prototype as any)[RATE_LIMIT_METADATA_KEY];\n if (metadataMap instanceof Map) {\n for (const [msgType, metadata] of metadataMap) {\n if (metadata.config && (metadata.config.messagesPerSecond || metadata.config.burstSize)) {\n const msgStrategy = createStrategy({\n messagesPerSecond: metadata.config.messagesPerSecond ?? messagesPerSecond,\n burstSize: metadata.config.burstSize ?? burstSize,\n strategy\n });\n this._messageStrategies.set(msgType, msgStrategy);\n }\n }\n }\n }\n\n /**\n * @zh 开始清理定时器\n * @en Start cleanup timer\n */\n private _startCleanup(): void {\n if (cleanupInterval > 0) {\n this._cleanupTimer = setInterval(() => {\n this._rateLimitStrategy.cleanup();\n for (const strategy of this._messageStrategies.values()) {\n strategy.cleanup();\n }\n }, cleanupInterval);\n }\n }\n\n /**\n * @zh 停止清理定时器\n * @en Stop cleanup timer\n */\n private _stopCleanup(): void {\n if (this._cleanupTimer) {\n clearInterval(this._cleanupTimer);\n this._cleanupTimer = null;\n }\n }\n }\n\n return RateLimitRoom as unknown as TBase & (new (...args: any[]) => IRateLimitRoom);\n}\n"]}
@@ -0,0 +1,386 @@
1
+ import { Codec } from '@esengine/rpc/codec';
2
+ import { G as GameServer } from '../index-DgaJIm6-.js';
3
+ import { R as Room, P as Player } from '../Room-BnKpl5Sj.js';
4
+ import '@esengine/rpc';
5
+
6
+ /**
7
+ * @zh 测试客户端
8
+ * @en Test client for server testing
9
+ */
10
+
11
+ /**
12
+ * @zh 测试客户端配置
13
+ * @en Test client options
14
+ */
15
+ interface TestClientOptions {
16
+ /**
17
+ * @zh 编解码器
18
+ * @en Codec
19
+ * @defaultValue json()
20
+ */
21
+ codec?: Codec;
22
+ /**
23
+ * @zh API 调用超时(毫秒)
24
+ * @en API call timeout in milliseconds
25
+ * @defaultValue 5000
26
+ */
27
+ timeout?: number;
28
+ /**
29
+ * @zh 连接超时(毫秒)
30
+ * @en Connection timeout in milliseconds
31
+ * @defaultValue 5000
32
+ */
33
+ connectTimeout?: number;
34
+ }
35
+ /**
36
+ * @zh 房间加入结果
37
+ * @en Room join result
38
+ */
39
+ interface JoinRoomResult {
40
+ roomId: string;
41
+ playerId: string;
42
+ }
43
+ /**
44
+ * @zh 收到的消息记录
45
+ * @en Received message record
46
+ */
47
+ interface ReceivedMessage {
48
+ type: string;
49
+ data: unknown;
50
+ timestamp: number;
51
+ }
52
+ /**
53
+ * @zh 测试客户端
54
+ * @en Test client for server integration testing
55
+ *
56
+ * @zh 专为测试设计的客户端,提供便捷的断言方法和消息记录功能
57
+ * @en Client designed for testing, with convenient assertion methods and message recording
58
+ *
59
+ * @example
60
+ * ```typescript
61
+ * const client = new TestClient(3000)
62
+ * await client.connect()
63
+ *
64
+ * // 加入房间
65
+ * const { roomId } = await client.joinRoom('game')
66
+ *
67
+ * // 发送消息
68
+ * client.sendToRoom('Move', { x: 10, y: 20 })
69
+ *
70
+ * // 等待收到特定消息
71
+ * const msg = await client.waitForMessage('PlayerMoved')
72
+ *
73
+ * // 断言收到消息
74
+ * expect(client.hasReceivedMessage('PlayerMoved')).toBe(true)
75
+ *
76
+ * await client.disconnect()
77
+ * ```
78
+ */
79
+ declare class TestClient {
80
+ private readonly _port;
81
+ private readonly _codec;
82
+ private readonly _timeout;
83
+ private readonly _connectTimeout;
84
+ private _ws;
85
+ private _callIdCounter;
86
+ private _connected;
87
+ private _currentRoomId;
88
+ private _currentPlayerId;
89
+ private readonly _pendingCalls;
90
+ private readonly _msgHandlers;
91
+ private readonly _receivedMessages;
92
+ constructor(port: number, options?: TestClientOptions);
93
+ /**
94
+ * @zh 是否已连接
95
+ * @en Whether connected
96
+ */
97
+ get isConnected(): boolean;
98
+ /**
99
+ * @zh 当前房间 ID
100
+ * @en Current room ID
101
+ */
102
+ get roomId(): string | null;
103
+ /**
104
+ * @zh 当前玩家 ID
105
+ * @en Current player ID
106
+ */
107
+ get playerId(): string | null;
108
+ /**
109
+ * @zh 收到的所有消息
110
+ * @en All received messages
111
+ */
112
+ get receivedMessages(): ReadonlyArray<ReceivedMessage>;
113
+ /**
114
+ * @zh 连接到服务器
115
+ * @en Connect to server
116
+ */
117
+ connect(): Promise<this>;
118
+ /**
119
+ * @zh 断开连接
120
+ * @en Disconnect from server
121
+ */
122
+ disconnect(): Promise<void>;
123
+ /**
124
+ * @zh 加入房间
125
+ * @en Join a room
126
+ */
127
+ joinRoom(roomType: string, options?: Record<string, unknown>): Promise<JoinRoomResult>;
128
+ /**
129
+ * @zh 通过 ID 加入房间
130
+ * @en Join a room by ID
131
+ */
132
+ joinRoomById(roomId: string): Promise<JoinRoomResult>;
133
+ /**
134
+ * @zh 离开房间
135
+ * @en Leave room
136
+ */
137
+ leaveRoom(): Promise<void>;
138
+ /**
139
+ * @zh 发送消息到房间
140
+ * @en Send message to room
141
+ */
142
+ sendToRoom(type: string, data: unknown): void;
143
+ /**
144
+ * @zh 调用 API
145
+ * @en Call API
146
+ */
147
+ call<T = unknown>(name: string, input: unknown): Promise<T>;
148
+ /**
149
+ * @zh 发送消息
150
+ * @en Send message
151
+ */
152
+ send(name: string, data: unknown): void;
153
+ /**
154
+ * @zh 监听消息
155
+ * @en Listen for message
156
+ */
157
+ on(name: string, handler: (data: unknown) => void): this;
158
+ /**
159
+ * @zh 取消监听消息
160
+ * @en Remove message listener
161
+ */
162
+ off(name: string, handler?: (data: unknown) => void): this;
163
+ /**
164
+ * @zh 等待收到指定消息
165
+ * @en Wait for a specific message
166
+ */
167
+ waitForMessage<T = unknown>(type: string, timeout?: number): Promise<T>;
168
+ /**
169
+ * @zh 等待收到指定房间消息
170
+ * @en Wait for a specific room message
171
+ */
172
+ waitForRoomMessage<T = unknown>(type: string, timeout?: number): Promise<T>;
173
+ /**
174
+ * @zh 是否收到过指定消息
175
+ * @en Whether received a specific message
176
+ */
177
+ hasReceivedMessage(type: string): boolean;
178
+ /**
179
+ * @zh 获取指定类型的所有消息
180
+ * @en Get all messages of a specific type
181
+ */
182
+ getMessagesOfType<T = unknown>(type: string): T[];
183
+ /**
184
+ * @zh 获取最后收到的指定类型消息
185
+ * @en Get the last received message of a specific type
186
+ */
187
+ getLastMessage<T = unknown>(type: string): T | undefined;
188
+ /**
189
+ * @zh 清空消息记录
190
+ * @en Clear message records
191
+ */
192
+ clearMessages(): void;
193
+ /**
194
+ * @zh 获取收到的消息数量
195
+ * @en Get received message count
196
+ */
197
+ getMessageCount(type?: string): number;
198
+ private _handleMessage;
199
+ private _handleApiResponse;
200
+ private _handleApiError;
201
+ private _handleMsg;
202
+ private _rejectAllPending;
203
+ }
204
+
205
+ /**
206
+ * @zh 测试服务器工具
207
+ * @en Test server utilities
208
+ */
209
+
210
+ /**
211
+ * @zh 测试服务器配置
212
+ * @en Test server options
213
+ */
214
+ interface TestServerOptions {
215
+ /**
216
+ * @zh 端口号,0 表示随机端口
217
+ * @en Port number, 0 for random port
218
+ * @defaultValue 0
219
+ */
220
+ port?: number;
221
+ /**
222
+ * @zh Tick 速率
223
+ * @en Tick rate
224
+ * @defaultValue 0
225
+ */
226
+ tickRate?: number;
227
+ /**
228
+ * @zh 是否禁用控制台日志
229
+ * @en Whether to suppress console logs
230
+ * @defaultValue true
231
+ */
232
+ silent?: boolean;
233
+ }
234
+ /**
235
+ * @zh 测试环境
236
+ * @en Test environment
237
+ */
238
+ interface TestEnvironment {
239
+ /**
240
+ * @zh 服务器实例
241
+ * @en Server instance
242
+ */
243
+ server: GameServer;
244
+ /**
245
+ * @zh 服务器端口
246
+ * @en Server port
247
+ */
248
+ port: number;
249
+ /**
250
+ * @zh 创建测试客户端
251
+ * @en Create test client
252
+ */
253
+ createClient(options?: TestClientOptions): Promise<TestClient>;
254
+ /**
255
+ * @zh 创建多个测试客户端
256
+ * @en Create multiple test clients
257
+ */
258
+ createClients(count: number, options?: TestClientOptions): Promise<TestClient[]>;
259
+ /**
260
+ * @zh 清理测试环境
261
+ * @en Cleanup test environment
262
+ */
263
+ cleanup(): Promise<void>;
264
+ /**
265
+ * @zh 所有已创建的客户端
266
+ * @en All created clients
267
+ */
268
+ readonly clients: ReadonlyArray<TestClient>;
269
+ }
270
+ /**
271
+ * @zh 创建测试服务器
272
+ * @en Create test server
273
+ *
274
+ * @example
275
+ * ```typescript
276
+ * const { server, port, cleanup } = await createTestServer()
277
+ * server.define('game', GameRoom)
278
+ *
279
+ * const client = new TestClient(port)
280
+ * await client.connect()
281
+ *
282
+ * // ... run tests ...
283
+ *
284
+ * await cleanup()
285
+ * ```
286
+ */
287
+ declare function createTestServer(options?: TestServerOptions): Promise<{
288
+ server: GameServer;
289
+ port: number;
290
+ cleanup: () => Promise<void>;
291
+ }>;
292
+ /**
293
+ * @zh 创建完整测试环境
294
+ * @en Create complete test environment
295
+ *
296
+ * @zh 包含服务器、客户端创建和清理功能的完整测试环境
297
+ * @en Complete test environment with server, client creation and cleanup
298
+ *
299
+ * @example
300
+ * ```typescript
301
+ * describe('GameRoom', () => {
302
+ * let env: TestEnvironment
303
+ *
304
+ * beforeEach(async () => {
305
+ * env = await createTestEnv()
306
+ * env.server.define('game', GameRoom)
307
+ * })
308
+ *
309
+ * afterEach(async () => {
310
+ * await env.cleanup()
311
+ * })
312
+ *
313
+ * it('should handle player join', async () => {
314
+ * const client = await env.createClient()
315
+ * const result = await client.joinRoom('game')
316
+ * expect(result.roomId).toBeDefined()
317
+ * })
318
+ *
319
+ * it('should broadcast to all players', async () => {
320
+ * const [client1, client2] = await env.createClients(2)
321
+ *
322
+ * await client1.joinRoom('game')
323
+ * const joinPromise = client1.waitForRoomMessage('PlayerJoined')
324
+ *
325
+ * await client2.joinRoom('game')
326
+ * const msg = await joinPromise
327
+ *
328
+ * expect(msg).toBeDefined()
329
+ * })
330
+ * })
331
+ * ```
332
+ */
333
+ declare function createTestEnv(options?: TestServerOptions): Promise<TestEnvironment>;
334
+
335
+ /**
336
+ * @zh 模拟房间
337
+ * @en Mock room for testing
338
+ */
339
+
340
+ /**
341
+ * @zh 模拟房间状态
342
+ * @en Mock room state
343
+ */
344
+ interface MockRoomState {
345
+ messages: Array<{
346
+ type: string;
347
+ data: unknown;
348
+ playerId: string;
349
+ }>;
350
+ joinCount: number;
351
+ leaveCount: number;
352
+ }
353
+ /**
354
+ * @zh 模拟房间
355
+ * @en Mock room for testing
356
+ *
357
+ * @zh 记录所有事件和消息,用于测试断言
358
+ * @en Records all events and messages for test assertions
359
+ *
360
+ * @example
361
+ * ```typescript
362
+ * const env = await createTestEnv()
363
+ * env.server.define('mock', MockRoom)
364
+ *
365
+ * const client = await env.createClient()
366
+ * await client.joinRoom('mock')
367
+ *
368
+ * client.sendToRoom('Test', { value: 123 })
369
+ * await wait(50)
370
+ *
371
+ * // MockRoom 会广播收到的消息
372
+ * const msg = client.getLastMessage('RoomMessage')
373
+ * ```
374
+ */
375
+ declare class MockRoom extends Room<MockRoomState> {
376
+ state: MockRoomState;
377
+ onCreate(): void;
378
+ onJoin(player: Player): void;
379
+ onLeave(player: Player): void;
380
+ handleAnyMessage(data: unknown, player: Player, type: string): void;
381
+ handleEcho(data: unknown, player: Player): void;
382
+ handleBroadcast(data: unknown, _player: Player): void;
383
+ handlePing(_data: unknown, player: Player): void;
384
+ }
385
+
386
+ export { MockRoom, TestClient, type TestClientOptions, type TestEnvironment, type TestServerOptions, createTestEnv, createTestServer };