@fastlanejs/base
Version:
Base Interface for communicating with the fastlane socket server
230 lines (229 loc) • 7.9 kB
JavaScript
;
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;