Instrumenting every use case with one generic wrapper
instrumentContainer<C> adds tracing to every use case in the dependency-injection container in one pass, and returns the exact type C — no caller needs a cast to get its use case back. The type guard was added after a real incident: a plain boolean in the container hit `new Proxy()` and took the API down on 24 September 2026.
export function instrumentUseCase<T extends object>(name: string, instance: T): T {
return new Proxy(instance, {
get(target, prop) {
const value = (target as Record<PropertyKey, unknown>)[prop]
if (prop !== 'execute' || typeof value !== 'function') {
return typeof value === 'function' ? value.bind(target) : value
}
// …
},
})
}
export function instrumentContainer<C extends Record<string, unknown>>(
container: C,
exclude: ReadonlySet<string>,
): C {
const isUseCase = (value: unknown): value is object =>
typeof value === 'object' &&
value !== null &&
typeof (value as { execute?: unknown }).execute === 'function'
return Object.fromEntries(
Object.entries(container).map(([key, value]) =>
exclude.has(key) || !isUseCase(value)
? [key, value]
: [key, instrumentUseCase(value.constructor.name, value)],
),
) as C
}SourceVotArenasrc/lib/observability/useCaseObservability.ts