feat(plugin-sdk): utilize details prop for richer o11y on waiting-deps
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { join } from 'node:path'
|
||||
|
||||
import { createContext, defineEventa, defineInvokeHandler } from '@moeru/eventa'
|
||||
import { moduleStatus } from '@proj-airi/plugin-protocol/types'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { FileSystemLoader, PluginHost } from '.'
|
||||
@@ -268,6 +269,50 @@ describe('for PluginHost', () => {
|
||||
expect(session.phase).toBe('ready')
|
||||
})
|
||||
|
||||
it('should emit dependency wait details while waiting for required capabilities', async () => {
|
||||
const host = new PluginHost({
|
||||
runtime: 'electron',
|
||||
transport: { kind: 'in-memory' },
|
||||
})
|
||||
|
||||
const session = await host.load(testManifest, { cwd: '' })
|
||||
const statusEvents: Array<{ body?: Record<string, unknown> }> = []
|
||||
session.channels.host.on(moduleStatus, (payload) => {
|
||||
statusEvents.push(payload as unknown as { body?: Record<string, unknown> })
|
||||
})
|
||||
|
||||
const started = host.init(session.id, {
|
||||
requiredCapabilities: ['cap:custom'],
|
||||
capabilityWaitTimeoutMs: 2000,
|
||||
})
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 20))
|
||||
|
||||
const waitingStatus = statusEvents.find((event) => {
|
||||
const body = event.body
|
||||
return body?.phase === 'preparing' && typeof body.reason === 'string' && body.reason.includes('Waiting for capabilities:')
|
||||
})
|
||||
|
||||
expect(waitingStatus).toBeDefined()
|
||||
expect(waitingStatus?.body).toMatchObject({
|
||||
phase: 'preparing',
|
||||
details: {
|
||||
lifecyclePhase: 'waiting-deps',
|
||||
requiredCapabilities: ['cap:custom'],
|
||||
unresolvedCapabilities: ['cap:custom'],
|
||||
timeoutMs: 2000,
|
||||
},
|
||||
})
|
||||
|
||||
reportPluginCapability(host, {
|
||||
key: 'cap:custom',
|
||||
state: 'ready',
|
||||
metadata: { source: 'test' },
|
||||
})
|
||||
const initialized = await started
|
||||
expect(initialized.phase).toBe('ready')
|
||||
})
|
||||
|
||||
it('should fail when required capabilities timeout', async () => {
|
||||
const host = new PluginHost({
|
||||
runtime: 'electron',
|
||||
|
||||
@@ -635,14 +635,23 @@ export class PluginHost {
|
||||
|
||||
// Optional dependency gate before plugin-owned initialization.
|
||||
if (options.requiredCapabilities?.length) {
|
||||
const capabilityTimeoutMs = options.capabilityWaitTimeoutMs ?? 15000
|
||||
const unresolvedCapabilities = options.requiredCapabilities.filter(key => !this.isCapabilityReady(key))
|
||||
assertTransition(session, 'waiting-deps')
|
||||
session.channels.host.emit(moduleStatus, {
|
||||
identity: session.identity,
|
||||
phase: 'preparing',
|
||||
reason: `Waiting for capabilities: ${options.requiredCapabilities.join(', ')}`,
|
||||
details: {
|
||||
// For richer observability
|
||||
lifecyclePhase: 'waiting-deps',
|
||||
requiredCapabilities: options.requiredCapabilities,
|
||||
unresolvedCapabilities,
|
||||
timeoutMs: capabilityTimeoutMs,
|
||||
},
|
||||
})
|
||||
|
||||
await this.waitForCapabilities(options.requiredCapabilities, options.capabilityWaitTimeoutMs)
|
||||
await this.waitForCapabilities(options.requiredCapabilities, capabilityTimeoutMs)
|
||||
assertTransition(session, 'prepared')
|
||||
session.channels.host.emit(modulePrepared, {
|
||||
identity: session.identity,
|
||||
|
||||
Reference in New Issue
Block a user