fix(ci): align plugin inventory with current contracts

This commit is contained in:
ZiyaZhang
2026-08-11 21:38:11 -07:00
parent eea356e785
commit 46c0e3dba7
8 changed files with 35 additions and 13 deletions

View File

@@ -7,6 +7,7 @@ import pluginInventoryRemote from '@deepseek-ai/dsh-host-plugin-inventory/remote
import type { TypeRTClientRemote } from '@deepseek-ai/dsh-type-meta' import type { TypeRTClientRemote } from '@deepseek-ai/dsh-type-meta'
export type { TypeRTClientRemote as ClientRemote } from '@deepseek-ai/dsh-type-meta' export type { TypeRTClientRemote as ClientRemote } from '@deepseek-ai/dsh-type-meta'
export type { PluginInventorySnapshot } from '@deepseek-ai/dsh-host-plugin-inventory/types'
export type {} from '@deepseek-ai/dsh-commands/remote' export type {} from '@deepseek-ai/dsh-commands/remote'
export type {} from '@deepseek-ai/dsh-goal/remote' export type {} from '@deepseek-ai/dsh-goal/remote'
export type {} from '@deepseek-ai/dsh-host-plugin-inventory/remote' export type {} from '@deepseek-ai/dsh-host-plugin-inventory/remote'

View File

