Files
buzz/desktop/src/shared/features/resolveEnabled.ts
Taylor Ho 5a58fd2b4a fix: address review feedback — migration, reactivity, standards, tests
1. Migration: existing users auto-seed experimental features as enabled
   on first load (no feature loss on upgrade). New installs get opt-in UX.
2. Reactivity: SettingsView uses useFeatureSnapshot() so section list
   updates instantly when toggles change (fixes stale useMemo deps).
3. Standards: Switch component replaces raw checkboxes, data-testid on
   every toggle (feature-toggle-{id}, feature-toggle-dev-global).
4. Tests: 11 unit tests for resolveEnabled covering all tiers + edge cases.
5. Architecture: @features-manifest Vite alias replaces fragile ../../../../
   relative import. Test loader also resolves the alias.
6. Versioned localStorage: keys now use -v1 suffix aligned to manifest version.
7. Cache: JSON.parse result cached across hook instances per render cycle.
8. Dev warning: console.warn in dev when getFeature() returns undefined.

Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
2026-06-08 13:02:58 -07:00

31 lines
872 B
TypeScript

import type { FeatureTier } from "./types";
/**
* Pure resolution logic for feature visibility.
* No side effects, no imports beyond types — safe to test in isolation.
*
* @param isDev - Whether the current build is a dev build.
* Defaults to `import.meta.env?.DEV ?? false` for runtime use.
*/
export function resolveEnabled(
tier: FeatureTier,
featureId: string,
overrides: Record<string, boolean>,
devToggle: boolean,
isDev: boolean = (import.meta.env?.DEV as boolean) ?? false,
): boolean {
switch (tier) {
case "stable":
return true;
case "experimental":
return overrides[featureId] === true;
case "dev":
if (!isDev) return false;
if (!devToggle) return false;
// Allow per-feature suppression even in dev
return overrides[featureId] !== false;
default:
return false;
}
}