Initialise flipper-ui-browser with socket connection

Summary:
This diff sets up the socket connection between flipper-browser and flipper-server, and verifies that the initial UI initialisation work (e.g. `get-config` command works). The initial RenderHost is initialised as well based on the config and browser APIs.

Note that flipper-ui-core itself isn't started yet, as that has still a plethora of node imports, so Metro will correctly refuse to bundle

Not in this diff
* remove Node usage from flipper-ui-core
* implement all RenderHost APIs

Reviewed By: aigoncharov

Differential Revision: D32644074

fbshipit-source-id: 2c8065caf0191771a3867b69a431ca50eeb7a5a3
This commit is contained in:
Michel Weststrate
2021-12-08 04:25:28 -08:00
committed by Facebook GitHub Bot
parent 29a907c733
commit 5d45bd741b
20 changed files with 570 additions and 45 deletions

View File

@@ -0,0 +1,120 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
import EventEmitter from 'eventemitter3';
import {FlipperServer} from 'flipper-common';
import {io, Socket} from 'socket.io-client';
const CONNECTION_TIMEOUT = 30 * 1000;
const EXEC_TIMOUT = 10 * 1000;
export function createFlipperServer(): Promise<FlipperServer> {
// TODO: polish this all!
window.flipperShowError?.('Connecting to server...');
return new Promise<FlipperServer>((resolve, reject) => {
const initialConnectionTimeout = setTimeout(() => {
reject(
new Error('Failed to connect to Flipper server in a timely manner'),
);
}, CONNECTION_TIMEOUT);
const eventEmitter = new EventEmitter();
// TODO: recycle the socket that is created in index.web.dev.html?
const socket: Socket = io();
const pendingRequests: Map<
number,
{
resolve: (data: any) => void;
reject: (data: any) => void;
timeout: ReturnType<typeof setTimeout>;
}
> = new Map();
let requestId = 0;
let connected = false;
socket.on('connect', () => {
window?.flipperHideError?.();
console.log('Socket to Flipper server connected');
connected = true;
});
socket.once('connect', () => {
// only relevant for the first connect
resolve(flipperServer);
clearTimeout(initialConnectionTimeout);
});
socket.on('disconnect', () => {
window?.flipperShowError?.('WebSocket connection lost');
console.warn('Socket to Flipper server disconnected');
connected = false;
});
socket.on('exec-response', (id: number, data: any) => {
console.debug('exec <<<', id, data);
const entry = pendingRequests.get(id);
if (!entry) {
console.warn(`Unknown request id `, id);
} else {
pendingRequests.delete(id);
clearTimeout(entry.timeout);
entry.resolve(data);
}
});
socket.on('exec-response-error', (id: number, error: any) => {
console.debug('exec <<< [SERVER ERROR]', id, error);
const entry = pendingRequests.get(id);
if (!entry) {
console.warn(`Unknown request id `, id);
} else {
pendingRequests.delete(id);
clearTimeout(entry.timeout);
entry.reject(error);
}
});
socket.on('event', (eventType, data) => {
eventEmitter.emit(eventType, data);
});
const flipperServer: FlipperServer = {
async connect() {},
close() {},
exec(command, ...args): any {
if (connected) {
return new Promise<any>((resolve, reject) => {
const id = ++requestId;
console.debug('exec >>>', id, command, args);
pendingRequests.set(id, {
resolve,
reject,
timeout: setInterval(() => {
pendingRequests.delete(id);
reject(new Error(`Timeout for command '${command}'`));
}, EXEC_TIMOUT),
});
socket.emit('exec', id, command, args);
});
// socket.
} else {
throw new Error('Not connected to Flipper Server');
}
},
on(event, callback) {
eventEmitter.on(event, callback);
},
off(event, callback) {
eventEmitter.off(event, callback);
},
};
});
}