import type { ChatAdapter } from '../ports/outbound/chat-adapter.port'; import type { User } from '../types/user.types'; import { type ChatEventMap, type ChatEventName, type ChatSendParams } from '../types/chat.types'; import type { Logger } from '../utils/logger'; import { BaseService } from './base.service'; /** * Dependencies injected by the Session composition root. */ export interface ChatServiceDeps { /** Local user id */ localUserId: string; /** Whether the local user is allowed to perform moderation actions */ canModerate: () => boolean; /** Resolve a user id to a `User` object for event payload hydration */ resolveUser: (userId: string) => User | undefined; } export declare class ChatService extends BaseService { private adapter; private deps; private eventHandlers; private unsubscribes; private sendTimestamps; private seenMsgIds; private seenMsgIdSet; private mutedUserIds; private roomMuted; /** * Sends awaiting adapter completion. Tracked so we can release awaiting * callers when the session is torn down — releases the awaiter only; * does not cancel work already handed to the adapter. */ private pendingSends; constructor(adapter: ChatAdapter, deps: ChatServiceDeps, logger: Logger); /** * Send a chat message. * * Resolves when the injected adapter reports the message as accepted by the * transport. Delivery semantics (e.g., whether resolution implies remote * receipt or only local hand-off) are defined by the adapter contract. * * @throws ChatMutedError if the room or local user is muted * @throws ChatPayloadTooLargeError if `text` exceeds {@link MAX_CHAT_MESSAGE_BYTES} * @throws ChatRateLimitedError if the per-second send budget is exhausted * @throws ChatSendInterruptedError if the session is destroyed while waiting */ send(params: ChatSendParams): Promise; /** * Wrap an adapter promise with an interruption hook — when the service is * destroyed (session disconnect), pending awaiters reject with * ChatSendInterruptedError so callers stop awaiting forever. The adapter * work itself is NOT cancelled; only the awaiter is released. */ private raceWithInterruption; /** * Set typing indicator state. Fire-and-forget; failures are swallowed * because typing is best-effort. */ setTyping(isTyping: boolean): void; muteParticipant(userId: string): Promise; unmuteParticipant(userId: string): Promise; muteRoom(): Promise; unmuteRoom(): Promise; on(event: E, handler: ChatEventMap[E]): void; off(event?: E, handler?: ChatEventMap[E]): void; /** @internal */ destroy(): void; private setupListeners; private handleIncomingMessage; private handleIncomingTyping; private handleIncomingControl; private tryAcquireRateLimit; private recordSeen; private assertCanModerate; private emit; } //# sourceMappingURL=chat.service.d.ts.map