import { normalizeTeamNotification } from "./normalizers"; import type { CloudApiSocket } from "./socket"; import type { ChatChannel, TeamAccentColor, TeamInviteLink, TeamInvitePreview, TeamMember, TeamNotification, TeamReceivedInvitation, TeamRole, TeamSentInvitation, TeamSummary, TeamUpdatedEvent, TeamUsernameInvitation, } from "./types"; type CloudApiRequest = (path: string, options?: RequestInit) => Promise; type TeamNotificationListener = (notification: TeamNotification) => void; type CloudEventListener = (data: unknown) => void; interface CloudTeamsApiOptions { request: CloudApiRequest; socket: CloudApiSocket; } export class CloudTeamsApi { constructor(private readonly options: CloudTeamsApiOptions) {} async listTeams(): Promise { const body = await this.options.request<{ teams: TeamSummary[] }>("/teams"); return body.teams; } async createTeam(input: { name: string; accentColor?: TeamAccentColor; shortName?: string; }): Promise { return this.options.request("/teams", { method: "POST", body: JSON.stringify(input), }); } async getTeamMembers( teamId: string, ): Promise<{ team: TeamSummary; members: TeamMember[] }> { return this.options.request<{ team: TeamSummary; members: TeamMember[] }>( `/teams/${encodeURIComponent(teamId)}/members`, ); } async inviteTeamMemberByUsername( teamId: string, username: string, ): Promise { return this.options.request( `/teams/${encodeURIComponent(teamId)}/invitations`, { method: "POST", body: JSON.stringify({ username }), }, ); } async listTeamInviteLinks(teamId: string): Promise { const body = await this.options.request<{ links: TeamInviteLink[] }>( `/teams/${encodeURIComponent(teamId)}/invite-links`, ); return body.links; } async createTeamInviteLink( teamId: string, options?: { expiresInDays?: number; maxUses?: number | null }, ): Promise { return this.options.request( `/teams/${encodeURIComponent(teamId)}/invite-links`, { method: "POST", body: JSON.stringify(options ?? {}), }, ); } async deleteTeamInviteLink(teamId: string, token: string): Promise { await this.options.request( `/teams/${encodeURIComponent(teamId)}/invite-links/${encodeURIComponent(token)}`, { method: "DELETE" }, ); } async previewTeamInviteLink(token: string): Promise { return this.options.request( `/teams/join/${encodeURIComponent(token)}`, ); } async joinTeamThroughLink(token: string): Promise { return this.options.request( `/teams/join/${encodeURIComponent(token)}`, { method: "POST", body: JSON.stringify({}), }, ); } async getTeamNotifications(): Promise { const body = await this.options.request<{ notifications: TeamNotification[]; }>("/teams/notifications"); return body.notifications.map((notification) => normalizeTeamNotification(notification), ); } async listTeamInvitations(teamId: string): Promise { const body = await this.options.request<{ invitations: TeamSentInvitation[] }>( `/teams/${encodeURIComponent(teamId)}/invitations`, ); return body.invitations; } async listMyTeamInvitations(): Promise { const body = await this.options.request<{ invitations: TeamReceivedInvitation[] }>( "/teams/invitations", ); return body.invitations; } async acceptTeamInvitation(invitationId: string): Promise { return this.options.request( `/teams/invitations/${encodeURIComponent(invitationId)}/accept`, { method: "POST", body: JSON.stringify({}) }, ); } async rejectTeamInvitation(invitationId: string): Promise { await this.options.request( `/teams/invitations/${encodeURIComponent(invitationId)}/reject`, { method: "POST", body: JSON.stringify({}) }, ); } async cancelTeamInvitation(teamId: string, invitationId: string): Promise { await this.options.request( `/teams/${encodeURIComponent(teamId)}/invitations/${encodeURIComponent(invitationId)}`, { method: "DELETE" }, ); } async updateTeam( teamId: string, data: { name?: string; accentColor?: TeamAccentColor; shortName?: string; allowMemberInvites?: boolean; }, ): Promise { return this.options.request(`/teams/${encodeURIComponent(teamId)}`, { method: "PATCH", body: JSON.stringify(data), }); } async updateTeamMemberRole( teamId: string, memberId: string, role: TeamRole, ): Promise { const body = await this.options.request<{ members: TeamMember[] }>( `/teams/${encodeURIComponent(teamId)}/members/${encodeURIComponent(memberId)}`, { method: "PATCH", body: JSON.stringify({ role }) }, ); return body.members; } async removeTeamMember(teamId: string, memberId: string): Promise { const body = await this.options.request<{ members: TeamMember[] }>( `/teams/${encodeURIComponent(teamId)}/members/${encodeURIComponent(memberId)}`, { method: "DELETE" }, ); return body.members; } async leaveTeam(teamId: string): Promise { await this.options.request(`/teams/${encodeURIComponent(teamId)}/leave`, { method: "POST", body: JSON.stringify({}), }); } async deleteTeam(teamId: string): Promise { await this.options.request(`/teams/${encodeURIComponent(teamId)}`, { method: "DELETE", }); } async listTeamChannels(teamId: string): Promise { const body = await this.options.request<{ channels: ChatChannel[] }>( `/teams/${encodeURIComponent(teamId)}/channels`, ); return body.channels; } async createTeamChannel(teamId: string, name: string): Promise { return this.options.request( `/teams/${encodeURIComponent(teamId)}/channels`, { method: "POST", body: JSON.stringify({ name }) }, ); } async deleteTeamChannel(teamId: string, channelId: string): Promise { await this.options.request( `/teams/${encodeURIComponent(teamId)}/channels/${encodeURIComponent(channelId)}`, { method: "DELETE" }, ); } /** `team.updated` frames: settings, members, channels, or the team is gone. */ subscribeTeamUpdates(listener: (event: TeamUpdatedEvent) => void): () => void { return this.options.socket.subscribeCloudEvent("team.updated", (data) => { const event = data as Partial | null; if (!event || typeof event.teamId !== "string") return; const change = event.change; if (change !== "settings" && change !== "members" && change !== "channels" && change !== "deleted") return; listener({ teamId: event.teamId, change }); }); } subscribeTeamNotifications(listener: TeamNotificationListener): () => void { return this.options.socket.subscribeTeamNotifications(listener); } subscribeCloudEvent(type: string, listener: CloudEventListener): () => void { return this.options.socket.subscribeCloudEvent(type, listener); } }