feat(client): inject slot declaration lifetimes
This commit is contained in:
@@ -39,12 +39,8 @@ export interface PlanChipInjected {
|
||||
exitPlanMode: () => Promise<string | null>
|
||||
}
|
||||
|
||||
/**
|
||||
* Required services: the seat's slot registry, the transport, the copy's
|
||||
* locale registry, and the conversation service whose presence guarantees
|
||||
* the seat is declared.
|
||||
*/
|
||||
export const inject = ['slots', 'connection', 'conversation', 'locale']
|
||||
/** Required services: the seat's slot registry, transport, and locale registry. */
|
||||
export const inject = ['slots', 'connection', 'locale']
|
||||
|
||||
/**
|
||||
* Client plugin body: register the plan chip over the command channel.
|
||||
@@ -53,7 +49,7 @@ export const inject = ['slots', 'connection', 'conversation', 'locale']
|
||||
export function apply(ctx: ClientContext): void {
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'ui-plan: dictionaries')
|
||||
|
||||
ctx.effect(() => ctx.slots.register({
|
||||
ctx.slots.inject('conversation.input.plan', () => ctx.slots.register({
|
||||
name: 'conversation.input.plan',
|
||||
locale: NS,
|
||||
inject: (sessionId: SessionId): PlanChipInjected => ({
|
||||
@@ -66,5 +62,5 @@ export function apply(ctx: ClientContext): void {
|
||||
return null
|
||||
},
|
||||
}),
|
||||
}, PlanChip), 'ui-plan: composer plan chip registration')
|
||||
}, PlanChip))
|
||||
}
|
||||
|
||||
@@ -28,28 +28,32 @@ async function bench() {
|
||||
const execute = vi.fn((_payload: { sessionId: SessionId; line: string }) =>
|
||||
Promise.resolve({ result: { ok: true as const, value: { matched: true as const, commandId: 'c1' } } }))
|
||||
ctx.provide('connection', { api: { commands: { execute } } })
|
||||
ctx.provide('conversation', {})
|
||||
ctx.provide('locale', new LocaleService(ctx))
|
||||
return { ctx, slots, execute }
|
||||
}
|
||||
|
||||
describe('ui-plan browser apply', () => {
|
||||
it('declares every service it binds', () => {
|
||||
expect(inject).toEqual(['slots', 'connection', 'conversation', 'locale'])
|
||||
expect(inject).toEqual(['slots', 'connection', 'locale'])
|
||||
})
|
||||
|
||||
it('node-half apply is an intentional no-op', () => {
|
||||
expect(() => { nodeApply() }).not.toThrow()
|
||||
})
|
||||
|
||||
it('fails loud when conversation did not declare the plan seat', async () => {
|
||||
it('waits until conversation declares the plan seat', async () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SlotsService).await()
|
||||
ctx.provide('connection', {})
|
||||
ctx.provide('conversation', {})
|
||||
ctx.provide('locale', new LocaleService(ctx))
|
||||
await expect(ctx.plugin({ inject: [...inject], apply }))
|
||||
.rejects.toThrow(/slot "conversation.input.plan" is not declared/)
|
||||
const fiber = ctx.plugin({ inject: [...inject], apply })
|
||||
await fiber.await()
|
||||
expect(ctx.slots.entries('conversation.input.plan')).toHaveLength(0)
|
||||
ctx.slots.register({
|
||||
name: 'root', children: { 'conversation.input.plan': { kind: 'single', scope: 'session' } },
|
||||
} as never, () => null)
|
||||
await Promise.resolve()
|
||||
expect(ctx.slots.entries('conversation.input.plan')).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('registers the chip, executes /plan off, and unregisters on teardown', async () => {
|
||||
|
||||
Reference in New Issue
Block a user