import { addUniqueAction, isUsedAction } from './utils/uniqueAction'; import { actionConfigStore, isAction } from './decorators/action/store'; import { ActionConfig } from './ActionConfig'; import type { Server } from './Server'; import { PropertyConfig } from './PropertyConfig'; import { ActionPropertySorter } from './utils/ActionPropertySorter'; import { initSpecialPropertiesConfig } from './utils/initSpecialPropertiesConfig'; export class ActionBox { public type: any; public readonly config = new ActionConfig(); public initialContext: Record; constructor(public readonly name: string, public readonly originalInstance: T, public readonly server: Server) { if (isUsedAction(originalInstance)) { throw new Error('This action object is already in use.'); } if (!isAction(originalInstance)) { throw new Error('You must use @action decorator with action class.'); } addUniqueAction(originalInstance); this.type = originalInstance.constructor; this.initConfig(); this.initialContext = this.getInitialContextData(); } public async init(): Promise { await this.onAdded(); (new ActionPropertySorter(this.name, this.config)).sortPropertiesInConfig(); initSpecialPropertiesConfig(this); Object.freeze(this.config); } public getSpecialPropertyNames(): string[] { const { specialProperties } = this.config; if (specialProperties) { return Object.keys(specialProperties); } return []; } public hasProperty(propertyName: string): boolean { const { properties, specialProperties, methods } = this.config; return !!(properties.has(propertyName) || (specialProperties || {})[propertyName] || methods[propertyName]); } public hasPropertyConfig(propertyName: string): boolean { const { properties } = this.config; return properties.has(propertyName); } public getPropertyConfig(propertyName: string, onlyStatic: boolean): PropertyConfig | null { const cfg = this.config.properties.get(propertyName); if (cfg && cfg.static === onlyStatic) { return cfg; } return null; } // public getAllPropertyConfig(): ActionConfig['properties'] { // return this.config.properties; // } public getInitialContextData() { const result: Record = {}; this.config.properties.forEach((cfg, prop) => { if (cfg.out && !cfg.get) { result[prop] = (this.originalInstance as any)[prop]; } }); return result; } public callHandler(type: string, ...args: any[]): any { const handlerName = this.config.handlers[type]; if (!handlerName) { throw new Error(`Action ${this.name} error: A ${type} handler is not found.`); } const cnt = this.originalInstance.constructor as any; return cnt[handlerName](...args); } public check() { this.checkSetCookieBox(); } private initConfig() { Object.assign( this.config, actionConfigStore.get(this.type, false), ); if (this.config.momentOfInit !== 'call' && !this.config.permanent) { throw new Error(`The "${this.name}" action must be permanent.`); } } private checkSetCookieBox() { const { config: { properties, handlers } } = this; if (properties.hasSetCookiesProperty() && !handlers['set-cookies-http-handler']) { throw new Error(`Action ${this.name} must have a "set-cookies-http-handler" handler. Use decorator @handler.`); } } private async onAdded() { await this.config.hooks.onAdded?.(this); } }