core: use serviceInstance for system model

This commit is contained in:
undefined committed 2025-10-25 23:43:12 +08:00
1 parent 2ad1e1d533
commit f6a9dd3557
30 files changed
+143 -104

No files matched your search

+43
View File
@@ -1,4 +1,5 @@
import { isNullable } from 'cosmokit';
import { Service } from './context';
export function buildProjection<T extends string | number = string>(fields: readonly T[]): Record<T, true> {
const o: Record<T, true> = Object.create(null);
@@ -28,4 +29,46 @@ export function unwrapExports(module: any) {
return module.default ?? module;
}
// EXPERIMENTAL: This function is subject to change
// Don't try to understand this wrapper
export function serviceInstance<T extends new (...args: any[]) => any>(ImplClass: T) {
let instance: any = null;
let state: 'pending' | 'initializing' | 'initialized' = 'pending';
const onClass: (symbol | string)[] = ['name', 'Config', 'inject', 'prototype'];
const name = ImplClass.name;
const proxyObj = new Proxy(ImplClass, {
get(_, key) {
if (key === 'Service') return proxyObj;
if (Reflect.has(ImplClass, key) || onClass.includes(key)) return Reflect.get(ImplClass, key);
if (state === 'pending') throw new Error(`${name} is not initialized yet (getting ${String(key)})`);
if (state === 'initializing') throw new Error(`${name} is still initializing (getting ${String(key)})`);
return Reflect.get(instance, key);
},
has(_, key) {
if (key === Symbol.for('hydro.initialize')) return state !== 'pending';
return Reflect.has(_, key);
},
construct(_, args, target) {
if (state !== 'pending') throw new Error(`${name} is already constructed`);
state = 'initializing';
const originalInit = ImplClass.prototype[Service.init];
if (originalInit) {
ImplClass.prototype[Service.init] = new Proxy(originalInit, {
apply(...applyArgs) {
const result = Reflect.apply(...applyArgs);
if (result instanceof Promise) result.then(() => { state = 'initialized'; });
else state = 'initialized';
return result;
},
});
}
instance = Reflect.construct(ImplClass, args, target);
if (!Reflect.has(ImplClass, Service.init)) state = 'initialized';
return instance;
},
}) as (T extends (new (...args: any[]) => infer R) ? (R & { Service: (new (...args: any[]) => R) }) : never);
return proxyObj;
}
export * from '@hydrooj/utils/lib/utils';