mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-23 07:50:15 +00:00
refactor: extract early bootstrap logic to bootstrapStore
- Add useBootstrapStore to centralize early initialization (api.init, fetchNodeDefs) - Move settings loading and custom nodes i18n loading to store bootstrap phase - Use VueUse's `until` to coordinate async dependencies in GraphCanvas - Load settings, i18n, and newUserService initialization in parallel where possible - Add unit tests for bootstrapStore Amp-Thread-ID: https://ampcode.com/threads/T-019bf48d-af90-738f-99ce-46309e4be688 Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
68
src/stores/bootstrapStore.test.ts
Normal file
68
src/stores/bootstrapStore.test.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { createPinia, setActivePinia } from 'pinia'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { useBootstrapStore } from './bootstrapStore'
|
||||
|
||||
vi.mock('@/scripts/api', () => ({
|
||||
api: {
|
||||
init: vi.fn().mockResolvedValue(undefined),
|
||||
getNodeDefs: vi.fn().mockResolvedValue({ TestNode: { name: 'TestNode' } }),
|
||||
getCustomNodesI18n: vi.fn().mockResolvedValue({}),
|
||||
getUserConfig: vi.fn().mockResolvedValue({})
|
||||
}
|
||||
}))
|
||||
|
||||
vi.mock('@/i18n', () => ({
|
||||
mergeCustomNodesI18n: vi.fn()
|
||||
}))
|
||||
|
||||
vi.mock('@/platform/settings/settingStore', () => ({
|
||||
useSettingStore: vi.fn(() => ({
|
||||
loadSettingValues: vi.fn().mockResolvedValue(undefined)
|
||||
}))
|
||||
}))
|
||||
|
||||
vi.mock('@/stores/workspaceStore', () => ({
|
||||
useWorkspaceStore: vi.fn(() => ({
|
||||
workflow: {
|
||||
syncWorkflows: vi.fn().mockResolvedValue(undefined)
|
||||
}
|
||||
}))
|
||||
}))
|
||||
|
||||
describe('bootstrapStore', () => {
|
||||
let store: ReturnType<typeof useBootstrapStore>
|
||||
|
||||
beforeEach(() => {
|
||||
setActivePinia(createPinia())
|
||||
store = useBootstrapStore()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
it('initializes with all flags false', () => {
|
||||
expect(store.isNodeDefsReady).toBe(false)
|
||||
expect(store.isSettingsReady).toBe(false)
|
||||
expect(store.isI18nReady).toBe(false)
|
||||
})
|
||||
|
||||
it('starts early bootstrap (node defs)', async () => {
|
||||
const { api } = await import('@/scripts/api')
|
||||
|
||||
store.startEarlyBootstrap()
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(store.isNodeDefsReady).toBe(true)
|
||||
})
|
||||
|
||||
expect(api.getNodeDefs).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('starts store bootstrap (settings, i18n)', async () => {
|
||||
void store.startStoreBootstrap()
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(store.isSettingsReady).toBe(true)
|
||||
expect(store.isI18nReady).toBe(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
112
src/stores/bootstrapStore.ts
Normal file
112
src/stores/bootstrapStore.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
import { useAsyncState } from '@vueuse/core'
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
import type { ComfyNodeDef } from '@/schemas/nodeDefSchema'
|
||||
import { api } from '@/scripts/api'
|
||||
import { useUserStore } from '@/stores/userStore'
|
||||
|
||||
export const useBootstrapStore = defineStore('bootstrap', () => {
|
||||
const {
|
||||
state: nodeDefs,
|
||||
isReady: isNodeDefsReady,
|
||||
error: nodeDefsError,
|
||||
execute: fetchNodeDefs
|
||||
} = useAsyncState<Record<string, ComfyNodeDef>>(
|
||||
async () => {
|
||||
const defs = await api.getNodeDefs()
|
||||
return defs
|
||||
},
|
||||
{},
|
||||
{ immediate: false }
|
||||
)
|
||||
|
||||
const {
|
||||
isReady: isSettingsReady,
|
||||
isLoading: isSettingsLoading,
|
||||
error: settingsError,
|
||||
execute: executeLoadSettings
|
||||
} = useAsyncState(
|
||||
async () => {
|
||||
const { useSettingStore } =
|
||||
await import('@/platform/settings/settingStore')
|
||||
await useSettingStore().loadSettingValues()
|
||||
},
|
||||
undefined,
|
||||
{ immediate: false }
|
||||
)
|
||||
|
||||
function loadSettings() {
|
||||
// TODO: This check makes the store "sticky" across logouts. Add a reset
|
||||
// method to clear isSettingsReady, then replace window.location.reload()
|
||||
// with router.push() in SidebarLogoutIcon.vue
|
||||
if (!isSettingsReady.value && !isSettingsLoading.value) {
|
||||
void executeLoadSettings()
|
||||
}
|
||||
}
|
||||
|
||||
const {
|
||||
isReady: isI18nReady,
|
||||
error: i18nError,
|
||||
execute: loadI18n
|
||||
} = useAsyncState(
|
||||
async () => {
|
||||
const { mergeCustomNodesI18n } = await import('@/i18n')
|
||||
const i18nData = await api.getCustomNodesI18n()
|
||||
mergeCustomNodesI18n(i18nData)
|
||||
},
|
||||
undefined,
|
||||
{ immediate: false }
|
||||
)
|
||||
|
||||
const {
|
||||
isReady: isWorkflowsReady,
|
||||
isLoading: isWorkflowsLoading,
|
||||
execute: executeSyncWorkflows
|
||||
} = useAsyncState(
|
||||
async () => {
|
||||
const { useWorkspaceStore } = await import('@/stores/workspaceStore')
|
||||
await useWorkspaceStore().workflow.syncWorkflows()
|
||||
},
|
||||
undefined,
|
||||
{ immediate: false }
|
||||
)
|
||||
|
||||
function syncWorkflows() {
|
||||
if (!isWorkflowsReady.value && !isWorkflowsLoading.value) {
|
||||
void executeSyncWorkflows()
|
||||
}
|
||||
}
|
||||
|
||||
function startEarlyBootstrap() {
|
||||
void fetchNodeDefs()
|
||||
}
|
||||
|
||||
async function startStoreBootstrap() {
|
||||
// Defer settings and workflows if multi-user login is required
|
||||
// (settings API requires authentication in multi-user mode)
|
||||
const userStore = useUserStore()
|
||||
await userStore.initialize()
|
||||
|
||||
// i18n can load without authentication
|
||||
void loadI18n()
|
||||
|
||||
if (!userStore.needsLogin) {
|
||||
loadSettings()
|
||||
syncWorkflows()
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
nodeDefs,
|
||||
isNodeDefsReady,
|
||||
nodeDefsError,
|
||||
isSettingsReady,
|
||||
settingsError,
|
||||
isI18nReady,
|
||||
i18nError,
|
||||
startEarlyBootstrap,
|
||||
startStoreBootstrap,
|
||||
loadSettings,
|
||||
syncWorkflows
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user