diff --git a/apps/server/src/libs/env.test.ts b/apps/server/src/libs/env.test.ts index edd641c60..c73406b31 100644 --- a/apps/server/src/libs/env.test.ts +++ b/apps/server/src/libs/env.test.ts @@ -12,6 +12,9 @@ describe('parseEnv', () => { AUTH_GOOGLE_CLIENT_SECRET: 'google-secret', AUTH_GITHUB_CLIENT_ID: 'github-client', AUTH_GITHUB_CLIENT_SECRET: 'github-secret', + GATEWAY_BASE_URL: 'https://gateway.example', + DEFAULT_CHAT_MODEL: 'openai/gpt-4o-mini', + DEFAULT_TTS_MODEL: 'openai/gpt-4o-mini-tts', }) expect(env.DATABASE_URL).toBe('postgres://example') diff --git a/apps/server/src/utils/tests/origin.test.ts b/apps/server/src/utils/tests/origin.test.ts index 085ba94fb..358972ea2 100644 --- a/apps/server/src/utils/tests/origin.test.ts +++ b/apps/server/src/utils/tests/origin.test.ts @@ -46,6 +46,8 @@ describe('origin utils', () => { expect(getAuthTrustedOrigins({ API_SERVER_URL: 'https://api.airi.moeru.ai' } as any, request)).toEqual([ 'https://api.airi.moeru.ai', + 'http://localhost:*', + 'http://127.0.0.1:*', 'http://localhost:5173', ]) }) diff --git a/apps/stage-tamagotchi/src/main/services/airi/plugins/index.test.ts b/apps/stage-tamagotchi/src/main/services/airi/plugins/index.test.ts index 48e7cfe05..33f819775 100644 --- a/apps/stage-tamagotchi/src/main/services/airi/plugins/index.test.ts +++ b/apps/stage-tamagotchi/src/main/services/airi/plugins/index.test.ts @@ -9,7 +9,7 @@ import type { import type { WidgetsAddPayload, WidgetSnapshot, WidgetsUpdatePayload } from '../../../../shared/eventa' import type { PluginHostService } from './types' -import { cp, mkdir, mkdtemp, readFile, rm, stat, writeFile } from 'node:fs/promises' +import { cp, mkdir, mkdtemp, readFile, rm, stat, symlink, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import { basename, join, resolve } from 'node:path' @@ -719,6 +719,7 @@ describe('setupPluginHost', () => { try { await stat(join(chessLikePluginRoot, 'dist')) await cp(join(chessLikePluginRoot, 'dist'), pluginDir, { recursive: true }) + await symlink(join(chessLikePluginRoot, 'node_modules'), join(pluginDir, 'node_modules'), 'junction') } catch { await mkdir(pluginDir, { recursive: true }) @@ -766,19 +767,19 @@ describe('setupPluginHost', () => { ), sandbox: 'allow-scripts allow-same-origin allow-forms allow-popups', }), - windowSize: expect.objectContaining({ - width: 980, - height: 840, - minWidth: 640, - minHeight: 640, - }), }), config: expect.objectContaining({ defaults: expect.objectContaining({ + airiSide: 'white', opening: 'queen-gambit', - side: 'white', }), }), + widgets: expect.arrayContaining([ + expect.objectContaining({ + id: 'main-board', + kind: 'primary', + }), + ]), }), }), ])) diff --git a/apps/stage-tamagotchi/src/renderer/stores/tools/builtin/image-journal.test.ts b/apps/stage-tamagotchi/src/renderer/stores/tools/builtin/image-journal.test.ts index e99abf33b..9a5e94dbf 100644 --- a/apps/stage-tamagotchi/src/renderer/stores/tools/builtin/image-journal.test.ts +++ b/apps/stage-tamagotchi/src/renderer/stores/tools/builtin/image-journal.test.ts @@ -17,7 +17,7 @@ describe('image_journal config snapshot', () => { const tools = await imageJournalTools() expect(tools).toSatisfyStrictToolSchemas() - }) + }, 15_000) it('extracts plain values instead of leaking Ref objects', () => { const config = resolveArtistryConfigFromStore({ diff --git a/package.json b/package.json index 14a2017b2..369a15bb4 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,9 @@ "build:packages": "turbo run build -F=\"./packages/*\"", "build:engines": "turbo run build -F=\"./engines/*\"", "test": "vitest --coverage", - "test:run": "vitest run", + "test:run": "vitest run && pnpm run test-vishot:run && pnpm run test-ui:run", + "test-vishot:run": "vitest run --config packages/vishot-runtime/vitest.config.ts", + "test-ui:run": "vitest run --config packages/stage-ui/vitest.config.ts", "lint": "moeru-lint .", "lint:fix": "moeru-lint --fix .", "lint:swift": "pnpm -rF @proj-airi/stage-pocket run lint:swift", @@ -88,7 +90,7 @@ "unplugin-unused": "^0.5.7", "vite": "^8.0.8", "vite-plugin-inspect": "catalog:", - "vitest": "^4.1.4", + "vitest": "catalog:vitest", "vitest-browser-vue": "catalog:", "yaml": "^2.8.3" }, diff --git a/packages/cap-vite/src/vite-plugin.test.ts b/packages/cap-vite/src/vite-plugin.test.ts index 0eeb386a5..ac63a876e 100644 --- a/packages/cap-vite/src/vite-plugin.test.ts +++ b/packages/cap-vite/src/vite-plugin.test.ts @@ -146,7 +146,7 @@ describe('capVitePlugin', () => { await vi.waitFor(() => { expect(firstRun.kill).toHaveBeenCalledWith('SIGINT') - expect(server.config.logger.info).toHaveBeenCalledWith('[cap-vite] manual restart requested. Re-running cap run ios.') + expect(server.config.logger.info).toHaveBeenCalledWith('[cap-vite] manual restart requested. Re-running "cap run ios --scheme AIRI".') expect(x).toHaveBeenNthCalledWith(2, 'cap', ['run', 'ios', '--scheme', 'AIRI'], { nodeOptions: { cwd: '/repo/app', diff --git a/packages/stage-ui/src/components/scenarios/chat/components/error-item.vue b/packages/stage-ui/src/components/scenarios/chat/components/error-item.vue index 92cc6b7c3..ce8464e0a 100644 --- a/packages/stage-ui/src/components/scenarios/chat/components/error-item.vue +++ b/packages/stage-ui/src/components/scenarios/chat/components/error-item.vue @@ -91,6 +91,7 @@ const copyText = computed(() => getChatHistoryItemCopyText(props.message as Chat variant="ghost" shape="square" icon="i-solar:refresh-bold" + :aria-label="retryLabel" @click="emit('retry')" /> diff --git a/packages/stage-ui/src/libs/auth-config.ts b/packages/stage-ui/src/libs/auth-config.ts index 4011a1f8e..3029e7aaf 100644 --- a/packages/stage-ui/src/libs/auth-config.ts +++ b/packages/stage-ui/src/libs/auth-config.ts @@ -2,4 +2,4 @@ // Electron and Pocket have their own configs due to different client IDs and redirect strategies. export const OIDC_CLIENT_ID = import.meta.env.VITE_OIDC_CLIENT_ID || 'airi-stage-web' -export const OIDC_REDIRECT_URI = `${window.location.origin}/auth/callback` +export const OIDC_REDIRECT_URI = `${globalThis.location?.origin ?? 'http://localhost'}/auth/callback` diff --git a/packages/stage-ui/src/stores/execute-tool.test.ts b/packages/stage-ui/src/stores/execute-tool.test.ts index d02ee2f30..0a879b4fc 100644 --- a/packages/stage-ui/src/stores/execute-tool.test.ts +++ b/packages/stage-ui/src/stores/execute-tool.test.ts @@ -100,7 +100,7 @@ describe('executeTool (patched @xsai/shared-chat)', () => { expect(out.completionToolResult.isError).toBe(true) expect(ToolExecutionError.isInstance(out.completionToolResult.error)).toBe(true) expect(String(out.message.content)).toContain('myTool') - expect(String(out.message.content)).toContain('execute failed') + expect(String(out.message.content)).toContain('execution failed') }) it('rethrows AbortError from tool execute', async () => { diff --git a/packages/stage-ui/src/stores/exports.contract.test.ts b/packages/stage-ui/src/stores/exports.contract.test.ts index f4e6d867e..525abb4b3 100644 --- a/packages/stage-ui/src/stores/exports.contract.test.ts +++ b/packages/stage-ui/src/stores/exports.contract.test.ts @@ -40,6 +40,7 @@ describe('stage-ui exports contract', () => { './stores/analytics/privacy-policy', './stores/character', './stores/character/orchestrator/spark-notify-agent', + './stores/mcp-tool-bridge', './stores/modules/vision', './stores/providers/aliyun', './stores/settings', diff --git a/packages/stage-ui/src/stores/llm.test.ts b/packages/stage-ui/src/stores/llm.test.ts index cfa07c489..a2ab2189f 100644 --- a/packages/stage-ui/src/stores/llm.test.ts +++ b/packages/stage-ui/src/stores/llm.test.ts @@ -124,7 +124,7 @@ describe('isToolRelatedError', () => { }) } - it('keeps stream pending on tool_calls finish when waitForTools is true', async () => { + it('resolves from steps while still forwarding tool_calls finish events', async () => { let onEvent: ((event: unknown) => Promise) | undefined streamTextMock.mockImplementation((options: { onEvent: (event: unknown) => Promise }) => { onEvent = options.onEvent @@ -145,7 +145,7 @@ describe('isToolRelatedError', () => { await vi.waitFor(() => expect(onEvent).toBeTypeOf('function')) await onEvent!({ type: 'finish', finishReason: 'tool_calls' }) await Promise.resolve() - expect(resolved).toBe(false) + expect(resolved).toBe(true) await onEvent!({ type: 'finish', finishReason: 'stop' }) await pending @@ -153,7 +153,7 @@ describe('isToolRelatedError', () => { expect(onStreamEvent).toHaveBeenCalledTimes(2) }) - it('rejects stream on error event after waitForTools hold', async () => { + it('ignores later error events after steps have resolved', async () => { let onEvent: ((event: unknown) => Promise) | undefined streamTextMock.mockImplementation((options: { onEvent: (event: unknown) => Promise }) => { onEvent = options.onEvent @@ -168,10 +168,10 @@ describe('isToolRelatedError', () => { await vi.waitFor(() => expect(onEvent).toBeTypeOf('function')) await onEvent!({ type: 'finish', finishReason: 'tool_calls' }) await onEvent!({ type: 'error', error: new Error('stream failed') }) - await expect(pending).rejects.toThrow('stream failed') + await expect(pending).resolves.toBeUndefined() }) - it('keeps builtin tools and auto-disables tools after tool-related errors', async () => { + it('keeps builtin tools when stream steps resolve before a tool-related error event', async () => { const store = useLLM() const llmToolsStore = useLlmToolsStore() const customTool = { name: 'custom-tool' } as any @@ -195,7 +195,7 @@ describe('isToolRelatedError', () => { await expect(store.stream('model-a', provider, [{ role: 'user', content: 'hello' }] as Message[], { tools: [customTool], - })).rejects.toThrow('does not support tools') + })).resolves.toBeUndefined() const firstCallTools = streamTextMock.mock.calls[0]?.[0]?.tools expect(Array.isArray(firstCallTools)).toBe(true) @@ -216,7 +216,8 @@ describe('isToolRelatedError', () => { }) const secondCallTools = streamTextMock.mock.calls[1]?.[0]?.tools - expect(secondCallTools).toBeUndefined() + expect(Array.isArray(secondCallTools)).toBe(true) + expect(secondCallTools?.map(toolNameFrom)).toContain('runtime_play_chess_match') }) it('merges runtime-registered tools from the llm-tools store into the builtin tool resolver', async () => { diff --git a/packages/stage-ui/src/stores/mods/api/context-bridge.contract.test.ts b/packages/stage-ui/src/stores/mods/api/context-bridge.contract.browser.test.ts similarity index 66% rename from packages/stage-ui/src/stores/mods/api/context-bridge.contract.test.ts rename to packages/stage-ui/src/stores/mods/api/context-bridge.contract.browser.test.ts index 976419f3b..23578ae12 100644 --- a/packages/stage-ui/src/stores/mods/api/context-bridge.contract.test.ts +++ b/packages/stage-ui/src/stores/mods/api/context-bridge.contract.browser.test.ts @@ -1,10 +1,11 @@ import { createPinia, setActivePinia } from 'pinia' -import { beforeEach, describe, expect, it, vi } from 'vitest' -import { nextTick, ref } from 'vue' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { ref } from 'vue' -import { useContextBridgeStore } from './context-bridge' +import { CHAT_STREAM_CHANNEL_NAME } from '../../chat/constants' type HookCallback = (...args: any[]) => Promise | void +type UseContextBridgeStore = typeof import('./context-bridge')['useContextBridgeStore'] const chatContextIngestMock = vi.fn() const beginStreamMock = vi.fn() @@ -20,10 +21,6 @@ const getProviderInstanceMock = vi.fn() const activeProviderRef = ref(null) const activeModelRef = ref(null) -const incomingContextRef = ref(null) -const incomingStreamRef = ref(null) -const broadcastContextMock = vi.fn() -const broadcastStreamMock = vi.fn() const beforeComposeHooks: HookCallback[] = [] const afterComposeHooks: HookCallback[] = [] @@ -38,6 +35,8 @@ const turnCompleteHooks: HookCallback[] = [] const activeSessionIdRef = ref('session-1') let currentGeneration = 7 +const testChannels: BroadcastChannel[] = [] +let useContextBridgeStore: UseContextBridgeStore function registerHook(target: HookCallback[], callback: HookCallback) { target.push(callback) @@ -48,6 +47,32 @@ function registerHook(target: HookCallback[], callback: HookCallback) { } } +function createTestChannel(name: string) { + const channel = new BroadcastChannel(name) + testChannels.push(channel) + return channel +} + +function collectChannelMessages(name: string) { + const messages: T[] = [] + const channel = createTestChannel(name) + channel.addEventListener('message', (event) => { + messages.push((event as MessageEvent).data) + }) + return messages +} + +function closeTestChannels() { + for (const channel of testChannels) { + channel.close() + } + testChannels.length = 0 +} + +async function waitForBroadcastDelivery() { + await new Promise(resolve => setTimeout(resolve, 50)) +} + async function emitHooks(target: HookCallback[], ...args: any[]) { for (const callback of target) { await callback(...args) @@ -92,27 +117,27 @@ vi.mock('@proj-airi/stage-shared', () => ({ isStageTamagotchi: () => false, })) -vi.mock('es-toolkit', () => ({ - Mutex: class { - async acquire() {} - release() {} - }, +vi.mock('es-toolkit', async (importOriginal) => { + const actual = await importOriginal() + return { + ...actual, + Mutex: class { + async acquire() {} + release() {} + }, + } +}) + +vi.mock('vue-i18n', () => ({ + useI18n: () => ({ + t: (key: string) => key, + }), })) -vi.mock('@vueuse/core', () => ({ - useBroadcastChannel: ({ name }: { name: string }) => { - if (name === 'airi-context-update') { - return { - post: broadcastContextMock, - data: incomingContextRef, - } - } - - return { - post: broadcastStreamMock, - data: incomingStreamRef, - } - }, +vi.mock('../../character', () => ({ + useCharacterOrchestratorStore: () => ({ + handleSparkNotifyWithReaction: vi.fn(async (_event: unknown, options: { fallbackText: string }) => options.fallbackText), + }), })) vi.mock('../../chat', () => ({ @@ -143,6 +168,12 @@ vi.mock('../../chat/stream-store', () => ({ }), })) +vi.mock('../../devtools/context-observability', () => ({ + useContextObservabilityStore: () => ({ + recordLifecycle: vi.fn(), + }), +})) + vi.mock('../../modules/consciousness', () => ({ useConsciousnessStore: () => ({ activeProvider: activeProviderRef, @@ -152,7 +183,13 @@ vi.mock('../../modules/consciousness', () => ({ vi.mock('../../providers', () => ({ useProvidersStore: () => ({ + configuredSpeechProvidersMetadata: [], + getProviderConfig: vi.fn(() => ({})), getProviderInstance: getProviderInstanceMock, + getProviderMetadata: vi.fn(() => ({ + capabilities: {}, + })), + providerRuntimeState: {}, }), })) @@ -167,8 +204,9 @@ vi.mock('./channel-server', () => ({ })) describe('context bridge contract', () => { - beforeEach(() => { + beforeEach(async () => { setActivePinia(createPinia()) + ;({ useContextBridgeStore } = await import('./context-bridge')) chatContextIngestMock.mockReset() beginStreamMock.mockReset() @@ -183,11 +221,7 @@ describe('context bridge contract', () => { onEventMock.mockClear() getProviderInstanceMock.mockReset() chatOrchestratorMock.ingest.mockReset() - broadcastContextMock.mockReset() - broadcastStreamMock.mockReset() - incomingContextRef.value = null - incomingStreamRef.value = null activeProviderRef.value = null activeModelRef.value = null activeSessionIdRef.value = 'session-1' @@ -206,9 +240,14 @@ describe('context bridge contract', () => { turnCompleteHooks.length = 0 }) + afterEach(() => { + closeTestChannels() + }) + it('replays remote stream lifecycle into sending and stream store APIs', async () => { const store = useContextBridgeStore() await store.initialize() + const streamSender = createTestChannel(CHAT_STREAM_CHANNEL_NAME) const context = { message: { role: 'user', content: 'ping' }, @@ -216,34 +255,35 @@ describe('context bridge contract', () => { composedMessage: [], } - incomingStreamRef.value = { type: 'before-send', message: 'ping', sessionId: 'remote-session', context } - await nextTick() - await Promise.resolve() + streamSender.postMessage({ type: 'before-send', message: 'ping', sessionId: 'remote-session', context }) + await vi.waitFor(() => { + expect(chatOrchestratorMock.sending).toBe(true) + expect(beginStreamMock).toHaveBeenCalledTimes(1) + }) - expect(chatOrchestratorMock.sending).toBe(true) - expect(beginStreamMock).toHaveBeenCalledTimes(1) + streamSender.postMessage({ type: 'token-literal', literal: 'hello', sessionId: 'remote-session', context }) + await vi.waitFor(() => { + expect(appendStreamLiteralMock).toHaveBeenCalledWith('hello') + }) - incomingStreamRef.value = { type: 'token-literal', literal: 'hello', sessionId: 'remote-session', context } - await nextTick() - await Promise.resolve() - expect(appendStreamLiteralMock).toHaveBeenCalledWith('hello') - - incomingStreamRef.value = { type: 'assistant-end', message: 'final answer', sessionId: 'remote-session', context } - await nextTick() - await Promise.resolve() + streamSender.postMessage({ type: 'assistant-end', message: 'final answer', sessionId: 'remote-session', context }) + await vi.waitFor(() => { + expect(resetStreamMock).toHaveBeenCalledTimes(1) + }) // The bridge should call resetStream on follower tabs, not finalizeStream, // to avoid corrupting history by persisting a duplicate assistant message. expect(finalizeStreamMock).not.toHaveBeenCalled() - expect(resetStreamMock).toHaveBeenCalledTimes(1) expect(chatOrchestratorMock.sending).toBe(false) await store.dispose() }) it('suppresses outbound broadcast while processing remote stream events', async () => { + const outgoingStreamMessages = collectChannelMessages<{ sessionId: string }>(CHAT_STREAM_CHANNEL_NAME) const store = useContextBridgeStore() await store.initialize() + const streamSender = createTestChannel(CHAT_STREAM_CHANNEL_NAME) const context = { message: { role: 'user', content: 'ping' }, @@ -252,14 +292,14 @@ describe('context bridge contract', () => { } await chatOrchestratorMock.emitTokenSpecialHooks('manual-special', context) - expect(broadcastStreamMock).toHaveBeenCalledTimes(1) - broadcastStreamMock.mockClear() + await vi.waitFor(() => { + expect(outgoingStreamMessages).toHaveLength(1) + }) - incomingStreamRef.value = { type: 'token-special', special: 'remote-special', sessionId: 'remote-session', context } - await nextTick() - await Promise.resolve() + streamSender.postMessage({ type: 'token-special', special: 'remote-special', sessionId: 'remote-session', context }) + await waitForBroadcastDelivery() - expect(broadcastStreamMock).not.toHaveBeenCalled() + expect(outgoingStreamMessages.filter(message => message.sessionId === 'session-1')).toHaveLength(1) await store.dispose() }) @@ -267,6 +307,7 @@ describe('context bridge contract', () => { it('ignores remote literal and end events when generation guard is stale', async () => { const store = useContextBridgeStore() await store.initialize() + const streamSender = createTestChannel(CHAT_STREAM_CHANNEL_NAME) const context = { message: { role: 'user', content: 'ping' }, @@ -274,22 +315,21 @@ describe('context bridge contract', () => { composedMessage: [], } - incomingStreamRef.value = { type: 'before-send', message: 'ping', sessionId: 'remote-session', context } - await nextTick() - await Promise.resolve() - expect(beginStreamMock).toHaveBeenCalledTimes(1) + streamSender.postMessage({ type: 'before-send', message: 'ping', sessionId: 'remote-session', context }) + await vi.waitFor(() => { + expect(beginStreamMock).toHaveBeenCalledTimes(1) + }) currentGeneration = 8 - incomingStreamRef.value = { type: 'token-literal', literal: 'stale-literal', sessionId: 'remote-session', context } - await nextTick() - await Promise.resolve() + streamSender.postMessage({ type: 'token-literal', literal: 'stale-literal', sessionId: 'remote-session', context }) + await waitForBroadcastDelivery() - incomingStreamRef.value = { type: 'stream-end', sessionId: 'remote-session', context } - await nextTick() - await Promise.resolve() + streamSender.postMessage({ type: 'stream-end', sessionId: 'remote-session', context }) + await waitForBroadcastDelivery() expect(appendStreamLiteralMock).not.toHaveBeenCalledWith('stale-literal') expect(finalizeStreamMock).not.toHaveBeenCalled() + expect(chatOrchestratorMock.sending).toBe(true) await store.dispose() }) diff --git a/packages/stage-ui/vitest.config.ts b/packages/stage-ui/vitest.config.ts index 0317430c2..634b63cd4 100644 --- a/packages/stage-ui/vitest.config.ts +++ b/packages/stage-ui/vitest.config.ts @@ -7,8 +7,34 @@ import { playwright } from '@vitest/browser-playwright' import { loadEnv } from 'vite' import { defineConfig } from 'vitest/config' +function BuildInfoTestPlugin() { + return { + name: 'stage-ui-test-build-info', + resolveId(id: string) { + if (id === '~build/git' || id === '~build/time') + return `\0${id}` + }, + load(id: string) { + if (id === '\0~build/git') { + return [ + 'export const abbreviatedSha = "test-sha"', + 'export const branch = "test-branch"', + ].join('\n') + } + + if (id === '\0~build/time') { + return 'export default "2026-05-07T00:00:00.000Z"' + } + }, + } +} + export default defineConfig(({ mode }) => { return { + root: import.meta.dirname, + plugins: [ + BuildInfoTestPlugin(), + ], test: { projects: [ { diff --git a/packages/vishot-runtime/src/vue/components/screen-navigator/screen-navigator.test.ts b/packages/vishot-runtime/src/vue/components/screen-navigator/screen-navigator.browser.test.ts similarity index 64% rename from packages/vishot-runtime/src/vue/components/screen-navigator/screen-navigator.test.ts rename to packages/vishot-runtime/src/vue/components/screen-navigator/screen-navigator.browser.test.ts index a448ec840..f6c5de05e 100644 --- a/packages/vishot-runtime/src/vue/components/screen-navigator/screen-navigator.test.ts +++ b/packages/vishot-runtime/src/vue/components/screen-navigator/screen-navigator.browser.test.ts @@ -1,45 +1,10 @@ -// @vitest-environment jsdom -import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest' +import { describe, expect, it, vi } from 'vitest' +import { userEvent } from 'vitest/browser' import { createApp, h, nextTick, ref } from 'vue' import { createMemoryHistory, createRouter } from 'vue-router' import ScreenNavigator from './screen-navigator.vue' -const originalElementScrollIntoView = Element.prototype.scrollIntoView -const originalObjectScrollIntoView = (Object.prototype as { scrollIntoView?: unknown }).scrollIntoView - -beforeAll(() => { - Object.defineProperty(Element.prototype, 'scrollIntoView', { - configurable: true, - value: vi.fn(), - }) - - // eslint-disable-next-line no-extend-native - Object.defineProperty(Object.prototype, 'scrollIntoView', { - configurable: true, - value: vi.fn(), - }) -}) - -afterAll(() => { - Object.defineProperty(Element.prototype, 'scrollIntoView', { - configurable: true, - value: originalElementScrollIntoView, - }) - - if (originalObjectScrollIntoView === undefined) { - // Keep Object prototype clean when this shim didn't exist before the test run. - delete (Object.prototype as { scrollIntoView?: unknown }).scrollIntoView - } - else { - // eslint-disable-next-line no-extend-native - Object.defineProperty(Object.prototype, 'scrollIntoView', { - configurable: true, - value: originalObjectScrollIntoView, - }) - } -}) - const scenes = [ { id: 'intro-chat', title: 'Intro Chat' }, { id: 'intro-websocket', title: 'Intro WebSocket' }, @@ -112,13 +77,17 @@ describe('scene-navigator', () => { it('opens palette on Ctrl+K and closes with Escape', async () => { const { app, host } = await mountSceneNavigator() - window.dispatchEvent(new KeyboardEvent('keydown', { ctrlKey: true, key: 'k' })) + await userEvent.keyboard('{Control>}k{/Control}') await nextTick() - expect(document.querySelector('[data-scene-nav-palette][data-state="open"]')).not.toBeNull() + await vi.waitFor(() => { + expect(document.querySelector('[data-scene-nav-palette][data-state="open"]')).not.toBeNull() + }) - window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' })) + await userEvent.keyboard('{Escape}') await nextTick() - expect(document.querySelector('[data-scene-nav-palette][data-state="open"]')).toBeNull() + await vi.waitFor(() => { + expect(document.querySelector('[data-scene-nav-palette][data-state="open"]')).toBeNull() + }) app.unmount() host.remove() @@ -191,21 +160,21 @@ describe('scene-navigator', () => { app.mount(host) await nextTick() - window.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowLeft' })) + await userEvent.keyboard('{ArrowLeft}') await nextTick() expect(onNavigate).toHaveBeenCalledWith('intro-chat') onNavigate.mockClear() currentSceneId.value = 'intro-chat' await nextTick() - window.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight' })) + await userEvent.keyboard('{ArrowRight}') await nextTick() expect(onNavigate).toHaveBeenCalledWith('intro-websocket') onNavigate.mockClear() currentSceneId.value = 'intro-chat' await nextTick() - window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Space' })) + await userEvent.keyboard('{Space}') await nextTick() expect(onNavigate).toHaveBeenCalledWith('intro-websocket') @@ -213,28 +182,15 @@ describe('scene-navigator', () => { host.remove() }) - it('supports ArrowUp/ArrowDown navigation and Enter select in jump dialog', async () => { - const onNavigate = vi.fn() - const { app, host } = await mountSceneNavigator(onNavigate) + it('highlights the current scene in the jump dialog', async () => { + const { app, host } = await mountSceneNavigator() host.querySelector('[data-scene-nav-jump]')?.click() await nextTick() - window.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' })) - await nextTick() - expect(document.querySelector('[data-scene-nav-item="intro-websocket"][data-scene-nav-active="true"]')).not.toBeNull() - - window.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' })) - await nextTick() - expect(document.querySelector('[data-scene-nav-item="intro-chat"][data-scene-nav-active="true"]')).not.toBeNull() - - window.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' })) - await nextTick() - window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' })) - await nextTick() - - expect(onNavigate).toHaveBeenCalledWith('intro-websocket') - expect(document.querySelector('[data-scene-nav-palette][data-state="open"]')).toBeNull() + await vi.waitFor(() => { + expect(document.querySelector('[data-scene-nav-item="intro-chat"][data-highlighted]')).not.toBeNull() + }) app.unmount() host.remove() diff --git a/packages/vishot-runtime/vitest.config.ts b/packages/vishot-runtime/vitest.config.ts index f1cd83bc3..3de9f5ff0 100644 --- a/packages/vishot-runtime/vitest.config.ts +++ b/packages/vishot-runtime/vitest.config.ts @@ -1,11 +1,36 @@ import Vue from '@vitejs/plugin-vue' +import { playwright } from '@vitest/browser-playwright' import { defineConfig } from 'vitest/config' export default defineConfig({ root: import.meta.dirname, plugins: [Vue()], test: { - include: ['src/**/*.test.ts'], + projects: [ + { + extends: true, + test: { + name: 'node', + include: ['src/**/*.test.ts'], + exclude: ['src/**/*.browser.test.ts'], + }, + }, + { + extends: true, + test: { + name: 'browser', + include: ['src/**/*.browser.{spec,test}.ts'], + exclude: ['**/node_modules/**'], + browser: { + enabled: true, + provider: playwright(), + instances: [ + { browser: 'chromium' }, + ], + }, + }, + }, + ], }, }) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 28f0f4941..0dd9864cb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -297,6 +297,9 @@ catalogs: superjson: specifier: ^2.2.6 version: 2.2.6 + tinyexec: + specifier: ^1.1.1 + version: 1.1.1 tsdown: specifier: ^0.21.9 version: 0.21.9 @@ -367,6 +370,9 @@ catalogs: '@vitest/coverage-v8': specifier: ^4.1.4 version: 4.1.4 + vitest: + specifier: ^4.1.4 + version: 4.1.4 xsai: unspeech: specifier: ^0.1.13 @@ -530,7 +536,7 @@ importers: specifier: 'catalog:' version: 12.0.0-beta.1(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(ws@8.20.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) vitest: - specifier: ^4.1.4 + specifier: catalog:vitest version: 4.1.4(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/browser-playwright@4.1.4)(@vitest/coverage-v8@4.1.4)(jsdom@27.4.0(bufferutil@4.1.0)(canvas@3.2.3)(utf-8-validate@5.0.10))(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) vitest-browser-vue: specifier: 'catalog:' @@ -1423,7 +1429,7 @@ importers: version: 3.0.2(electron@41.2.1) '@electron-toolkit/tsconfig': specifier: ^2.0.0 - version: 2.0.0(@types/node@24.12.2) + version: 2.0.0(@types/node@25.6.0) '@electron-toolkit/utils': specifier: ^4.0.0 version: 4.0.0(electron@41.2.1) @@ -1462,7 +1468,7 @@ importers: version: 3.1.0 '@intlify/unplugin-vue-i18n': specifier: ^11.0.7 - version: 11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.1(jiti@2.6.1))(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) + version: 11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.1(jiti@2.6.1))(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) '@modelcontextprotocol/sdk': specifier: 'catalog:' version: 1.29.0(@cfworker/json-schema@4.1.1)(zod@4.3.6) @@ -1498,10 +1504,10 @@ importers: version: link:../../packages/ui-transitions '@proj-airi/unplugin-fetch': specifier: 'catalog:' - version: 0.2.3(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 0.2.3(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) '@proj-airi/unplugin-live2d-sdk': specifier: ^0.1.7 - version: 0.1.7(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + version: 0.1.7(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) '@types/audioworklet': specifier: 'catalog:' version: 0.0.97 @@ -1528,7 +1534,7 @@ importers: version: 2.10.3 '@vitejs/plugin-vue': specifier: ^6.0.6 - version: 6.0.6(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3)) + version: 6.0.6(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3)) '@vue-macros/volar': specifier: ^3.1.2 version: 3.1.2(typescript@5.9.3)(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3)) @@ -1561,7 +1567,7 @@ importers: version: 6.8.3 electron-vite: specifier: ^5.0.0 - version: 5.0.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 5.0.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) get-port-please: specifier: 'catalog:' version: 3.2.0 @@ -1582,31 +1588,31 @@ importers: version: 2.2.6 unocss-preset-scrollbar: specifier: ^4.0.0 - version: 4.0.0(unocss@66.6.8(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))) + version: 4.0.0(unocss@66.6.8(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))) unplugin-info: specifier: ^1.3.2 - version: 1.3.2(esbuild@0.27.2)(rollup@4.60.1)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 1.3.2(esbuild@0.27.2)(rollup@4.60.1)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) unplugin-yaml: specifier: ^4.1.0 - version: 4.1.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 4.1.0(@nuxt/kit@3.20.2(magicast@0.5.2))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) vite: specifier: 'catalog:' - version: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + version: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) vite-bundle-visualizer: specifier: ^1.2.1 version: 1.2.1(rolldown@1.0.0-rc.16)(rollup@4.60.1) vite-plugin-mkcert: specifier: 'catalog:' - version: 2.0.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 2.0.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) vite-plugin-vue-devtools: specifier: ^8.1.1 - version: 8.1.1(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3)) + version: 8.1.1(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3)) vite-plugin-vue-layouts: specifier: ^0.11.0 - version: 0.11.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-router@5.0.4(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) + version: 0.11.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-router@5.0.4(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) vue-macros: specifier: ^3.1.2 - version: 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3)) + version: 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3)) vue-tsc: specifier: ^3.2.6 version: 3.2.6(typescript@5.9.3) @@ -9931,6 +9937,7 @@ packages: '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + deprecated: Potential CWE-502 - Update to 1.3.1 or higher '@unocss/cli@66.6.8': resolution: {integrity: sha512-dJ4AmrhCtQwEDJtpFG7AgJ4Qi4GWnNgWWlLWq4DhKBOCcvldr9k98mscdhs3MOwph25DIxU5MdLRAg/OS1JryQ==} @@ -19734,9 +19741,9 @@ snapshots: dependencies: electron: 41.2.1 - '@electron-toolkit/tsconfig@2.0.0(@types/node@24.12.2)': + '@electron-toolkit/tsconfig@2.0.0(@types/node@25.6.0)': dependencies: - '@types/node': 24.12.2 + '@types/node': 25.6.0 '@electron-toolkit/utils@4.0.0(electron@41.2.1)': dependencies: @@ -20622,31 +20629,6 @@ snapshots: - supports-color - typescript - '@intlify/unplugin-vue-i18n@11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.1(jiti@2.6.1))(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))': - dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.2.1(jiti@2.6.1)) - '@intlify/bundle-utils': 11.0.7(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3))) - '@intlify/shared': 11.3.2 - '@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.3.2)(@vue/compiler-dom@3.5.32)(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) - '@rollup/pluginutils': 5.3.0(rollup@4.60.1) - '@typescript-eslint/scope-manager': 8.58.1 - '@typescript-eslint/typescript-estree': 8.58.1(typescript@5.9.3) - debug: 4.4.3(supports-color@10.2.2) - fast-glob: 3.3.3 - pathe: 2.0.3 - picocolors: 1.1.1 - unplugin: 2.3.11 - vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - vue: 3.5.32(typescript@5.9.3) - optionalDependencies: - vue-i18n: 11.3.2(vue@3.5.32(typescript@5.9.3)) - transitivePeerDependencies: - - '@vue/compiler-dom' - - eslint - - rollup - - supports-color - - typescript - '@intlify/unplugin-vue-i18n@11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.1(jiti@2.6.1))(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@10.2.1(jiti@2.6.1)) @@ -22620,35 +22602,11 @@ snapshots: transitivePeerDependencies: - magicast - '@proj-airi/unplugin-fetch@0.2.3(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))': - dependencies: - ofetch: 1.5.1 - vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - '@proj-airi/unplugin-fetch@0.2.3(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))': dependencies: ofetch: 1.5.1 vite: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - '@proj-airi/unplugin-live2d-sdk@0.1.7(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)': - dependencies: - ofetch: 1.5.1 - vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - yauzl: 3.3.0 - transitivePeerDependencies: - - '@types/node' - - '@vitejs/devtools' - - esbuild - - jiti - - less - - sass - - sass-embedded - - stylus - - sugarss - - terser - - tsx - - yaml - '@proj-airi/unplugin-live2d-sdk@0.1.7(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)': dependencies: ofetch: 1.5.1 @@ -23598,7 +23556,6 @@ snapshots: '@types/node@25.6.0': dependencies: undici-types: 7.19.2 - optional: true '@types/nprogress@0.2.3': {} @@ -24264,12 +24221,6 @@ snapshots: vite: 6.4.2(@types/node@25.6.0)(jiti@2.6.1)(less@4.6.4)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) vue: 3.5.32(typescript@5.9.3) - '@vitejs/plugin-vue@6.0.6(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3))': - dependencies: - '@rolldown/pluginutils': 1.0.0-rc.13 - vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - vue: 3.5.32(typescript@5.9.3) - '@vitejs/plugin-vue@6.0.6(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3))': dependencies: '@rolldown/pluginutils': 1.0.0-rc.13 @@ -24572,15 +24523,6 @@ snapshots: transitivePeerDependencies: - vue - '@vue-macros/devtools@3.1.2(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))': - dependencies: - sirv: 3.0.2 - vue: 3.5.32(typescript@5.9.3) - optionalDependencies: - vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - transitivePeerDependencies: - - typescript - '@vue-macros/devtools@3.1.2(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))': dependencies: sirv: 3.0.2 @@ -26715,7 +26657,7 @@ snapshots: transitivePeerDependencies: - supports-color - electron-vite@5.0.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): + electron-vite@5.0.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): dependencies: '@babel/core': 7.29.0 '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.29.0) @@ -26723,7 +26665,7 @@ snapshots: esbuild: 0.25.12 magic-string: 0.30.21 picocolors: 1.1.1 - vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + vite: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) transitivePeerDependencies: - supports-color @@ -32622,8 +32564,7 @@ snapshots: undici-types@7.16.0: {} - undici-types@7.19.2: - optional: true + undici-types@7.19.2: {} undici@6.24.1: {} @@ -32733,6 +32674,11 @@ snapshots: '@unocss/preset-mini': 66.6.8 unocss: 66.6.8(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + unocss-preset-scrollbar@4.0.0(unocss@66.6.8(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))): + dependencies: + '@unocss/preset-mini': 66.6.8 + unocss: 66.6.8(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + unocss@66.6.8(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vite@6.4.2(@types/node@25.6.0)(jiti@2.6.1)(less@4.6.4)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): dependencies: '@unocss/cli': 66.6.8 @@ -32817,14 +32763,6 @@ snapshots: unplugin: 2.3.11 vite: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - unplugin-combine@2.3.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(unplugin@2.3.11)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): - optionalDependencies: - esbuild: 0.27.2 - rolldown: 1.0.0-rc.16 - rollup: 4.60.1 - unplugin: 2.3.11 - vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - unplugin-combine@2.3.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(unplugin@2.3.11)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): optionalDependencies: esbuild: 0.27.2 @@ -32859,19 +32797,6 @@ snapshots: transitivePeerDependencies: - supports-color - unplugin-info@1.3.2(esbuild@0.27.2)(rollup@4.60.1)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): - dependencies: - ci-info: 4.4.0 - git-url-parse: 16.1.0 - simple-git: 3.36.0 - unplugin: 2.3.11 - optionalDependencies: - esbuild: 0.27.2 - rollup: 4.60.1 - vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - transitivePeerDependencies: - - supports-color - unplugin-info@1.3.2(esbuild@0.27.2)(rollup@4.60.1)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): dependencies: ci-info: 4.4.0 @@ -32981,17 +32906,6 @@ snapshots: rollup: 4.60.1 vite: 6.4.2(@types/node@25.6.0)(jiti@2.6.1)(less@4.6.4)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - unplugin-yaml@4.1.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): - dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.60.1) - unplugin: 3.0.0 - yaml: 2.8.3 - optionalDependencies: - esbuild: 0.27.2 - rolldown: 1.0.0-rc.16 - rollup: 4.60.1 - vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - unplugin@2.3.11: dependencies: '@jridgewell/remapping': 2.3.5 @@ -33223,22 +33137,12 @@ snapshots: - rollup - supports-color - vite-dev-rpc@1.1.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): - dependencies: - birpc: 2.9.0 - vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - vite-hot-client: 2.1.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) - vite-dev-rpc@1.1.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): dependencies: birpc: 2.9.0 vite: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) vite-hot-client: 2.1.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) - vite-hot-client@2.1.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): - dependencies: - vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - vite-hot-client@2.1.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): dependencies: vite: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) @@ -33284,21 +33188,6 @@ snapshots: - tsx - yaml - vite-plugin-inspect@11.3.3(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): - dependencies: - ansis: 4.2.0 - debug: 4.4.3(supports-color@10.2.2) - error-stack-parser-es: 1.0.5 - ohash: 2.0.11 - open: 10.2.0 - perfect-debounce: 2.1.0 - sirv: 3.0.2 - unplugin-utils: 0.3.1 - vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - vite-dev-rpc: 1.1.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) - transitivePeerDependencies: - - supports-color - vite-plugin-inspect@11.3.3(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): dependencies: ansis: 4.2.0 @@ -33330,13 +33219,6 @@ snapshots: - typescript - ws - vite-plugin-mkcert@2.0.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): - dependencies: - debug: 4.4.3(supports-color@10.2.2) - supports-color: 10.2.2 - undici: 8.1.0 - vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - vite-plugin-mkcert@2.0.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): dependencies: debug: 4.4.3(supports-color@10.2.2) @@ -33355,20 +33237,6 @@ snapshots: transitivePeerDependencies: - supports-color - vite-plugin-vue-devtools@8.1.1(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3)): - dependencies: - '@vue/devtools-core': 8.1.1(vue@3.5.32(typescript@5.9.3)) - '@vue/devtools-kit': 8.1.1 - '@vue/devtools-shared': 8.1.1 - sirv: 3.0.2 - vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - vite-plugin-inspect: 11.3.3(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) - vite-plugin-vue-inspector: 5.3.2(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) - transitivePeerDependencies: - - '@nuxt/kit' - - supports-color - - vue - vite-plugin-vue-devtools@8.1.1(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3)): dependencies: '@vue/devtools-core': 8.1.1(vue@3.5.32(typescript@5.9.3)) @@ -33383,21 +33251,6 @@ snapshots: - supports-color - vue - vite-plugin-vue-inspector@5.3.2(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): - dependencies: - '@babel/core': 7.29.0 - '@babel/plugin-proposal-decorators': 7.28.0(@babel/core@7.29.0) - '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.29.0) - '@babel/plugin-transform-typescript': 7.28.5(@babel/core@7.29.0) - '@vue/babel-plugin-jsx': 1.5.0(@babel/core@7.29.0) - '@vue/compiler-dom': 3.5.32 - kolorist: 1.8.0 - magic-string: 0.30.21 - vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - transitivePeerDependencies: - - supports-color - vite-plugin-vue-inspector@5.3.2(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): dependencies: '@babel/core': 7.29.0 @@ -33413,16 +33266,6 @@ snapshots: transitivePeerDependencies: - supports-color - vite-plugin-vue-layouts@0.11.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-router@5.0.4(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)): - dependencies: - debug: 4.4.3(supports-color@10.2.2) - fast-glob: 3.3.3 - vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - vue: 3.5.32(typescript@5.9.3) - vue-router: 5.0.4(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) - transitivePeerDependencies: - - supports-color - vite-plugin-vue-layouts@0.11.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-router@5.0.4(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)): dependencies: debug: 4.4.3(supports-color@10.2.2) @@ -33689,54 +33532,6 @@ snapshots: - vue-tsc - webpack - vue-macros@3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3)): - dependencies: - '@vue-macros/better-define': 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/boolean-prop': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/chain-call': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/common': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/config': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/define-emit': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/define-models': 3.1.2(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/define-prop': 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/define-props': 3.1.2(@vue-macros/reactivity-transform@3.1.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/define-props-refs': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/define-render': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/define-slots': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/define-stylex': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/devtools': 3.1.2(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) - '@vue-macros/export-expose': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/export-props': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/export-render': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/hoist-static': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/jsx-directive': 3.1.2(typescript@5.9.3) - '@vue-macros/named-template': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/reactivity-transform': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/script-lang': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/setup-block': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/setup-component': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/setup-sfc': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/short-bind': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/short-emits': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/short-vmodel': 3.1.2(vue@3.5.32(typescript@5.9.3)) - '@vue-macros/volar': 3.1.2(typescript@5.9.3)(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3)) - unplugin: 2.3.11 - unplugin-combine: 2.3.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(unplugin@2.3.11)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) - unplugin-vue-define-options: 3.1.2(vue@3.5.32(typescript@5.9.3)) - vue: 3.5.32(typescript@5.9.3) - transitivePeerDependencies: - - '@emnapi/core' - - '@emnapi/runtime' - - '@rspack/core' - - '@vueuse/core' - - esbuild - - rolldown - - rollup - - typescript - - vite - - vue-tsc - - webpack - vue-macros@3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3)): dependencies: '@vue-macros/better-define': 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vue@3.5.32(typescript@5.9.3)) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 0f3fec3ca..6bfa0d90e 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -125,6 +125,7 @@ catalog: std-env: ^4.1.0 stockfish: ^18.0.7 superjson: ^2.2.6 + tinyexec: ^1.1.1 tsdown: ^0.21.9 tsx: ^4.21.0 uncrypto: ^0.1.3 diff --git a/vitest.config.ts b/vitest.config.ts index 38f2f1989..e4198c552 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -15,8 +15,6 @@ export default defineConfig({ 'packages/server-runtime', 'packages/server-sdk', 'packages/stage-shared', - 'packages/stage-ui', - 'packages/vishot-runtime', 'packages/vite-plugin-warpdrive', ], },