import { EventEmitter } from 'eventemitter3'; import { ClientConfig, FactoryStore } from './ClientConfig'; import { ClientAdapter } from './adapter/ClientAdapter'; import { ClientEvent } from './ClientEvent'; import { ClientActionInstance } from './ClientActionInstance'; import { InstanceManager, InstanceManagerClient } from './InstanceManager'; import { MessageProcessor } from './MessageProcessor'; import { Meta } from '../server/actions/Meta'; import { ActionBoxManager } from './ActionBoxManager'; import { ConnectionProcessor } from './ConnectionProcessor'; export type ClientEvents = 'connected' | 'ready' | 'disconnect' | 'closed' | 'error'; export class Client extends EventEmitter implements InstanceManagerClient { public readonly adapter: ClientAdapter; public readonly actionBoxManager: ActionBoxManager; public readonly instances: InstanceManager; public readonly connection: ConnectionProcessor; public get isReady() { return this.connection.connected; } protected localInstanceIdSeq = 1; protected messages = new MessageProcessor(this); private readonly specialFactories: FactoryStore = { defaultFactory: () => { throw new Error('Unknown special property.'); }, event: () => new ClientEvent(), }; constructor(public readonly config: ClientConfig) { super(); const { adapter, specialFactories } = config; const cfg = { ...config } as Partial; Object.assign(this, config); Object.assign(this.specialFactories, specialFactories || {}); if (adapter) { adapter.setClient(this); this.adapter = adapter; } else { throw new Error('Adapter is required.'); } delete cfg.adapter; delete cfg.specialFactories; delete cfg.actionFactories; this.actionBoxManager = new ActionBoxManager(this); this.instances = new InstanceManager(this); this.connection = new ConnectionProcessor(this); } getActionBoxManager(): ActionBoxManager { return this.actionBoxManager; } public async initMetaAction() { this.actionBoxManager.setMeta(await this.create('#meta')); } public getMessageDispatcher() { return this.messages; } public async create(actionName: string, contextData: Record = {}): Promise { return this.instances.create(actionName, contextData); } public async destroy(instance: any & ClientActionInstance): Promise { await this.instances.destroy(instance); } public getUrl() { return this.adapter.getUrl(); } public createTransaction() { return this.messages.createTransaction(); } public getLastError(): any { return this.messages.lastError; } public getSpecialFactory(type: string) { return this.specialFactories[type] || this.specialFactories.defaultFactory; } public getActionBox(actionName: string) { return this.actionBoxManager.getActionBox(actionName); } protected getLocalInstanceId(actionName: string): string { const id = `clnt-${actionName}-inst-${this.localInstanceIdSeq}`; this.localInstanceIdSeq += 1; return id; } }