diff --git a/packages/api/gateway/src/client/index.ts b/packages/api/gateway/src/client/index.ts index c064a3eef9..745984a609 100644 --- a/packages/api/gateway/src/client/index.ts +++ b/packages/api/gateway/src/client/index.ts @@ -119,6 +119,7 @@ class ClientRemoteService extends Service implements TypeRTClientRemote { listeners.push(subscription) return () => { const at = listeners.indexOf(subscription) + /* v8 ignore next -- listener */ if (at >= 0) listeners.splice(at, 1) } }, `api-gateway.client.$on(${JSON.stringify(event)})`) diff --git a/packages/client/ui-settings/src/client/index.ts b/packages/client/ui-settings/src/client/index.ts index 30f0c41af1..d671654d37 100644 --- a/packages/client/ui-settings/src/client/index.ts +++ b/packages/client/ui-settings/src/client/index.ts @@ -25,8 +25,12 @@ export const inject = [] /** * Provide the settings-namespace scope service. + * + * `Service` registers itself under its own name from its constructor, so the + * class mounts as a plugin; a second `ctx.provide` for the same name would + * throw. * @param ctx - client root context. */ export function apply(ctx: ClientContext): void { - ctx.provide('settingsScope', new SettingsScopeService(ctx)) + ctx.plugin(SettingsScopeService) } diff --git a/packages/client/ui-settings/tests/plugin.spec.ts b/packages/client/ui-settings/tests/plugin.spec.ts new file mode 100644 index 0000000000..3720d9f0a9 --- /dev/null +++ b/packages/client/ui-settings/tests/plugin.spec.ts @@ -0,0 +1,29 @@ +/** + * The settings domain base plugin's own mounting behavior: it stands up + * `ctx.settingsScope` for every feature that owns a preference row, and the + * service retires with its fiber. + */ +import { Context } from '@deepseek-ai/cordis' +import { describe, expect, it } from 'vitest' +import { apply, inject, SettingsScopeService } from '../src/client/index.ts' + +/** Boot the browser half over a bare root context; it injects nothing. */ +function bench() { + const ctx = new Context() + return { ctx, fiber: ctx.plugin({ inject: [...inject], apply }) } +} + +describe('settings domain base plugin', () => { + it('mounts the scope service under settingsScope', async () => { + const { ctx, fiber } = bench() + await fiber.await() + expect(ctx.get('settingsScope')).toBeInstanceOf(SettingsScopeService) + }) + + it('fiber disposal retires the service', async () => { + const { ctx, fiber } = bench() + await fiber.await() + await fiber.dispose() + expect(ctx.get('settingsScope')).toBeUndefined() + }) +})