/** * Browser/React Native-safe referral client for platform-service. * * Wraps platform-service /referrals/* endpoints. * No Node.js dependencies — uses globalThis.fetch. */ import type { ReferralClient, ReferralClientConfig, ReferralDoc } from './types.js'; function generateRequestId(): string { return typeof globalThis.crypto?.randomUUID === 'function' ? globalThis.crypto.randomUUID() : `${Date.now()}-${Math.random().toString(36).slice(2, 10)}`; } export function createReferralClient(config: ReferralClientConfig): ReferralClient { const { baseUrl, productId, getAccessToken, defaultRewardTokens } = config; const defaultReferrerTokens = defaultRewardTokens?.referrer ?? 1000; const defaultReferredTokens = defaultRewardTokens?.referred ?? 500; function headers(): Record { const h: Record = { 'Content-Type': 'application/json', 'x-product-id': productId, 'x-request-id': generateRequestId(), }; const token = getAccessToken(); if (token) h['Authorization'] = `Bearer ${token}`; return h; } async function listMyReferrals( referrerId: string ): Promise<{ referrals: ReferralDoc[]; count: number }> { const res = await globalThis.fetch( `${baseUrl}/referrals/by-referrer/${encodeURIComponent(referrerId)}`, { headers: headers() } ); if (!res.ok) throw new Error(`listMyReferrals failed: ${res.status}`); const data = (await res.json()) as { referrals: ReferralDoc[]; count: number }; return data; } async function getReferralStats(): Promise<{ total: number; completed: number; rewarded: number; }> { const res = await globalThis.fetch(`${baseUrl}/referrals/stats`, { headers: headers() }); if (!res.ok) throw new Error(`getReferralStats failed: ${res.status}`); return (await res.json()) as { total: number; completed: number; rewarded: number }; } async function createReferral(input: { referrerId: string; referrerEmail: string; referredEmail: string; }): Promise { const body = { ...input, productId, referrerRewardTokens: defaultReferrerTokens, referredRewardTokens: defaultReferredTokens, }; const res = await globalThis.fetch(`${baseUrl}/referrals`, { method: 'POST', headers: headers(), body: JSON.stringify(body), }); if (!res.ok) throw new Error(`createReferral failed: ${res.status}`); return (await res.json()) as ReferralDoc; } async function updateReferralStatus( id: string, referrerId: string, status: ReferralDoc['status'] ): Promise { const res = await globalThis.fetch(`${baseUrl}/referrals/${encodeURIComponent(id)}`, { method: 'PUT', headers: headers(), body: JSON.stringify({ referrerId, status }), }); if (!res.ok) throw new Error(`updateReferralStatus failed: ${res.status}`); return (await res.json()) as ReferralDoc; } async function getByEmail(email: string): Promise { const res = await globalThis.fetch( `${baseUrl}/referrals/by-email/${encodeURIComponent(email)}`, { headers: headers() } ); if (res.status === 404) return null; if (!res.ok) throw new Error(`getByEmail failed: ${res.status}`); return (await res.json()) as ReferralDoc; } function buildShareLink(code: string): string { return `https://bytelyst.com/refer/${encodeURIComponent(code)}?product=${encodeURIComponent(productId)}`; } function buildShareMessage(code: string, productName: string): string { const link = buildShareLink(code); return `Try ${productName}! Use my referral link to get started: ${link}`; } function calculateEarnedDays(conversions: number, daysPerReferral = 7): number { return Math.max(0, conversions * daysPerReferral); } return { listMyReferrals, getReferralStats, createReferral, updateReferralStatus, getByEmail, buildShareLink, buildShareMessage, calculateEarnedDays, }; }