type callModule = typeof import("libkmodule").callModule; type connectModule = typeof import("libkmodule").connectModule; type logErr = typeof import("libkmodule").logErr; type log = typeof import("libkmodule").log; import type { ErrTuple, DataFn } from "@siaweb/libweb"; import EventEmitter from "eventemitter2"; type callModuleBound = (method: string, data?: any) => Promise; type connectModuleBound = ( method: string, data: any, receiveUpdate: DataFn ) => [sendUpdate: DataFn, response: Promise]; let callModule: callModule; let connectModule: connectModule; let log: log; let logErr: logErr; export interface ModuleBag { callModule: callModule; connectModule: connectModule; log: log; logErr: logErr; } export interface ModuleBagBound extends ModuleBag { callModule: callModuleBound; connectModule: connectModuleBound; } export abstract class Client extends EventEmitter { private _callModule?: callModuleBound; get callModule(): callModuleBound { return this._callModule as callModuleBound; } private _log?: log; get log(): log { return this._log as log; } private _logErr?: logErr; get logErr(): logErr { return this._logErr as logErr; } private _connectModule?: connectModuleBound; get connectModule(): connectModuleBound { return this._connectModule as connectModuleBound; } public async loadLibs(module: string): Promise { if (this._callModule && this._connectModule) { return; } const moduleBag = await this.loadBound(module); this._callModule = async (...args) => { const ret = await moduleBag.callModule(...args); this.handleError(ret); return ret; }; this._connectModule = moduleBag.connectModule; this._log = log; this._logErr = logErr; } public async loadBound(module: string): Promise { return (await load(module)) as ModuleBagBound; } protected handleError(ret: ErrTuple): void { if (ret[1]) { throw new Error(ret[1]); } } protected handleErrorOrReturn(ret: ErrTuple): any { this.handleError(ret); return ret[0]; } protected async callModuleReturn(method: string, data?: any): Promise { const ret = await this.callModule(method, data); return ret[0]; } } export async function load( module?: string ): Promise { if (callModule && connectModule) { if (module) { return { callModule: callModule.bind(undefined, module), connectModule: connectModule.bind(undefined, module), log, logErr, } as ModuleBagBound; } return { callModule, connectModule, log, logErr, } as ModuleBag; } const pkg = typeof window !== "undefined" && window?.document ? await import("libkernel") : await import("libkmodule"); callModule = pkg.callModule; connectModule = pkg.connectModule; // @ts-ignore log = pkg.log; // @ts-ignore logErr = pkg.logErr; return load(module); } type ClientConstructor = new (...args: any[]) => U; export const factory = function ( type: ClientConstructor, module: string ) { return function (...args: any): T { return new Proxy(new type(...args), { get(target: T, property: string) { let desc = Object.getOwnPropertyDescriptor( target?.constructor?.prototype, property ); if (!desc?.get) { const prop = target[property as keyof T]; if (typeof prop !== "function") { return prop; } } return async (...args: any[]): Promise => { await target.loadLibs(module); if (desc?.get) { return target[property as keyof T]; } return (target[property as keyof T] as Function)(...args); }; }, }); }; }; export async function maybeGetAsyncProperty(object: any) { if (typeof object === "function") { object = object(); } if (isPromise(object)) { object = await object; } return object; } export function isPromise(obj: Promise) { return ( !!obj && (typeof obj === "object" || typeof obj === "function") && typeof obj.then === "function" ); }