UNPKG

@fastlanejs/base

Version:

Base Interface for communicating with the fastlane socket server

230 lines (229 loc) 7.9 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 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) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.withFastlaneBase = exports.doActionOnce = exports.FastlaneBase = void 0; const net_1 = require("net"); const child_process_1 = require("child_process"); class Deferred { constructor() { this.promise = new Promise((resolve, reject) => { this.resolve = resolve; this.reject = reject; }); } } class FastlaneError extends Error { constructor(payload) { super(payload.failure_message); this.class = payload.failure_class; this.information = payload.failure_information; } } class FastlaneBase { constructor(port = 2000, isInteractive = true, debug = false) { /** @private */ this.port = undefined; /** @private */ this.socket = undefined; /** @private */ this.isInteractive = true; /** @private */ this.childProcess = undefined; /** @private */ this._debug = false; this.port = port; this.socket = null; this.isInteractive = isInteractive; this.childProcess = null; this._debug = debug; } setDebug(newValue = true) { this._debug = newValue; } isDebug() { return this._debug; } log(...args) { if (this._debug) console.log(args); } start() { return __awaiter(this, void 0, void 0, function* () { if (!this.childProcess) this.childProcess = yield launch(this.isInteractive, this.port); if (!this.socket) this.socket = yield init(this.port); }); } close() { return __awaiter(this, void 0, void 0, function* () { const { resolve, promise } = new Deferred(); if (!this.socket) { this.socket = null; if (this.childProcess) this.childProcess.kill("SIGHUP"); this.childProcess = null; return; } const remove = once(this.socket, "error", () => { }); this.socket.end(() => { remove(); this.socket = null; this.childProcess.kill("SIGHUP"); this.childProcess = null; resolve(); }); return promise; }); } send({ commandType, command }) { return __awaiter(this, void 0, void 0, function* () { if (!this.socket) throw "Socket not initialized"; const json = JSON.stringify({ commandType, command }); this.log("Transmitting JSON", json); this.socket.write(json); const { resolve, promise, reject } = new Deferred(); this.socket.setEncoding("utf8"); const removeError = once(this.socket, "error", (d) => reject(d)); once(this.socket, "data", (d) => { try { removeError(); const o = JSON.parse(d); try { if (o.payload) { if (o.payload.status === "failure") { this.log("RECEIVED FAILURE", o); const e = new FastlaneError(o.payload); reject(e); } else if (typeof o.payload.return_object === "undefined") { reject(o); } const result = o.payload.return_object; resolve(result); } } catch (e) { reject(e); } } catch (e) { removeError(); reject(e); } }); return promise; }); } doAction(action, argObj) { return __awaiter(this, void 0, void 0, function* () { yield this.start(); const args = argObj ? Object.entries(argObj).map(([name, value]) => ({ name, value })) : undefined; const command = { commandType: "action", command: { methodName: action, args }, }; this.log("Do action sending command ", command); return this.send(command); }); } } exports.FastlaneBase = FastlaneBase; //#region Internal utility functions const asyncConnect = (options) => __awaiter(void 0, void 0, void 0, function* () { const { resolve, reject, promise } = new Deferred(); const initError = (e) => reject(e); try { const c = net_1.connect(options, () => { c.removeListener("error", initError); resolve(c); }); c.on("error", initError); } catch (e) { reject(e); } const out = yield promise; return out; }); const sleep = (ms) => new Promise((r) => setTimeout(() => r(), ms)); const launch = (interactive = true, port = 2000) => { return child_process_1.spawn("bundle", [ "exec", "fastlane", "socket_server", "--verbose", "-c", "30", "-s", "-p", port.toString(), ], Object.assign({}, (interactive ? { stdio: "inherit" } : {}))); }; const init = (port = 2000) => __awaiter(void 0, void 0, void 0, function* () { while (true) { let s; try { s = (yield Promise.all(["::1", "127.0.0.1"].map((host) => __awaiter(void 0, void 0, void 0, function* () { try { const socket = yield asyncConnect({ host, port }); return socket; } catch (e) { return null; } })))).find(Boolean); } catch (e) { } if (s) { return s; } yield sleep(500); } }); const once = (socket, event, f) => { const data = []; const listener = (d) => { data.push(d); if (d.length < 65536) { socket.removeListener(event, listener); const text = data.map((b) => b.toString("utf8")).join(""); f(text); } }; socket.on(event, listener); return () => socket.removeListener(event, listener); }; //#endregion //#region Exported Functions const withFastlaneBase = (f, { port = 2000, isInteractive = true, }) => __awaiter(void 0, void 0, void 0, function* () { const fastlane = new FastlaneBase(port, isInteractive); try { const result = yield f(fastlane); fastlane.close(); return result; } catch (e) { yield fastlane.close(); throw e; } }); exports.withFastlaneBase = withFastlaneBase; const doActionOnce = (action, argobj, isInteractive = true, port = 2000) => __awaiter(void 0, void 0, void 0, function* () { return withFastlaneBase(({ doAction }) => doAction(action, argobj), { port, isInteractive, }); }); exports.doActionOnce = doActionOnce;