UNPKG

@ha4us/script.adapter

Version:

Scripting Adapter for the ha4us

233 lines 8.14 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); const vm = require("vm"); const domain = require("domain"); const rxjs_1 = require("rxjs"); const core_1 = require("@babel/core"); const core_2 = require("@ha4us/core"); const sandbox_class_1 = require("./sandbox.class"); var ScriptEventType; (function (ScriptEventType) { ScriptEventType["Error"] = "error"; ScriptEventType["Log"] = "log"; ScriptEventType["State"] = "state"; })(ScriptEventType = exports.ScriptEventType || (exports.ScriptEventType = {})); class ScriptEvent { constructor(name, type, data) { this.name = name; this.type = type; this.data = data; } } exports.ScriptEvent = ScriptEvent; class LogEvent { constructor(severity, message, attachments) { this.severity = severity; this.message = message; this.attachments = attachments; } } exports.LogEvent = LogEvent; class Ha4usScript { constructor(scriptObject, opts) { this.opts = opts; this.autostart = false; this._status = 'stopped'; this.status$ = new rxjs_1.Subject(); this.log$ = new rxjs_1.Subject(); this.name = scriptObject.topic; this.topic = core_2.MqttUtil.slice(this.name, 1); this.path = opts.$args.scriptsDir || process.cwd(); this.$log = opts.$log; this._source = scriptObject.native.source; this.autostart = scriptObject.native.autostart || typeof scriptObject.native.autostart === 'undefined'; } set source(val) { this._source = val; } set status(val) { this._status = val; this.status$.next(val); } get status() { return this._status; } log(severity, msg, attachments) { this.log$.next(new LogEvent(severity, msg, attachments)); } prepareStack(e, match) { const lines = e.stack.split('\n'); const stack = []; stack.push(e.message); for (const line of lines) { stack.push(line); if (line.match(match)) { // stack.splice(-1, 1) break; } } return stack.join('\n'); } init() { return __awaiter(this, void 0, void 0, function* () { this.status = 'stopped'; this.sandbox = new sandbox_class_1.Sandbox(this); this.domain = domain.create(); this.enterDomain(); this.$log.debug(`creating sandbox for ${this.name}`); this.domain.on('error', e => { this.$log.warn(`catched domain error ${e.message} in ${this.name}`); this.log$.next(new LogEvent('error', this.prepareStack(e, /domain:$/))); }); return this; }); } transpile(source) { return __awaiter(this, void 0, void 0, function* () { return new Promise((resolve, reject) => { core_1.transform(source, { presets: [ [ '@babel/env', { targets: { node: 'current', }, }, ], ], }, (err, res) => { if (err) { reject(err); } else { this.$log.debug(`${this.name} transpiled`); resolve(res.code); } }); }); }); } compile() { return __awaiter(this, void 0, void 0, function* () { if (!this._source) { throw new core_2.Ha4usError(500, `source not available`); } try { const transpiled = yield this.transpile(this._source); const extSource = `((async ()=>{${transpiled}})())`; this.script = new vm.Script(extSource, { filename: this.name, timeout: 1000, }); this.log('info', `script successfully compiled`); this.$log.debug(`${this.name} compiled`); return this; } catch (e) { this.$log.warn(`compilation of ${this.name} failed ${e.message}`); this.status = 'error'; this.log('error', this.prepareStack(e, /at Ha4usScript.compile|at Parser.raise/)); throw e; } }); } enterDomain() { if (process.domain !== this.domain) { this.$log.debug(`${this.name} entering script domain`); this.domain.enter(); } } start() { return __awaiter(this, void 0, void 0, function* () { if (this.status === 'running') { return this; } if (!this.script) { throw new core_2.Ha4usError(500, `script is not compiled`); } this.$log.debug('Starting script'); const context = vm.createContext(this.sandbox.sandbox); return this.script .runInContext(context) .then((val) => { this.result = val; if (!this.sandbox._onDestroy) { this.log('info', `script has no $onDestroy function`); } this.log('info', `script finished with result ${val}`); this.status = 'running'; return this; }) .catch(e => { this.log('error', this.prepareStack(e, /Script.runInContext/)); this.status = 'error'; throw new core_2.Ha4usError(500, 'error script execution', e); }); }); } stop() { return __awaiter(this, void 0, void 0, function* () { if (this.status !== 'running') { return this; } this.$log.debug('Stopping script'); this.sandbox.stop$.next(); try { if (this.sandbox._onDestroy) { this.sandbox._onDestroy(); } } catch (e) { this.log('error', this.prepareStack(e, /at ContextifyScript.Script.runInContext/)); } finally { this.status = 'stopped'; } return this; }); } restart() { return __awaiter(this, void 0, void 0, function* () { this.$log.debug('Restarting script'); yield this.stop(); if (this.autostart === true) { yield this.start(); } return this; }); } destroy() { return __awaiter(this, void 0, void 0, function* () { yield this.stop(); this.log('info', `script destroyed`); this.log$.complete(); this.status$.complete(); if (this.domain) { this.domain.removeAllListeners(); this.domain.exit(); } return this; }); } toHa4usObject() { return { role: core_2.Ha4usRole.Script, native: { source: this._source, }, }; } } exports.Ha4usScript = Ha4usScript; //# sourceMappingURL=ha4us-script.js.map