import { AsyncLocalStorage } from 'node:async_hooks'; export type Fn = (...args: T) => R; export const createContext = () => { const storage = new AsyncLocalStorage<{ context: T }>(); return { contextualize(context: T, callback: Fn<[], R>): R { return storage.run({ context }, callback); }, getContext() { const store = storage.getStore(); if (!store) { throw new Error('Context not initialized'); } return store.context; } }; }; type Constructor = new (...args: any) => any; export interface Contextualized { contextualize(callback: Fn<[], R>): Promise; } export function Contextable(BaseClass: Constructor = class { }) { const context = createContext(); return class Contextual extends BaseClass { static getContext(this: C) { return context.getContext() as InstanceType; } constructor(...args: unknown[]) { super(...args); } contextualize(callback: Fn<[], R>): R { return context.contextualize(this, callback); } }; } export function contextualize(...args: unknown[]) { const [, , descriptor] = args as [T, K, TypedPropertyDescriptor]; const property = descriptor?.value; if (typeof property === 'function') { descriptor.value = function(this: Contextualized, ...args: unknown[]) { return this.contextualize(() => property.apply(this, args)); } as T[K]; } }