From de131133bdbbe6ab1a1ad68b8d64a223ca23662b Mon Sep 17 00:00:00 2001 From: Dante Date: Sat, 21 Feb 2026 18:43:58 +0900 Subject: [PATCH] fix: make serverFeatureFlags reactive via Vue ref (#9051) --- browser_tests/tests/featureFlags.spec.ts | 27 ++++++++++------------ src/scripts/api.featureFlags.test.ts | 29 +++++++++++++++++++----- src/scripts/api.ts | 13 ++++++----- 3 files changed, 42 insertions(+), 27 deletions(-) diff --git a/browser_tests/tests/featureFlags.spec.ts b/browser_tests/tests/featureFlags.spec.ts index aec0fc5bc..3f81a924e 100644 --- a/browser_tests/tests/featureFlags.spec.ts +++ b/browser_tests/tests/featureFlags.spec.ts @@ -37,12 +37,9 @@ test.describe('Feature Flags', { tag: ['@slow', '@settings'] }, () => { // Monitor for server feature flags const checkInterval = setInterval(() => { - if ( - window.app?.api?.serverFeatureFlags && - Object.keys(window.app.api.serverFeatureFlags).length > 0 - ) { - window.__capturedMessages!.serverFeatureFlags = - window.app.api.serverFeatureFlags + const flags = window.app?.api?.serverFeatureFlags?.value + if (flags && Object.keys(flags).length > 0) { + window.__capturedMessages!.serverFeatureFlags = flags clearInterval(checkInterval) } }, 100) @@ -96,7 +93,7 @@ test.describe('Feature Flags', { tag: ['@slow', '@settings'] }, () => { }) => { // Get the actual server feature flags from the backend const serverFlags = await comfyPage.page.evaluate(() => { - return window.app!.api.serverFeatureFlags + return window.app!.api.serverFeatureFlags.value }) // Verify we received real feature flags from the backend @@ -129,8 +126,8 @@ test.describe('Feature Flags', { tag: ['@slow', '@settings'] }, () => { // Test that the method only returns true for boolean true values const testResults = await comfyPage.page.evaluate(() => { // Temporarily modify serverFeatureFlags to test behavior - const original = window.app!.api.serverFeatureFlags - window.app!.api.serverFeatureFlags = { + const original = window.app!.api.serverFeatureFlags.value + window.app!.api.serverFeatureFlags.value = { bool_true: true, bool_false: false, string_value: 'yes', @@ -147,7 +144,7 @@ test.describe('Feature Flags', { tag: ['@slow', '@settings'] }, () => { } // Restore original - window.app!.api.serverFeatureFlags = original + window.app!.api.serverFeatureFlags.value = original return results }) @@ -282,8 +279,8 @@ test.describe('Feature Flags', { tag: ['@slow', '@settings'] }, () => { // Monitor when feature flags arrive by checking periodically const checkFeatureFlags = setInterval(() => { if ( - window.app?.api?.serverFeatureFlags?.supports_preview_metadata !== - undefined + window.app?.api?.serverFeatureFlags?.value + ?.supports_preview_metadata !== undefined ) { window.__appReadiness!.featureFlagsReceived = true clearInterval(checkFeatureFlags) @@ -320,8 +317,8 @@ test.describe('Feature Flags', { tag: ['@slow', '@settings'] }, () => { // Wait for feature flags to be received await newPage.waitForFunction( () => - window.app?.api?.serverFeatureFlags?.supports_preview_metadata !== - undefined, + window.app?.api?.serverFeatureFlags?.value + ?.supports_preview_metadata !== undefined, { timeout: 10000 } @@ -331,7 +328,7 @@ test.describe('Feature Flags', { tag: ['@slow', '@settings'] }, () => { const readiness = await newPage.evaluate(() => { return { ...window.__appReadiness, - currentFlags: window.app!.api.serverFeatureFlags + currentFlags: window.app!.api.serverFeatureFlags.value } }) diff --git a/src/scripts/api.featureFlags.test.ts b/src/scripts/api.featureFlags.test.ts index 970521c7e..adc8d932d 100644 --- a/src/scripts/api.featureFlags.test.ts +++ b/src/scripts/api.featureFlags.test.ts @@ -1,5 +1,6 @@ import type { Mock } from 'vitest' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { computed, nextTick } from 'vue' import { api } from '@/scripts/api' @@ -38,7 +39,7 @@ describe('API Feature Flags', () => { }) // Reset API state - api.serverFeatureFlags = {} + api.serverFeatureFlags.value = {} // Mock getClientFeatureFlags to return test feature flags vi.spyOn(api, 'getClientFeatureFlags').mockReturnValue({ @@ -102,7 +103,7 @@ describe('API Feature Flags', () => { await initPromise // Check that server features were stored - expect(api.serverFeatureFlags).toEqual({ + expect(api.serverFeatureFlags.value).toEqual({ supports_preview_metadata: true, async_execution: true, supported_formats: ['webp', 'jpeg', 'png'], @@ -144,14 +145,14 @@ describe('API Feature Flags', () => { await initPromise // Server features should remain empty - expect(api.serverFeatureFlags).toEqual({}) + expect(api.serverFeatureFlags.value).toEqual({}) }) }) describe('Feature checking methods', () => { beforeEach(() => { // Set up some test features - api.serverFeatureFlags = { + api.serverFeatureFlags.value = { supports_preview_metadata: true, async_execution: false, capabilities: ['isolated_nodes', 'dynamic_models'] @@ -208,12 +209,28 @@ describe('API Feature Flags', () => { describe('Integration with preview messages', () => { it('should affect preview message handling based on feature support', () => { // Test with metadata support - api.serverFeatureFlags = { supports_preview_metadata: true } + api.serverFeatureFlags.value = { supports_preview_metadata: true } expect(api.serverSupportsFeature('supports_preview_metadata')).toBe(true) // Test without metadata support - api.serverFeatureFlags = {} + api.serverFeatureFlags.value = {} expect(api.serverSupportsFeature('supports_preview_metadata')).toBe(false) }) }) + + describe('Reactivity', () => { + it('should trigger computed updates when serverFeatureFlags changes', async () => { + api.serverFeatureFlags.value = {} + + const flag = computed(() => + api.getServerFeature('supports_preview_metadata', false) + ) + expect(flag.value).toBe(false) + + api.serverFeatureFlags.value = { supports_preview_metadata: true } + await nextTick() + + expect(flag.value).toBe(true) + }) + }) }) diff --git a/src/scripts/api.ts b/src/scripts/api.ts index c62a19d59..557d36b8e 100644 --- a/src/scripts/api.ts +++ b/src/scripts/api.ts @@ -2,6 +2,7 @@ import { promiseTimeout, until } from '@vueuse/core' import axios from 'axios' import { get } from 'es-toolkit/compat' import { trimEnd } from 'es-toolkit' +import { ref } from 'vue' import defaultClientFeatureFlags from '@/config/clientFeatureFlags.json' with { type: 'json' } import type { @@ -340,7 +341,7 @@ export class ComfyApi extends EventTarget { /** * Feature flags received from the backend server. */ - serverFeatureFlags: Record = {} + serverFeatureFlags = ref>({}) /** * The auth token for the comfy org account if the user is logged in. @@ -695,10 +696,10 @@ export class ComfyApi extends EventTarget { break case 'feature_flags': // Store server feature flags - this.serverFeatureFlags = msg.data + this.serverFeatureFlags.value = msg.data console.log( 'Server feature flags received:', - this.serverFeatureFlags + this.serverFeatureFlags.value ) this.dispatchCustomEvent('feature_flags', msg.data) break @@ -1291,7 +1292,7 @@ export class ComfyApi extends EventTarget { * @returns true if the feature is supported, false otherwise */ serverSupportsFeature(featureName: string): boolean { - return get(this.serverFeatureFlags, featureName) === true + return get(this.serverFeatureFlags.value, featureName) === true } /** @@ -1301,7 +1302,7 @@ export class ComfyApi extends EventTarget { * @returns The feature value or default */ getServerFeature(featureName: string, defaultValue?: T): T { - return get(this.serverFeatureFlags, featureName, defaultValue) as T + return get(this.serverFeatureFlags.value, featureName, defaultValue) as T } /** @@ -1309,7 +1310,7 @@ export class ComfyApi extends EventTarget { * @returns Copy of all server feature flags */ getServerFeatures(): Record { - return { ...this.serverFeatureFlags } + return { ...this.serverFeatureFlags.value } } async getFuseOptions(): Promise | null> {