refactor(gui): move the snapshot-store engine into the client runtime

The data layer no longer depends on the React glue package, and business
plugins no longer depend on web-react at all:

- The store engine (zustand vanilla + immer + persist + dev freeze),
  defineStore, and shallowEqual move to @deepseek-ai/dsh-client-runtime,
  exported from the ./client main entry — no ./store subpath survives on
  either package (the web-react one is deleted, none is opened on runtime).
- Store products are bare snapshot sources: useSelector leaves
  SnapshotStore/StoreInstance and Session; every hook is composed at the
  binding site in web-react's renderer (per-source cached uSES binding).
  The SlotRendererHost sessions face carries bare observables only.
- SessionProvider becomes a standard-kit seat: an entry whose children
  declare a session-scope slot receives the framework component as a prop,
  retiring the last value import of web-react from plugin packages.
  UseSession and the session-area types now live in ui-slots.
- web-react shrinks to the shell-only React glue (renderer, providers,
  uSES bridge); zustand/immer belong to runtime alone; the module-table
  seed and tsdown externals drop the web-react/store seat.
- NODE_ENV replacement is defined once in the shared tsdown client preset
  (browser bundles inline the engine and lost vite's define); the 3-line
  process.env typecheck shim moves to runtime with the engine.
- Stray tsc artifacts (.js/.d.ts/.d.ts.map beside sources under src/)
  swept repo-wide; they shadow real sources under vitest resolution.

Verified: both aggregate typecheck programs at zero; 604 client tests
green; repo-wide grep for web-react/store at zero; real-host playwright
run 7/7 including persist round-trip.

ci: fix test/docs
This commit is contained in:
imccyu
2026-07-23 02:56:16 +08:00
parent 004a988168
commit 8b3d1ac943
77 changed files with 484 additions and 317 deletions

View File

@@ -30,7 +30,6 @@
"react-dom": "^18.2.0"
},
"devDependencies": {
"@deepseek-ai/dsh-client-ui-layout": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@types/react": "~18.3.1",
"@types/react-dom": "~18.3.0",

View File

@@ -7,7 +7,7 @@
*/
import { useSyncExternalStore } from 'react'
import type { ReactNode } from 'react'
import type { ObservableSnapshot, SnapshotStore } from '@deepseek-ai/dsh-client-web-react'
import type { ObservableSnapshot, SnapshotStore } from '@deepseek-ai/dsh-client-runtime/client'
import type { LoaderStatus } from '@deepseek-ai/dsh-client-runtime/client'
import css from './AppRoot.module.css'

View File

@@ -10,7 +10,7 @@
import { Context } from 'cordis'
import { createRoot } from 'react-dom/client'
import type { ReactNode } from 'react'
import type { ObservableSnapshot } from '@deepseek-ai/dsh-client-web-react'
import type { ObservableSnapshot } from '@deepseek-ai/dsh-client-runtime/client'
import { createSlotRenderer } from '@deepseek-ai/dsh-client-web-react'
import { createClientLoader, type ClientLoaderOptions } from '@deepseek-ai/dsh-client-runtime/loader'
import { AppRoot } from './AppRoot.tsx'

View File

@@ -13,7 +13,6 @@ import * as ReactDomClient from 'react-dom/client'
import * as Cordis from 'cordis'
import * as UiSlots from '@deepseek-ai/dsh-client-ui-slots'
import * as WebReact from '@deepseek-ai/dsh-client-web-react'
import * as WebReactStore from '@deepseek-ai/dsh-client-web-react/store'
import * as UiPrimitives from '@deepseek-ai/dsh-client-ui-primitives'
/**
@@ -29,7 +28,6 @@ export function seedModules(): Record<string, unknown> {
'cordis': Cordis,
'@deepseek-ai/dsh-client-ui-slots': UiSlots,
'@deepseek-ai/dsh-client-web-react': WebReact,
'@deepseek-ai/dsh-client-web-react/store': WebReactStore,
'@deepseek-ai/dsh-client-ui-primitives': UiPrimitives,
}
}

View File

@@ -9,10 +9,9 @@ import { afterEach, describe, expect, it } from 'vitest'
import { act, cleanup, render } from '@testing-library/react'
afterEach(cleanup)
// createSnapshotStore left the public face (framework-internal engine); the
// status-store stub reaches it through the same ./store channel runtime uses.
import { createSnapshotStore } from '@deepseek-ai/dsh-client-web-react/store'
import type { ObservableSnapshot } from '@deepseek-ai/dsh-client-web-react'
// The snapshot-store engine lives with runtime now; the status-store stub
// uses the same channel production code does.
import { createSnapshotStore, type ObservableSnapshot } from '@deepseek-ai/dsh-client-runtime/client'
import type { LoaderStatus } from '@deepseek-ai/dsh-client-runtime/client'
import { AppRoot } from '@deepseek-ai/dsh-client-web/src/AppRoot.tsx'

View File

@@ -13,12 +13,13 @@
import { afterEach, describe, expect, it } from 'vitest'
import { act } from '@testing-library/react'
import { bootWebShell } from '@deepseek-ai/dsh-client-web'
import { SlotsService } from '@deepseek-ai/dsh-client-runtime/client'
import { createSnapshotStore, defineStore, SlotsService } from '@deepseek-ai/dsh-client-runtime/client'
interface BootWindow extends Window {
__DSH_BOOT__?: { plugins: { id: string; url: string; inject: string[]; immediately?: boolean }[] }
DSHClientProxy?: unknown
__TEST_SLOTS_SERVICE__?: unknown
__TEST_RUNTIME_STORE__?: { createSnapshotStore: unknown; defineStore: unknown }
}
const win = window as unknown as BootWindow
@@ -34,14 +35,14 @@ window.DSHClientProxy.loadPlugin({
id: 'fake-runtime',
factory: (require) => {
const SlotsService = window.__TEST_SLOTS_SERVICE__
const { createSnapshotStore } = require('@deepseek-ai/dsh-client-web-react/store')
const { createSnapshotStore } = window.__TEST_RUNTIME_STORE__
return {
apply: (ctx) => {
ctx.plugin(SlotsService)
const list = createSnapshotStore({ ids: ['s1'], byId: { s1: { id: 's1', title: 'S1', running: false, updatedAt: 1 } }, current: 's1' })
ctx.provide('sessions', {
list,
cell: (id) => (id === 's1' ? { sessionId: 's1', useSelector: (sel) => sel({}) } : undefined),
cell: (id) => (id === 's1' ? { sessionId: 's1', session: { getSnapshot: () => ({}), subscribe: () => () => {} } } : undefined),
})
},
}
@@ -55,7 +56,7 @@ window.DSHClientProxy.loadPlugin({
id: 'fake-layout',
factory: (require) => {
const React = require('react')
const { defineStore } = require('@deepseek-ai/dsh-client-web-react')
const { defineStore } = window.__TEST_RUNTIME_STORE__
return {
inject: ['slots'],
apply: (ctx) => {
@@ -130,13 +131,15 @@ afterEach(() => {
delete win.__DSH_BOOT__
delete win.DSHClientProxy
delete win.__TEST_SLOTS_SERVICE__
delete win.__TEST_RUNTIME_STORE__
document.body.innerHTML = ''
document.head.querySelectorAll('script').forEach((s) => { s.remove() })
})
/** Hand the real service class to the stub bundle (runtime is not a seeded library). */
/** Hand the real runtime surface to the stub bundle (runtime is not a seeded library). */
function seedSlotsService(): void {
win.__TEST_SLOTS_SERVICE__ = SlotsService
win.__TEST_RUNTIME_STORE__ = { createSnapshotStore, defineStore }
}
describe('bootWebShell (real loader + real script execution)', () => {