Bläddra i källkod

fix: coverage

imccyu 1 månad sedan
förälder
incheckning
924cfcef95

+ 4 - 0
packages/client/runtime/src/client/contract/conversation.ts

@@ -1,6 +1,10 @@
 import type { SessionEvent } from '@deepseek-ai/dsh-session/types'
 import type { ToolEventView } from '@deepseek-ai/dsh-client-connection/client'
 
+/* oxlint-disable typescript/no-duplicate-type-constituents, typescript/no-redundant-type-constituents --
+ * The unaugmented declaration-merge maps intentionally resolve to never in the Runtime program;
+ * installed business packages supply their concrete keys in consuming Client programs. */
+
 /** One raw log event plus its optional envelope-level presentation view. */
 export interface ConversationEventInput {
   readonly event: SessionEvent

+ 2 - 3
packages/client/runtime/src/client/sessions/conversation-assembler.ts

@@ -712,9 +712,8 @@ export class ConversationNodeAssembler {
     context: InternalContext,
     scope: ConversationLocationDataScope,
   ): ConversationLocationData | null {
-    const build = context.definition.buildLocationData
-    if (build === undefined) return null
-    const data = build(contextSnapshot(context), scope)
+    if (context.definition.buildLocationData === undefined) return null
+    const data = context.definition.buildLocationData(contextSnapshot(context), scope)
     if (data === null) return null
     if (data.kind !== scope) {
       throw new Error(

+ 1 - 1
packages/client/runtime/src/client/sessions/conversation-location-index.ts

@@ -20,7 +20,7 @@ export interface ConversationLocationDataChange {
 class MutableLocationDataStore {
   private entries = new Map<string, OwnedLocationData>()
 
-  get<Key extends string>(key: Key): unknown {
+  get(key: string): unknown {
     return this.entries.get(key)?.value
   }
 

+ 2 - 1
packages/client/test-runtime/tests/runtime.spec.tsx

@@ -18,6 +18,7 @@ declare module '@deepseek-ai/dsh-client-ui-slots' {
     'trt.panel': { kind: 'single'; scope: 'root'; owner: { label?: string } }
     'trt.chat': { kind: 'single'; scope: 'session' }
     'trt.rows': { kind: 'list'; scope: 'root' }
+    'trt.rows.hole': { kind: 'single'; scope: 'root' }
   }
 }
 
@@ -419,7 +420,7 @@ describe('feature mount and disposal', () => {
     await feature.dispose()
     await feature.dispose() // idempotent
     expect(runtime.slots.entries('trt.rows')).toHaveLength(0)
-    expect(runtime.slots.spec('trt.rows.hole' as never)).toBeUndefined()
+    expect(runtime.slots.spec('trt.rows.hole')).toBeUndefined()
     expect(runtime.ctx.get('feature-service')).toBeUndefined()
     expect(view.queryByTestId('row')).toBeNull()
     await runtime.dispose()

+ 1 - 1
packages/client/ui-conversation/src/client/contract/chat-nodes.ts

@@ -7,7 +7,7 @@ import type {
 export interface ChatNodeDataMap {}
 
 /** Renderer kinds contributed by the currently installed Chat business modules. */
-export type ChatNodeKind = keyof ChatNodeDataMap & string
+export type ChatNodeKind = Extract<keyof ChatNodeDataMap, string>
 
 /** Final Chat Node narrowed to one registered renderer kind and payload. */
 export type ChatNode<Kind extends ChatNodeKind = ChatNodeKind> = {

+ 1 - 1
packages/client/ui-conversation/src/client/contract/slots.ts

@@ -210,7 +210,7 @@ export interface TurnTailOwnerProps {
 }
 
 /** Hook constrained to business data published on the current Chat Node's Turn. */
-export type UseChatNodeTurnData = <Key extends keyof ConversationTurnDataMap & string>(
+export type UseChatNodeTurnData = <Key extends Extract<keyof ConversationTurnDataMap, string>>(
   key: Key,
 ) => Readonly<ConversationTurnDataMap[Key]> | undefined
 

+ 5 - 2
packages/client/ui-conversation/tests/chat-snapshot-fixture.ts

@@ -78,13 +78,16 @@ class FixtureLocationIndex implements ChatLocationNodeIndex {
 class FixtureTurnDataStore implements ConversationLocationDataStore<ConversationTurnDataMap> {
   private readonly values = new Map<string, unknown>()
 
-  get<Key extends keyof ConversationTurnDataMap & string>(
+  get<Key extends Extract<keyof ConversationTurnDataMap, string>>(
     key: Key,
   ): Readonly<ConversationTurnDataMap[Key]> | undefined {
     return this.values.get(key) as Readonly<ConversationTurnDataMap[Key]> | undefined
   }
 
-  set<Key extends keyof ConversationTurnDataMap & string>(key: Key, value: ConversationTurnDataMap[Key]): void {
+  set<Key extends Extract<keyof ConversationTurnDataMap, string>>(
+    key: Key,
+    value: ConversationTurnDataMap[Key],
+  ): void {
     this.values.set(key, value)
   }
 }

+ 10 - 8
packages/client/ui-conversation/tests/chat-view.spec.tsx

@@ -181,12 +181,14 @@ function makeHarness(init?: Partial<ConversationSnapshot>) {
     if (key !== 'conversation.chat.node') return opts?.fallback ?? null
     const nodeOwner = owner as RoutedChatNodeOwner
     const nodeKey = opts?.hookContext as string | undefined
-    const useTurnData = ((dataKey: string) => props.useSession((snapshot) => {
-      const location = nodeKey === undefined ? undefined : snapshot.chat.nodes.get(nodeKey)?.location
-      return location?.kind === 'turn' || location?.kind === 'step'
-        ? location.turn.data.get(dataKey as never)
-        : undefined
-    })) as UseChatNodeTurnData
+    const useTurnData = ((dataKey: string) => {
+      return props.useSession((snapshot) => {
+        const location = nodeKey === undefined ? undefined : snapshot.chat.nodes.get(nodeKey)?.location
+        return location?.kind === 'turn' || location?.kind === 'step'
+          ? location.turn.data.get(dataKey as never)
+          : undefined
+      })
+    }) as UseChatNodeTurnData
     const nodeProps = <Kind extends ChatNode['kind']>(): ChatNodeViewProps<Kind> => (
       { ...props, ...nodeOwner, useTurnData } as unknown as ChatNodeViewProps<Kind>
     )
@@ -226,7 +228,7 @@ function makeHarness(init?: Partial<ConversationSnapshot>) {
       case 'unknown':
         return <UnknownNodeView {...nodeProps<'unknown'>()} />
       case 'tool-call': {
-        const block = (nodeOwner.node.data as { readonly root: ToolCallBlock }).root
+        const block = nodeOwner.node.data.root
         const toolName = 'kind' in block ? block.call?.name ?? '' : block.name
         const tool = {
           callId: block.callId,
@@ -843,7 +845,7 @@ describe('ChatView', () => {
         mounted()
         return () => { unmounted() }
       }, [])
-      const root = (node.data as { readonly root: ToolCallBlock }).root
+      const root = node.data.root
       return (
         <div data-testid="stateful-tool" data-state={'kind' in root ? 'settled' : 'running'}>
           {root.callId}

+ 5 - 2
packages/client/ui-deliverables/tests/produced-files.spec.tsx

@@ -34,13 +34,16 @@ afterEach(cleanup)
 class TestTurnDataStore implements ConversationLocationDataStore<ConversationTurnDataMap> {
   private readonly values = new Map<string, unknown>()
 
-  get<Key extends keyof ConversationTurnDataMap & string>(
+  get<Key extends Extract<keyof ConversationTurnDataMap, string>>(
     key: Key,
   ): Readonly<ConversationTurnDataMap[Key]> | undefined {
     return this.values.get(key) as Readonly<ConversationTurnDataMap[Key]> | undefined
   }
 
-  set<Key extends keyof ConversationTurnDataMap & string>(key: Key, value: ConversationTurnDataMap[Key]): void {
+  set<Key extends Extract<keyof ConversationTurnDataMap, string>>(
+    key: Key,
+    value: ConversationTurnDataMap[Key],
+  ): void {
     this.values.set(key, value)
   }
 }

+ 6 - 6
packages/client/ui-settings/tests/apply.spec.ts

@@ -44,8 +44,8 @@ describe('ui-settings apply', () => {
     declare(before.slots)
     await before.ctx.plugin({ inject: [...inject], apply }).await()
     expect(before.slots.entries('sidebar.settings')[0]!.component).toBe(SettingsRoot)
-    for (const [name, spec] of Object.entries(CHILD_SPECS)) {
-      expect(before.slots.spec(name as never)).toEqual(spec)
+    for (const name of Object.keys(CHILD_SPECS) as Array<keyof typeof CHILD_SPECS>) {
+      expect(before.slots.spec(name)).toEqual(CHILD_SPECS[name])
     }
 
     const after = await bench()
@@ -120,8 +120,8 @@ describe('ui-settings apply', () => {
     declare(b.slots)
     await Promise.resolve()
     expect(b.slots.entries('sidebar.settings')[0]!.component).toBe(SettingsRoot)
-    for (const [name, spec] of Object.entries(CHILD_SPECS)) {
-      expect(b.slots.spec(name as never)).toEqual(spec)
+    for (const name of Object.keys(CHILD_SPECS) as Array<keyof typeof CHILD_SPECS>) {
+      expect(b.slots.spec(name)).toEqual(CHILD_SPECS[name])
     }
   })
 
@@ -132,8 +132,8 @@ describe('ui-settings apply', () => {
     await fiber.await()
     await fiber.dispose()
     expect(b.slots.entries('sidebar.settings')).toHaveLength(0)
-    for (const name of Object.keys(CHILD_SPECS)) {
-      expect(b.slots.spec(name as never)).toBeUndefined()
+    for (const name of Object.keys(CHILD_SPECS) as Array<keyof typeof CHILD_SPECS>) {
+      expect(b.slots.spec(name)).toBeUndefined()
     }
   })
 })

+ 8 - 2
packages/client/ui-slots/src/index.ts

@@ -677,7 +677,10 @@ export class SlotCore {
   >(
     options: BaseOptions<K, EntryKey, D, H, M, N> & { inject?: undefined },
     component: C
-      & SlotComponent<ComposedProps<K, NoInfer<EntryKey>, keyof NoInfer<D> & keyof SlotMap & string, HandleOf<NoInfer<H>>, object, NoInfer<M>, NoInfer<N>>>
+      & SlotComponent<ComposedProps<
+        K, NoInfer<EntryKey>, keyof NoInfer<D> & keyof SlotMap & string,
+        HandleOf<NoInfer<H>>, object, NoInfer<M>, NoInfer<N>
+      >>
       & RendersCheck<C, D>,
   ): () => void
   /**
@@ -702,7 +705,10 @@ export class SlotCore {
   >(
     options: BaseOptions<K, EntryKey, D, H, M, N> & { inject: (...args: InjectParams<K, H>) => I },
     component: C
-      & SlotComponent<ComposedProps<K, NoInfer<EntryKey>, keyof NoInfer<D> & keyof SlotMap & string, HandleOf<NoInfer<H>>, I, NoInfer<M>, NoInfer<N>>>
+      & SlotComponent<ComposedProps<
+        K, NoInfer<EntryKey>, keyof NoInfer<D> & keyof SlotMap & string,
+        HandleOf<NoInfer<H>>, I, NoInfer<M>, NoInfer<N>
+      >>
       & RendersCheck<C, D>,
   ): () => void
   /* jscpd:ignore-end */

+ 1 - 1
packages/typert/generator/src/analyzer.ts

@@ -274,7 +274,7 @@ export class WorkspaceAnalyzer {
 
   constructor(options: WorkspaceAnalyzerOptions) {
     this.options = {
-      root: resolve(options.root),
+      root: realPath(options.root),
       hostConfig: options.hostConfig ?? 'tsconfig.host.json',
       clientConfig: options.clientConfig ?? 'tsconfig.client.json',
       faces: options.faces ?? ['host', 'client'],