UNPKG

@ayanaware/bento

Version:

Modular runtime framework designed to solve complex tasks

369 lines 13.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.EntityAPI = void 0; const errors_1 = require("@ayanaware/errors"); const ApiError_1 = require("../../errors/ApiError"); const Entity_1 = require("../interfaces/Entity"); /** * Shared functions for ComponentAPI and PluginAPI */ class EntityAPI { constructor(bento) { /** * Currently existing subscriptions of this component. * The key is the namespace where a subscription was added, * the value is an array of subscription ids on that namespace. */ this.subscriptions = new Map(); this.bento = bento; } /** * Get the semantic version string of the bento instance attached to this component api * @returns Semantic version string (https://semver.org) */ getBentoVersion() { return this.bento.version; } /** * Check if bento has a given property * @param name name of property * * @returns boolean */ hasProperty(name) { return this.bento.properties.hasProperty(name); } /** * Fetch the value of given application property * @param name name of application property * * @returns Property value */ getProperty(name) { return this.bento.properties.getProperty(name); } /** * Check if bento has a given variable * @param name name of variable * * @returns boolean */ hasVariable(name) { return this.bento.variables.hasVariable(name); } /** * Gets the value of a variable * @param definition Variable name or definition * * @returns Variable value */ getVariable(definition) { // if string, convert to basic definition if (typeof definition === 'string') { definition = { name: definition, }; } // validate definition if (!definition.name) throw new ApiError_1.ApiError(this.entity, 'VariableDefinition must define a name'); const value = this.bento.variables.getVariable(definition.name, definition.default); // if undefined. then is a required variable that is not in bento if (value === undefined) throw new ApiError_1.ApiError(this.entity, `No value available for "${definition.name}" variable`); return value; } /** * Check if Entity exists * @param reference EntityReference * * @returns boolean */ hasEntity(reference) { return this.bento.entities.hasEntity(reference); } /** * Get Entity * @param reference EntityReference * * @returns Entity */ getEntity(reference) { const name = this.bento.entities.resolveReference(reference); const entity = this.bento.entities.getEntity(name); if (!entity) throw new ApiError_1.ApiError(this.entity, `Entity "${name}" does not exist`); return entity; } /** * Check if Plugin exists * @param reference PluginReference * * @returns boolean */ hasPlugin(reference) { return this.bento.entities.hasPlugin(reference); } /** * Get Plugin * @param reference PluginReference * * @returns Plugin */ getPlugin(reference) { const name = this.bento.entities.resolveReference(reference); const plugin = this.bento.entities.getPlugin(name); if (!plugin) throw new ApiError_1.ApiError(this.entity, `Plugin "${name}" does not exist`); return plugin; } /** * Check if Component exists * @param reference ComponentReference * * @returns boolean */ hasComponent(reference) { return this.bento.entities.hasComponent(reference); } /** * Get Component * @param reference ComponentReference * * @returns Component */ getComponent(reference) { const name = this.bento.entities.resolveReference(reference); const component = this.bento.entities.getComponent(name); if (!component) throw new ApiError_1.ApiError(this.entity, `Component "${name}" does not exist`); return component; } /** * Check Plugin Depend on Component * @param reference EntityReference * * @throws APIError if this.entity is plugin and reference is component */ checkPdc(reference) { if (this.entity.type !== Entity_1.EntityType.PLUGIN) return; const entity = this.bento.entities.getEntity(reference); if (!entity) return; if (entity.type === Entity_1.EntityType.COMPONENT) { throw new ApiError_1.ApiError(this.entity, `Plugin cannot depend on ${entity.name}(component)`); } } /** * Inject an entity into invoking entity * @param reference Entity name or reference * @param injectName Property name to inject to */ injectEntity(reference, injectName) { const name = this.bento.entities.resolveReference(reference); if (this.hasPlugin(name)) this.injectPlugin(name, injectName); else if (this.hasComponent(name)) this.injectComponent(name, injectName); else throw new ApiError_1.ApiError(this.entity, 'Unable to inject non-existent entity'); } /** * Inject plugin into invoking entity * @param reference Plugin name or reference * @param injectName property name to inject into */ injectPlugin(reference, injectName) { if (!this.hasPlugin(reference)) throw new ApiError_1.ApiError(this.entity, `Unable to inject non-existent plugin "${reference}"`); Object.defineProperty(this.entity, injectName, { configurable: true, enumerable: true, get: () => this.getPlugin(reference), set: () => { throw new errors_1.IllegalAccessError('Cannot write to injected plugin'); }, }); } /** * Inject component dependency into invoking entity * @param reference Component name or reference * @param injectName property name to inject into */ injectComponent(reference, injectName) { if (!this.hasComponent(reference)) throw new ApiError_1.ApiError(this.entity, `Unable to inject non-existent component "${reference}"`); // prevent inject of component into plugin this.checkPdc(reference); Object.defineProperty(this.entity, injectName, { configurable: true, enumerable: true, get: () => this.getComponent(reference), set: () => { throw new errors_1.IllegalAccessError('Cannot write to injected component'); }, }); } /** * Defines and attaches a variable to component * @param definition Variable definition * @param injectName property name to inject into */ injectVariable(definition, injectName) { if (!definition.name) throw new ApiError_1.ApiError(this.entity, 'VariableDefinition must have a name'); // if variable not in bento, and no default defined. Throw an error if (!this.hasVariable(definition.name) && definition.default === undefined) { throw new ApiError_1.ApiError(this.entity, `Cannot inject non-existant variable "${definition.name}"`); } const property = injectName || definition.name; // attach property to component Object.defineProperty(this.entity, property, { configurable: true, enumerable: false, get: () => this.getVariable(definition), set: () => { throw new errors_1.IllegalAccessError('Cannot write to injected variable'); }, }); } /** * Emit a event on Bento Events * @param eventName Name of event * @param args Ordered Array of args to emit */ emit(eventName, ...args) { const emitter = this.bento.entities.getEvents(this.entity.name); emitter.emit(eventName, ...args); } /** * Emit subject event on Bento Events * @param eventName Name of event * @param args Ordered Array of args to emit */ emitSubject(eventName, ...args) { const emitter = this.bento.entities.getEvents(this.entity.name); emitter.emitSubject(eventName, ...args); } // TODO: Add a error handler /** * Re-emits events from a standard event emitter into Bento events. * * @param fromEmitter Emitter to re-emit from * @param events Events to watch for * * @throws IllegalArgumentError if fromEmitter is not an EventEmitter or events is not an array */ forwardEvents(fromEmitter, events) { if (events != null && !Array.isArray(events)) throw new ApiError_1.ApiError(this.entity, 'Events is not an array'); const emitter = this.bento.entities.getEvents(this.entity.name); events.forEach(event => { fromEmitter.addListener(event, (...args) => { try { emitter.emit(event, ...args); } catch (e) { // TODO call error handler } }); }); } /** * Subscribe to Bento events * @param reference Entity Reference / Name * @param event Name of the event * @param handler The function to be called * @param context Optional `this` context for prior handler function * * @returns Subscription ID */ // tslint:disable-next-line:max-params subscribe(reference, event, handler, context) { const name = this.bento.entities.resolveReference(reference); if (!name) throw new ApiError_1.ApiError(this.entity, 'Unable to subscribe to non-existant entity'); // prevent plugin subscribing to component events this.checkPdc(reference); // Get the namespace const events = this.bento.entities.getEvents(name); const id = events.subscribe(event, handler, context); // Register subscription so if the current component unloads we can remove all events // TODO: If the componentName component unloads we need to remove that array if (!this.subscriptions.has(name)) this.subscriptions.set(name, []); this.subscriptions.get(name).push(id); return id; } /** * Alias for subscribe with normal event * @param reference Entity Reference / Name * @param event Name of the event * @param handler The function to be called * @param context Optional `this` context for above handler function * * @deprecated * * @returns Subscription ID */ subscribeEvent(reference, event, handler, context) { return this.subscribe(reference, event, handler, context); } /** * Ubsubscribe from a Component Event * @param reference - Component Reference / Name * @param id - subscription id provided by subscribe */ unsubscribe(reference, id) { const name = this.bento.entities.resolveReference(reference); if (!name) throw new ApiError_1.ApiError(this.entity, 'Unable to unsubscribe from non-existant entity'); // Check if the component events exists const events = this.bento.entities.getEvents(name); if (events == null) return; // Check if this subscriber actually exists const subscriber = this.subscriptions.get(name); if (subscriber == null || !subscriber.includes(id)) throw new errors_1.IllegalArgumentError(`Tried to unsubscribe from unknown id "${id}"`); // Unsubscribe events.unsubscribe(id); // Remove subID subscriber.splice(subscriber.indexOf(id), 1); } /** * Unsubscribes from all events in a namespace or all events alltogether. * This will automatically get called when your component gets unloaded * * @param reference - Optional. A namespace where all events should be unsubscribed */ unsubscribeAll(reference) { if (reference != null) { const name = this.bento.entities.resolveReference(reference); if (!name) throw new ApiError_1.ApiError(this.entity, 'Unable to ubsubscribeAll to non-existant entity'); if (!this.bento.entities.hasEvents(name)) return; const events = this.bento.entities.getEvents(name); // Get subscriptions on that component const subscriptions = this.subscriptions.get(name); if (subscriptions == null) return; // Unsubscribe from all events for (const subId of subscriptions) { events.unsubscribe(subId); } // Remove array this.subscriptions.delete(name); } else { // No componentName was given so we unsubscribe everything for (const ns of this.subscriptions.keys()) { this.unsubscribeAll(ns); } } } } exports.EntityAPI = EntityAPI; //# sourceMappingURL=EntityAPI.js.map