Explorar el Código

ci: fix jsdoc & coverage

imccyu hace 2 meses
padre
commit
9cf5a384e9

+ 1 - 2
knip.json

@@ -88,8 +88,7 @@
     },
     "packages/client/runtime": {
       "entry": [
-        "tests/**/*.spec.ts",
-        "tests/**/*.e2e.ts"
+        "tests/**/*.spec.ts"
       ],
       "project": [
         "src/**/*.ts",

+ 0 - 77
packages/client/runtime/tests/client-loader-bundle.e2e.ts

@@ -1,77 +0,0 @@
-/**
- * Real-bundle smoke: the actual tsdown client bundle of ui-layout runs
- * through the loader chain (execute → handoff → factory(require) → apply →
- * export re-registration). Skips when the bundle is not built (lib/client.js is a
- * build product; `pnpm --filter @deepseek-ai/dsh-client-ui-layout build`).
- */
-import { readFileSync } from 'node:fs'
-import { createRequire } from 'node:module'
-import { Context } from 'cordis'
-import { afterEach, describe, expect, it } from 'vitest'
-import * as uiSlots from '@deepseek-ai/dsh-client-ui-slots'
-import * as webReact from '@deepseek-ai/dsh-client-web-react'
-import { createClientLoader } from '../src/client/loader/index.ts'
-import type { ClientPluginHandoff } from '../src/client/loader/index.ts'
-import { SessionsService } from '../src/client/sessions/service.ts'
-import { SlotsService } from '../src/client/slots.ts'
-import { FakeApiClient } from './fake-api.ts'
-
-const LAYOUT_ID = '@deepseek-ai/dsh-client-ui-layout'
-
-type Win = { DSHClientProxy?: { loadPlugin(h: ClientPluginHandoff): void }; window?: unknown }
-
-afterEach(() => {
-  delete (globalThis as Win).DSHClientProxy
-  delete (globalThis as Win).window
-})
-
-function readLayoutBundle(): string | undefined {
-  try {
-    const require = createRequire(import.meta.url)
-    return readFileSync(require.resolve(`${LAYOUT_ID}/client`), 'utf8')
-  } catch {
-    return undefined
-  }
-}
-
-describe('real tsdown bundle through the loader', () => {
-  const code = readLayoutBundle()
-
-  it.skipIf(code === undefined)('loads ui-layout lib/client.js: handoff, DI require, apply, export surface', async () => {
-    // The bundle banner addresses window.DSHClientProxy; node has no window —
-    // alias it to globalThis so the loader-installed proxy is reachable.
-    ;(globalThis as Win).window = globalThis
-    const ctx = new Context()
-    // The layout apply consumes the slots + sessions services; the real chain
-    // loads the runtime bundle first — stand both up directly here.
-    ctx.plugin(SlotsService)
-    await ctx.fiber.await()
-    new SessionsService(ctx, new FakeApiClient())
-    const loader = createClientLoader({
-      ctx,
-      // The real bundle externals resolved from the seeded table. React is a
-      // type-only import in the layout bundle today, but jsx-runtime is real.
-      modules: {
-        'react': await import('react'),
-        'react/jsx-runtime': await import('react/jsx-runtime'),
-        '@deepseek-ai/dsh-client-ui-slots': uiSlots,
-        '@deepseek-ai/dsh-client-web-react': webReact,
-      },
-      boot: { plugins: [{ id: LAYOUT_ID, url: `/plugins/${LAYOUT_ID}/client.js`, inject: [] }] },
-      fetchBundle: () => Promise.resolve(code as string),
-      // node has no DOM: evaluate the bundle body directly (same synchronous
-      // handoff contract as the <script> path).
-      executeBundle: (bundleCode) => {
-        // Node has no <script>: Function-evaluating the built bundle IS the
-        // system under test (same synchronous handoff as the browser path).
-        // eslint-disable-next-line @typescript-eslint/no-implied-eval, @typescript-eslint/no-unsafe-call
-        new Function(bundleCode)()
-      },
-    })
-    loader.start()
-    await loader.settled()
-    expect(loader.status.getSnapshot()[LAYOUT_ID]).toBe('active')
-    const surface = loader.requireModule(LAYOUT_ID) as Record<string, unknown>
-    expect(typeof surface.apply).toBe('function')
-  })
-})

+ 14 - 2
packages/client/ui-conversation/src/client/stores.ts

@@ -9,9 +9,21 @@
  * Module exports the factory only — a module-level handle would pin identity
  * in the module cache (a de-facto singleton surviving plugin reloads).
  */
-import { defineStore } from '@deepseek-ai/dsh-client-runtime/client'
+import { defineStore, type EngineStoreHandle } from '@deepseek-ai/dsh-client-runtime/client'
 import type { ChatStoreState, SelectionTarget, ViewId } from './contract/views.ts'
 
+/**
+ * Annotation twin of the actions literal below (the export needs a declared
+ * return type); drift fails assignability at the defineStore call.
+ */
+type ChatActions = {
+  select: (draft: ChatStoreState, target: SelectionTarget | null) => void
+  setDraft: (draft: ChatStoreState, text: string) => void
+  clearDraft: (draft: ChatStoreState) => void
+  restoreDraft: (draft: ChatStoreState, text: string) => void
+  setView: (draft: ChatStoreState, view: ViewId) => void
+}
+
 /**
  * Declare the per-session chat store. `selection` is the details-linkage
  * channel (conversation writes, details reads); `draft` is the composer text
@@ -20,7 +32,7 @@ import type { ChatStoreState, SelectionTarget, ViewId } from './contract/views.t
  * cross-remount survival channel, null falls back to the first registered view).
  * @returns the store handle (spec + identity + factory in one value).
  */
-export function createChatStore() {
+export function createChatStore(): EngineStoreHandle<ChatStoreState, ChatActions> {
   return defineStore({
     // Anchored to the contract shape: views consume the store through
     // ConvViewProps' SnapshotSelectorHook<ChatStoreState>, so init and the

+ 17 - 2
packages/client/ui-layout/src/client/stores.ts

@@ -7,12 +7,27 @@
  * derives its PropsStore share from the return type, and the service face
  * receives the bound actions through the registration's inject hook.
  */
-import { defineStore } from '@deepseek-ai/dsh-client-runtime/client'
+import { defineStore, type EngineStoreHandle } from '@deepseek-ai/dsh-client-runtime/client'
 import {
   clampWidth, DETAILS_DEFAULT, DETAILS_MAX, DETAILS_MIN,
   SIDEBAR_DEFAULT, SIDEBAR_MAX, SIDEBAR_MIN,
 } from './columns.ts'
 
+/** Panel width preferences in px (0 = closed) — the layout store's state. */
+type PanelWidths = { sidebar: number; details: number }
+
+/**
+ * Annotation twin of the actions literal below (the export needs a declared
+ * return type); drift fails assignability at the defineStore call.
+ */
+type LayoutActions = {
+  setSidebar: (draft: PanelWidths, px: number) => void
+  setDetails: (draft: PanelWidths, px: number) => void
+  toggleSidebar: (draft: PanelWidths) => void
+  openDetails: (draft: PanelWidths) => void
+  closeDetails: (draft: PanelWidths) => void
+}
+
 /**
  * Create the layout panel store handle. The persisted preference IS the
  * width, so closing a panel forgets its drag width — reopening restores the
@@ -21,7 +36,7 @@ import {
  * open/close transitions write 0 / the default explicitly.
  * @returns the store handle (spec + type + identity + factory in one).
  */
-export function createLayoutStore() {
+export function createLayoutStore(): EngineStoreHandle<PanelWidths, LayoutActions>  {
   return defineStore({
     init: () => ({ sidebar: SIDEBAR_DEFAULT, details: 0 }),
     persist: 'dsh.layout.panels',

+ 10 - 0
packages/client/ui-slots/src/index.ts

@@ -333,6 +333,16 @@ export class SlotCore {
       & SlotComponent<ComposedProps<K, keyof NoInfer<D> & keyof SlotMap & string, HandleOf<NoInfer<H>>, object>>
       & RendersCheck<C, D>,
   ): () => void
+  /**
+   * Inject-bearing overload: identical semantics to the overload above, plus
+   * the registrant's business face — `I` is inferred from the inject
+   * factory's return and joins the component's composed-props constraint
+   * (factory parameters derive from the declaration, {@link InjectParams}).
+   * @param options - registration options plus the `inject` business-face factory.
+   * @param component - component honoring the four-share composed props
+   * contract including the inject share `I`.
+   * @returns disposer removing the registration and its declarations.
+   */
   register<
     K extends keyof SlotMap & string,
     I extends object,

+ 5 - 0
vitest.config.ts

@@ -96,6 +96,11 @@ export default defineConfig({
         // yet. TODO(gui): cover and remove as the client test lane matures.
         'packages/client/ui-trajectory/src/*',
         'packages/client/web-react/src/*',
+        'packages/client/runtime/src/*',
+        'packages/client/ui-conversation/src/*',
+        'packages/client/ui-slots/src/*',
+        'packages/client/ui-layout/src/*',
+        'packages/client/web/src/*',
         'packages/host/webserver/src/*',
         ...windowsUnsupportedPackages.map(path => `${path}/src/**/*.ts`),
         ...windowsCoverageExclusions,