/** * Anonymous invite registration. The invite token travels only in request bodies, * never in a URL path or query, and the server scopes every call to that token. */ import { isHTTPError } from "ky"; import { apiPost } from "@/api/api"; import { API_PATHS } from "@/api/api-paths"; import { handleApiResponse } from "@/api/handle-api-response"; import type { AuthUser } from "@/domain/auth/types/auth-user"; import type { CompleteRegistrationInput, TeamMemberInviteDetails, TeamMemberInviteErrorCode, } from "@/domain/team-member-invite/types/team-member-invite"; import { getAuthUser, setAuthUser } from "@/lib/auth/auth-storage"; export const teamMemberInviteApi = { resolve: async (token: string): Promise => { const data = await apiPost(API_PATHS.teamMemberInvite.resolve, { token }); return handleApiResponse(data); }, sendCode: async (token: string): Promise => { await apiPost(API_PATHS.teamMemberInvite.sendCode, { token }); }, verifyCode: async ({ token, code }: { token: string; code: string }): Promise => { await apiPost(API_PATHS.teamMemberInvite.verifyCode, { token, code }); }, /** Finishes registration and stores the returned session exactly as login does. */ complete: async (input: CompleteRegistrationInput): Promise => { const data = await apiPost(API_PATHS.teamMemberInvite.complete, { token: input.token, password: input.password, // An empty value clears the phone the admin entered. phone: input.phone.trim(), }); const user = handleApiResponse(data); setAuthUser(user); return getAuthUser() ?? user; }, }; /** The server's failure code for an invite call, when the response carried one. */ export function inviteErrorCode(error: unknown): TeamMemberInviteErrorCode | null { if (!isHTTPError(error)) { return null; } const data: unknown = error.data; if (typeof data === "object" && data !== null && "code" in data) { const code = (data as { code: unknown }).code; return typeof code === "string" ? (code as TeamMemberInviteErrorCode) : null; } return null; }