@@ -1,7 +1,7 @@
{ {
"name": "@deepseek-ai/dsh-client-ui-plugins", "name": "@deepseek-ai/dsh-client-ui-plugins",
"description": "Read-only Cordis Loader plugin inventory in Web settings", "description": "Read-only Cordis Loader plugin inventory in Web settings",
"version": "0.0.1-rc.1", "version": "0.0.1-rc.2",
"publishConfig": { "publishConfig": {
"access": "restricted" "access": "restricted"
}, },

View File

@@ -1,5 +1,5 @@
import { useEffect, useId, useMemo, useState, type ReactNode } from 'react' import { useEffect, useId, useMemo, useState, type ReactNode } from 'react'
import type { ClientRemote } from '@deepseek-ai/dsh-api-remotes/client' import type { PluginInventorySnapshot } from '@deepseek-ai/dsh-api-remotes/client'
import { import {
IconChevronDownOutline14, IconChevronDownOutline14,
IconSearchOutline16, IconSearchOutline16,
@@ -11,10 +11,9 @@ import css from './PluginSettingsSection.module.css'
/** Registration-side Remote face used by the section. */ /** Registration-side Remote face used by the section. */
export interface PluginSettingsSectionInjected { export interface PluginSettingsSectionInjected {
/** Read a current Host inventory snapshot. */ /** Read a current Host inventory snapshot. */
list: ClientRemote['pluginInventory']['list'] list: () => Promise<PluginInventorySnapshot>
} }
type PluginInventorySnapshot = Awaited<ReturnType<PluginSettingsSectionInjected['list']>>
type PluginInventoryEntry = PluginInventorySnapshot['entries'][number] type PluginInventoryEntry = PluginInventorySnapshot['entries'][number]
type PluginFiberPhase = PluginInventoryEntry['fiberPhase'] type PluginFiberPhase = PluginInventoryEntry['fiberPhase']
@@ -119,7 +118,7 @@ export function PluginSettingsSection({ list, t }: PluginSettingsSectionProps):
value={query} value={query}
placeholder={t('search')} placeholder={t('search')}
aria-label={t('search')} aria-label={t('search')}
onChange={event => setQuery(event.currentTarget.value)} onChange={(event) => { setQuery(event.currentTarget.value) }}
/> />
</label> </label>
<div className={css.catalogHeading}> <div className={css.catalogHeading}>

View File

@@ -1,6 +1,5 @@
/** Read-only Host plugin inventory registered into Web Settings. */ /** Read-only Host plugin inventory registered into Web Settings. */
import type { ClientRemote } from '@deepseek-ai/dsh-api-remotes/client'
import type {} from '@deepseek-ai/dsh-client-locale/client' import type {} from '@deepseek-ai/dsh-client-locale/client'
import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client' import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'
import type {} from '@deepseek-ai/dsh-client-ui-settings/client' import type {} from '@deepseek-ai/dsh-client-ui-settings/client'
@@ -28,7 +27,13 @@ export function apply(ctx: ClientContext): void {
ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'ui-plugins: dictionaries') ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'ui-plugins: dictionaries')
const t = ctx.locale.bind(NS) const t = ctx.locale.bind(NS)
const list: ClientRemote['pluginInventory']['list'] = () => ctx.remote.pluginInventory.list() const list: PluginSettingsSectionInjected['list'] = async () => {
const result = await ctx.remote.pluginInventory.list()
if (!result.ok) {
throw new Error(`pluginInventory.list failed: ${result.error.code}: ${result.error.message}`)
}
return result.value
}
const injected = (): PluginSettingsSectionInjected => ({ list }) const injected = (): PluginSettingsSectionInjected => ({ list })
ctx.slots.inject('settings.section', () => ctx.slots.register({ ctx.slots.inject('settings.section', () => ctx.slots.register({

View File

@@ -14,6 +14,9 @@ usePinnedBrowserLanguages('zh-CN')
afterEach(cleanup) afterEach(cleanup)
const EMPTY = { entries: [] } const EMPTY = { entries: [] }
type ListResult =
| { readonly ok: true; readonly value: typeof EMPTY }
| { readonly ok: false; readonly error: { readonly code: string; readonly message: string } }
async function bench() { async function bench() {
const ctx = new Context() const ctx = new Context()
@@ -26,7 +29,8 @@ async function bench() {
} }
} }
new RemoteService(ctx) new RemoteService(ctx)
const list = vi.fn(() => Promise.resolve(EMPTY)) const list = vi.fn<() => Promise<ListResult>>()
.mockResolvedValue({ ok: true, value: EMPTY })
ctx.provide('remote.pluginInventory', { list }) ctx.provide('remote.pluginInventory', { list })
return { ctx, slots: ctx.get('slots') as SlotsService, locale, list } return { ctx, slots: ctx.get('slots') as SlotsService, locale, list }
} }
@@ -58,6 +62,8 @@ describe('ui-plugins browser plugin', () => {
const injected = (entry.inject as unknown as () => PluginSettingsSectionInjected)() const injected = (entry.inject as unknown as () => PluginSettingsSectionInjected)()
await expect(injected.list()).resolves.toEqual(EMPTY) await expect(injected.list()).resolves.toEqual(EMPTY)
expect(b.list).toHaveBeenCalledOnce() expect(b.list).toHaveBeenCalledOnce()
b.list.mockResolvedValueOnce({ ok: false, error: { code: 'REMOTE_ERROR', message: 'unavailable' } })
await expect(injected.list()).rejects.toThrow('pluginInventory.list failed: REMOTE_ERROR: unavailable')
await b.ctx.fiber.dispose() await b.ctx.fiber.dispose()
}) })

View File

@@ -27,7 +27,7 @@ function props(list: PluginSettingsSectionInjected['list']): PluginSettingsSecti
const SNAPSHOT = { const SNAPSHOT = {
entries: [ entries: [
{ entryId: '8a1b2c3d', moduleName: '@deepseek-ai/cordis-plugin-hmr', enabled: true, fiberPhase: 'active' }, { entryId: '8a1b2c3d', moduleName: '@deepseek-ai/cordis-plugin-hmr', enabled: true, fiberPhase: 'active' },
{ entryId: 'pending', moduleName: '@fixture/pending-name', enabled: true, fiberPhase: 'pending' }, { entryId: 'pending', moduleName: 'cordis:pending-name', enabled: true, fiberPhase: 'pending' },
{ entryId: 'loading', moduleName: '@fixture/loading-name', enabled: true, fiberPhase: 'loading' }, { entryId: 'loading', moduleName: '@fixture/loading-name', enabled: true, fiberPhase: 'loading' },
{ entryId: 'failed', moduleName: '@fixture/failed-name', enabled: true, fiberPhase: 'failed' }, { entryId: 'failed', moduleName: '@fixture/failed-name', enabled: true, fiberPhase: 'failed' },
{ entryId: 'unloading', moduleName: '@fixture/unloading-name', enabled: true, fiberPhase: 'unloading' }, { entryId: 'unloading', moduleName: '@fixture/unloading-name', enabled: true, fiberPhase: 'unloading' },
@@ -69,6 +69,14 @@ describe('PluginSettingsSection', () => {
expect(screen.getByText(en.cordis)).toBeTruthy() expect(screen.getByText(en.cordis)).toBeTruthy()
fireEvent.click(active) fireEvent.click(active)
expect(view.container.querySelector('[data-loader-entry]')).toBeNull() expect(view.container.querySelector('[data-loader-entry]')).toBeNull()
fireEvent.click(active)
fireEvent.change(screen.getByRole('searchbox', { name: en.search }), {
target: { value: 'disabled-entry' },
})
expect(view.container.querySelector('[data-loader-entry]')).toBeNull()
fireEvent.click(screen.getByRole('button', { name: 'directory-picker-native, Not mounted, Disabled' }))
expect(screen.getAllByText(en.disabledTag)).toHaveLength(2)
}) })
it('filters by module name or Loader entry id', async () => { it('filters by module name or Loader entry id', async () => {
@@ -111,5 +119,10 @@ describe('PluginSettingsSection', () => {
const pending = render(<PluginSettingsSection {...props(() => deferred.promise)} />) const pending = render(<PluginSettingsSection {...props(() => deferred.promise)} />)
pending.unmount() pending.unmount()
await act(async () => { deferred.resolve(SNAPSHOT) }) await act(async () => { deferred.resolve(SNAPSHOT) })
const deferredFailure = Promise.withResolvers<Snapshot>()
const pendingFailure = render(<PluginSettingsSection {...props(() => deferredFailure.promise)} />)
pendingFailure.unmount()
await act(async () => { deferredFailure.reject(new Error('late failure')) })
}) })
}) })

View File

@@ -1,7 +1,7 @@
{ {
"name": "@deepseek-ai/dsh-host-plugin-inventory", "name": "@deepseek-ai/dsh-host-plugin-inventory",
"description": "Read-only Remote projection of current Cordis Loader plugin state", "description": "Read-only Remote projection of current Cordis Loader plugin state",
"version": "0.0.1-rc.1", "version": "0.0.1-rc.2",
"publishConfig": { "publishConfig": {
"access": "restricted" "access": "restricted"
}, },
@@ -45,9 +45,7 @@
"lib/typert.host.js", "lib/typert.host.js",
"lib/typert.host.d.ts", "lib/typert.host.d.ts",
"lib/typert.remote-client.js", "lib/typert.remote-client.js",
"lib/typert.remote-client.d.ts", "lib/typert.remote-client.d.ts"
"lib/typert.remote-client.d.ts.map",
"src"
], ],
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"dependencies": { "dependencies": {