/** * Browser/React Native-safe kill switch client for platform-service. * * Checks GET /api/flags/kill-switch to determine if the app is disabled. * Fail-open: returns { disabled: false } on any network error. * * @example * ```ts * import { createKillSwitchClient } from '@bytelyst/kill-switch-client'; * * const ks = createKillSwitchClient({ * baseUrl: 'http://localhost:4003/api', * productId: 'nomgap', * }); * * const result = await ks.check(); * if (result.disabled) showBlockScreen(result.message); * ``` */ export interface KillSwitchClientConfig { /** Platform-service base URL (e.g. "http://localhost:4003/api"). */ baseUrl: string; /** Product identifier sent as x-product-id header. */ productId: string; /** Platform string for the query (e.g. "mobile", "web"). Default: "mobile". */ platform?: string; } export interface KillSwitchResult { disabled: boolean; message: string | null; } export interface KillSwitchClient { /** Check if the app is disabled. Fail-open on any error. */ check(): Promise; } export function createKillSwitchClient(config: KillSwitchClientConfig): KillSwitchClient { const { baseUrl, productId, platform = 'mobile' } = config; async function check(): Promise { try { const requestId = typeof globalThis.crypto?.randomUUID === 'function' ? globalThis.crypto.randomUUID() : `${Date.now()}-${Math.random().toString(36).slice(2, 10)}`; const res = await globalThis.fetch( `${baseUrl}/flags/kill-switch?platform=${encodeURIComponent(platform)}`, { headers: { 'x-product-id': productId, 'x-request-id': requestId }, } ); if (!res.ok) return { disabled: false, message: null }; const data = (await res.json()) as KillSwitchResult; return { disabled: data.disabled ?? false, message: data.message ?? null, }; } catch { // Fail-open: network errors should NOT block the user return { disabled: false, message: null }; } } return { check }; }