mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-09 23:20:04 +00:00
feat: add dev-time feature flag overrides via localStorage (#9075)
## Summary
- Adds `localStorage`-based dev-time override for feature flags, with
`ff:` key prefix (e.g.
`localStorage.setItem('ff:team_workspaces_enabled', 'true')`)
- Override priority: dev localStorage > remoteConfig >
serverFeatureFlags
- Guarded by `import.meta.env.DEV` — tree-shaken to empty function in
production builds
- Extracts `resolveFlag` helper in `useFeatureFlags` to eliminate
repeated fallback pattern
Fixes #9054
## Test plan
- [x] `getDevOverride` unit tests: boolean/number/string/object parsing,
prefix isolation, invalid JSON warning
- [x] `api.getServerFeature` / `serverSupportsFeature` override tests
- [x] `useFeatureFlags` override priority tests, including
`teamWorkspacesEnabled` bypassing guards
- [x] Production build verified: `getDevOverride` compiles to empty
function body, localStorage never accessed
- [x] `pnpm typecheck`, `pnpm lint` clean
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9075-feat-add-dev-time-feature-flag-overrides-via-localStorage-30f6d73d365081b394d3ccc461987b1a)
by [Unito](https://www.unito.io)
This commit is contained in:
@@ -233,4 +233,37 @@ describe('API Feature Flags', () => {
|
||||
expect(flag.value).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('Dev override via localStorage', () => {
|
||||
afterEach(() => {
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
it('getServerFeature returns localStorage override over server value', () => {
|
||||
api.serverFeatureFlags.value = { some_flag: false }
|
||||
localStorage.setItem('ff:some_flag', 'true')
|
||||
|
||||
expect(api.getServerFeature('some_flag')).toBe(true)
|
||||
})
|
||||
|
||||
it('serverSupportsFeature returns localStorage override over server value', () => {
|
||||
api.serverFeatureFlags.value = { some_flag: false }
|
||||
localStorage.setItem('ff:some_flag', 'true')
|
||||
|
||||
expect(api.serverSupportsFeature('some_flag')).toBe(true)
|
||||
})
|
||||
|
||||
it('getServerFeature falls through when no override is set', () => {
|
||||
api.serverFeatureFlags.value = { some_flag: 'server_value' }
|
||||
|
||||
expect(api.getServerFeature('some_flag')).toBe('server_value')
|
||||
})
|
||||
|
||||
it('getServerFeature override works with numeric values', () => {
|
||||
api.serverFeatureFlags.value = { max_upload_size: 100 }
|
||||
localStorage.setItem('ff:max_upload_size', '999')
|
||||
|
||||
expect(api.getServerFeature('max_upload_size')).toBe(999)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -5,6 +5,7 @@ import { trimEnd } from 'es-toolkit'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import defaultClientFeatureFlags from '@/config/clientFeatureFlags.json' with { type: 'json' }
|
||||
import { getDevOverride } from '@/utils/devFeatureFlagOverride'
|
||||
import type {
|
||||
ModelFile,
|
||||
ModelFolderInfo
|
||||
@@ -1299,6 +1300,8 @@ export class ComfyApi extends EventTarget {
|
||||
* @returns true if the feature is supported, false otherwise
|
||||
*/
|
||||
serverSupportsFeature(featureName: string): boolean {
|
||||
const override = getDevOverride<boolean>(featureName)
|
||||
if (override !== undefined) return override
|
||||
return get(this.serverFeatureFlags.value, featureName) === true
|
||||
}
|
||||
|
||||
@@ -1309,6 +1312,8 @@ export class ComfyApi extends EventTarget {
|
||||
* @returns The feature value or default
|
||||
*/
|
||||
getServerFeature<T = unknown>(featureName: string, defaultValue?: T): T {
|
||||
const override = getDevOverride<T>(featureName)
|
||||
if (override !== undefined) return override
|
||||
return get(this.serverFeatureFlags.value, featureName, defaultValue) as T
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user