Some checks are pending
Veza CI / Backend (Go) (push) Waiting to run
Veza CI / Frontend (Web) (push) Waiting to run
Veza CI / Rust (Stream Server) (push) Waiting to run
Veza CI / Notify on failure (push) Blocked by required conditions
E2E Playwright / e2e (full) (push) Waiting to run
Security Scan / Secret Scanning (gitleaks) (push) Waiting to run
Backend default was flipped to HLS_STREAMING=true on Day 17 of the
v1.0.9 sprint (config.go:418), and docker-compose.{prod,staging}.yml
already pass HLS_STREAMING=true to the backend service. The frontend
feature flag in apps/web/src/config/features.ts kept the old `false`
default with a stale comment about matching the backend — so HLS
playback was silently skipped on every deploy that didn't override
VITE_FEATURE_HLS_STREAMING=true.
Net effect: useAudioPlayerLifecycle treated `FEATURES.HLS_STREAMING`
as false → fell through to the MP3 range fallback even when the
transcoder had segments ready. Adaptive bitrate was on paper, off in
practice.
Flipped the default to true with a refreshed comment. Operators can
still set VITE_FEATURE_HLS_STREAMING=false for unit tests or
playback-regression bisection.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
108 lines
2.9 KiB
TypeScript
108 lines
2.9 KiB
TypeScript
/**
|
|
* Feature Flags Configuration
|
|
*
|
|
* Controls which features are enabled/disabled for MVP.
|
|
* Features marked as false are not yet implemented in the backend.
|
|
*
|
|
* All flags can be overridden via VITE_FEATURE_* env vars (see .env.example).
|
|
* Feature status is documented in docs/FEATURE_STATUS.md
|
|
*/
|
|
|
|
function parseFeatureEnv(value: string | undefined, defaultValue: boolean): boolean {
|
|
if (value === undefined || value === '') return defaultValue;
|
|
const v = value.toLowerCase().trim();
|
|
return v === 'true' || v === '1' || v === 'yes';
|
|
}
|
|
|
|
export const FEATURES = {
|
|
/**
|
|
* Two-Factor Authentication
|
|
* Backend endpoints: /auth/2fa/setup, /auth/2fa/verify, /auth/2fa/disable, /auth/2fa/status
|
|
*/
|
|
TWO_FACTOR_AUTH: parseFeatureEnv(
|
|
import.meta.env.VITE_FEATURE_TWO_FACTOR_AUTH,
|
|
true,
|
|
),
|
|
|
|
/**
|
|
* Playlist Collaboration Features
|
|
* Backend endpoints: /playlists/:id/collaborators (GET, POST, PUT, DELETE)
|
|
*/
|
|
PLAYLIST_COLLABORATION: parseFeatureEnv(
|
|
import.meta.env.VITE_FEATURE_PLAYLIST_COLLABORATION,
|
|
true,
|
|
),
|
|
PLAYLIST_SEARCH: parseFeatureEnv(
|
|
import.meta.env.VITE_FEATURE_PLAYLIST_SEARCH,
|
|
true,
|
|
),
|
|
PLAYLIST_SHARE: parseFeatureEnv(
|
|
import.meta.env.VITE_FEATURE_PLAYLIST_SHARE,
|
|
true,
|
|
),
|
|
PLAYLIST_RECOMMENDATIONS: parseFeatureEnv(
|
|
import.meta.env.VITE_FEATURE_PLAYLIST_RECOMMENDATIONS,
|
|
true,
|
|
),
|
|
|
|
/**
|
|
* HLS Streaming
|
|
* Backend endpoints: /api/v1/tracks/:id/hls/info, /api/v1/tracks/:id/hls/status
|
|
*
|
|
* Default flipped to `true` in v1.0.10 polish to match backend
|
|
* `HLS_STREAMING=true` (Day 17 of the v1.0.9 sprint). Adaptive
|
|
* bitrate via HLS is the canonical playback path; MP3 range
|
|
* requests via `/api/v1/tracks/:id/stream` remain a fallback when
|
|
* the browser can't play HLS or the transcoder hasn't produced
|
|
* segments yet.
|
|
*
|
|
* Set VITE_FEATURE_HLS_STREAMING=false to opt out (unit-test envs
|
|
* without a transcoder, or to bisect playback regressions).
|
|
*/
|
|
HLS_STREAMING: parseFeatureEnv(
|
|
import.meta.env.VITE_FEATURE_HLS_STREAMING,
|
|
true,
|
|
),
|
|
|
|
/**
|
|
* Role Management
|
|
* Backend endpoints: /api/v1/users/:userId/roles, /api/v1/roles/* (NOT IMPLEMENTED)
|
|
*/
|
|
ROLE_MANAGEMENT: parseFeatureEnv(
|
|
import.meta.env.VITE_FEATURE_ROLE_MANAGEMENT,
|
|
true,
|
|
),
|
|
|
|
/**
|
|
* Notifications API
|
|
* Backend endpoints: /api/v1/notifications/*
|
|
*/
|
|
NOTIFICATIONS: parseFeatureEnv(
|
|
import.meta.env.VITE_FEATURE_NOTIFICATIONS,
|
|
true,
|
|
),
|
|
|
|
} as const;
|
|
|
|
/**
|
|
* Type for feature flags
|
|
*/
|
|
export type FeatureFlag = keyof typeof FEATURES;
|
|
|
|
/**
|
|
* Check if a feature is enabled
|
|
*/
|
|
export function isFeatureEnabled(feature: FeatureFlag): boolean {
|
|
return Boolean(FEATURES[feature]);
|
|
}
|
|
|
|
/**
|
|
* Assert that a feature is enabled, throw error if not
|
|
*/
|
|
export function requireFeature(feature: FeatureFlag): void {
|
|
if (!isFeatureEnabled(feature)) {
|
|
throw new Error(
|
|
`Feature "${feature}" is not enabled. This feature is not available in the MVP.`,
|
|
);
|
|
}
|
|
}
|