feat(web): add feature flag client with platform-service polling

This commit is contained in:
saravanakumardb1 2026-02-28 14:18:06 -08:00
parent 09ded150f4
commit 95f71f4625
2 changed files with 72 additions and 0 deletions

View File

@ -4,6 +4,7 @@ import { useEffect } from 'react';
import { usePathname } from 'next/navigation';
import { AuthProvider } from '@/lib/auth-context';
import { initTelemetry, trackPageView } from '@/lib/telemetry';
import { initFeatureFlags } from '@/lib/feature-flags';
import type { ReactNode } from 'react';
export function Providers({ children }: { children: ReactNode }) {
@ -11,6 +12,7 @@ export function Providers({ children }: { children: ReactNode }) {
useEffect(() => {
initTelemetry();
initFeatureFlags({ platform: 'web' });
}, []);
useEffect(() => {

View File

@ -0,0 +1,70 @@
/**
* Feature flag client polls platform-service /flags/poll endpoint.
*
* Flags are fetched on init and cached in memory. Re-polls on a configurable
* interval (default 5 min). Consumers call `isEnabled('flag_key')`.
*
* Privacy: sends userId + platform only. No PII.
*/
import { PRODUCT_ID } from './auth-api';
const PLATFORM_URL = process.env.NEXT_PUBLIC_PLATFORM_SERVICE_URL ?? 'https://api.chronomind.app';
const POLL_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes
let _flags: Record<string, boolean> = {};
let _initialized = false;
let _intervalId: ReturnType<typeof setInterval> | null = null;
interface PollParams {
userId?: string;
platform?: string;
}
async function fetchFlags(params: PollParams): Promise<Record<string, boolean>> {
try {
const qs = new URLSearchParams();
if (params.userId) qs.set('userId', params.userId);
if (params.platform) qs.set('platform', params.platform);
const res = await fetch(`${PLATFORM_URL}/api/flags/poll?${qs.toString()}`, {
headers: { 'x-product-id': PRODUCT_ID },
});
if (!res.ok) return _flags;
const data = await res.json();
return (data as { flags: Record<string, boolean> }).flags ?? {};
} catch {
return _flags;
}
}
/**
* Initialize the flag client. Call once on app startup.
* Fetches flags immediately and starts polling.
*/
export async function initFeatureFlags(params: PollParams = {}): Promise<void> {
if (_initialized) return;
_initialized = true;
const pollParams: PollParams = { platform: 'web', ...params };
_flags = await fetchFlags(pollParams);
_intervalId = setInterval(async () => {
_flags = await fetchFlags(pollParams);
}, POLL_INTERVAL_MS);
}
/** Check if a feature flag is enabled. Returns false if not found or not initialized. */
export function isEnabled(key: string): boolean {
return _flags[key] === true;
}
/** Get all currently cached flags. */
export function getAllFlags(): Readonly<Record<string, boolean>> {
return _flags;
}
/** Stop polling and reset state. Useful for tests. */
export function resetFeatureFlags(): void {
if (_intervalId) clearInterval(_intervalId);
_intervalId = null;
_flags = {};
_initialized = false;
}