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:
@@ -57,7 +57,11 @@ function makeHost(bodies: { root: (rp: (key: string, owner: object) => React.Rea
|
||||
host,
|
||||
current,
|
||||
addSession: (id: string) => {
|
||||
const cell: SessionCell = { sessionId: id, useSession: { hookTag: id } }
|
||||
// Bare source per cell (identity-stable): the machinery binds useSession from it.
|
||||
const cell: SessionCell = {
|
||||
sessionId: id,
|
||||
session: { getSnapshot: () => ({ sid: id }), subscribe: () => () => {} },
|
||||
}
|
||||
cells.set(id, cell)
|
||||
return cell
|
||||
},
|
||||
@@ -121,15 +125,23 @@ describe('SessionProvider', () => {
|
||||
const h = makeHost({
|
||||
root: (renderSlot) => <SessionProvider>{() => renderSlot('k.session', {})}</SessionProvider>,
|
||||
})
|
||||
const s1 = h.addSession('s1')
|
||||
const s2 = h.addSession('s2')
|
||||
h.registerSession({ component: (props: object) => { seen.push(props as Record<string, unknown>); return null }, options: {} })
|
||||
h.addSession('s1')
|
||||
h.addSession('s2')
|
||||
h.registerSession({
|
||||
component: (props: { useSession?: <S>(sel: (s: { sid: string }) => S) => S; sessionId?: string }) => {
|
||||
// The bound hook reads the cell's bare source — asserting through it
|
||||
// proves the machinery wired THIS session's source, not another's.
|
||||
seen.push({ sessionId: props.sessionId, read: props.useSession!((s) => s.sid) })
|
||||
return null
|
||||
},
|
||||
options: {},
|
||||
})
|
||||
render(<>{createSlotRenderer().renderRoot(h.host, {})}</>)
|
||||
act(() => { h.current.set('s1') })
|
||||
expect(seen.at(-1)!['useSession']).toBe(s1.useSession)
|
||||
expect(seen.at(-1)!['read']).toBe('s1')
|
||||
expect(seen.at(-1)!['sessionId']).toBe('s1')
|
||||
act(() => { h.current.set('s2') })
|
||||
expect(seen.at(-1)!['useSession']).toBe(s2.useSession)
|
||||
expect(seen.at(-1)!['read']).toBe('s2')
|
||||
expect(seen.at(-1)!['sessionId']).toBe('s2')
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user