/** * Domain Entity: LeagueMembership and JoinRequest * * Represents a driver's membership in a league and join requests. */ import type { IEntity } from '@gridpilot/shared/domain'; import { RacingDomainValidationError } from '../errors/RacingDomainError'; export type MembershipRole = 'owner' | 'admin' | 'steward' | 'member'; export type MembershipStatus = 'active' | 'pending' | 'none'; export interface LeagueMembershipProps { id?: string; leagueId: string; driverId: string; role: MembershipRole; status?: MembershipStatus; joinedAt?: Date; } export class LeagueMembership implements IEntity { readonly id: string; readonly leagueId: string; readonly driverId: string; readonly role: MembershipRole; readonly status: MembershipStatus; readonly joinedAt: Date; private constructor(props: Required) { this.id = props.id; this.leagueId = props.leagueId; this.driverId = props.driverId; this.role = props.role; this.status = props.status; this.joinedAt = props.joinedAt; } static create(props: LeagueMembershipProps): LeagueMembership { this.validate(props); const id = props.id && props.id.trim().length > 0 ? props.id : `${props.leagueId}:${props.driverId}`; const status = props.status ?? 'pending'; const joinedAt = props.joinedAt ?? new Date(); return new LeagueMembership({ id, leagueId: props.leagueId, driverId: props.driverId, role: props.role, status, joinedAt, }); } private static validate(props: LeagueMembershipProps): void { if (!props.leagueId || props.leagueId.trim().length === 0) { throw new RacingDomainValidationError('League ID is required'); } if (!props.driverId || props.driverId.trim().length === 0) { throw new RacingDomainValidationError('Driver ID is required'); } if (!props.role) { throw new RacingDomainValidationError('Membership role is required'); } } } export interface JoinRequest { id: string; leagueId: string; driverId: string; requestedAt: Date; message?: string; }