| 32 | // EXPERIMENTAL: This function is subject to change |
| 33 | // Don't try to understand this wrapper |
| 34 | export function serviceInstance<T extends new (...args: any[]) => any>(ImplClass: T) { |
| 35 | let instance: any = null; |
| 36 | let state: 'pending' | 'initializing' | 'initialized' = 'pending'; |
| 37 | const onClass: (symbol | string)[] = ['name', 'Config', 'inject', 'prototype']; |
| 38 | const name = ImplClass.name; |
| 39 | |
| 40 | const proxyObj = new Proxy(ImplClass, { |
| 41 | get(_, key) { |
| 42 | if (key === 'Service') return proxyObj; |
| 43 | if (Reflect.has(ImplClass, key) || onClass.includes(key)) return Reflect.get(ImplClass, key); |
| 44 | if (state === 'pending') throw new Error(`${name} is not initialized yet (getting ${String(key)})`); |
| 45 | if (state === 'initializing') throw new Error(`${name} is still initializing (getting ${String(key)})`); |
| 46 | return Reflect.get(instance, key); |
| 47 | }, |
| 48 | has(_, key) { |
| 49 | if (key === Symbol.for('hydro.initialize')) return state !== 'pending'; |
| 50 | return Reflect.has(_, key); |
| 51 | }, |
| 52 | construct(_, args, target) { |
| 53 | if (state !== 'pending') throw new Error(`${name} is already constructed`); |
| 54 | state = 'initializing'; |
| 55 | const originalInit = ImplClass.prototype[Service.init]; |
| 56 | if (originalInit) { |
| 57 | ImplClass.prototype[Service.init] = new Proxy(originalInit, { |
| 58 | apply(...applyArgs) { |
| 59 | const result = Reflect.apply(...applyArgs); |
| 60 | if (result instanceof Promise) result.then(() => { state = 'initialized'; }); |
| 61 | else state = 'initialized'; |
| 62 | return result; |
| 63 | }, |
| 64 | }); |
| 65 | } |
| 66 | instance = Reflect.construct(ImplClass, args, target); |
| 67 | if (!Reflect.has(ImplClass, Service.init)) state = 'initialized'; |
| 68 | return instance; |
| 69 | }, |
| 70 | }) as (T extends (new (...args: any[]) => infer R) ? (R & { Service: (new (...args: any[]) => R) }) : never); |
| 71 | return proxyObj; |
| 72 | } |
| 73 | |
| 74 | export * from '@hydrooj/utils/lib/utils'; |