flipper-server-client
Version:
Lightweight abstraction to connect to Flipper Server and provide an API to interact with it
157 lines • 6.9 kB
JavaScript
;
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createFlipperServerWithSocket = exports.createFlipperServer = exports.FlipperServerState = void 0;
const eventemitter3_1 = __importDefault(require("eventemitter3"));
const flipper_common_1 = require("flipper-common");
const reconnecting_websocket_1 = __importDefault(require("reconnecting-websocket"));
const CONNECTION_TIMEOUT = 30 * 1000;
const EXEC_TIMEOUT = 45 * 1000;
var FlipperServerState;
(function (FlipperServerState) {
FlipperServerState[FlipperServerState["CONNECTING"] = 0] = "CONNECTING";
FlipperServerState[FlipperServerState["CONNECTED"] = 1] = "CONNECTED";
FlipperServerState[FlipperServerState["DISCONNECTED"] = 2] = "DISCONNECTED";
})(FlipperServerState || (exports.FlipperServerState = FlipperServerState = {}));
function createFlipperServer(host, port, tokenProvider, onStateChange) {
const URLProvider = () => {
const token = tokenProvider();
return `ws://${host}:${port}?token=${token}`;
};
const socket = new reconnecting_websocket_1.default(URLProvider);
return createFlipperServerWithSocket(socket, port, onStateChange);
}
exports.createFlipperServer = createFlipperServer;
function createFlipperServerWithSocket(socket, port, onStateChange) {
onStateChange(FlipperServerState.CONNECTING);
return new Promise((resolve, reject) => {
let initialConnectionTimeout = setTimeout(() => {
reject(new Error(`Failed to connect to the server in a timely manner.
It may be unresponsive. Run the following from the terminal
'sudo kill -9 $(lsof -t -i :${port})' as to kill any existing running instance, if any.`));
}, CONNECTION_TIMEOUT);
const eventEmitter = new eventemitter3_1.default();
const pendingRequests = new Map();
let requestId = 0;
let connected = false;
socket.addEventListener('open', () => {
connected = true;
onStateChange(FlipperServerState.CONNECTED);
if (initialConnectionTimeout) {
clearTimeout(initialConnectionTimeout);
initialConnectionTimeout = undefined;
resolve(flipperServer);
}
});
socket.addEventListener('close', () => {
connected = false;
onStateChange(FlipperServerState.DISCONNECTED);
pendingRequests.forEach((r) => r.reject(new flipper_common_1.FlipperServerDisconnectedError('ws-close')));
pendingRequests.clear();
});
socket.addEventListener('message', ({ data }) => {
try {
const { event, payload } = JSON.parse(data.toString());
switch (event) {
case 'exec-response': {
const entry = pendingRequests.get(payload.id);
if (!entry) {
console.warn(`Unknown request id `, payload.id);
}
else {
pendingRequests.delete(payload.id);
clearTimeout(entry.timeout);
entry.resolve(payload.data);
}
break;
}
case 'exec-response-error': {
const entry = pendingRequests.get(payload.id);
if (!entry) {
console.warn(`flipper-server: Unknown request id `, payload.id);
}
else {
pendingRequests.delete(payload.id);
clearTimeout(entry.timeout);
entry.reject(payload.data);
}
break;
}
case 'server-event': {
eventEmitter.emit(payload.event, payload.data);
break;
}
default: {
console.warn('flipper-server: received unknown message type', data.toString());
}
}
}
catch (e) {
console.warn('flipper-server: failed to process message', data.toString());
}
});
const commandOrOptionsIsOptions = (commandOrOptions) => typeof commandOrOptions === 'object';
const flipperServer = {
async connect() { },
close() { },
exec(commandOrOptions, ...argsAmbiguous) {
let timeout;
let command;
let args;
if (commandOrOptionsIsOptions(commandOrOptions)) {
timeout = commandOrOptions.timeout;
command = argsAmbiguous[0];
args = argsAmbiguous.slice(1);
}
else {
timeout = EXEC_TIMEOUT;
command = commandOrOptions;
args = argsAmbiguous;
}
if (connected) {
const id = ++requestId;
return new Promise((resolve, reject) => {
pendingRequests.set(id, {
resolve,
reject,
timeout: setInterval(() => {
pendingRequests.delete(id);
reject(new flipper_common_1.FlipperServerTimeoutError(`timeout for command '${command}'`));
}, timeout),
});
const execMessage = {
event: 'exec',
payload: {
id,
command,
args,
},
};
socket.send(JSON.stringify(execMessage));
});
}
else {
throw new Error('Not connected to Flipper server');
}
},
on(event, callback) {
eventEmitter.on(event, callback);
},
off(event, callback) {
eventEmitter.off(event, callback);
},
};
});
}
exports.createFlipperServerWithSocket = createFlipperServerWithSocket;
//# sourceMappingURL=FlipperServerClient.js.map