import * as cordis from 'cordis'; import Schema from 'schemastery'; import type { ServerEvents, WebService } from '@hydrooj/framework'; import type { DomainDoc, GeoIP, ModuleInterfaces } from './interface'; import { inject } from './lib/ui'; import { Loader } from './loader'; import type { EventMap } from './service/bus'; import type { CheckService } from './service/check'; import type { } from './service/migration'; import type { ConnectionHandler, Handler } from './service/server'; export interface Events extends cordis.Events, EventMap, ServerEvents { } function addScript(name: string, description: string, validate: Schema, run: (args: K, report: any) => boolean | Promise) { if (global.Hydro.script[name]) throw new Error(`duplicate script ${name} registered.`); global.Hydro.script[name] = { description, validate, run }; return () => delete global.Hydro.script[name]; } function provideModule(type: T, id: string, module: ModuleInterfaces[T]) { if (global.Hydro.module[type][id]) throw new Error(`duplicate script ${type}/${id} registered.`); global.Hydro.module[type as any][id] = module; return () => delete global.Hydro.module[type][id]; } export type EffectScope = cordis.EffectScope; export type ForkScope = cordis.ForkScope; export type MainScope = cordis.MainScope; export type { Disposable, ScopeStatus, Plugin } from 'cordis'; export interface Context extends cordis.Context, Pick { // @ts-ignore [Context.events]: Events; loader: Loader; check: CheckService; setImmediate: typeof setImmediate; addScript: typeof addScript; provideModule: typeof provideModule; injectUI: typeof inject; broadcast: Context['emit']; geoip?: GeoIP; } export abstract class Service extends cordis.Service { [cordis.Service.setup]() { this.ctx = new Context() as C; } } const T = any>(origFunc: F, disposeFunc?) => function method(this: cordis.Service, ...args: Parameters) { const res = origFunc(...args); this[Context.current]?.on('dispose', () => (disposeFunc ? disposeFunc(res) : res())); }; export class ApiMixin extends Service { addScript = T(addScript); setImmediate = T(setImmediate, clearImmediate); provideModule = T(provideModule); injectUI = T(inject); broadcast = (event: keyof EventMap, ...payload) => this.ctx.emit('bus/broadcast', event, payload); constructor(ctx) { super(ctx, '$api', true); ctx.mixin('$api', ['addScript', 'setImmediate', 'provideModule', 'injectUI', 'broadcast']); } } export class Context extends cordis.Context { domain?: DomainDoc; constructor(config: {} = {}) { super(config); this.plugin(ApiMixin); } } const old = cordis.Registry.prototype.inject; // cordis.Registry.prototype.using = old; cordis.Registry.prototype.inject = function wrapper(...args) { if (typeof args[0] === 'string') { console.warn('old functionality of ctx.inject is deprecated. please use ctx.injectUI instead.'); return T(inject).call(this, ...args as any) as any; } return old.call(this, ...args); };