import type { SfuConsumerDescriptor, SfuMediaKind, SfuProducerDescriptor, SfuSessionDescriptor, SfuSessionState, SfuTransportDescriptor, SfuTransportDirection, } from './types'; type StoredSfuSession = SfuSessionDescriptor & { updatedAt: string; }; const nowIso = (): string => new Date().toISOString(); export class SfuSessionRegistry { private readonly sessions = new Map(); private readonly transports = new Map(); private readonly producers = new Map(); private readonly consumers = new Map(); get(streamSessionId: string): SfuSessionDescriptor | null { const found = this.sessions.get(streamSessionId); if (!found) return null; const { updatedAt: _updatedAt, ...descriptor } = found; return descriptor; } set(session: SfuSessionDescriptor): SfuSessionDescriptor { this.sessions.set(session.streamSessionId, { ...session, updatedAt: nowIso() }); return session; } updateState(streamSessionId: string, state: SfuSessionState): SfuSessionDescriptor | null { const existing = this.sessions.get(streamSessionId); if (!existing) return null; const next: StoredSfuSession = { ...existing, state, updatedAt: nowIso() }; this.sessions.set(streamSessionId, next); const { updatedAt: _updatedAt, ...descriptor } = next; return descriptor; } list(): SfuSessionDescriptor[] { return Array.from(this.sessions.values()).map(({ updatedAt: _updatedAt, ...descriptor }) => descriptor); } addTransport(input: { transportId: string; streamSessionId: string; ownerDeviceId: string; direction: SfuTransportDirection; }): SfuTransportDescriptor { const descriptor: SfuTransportDescriptor = { transportId: input.transportId, streamSessionId: input.streamSessionId, ownerDeviceId: input.ownerDeviceId, direction: input.direction, state: 'new', createdAt: nowIso(), }; this.transports.set(descriptor.transportId, descriptor); return descriptor; } getTransport(transportId: string): SfuTransportDescriptor | null { return this.transports.get(transportId) ?? null; } listTransports(streamSessionId: string): SfuTransportDescriptor[] { return Array.from(this.transports.values()).filter((transport) => transport.streamSessionId === streamSessionId); } connectTransport(transportId: string): SfuTransportDescriptor | null { const existing = this.transports.get(transportId); if (!existing) return null; const next: SfuTransportDescriptor = { ...existing, state: 'connected' }; this.transports.set(transportId, next); return next; } addProducer(input: { producerId: string; streamSessionId: string; transportId: string; cameraDeviceId: string; kind: SfuMediaKind; rtpParameters: Record; }): SfuProducerDescriptor { const descriptor: SfuProducerDescriptor = { producerId: input.producerId, streamSessionId: input.streamSessionId, transportId: input.transportId, cameraDeviceId: input.cameraDeviceId, kind: input.kind, rtpParameters: input.rtpParameters, createdAt: nowIso(), }; this.producers.set(descriptor.producerId, descriptor); return descriptor; } getProducer(producerId: string): SfuProducerDescriptor | null { return this.producers.get(producerId) ?? null; } listProducers(streamSessionId: string): SfuProducerDescriptor[] { return Array.from(this.producers.values()) .filter((producer) => producer.streamSessionId === streamSessionId) .sort((left, right) => left.createdAt.localeCompare(right.createdAt)); } addConsumer(input: { consumerId: string; streamSessionId: string; transportId: string; viewerDeviceId: string; producerId: string; kind: SfuMediaKind; rtpParameters: Record; }): SfuConsumerDescriptor { const descriptor: SfuConsumerDescriptor = { consumerId: input.consumerId, streamSessionId: input.streamSessionId, transportId: input.transportId, viewerDeviceId: input.viewerDeviceId, producerId: input.producerId, kind: input.kind, rtpParameters: input.rtpParameters, createdAt: nowIso(), }; this.consumers.set(descriptor.consumerId, descriptor); return descriptor; } listConsumers(streamSessionId: string): SfuConsumerDescriptor[] { return Array.from(this.consumers.values()).filter((consumer) => consumer.streamSessionId === streamSessionId); } }