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

@@ -26,7 +26,13 @@ import {
loadSettings,
setupPrefetcher,
} from 'flipper-server-core';
import {getLogger, isTest, Logger, setLoggerInstance} from 'flipper-common';
import {
getLogger,
isTest,
Logger,
setLoggerInstance,
Settings,
} from 'flipper-common';
import constants from './fb-stubs/constants';
import {initializeElectron} from './electron/initializeElectron';
import path from 'path';
@@ -99,6 +105,8 @@ async function start() {
initializeElectron(flipperServer, flipperServerConfig);
setProcessState(flipperServerConfig.settings);
// By turning this in a require, we force the JS that the body of this module (init) has completed (initializeElectron),
// before starting the rest of the Flipper process.
// This prevent issues where the render host is referred at module initialisation level,
@@ -185,3 +193,21 @@ function createDelegatedLogger(): Logger {
},
};
}
function setProcessState(settings: Settings) {
const androidHome = settings.androidHome;
const idbPath = settings.idbPath;
if (!process.env.ANDROID_HOME && !process.env.ANDROID_SDK_ROOT) {
process.env.ANDROID_HOME = androidHome;
}
// emulator/emulator is more reliable than tools/emulator, so prefer it if
// it exists
process.env.PATH =
['emulator', 'tools', 'platform-tools']
.map((directory) => path.resolve(androidHome, directory))
.join(':') +
`:${idbPath}` +
`:${process.env.PATH}`;
}

View File

@@ -0,0 +1,35 @@
/**
* 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 {default as doTransform} from './transform';
import {default as getCacheKey} from './get-cache-key';
const presets = [require('@babel/preset-react')];
const plugins = [
require('./import-react'),
require('./app-flipper-requires'),
require('./fb-stubs'),
];
module.exports = {
transform,
getCacheKey,
};
function transform({
filename,
options,
src,
}: {
filename: string;
options: any;
src: string;
}) {
return doTransform({filename, options, src, presets, plugins});
}

View File

@@ -153,6 +153,14 @@ export class FlipperServerImpl implements FlipperServer {
this.events.off(event, callback);
}
onAny(callback: (event: keyof FlipperServerEvents, payload: any) => void) {
this.events.on('*', callback);
}
offAny(callback: (event: keyof FlipperServerEvents, payload: any) => void) {
this.events.off('*', callback);
}
/**
* @internal
*/
@@ -161,6 +169,7 @@ export class FlipperServerImpl implements FlipperServer {
payload: FlipperServerEvents[Event],
): void {
this.events.emit(event, payload);
this.events.emit('*', event, payload);
}
exec<Event extends keyof FlipperServerCommands>(

View File

@@ -32,7 +32,7 @@
"reset": "rimraf lib *.tsbuildinfo",
"build": "tsc -b",
"prepack": "yarn reset && yarn build",
"start": "cross-env NODE_ENV=development nodemon --watch './src/*.tsx' --exec '../ts-node' src/index.tsx"
"start": "cross-env NODE_ENV=development nodemon --watch './src/**/*.tsx' --watch '../flipper-server-core/src/**/*.tsx' --exec '../ts-node' src/index.tsx"
},
"files": [
"lib/**/*"

View File

@@ -7,14 +7,15 @@
* @format
*/
import chalk from 'chalk';
import path from 'path';
// TODO: currently flipper-server is only suitable for development,
// needs to be come independently runnable, prebundled, distributed, etc!
// in future require conditionally
import {startWebServerDev} from './startWebServerDev';
import {startFlipperServer} from './startFlipperServer';
import {startBaseServer} from './startBaseServer';
import chalk from 'chalk';
import path from 'path';
import {startSocketServer} from './startSocketServer';
const PORT = 52342;
const rootDir = path.resolve(__dirname, '..', '..');
@@ -27,10 +28,12 @@ async function start() {
entry: 'index.web.dev.html',
});
return Promise.all([
const [flipperServer] = await Promise.all([
startFlipperServer(rootDir, staticDir),
startWebServerDev(app, server, socket, rootDir),
]);
startSocketServer(flipperServer, socket);
}
start()

View File

@@ -64,12 +64,7 @@ function startAssetServer(
}
function addWebsocket(server: http.Server) {
const io = new socketio.Server(server); // 3.1.0 socket.io doesn't have type definitions
io.on('connection', (client) => {
console.log(chalk.green(`Client connected ${client.id}`));
});
const io = new socketio.Server(server);
return io;
}

View File

@@ -24,7 +24,10 @@ import {
import path from 'path';
import fs from 'fs';
export async function startFlipperServer(rootDir: string, staticDir: string) {
export async function startFlipperServer(
rootDir: string,
staticDir: string,
): Promise<FlipperServerImpl> {
if (os.platform() === 'darwin') {
// By default Node.JS has its internal certificate storage and doesn't use
// the system store. Because of this, it's impossible to access ondemand / devserver
@@ -90,6 +93,7 @@ export async function startFlipperServer(rootDir: string, staticDir: string) {
);
await flipperServer.connect();
return flipperServer;
}
function createLogger(): Logger {
@@ -103,7 +107,7 @@ function createLogger(): Logger {
// console.warn('(skipped trackTimeSince)', args);
},
debug(..._args: any[]) {
// TODO: only if verbose console.debug(...args);
// TODO: only if double verbose console.debug(...args);
},
error(...args: any[]) {
console.error(...args);
@@ -113,8 +117,9 @@ function createLogger(): Logger {
console.warn(...args);
console.warn('(skipped error reporting)');
},
info(...args: any[]) {
console.info(...args);
info(..._args: any[]) {
// TODO: only if verbose console.debug(...args);
// console.info(...args);
},
};
}

View File

@@ -0,0 +1,51 @@
/**
* 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 chalk from 'chalk';
import {FlipperServerImpl} from 'flipper-server-core';
import socketio from 'socket.io';
export function startSocketServer(
flipperServer: FlipperServerImpl,
socket: socketio.Server,
) {
socket.on('connection', (client) => {
console.log(chalk.green(`Client connected ${client.id}`));
let connected = true;
function onServerEvent(event: string, payoad: any) {
client.emit('event', event, payoad);
}
flipperServer.onAny(onServerEvent);
client.on('exec', (id, command, args) => {
console.log(id, command, args);
flipperServer
.exec(command, ...args)
.then((result: any) => {
if (connected) {
client.emit('exec-response', id, result);
}
})
.catch((error: any) => {
if (connected) {
client.emit('exec-response-error', id, error.toString());
}
});
});
client.on('disconnect', () => {
console.log(chalk.red(`Client disconnected ${client.id}`));
connected = false;
flipperServer.offAny(onServerEvent);
});
});
}

View File

@@ -76,15 +76,20 @@ async function startMetroServer(
watchFolders,
transformer: {
...baseConfig.transformer,
babelTransformerPath: path.join(babelTransformationsDir, 'transform-app'),
babelTransformerPath: path.join(
babelTransformationsDir,
'transform-browser',
),
},
resolver: {
...baseConfig.resolver,
resolverMainFields: ['flipperBundlerEntry', 'module', 'main'],
resolverMainFields: ['flipperBundlerEntry', 'browser', 'module', 'main'],
blacklistRE: /\.native\.js$/,
sourceExts: ['js', 'jsx', 'ts', 'tsx', 'json', 'mjs', 'cjs'],
},
watch: true,
// only needed when medling with babel transforms
// cacheVersion: Math.random(), // only cache for current run
});
const connectMiddleware = await Metro.createConnectMiddleware(config);
app.use(connectMiddleware.middleware);

View File

@@ -12,9 +12,13 @@
"dependencies": {},
"devDependencies": {
"invariant": "^2.2.4",
"eventemitter3": "^4.0.7",
"flipper-common": "0.0.0",
"flipper-ui-core": "0.0.0",
"metro-runtime": "^0.66.2",
"pretty-format": "^27.3.1",
"react-refresh": "^0.10.0"
"react-refresh": "^0.10.0",
"socket.io-client": "^4.4.0"
},
"peerDependencies": {},
"scripts": {

View File

@@ -270,7 +270,7 @@ function showCompileError() {
// Symbolicating compile errors is wasted effort
// because the stack trace is meaningless:
(error as any).preventSymbolication = true;
(window as any).flipperShowError?.(message);
window.flipperShowError?.(message);
throw error;
}

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);
},
};
});
}

View File

@@ -0,0 +1,25 @@
/**
* 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 {RenderHost} from 'flipper-ui-core';
declare global {
interface Window {
flipperConfig: {
theme: 'light' | 'dark' | 'system';
entryPoint: string;
debug: boolean;
};
FlipperRenderHostInstance: RenderHost;
flipperShowError?(error: string): void;
flipperHideError?(): void;
}
}

View File

@@ -7,4 +7,86 @@
* @format
*/
import {getLogger, Logger, setLoggerInstance} from 'flipper-common';
import {initializeRenderHost} from './initializeRenderHost';
import {createFlipperServer} from './flipperServerConnection';
document.getElementById('root')!.innerText = 'flipper-ui-browser started';
async function start() {
const logger = createDelegatedLogger();
setLoggerInstance(logger);
const flipperServer = await createFlipperServer();
await flipperServer.connect();
const flipperServerConfig = await flipperServer.exec('get-config');
initializeRenderHost(flipperServer, flipperServerConfig);
// By turning this in a require, we force the JS that the body of this module (init) has completed (initializeElectron),
// before starting the rest of the Flipper process.
// This prevent issues where the render host is referred at module initialisation level,
// but not set yet, which might happen when using normal imports.
// eslint-disable-next-line import/no-commonjs
// TODO: replace
window.flipperShowError?.('Connected to Flipper Server successfully');
// TODO: require('flipper-ui-core').startFlipperDesktop(flipperServer);
}
start().catch((e) => {
console.error('Failed to start flipper-ui-browser', e);
window.flipperShowError?.('Failed to start flipper-ui-browser: ' + e);
});
// getLogger() is not yet created when the electron app starts.
// we can't create it here yet, as the real logger is wired up to
// the redux store and the rest of the world. So we create a delegating logger
// that uses a simple implementation until the real one comes available
function createDelegatedLogger(): Logger {
const naiveLogger: Logger = {
track(...args: [any, any, any?, any?]) {
console.warn('(skipper track)', args);
},
trackTimeSince(...args: [any, any, any?]) {
console.warn('(skipped trackTimeSince)', args);
},
debug(...args: any[]) {
console.debug(...args);
},
error(...args: any[]) {
console.error(...args);
console.warn('(skipped error reporting)');
},
warn(...args: any[]) {
console.warn(...args);
console.warn('(skipped error reporting)');
},
info(...args: any[]) {
console.info(...args);
},
};
// will be overwrittingen later
setLoggerInstance(naiveLogger);
return {
track() {
// noop
},
trackTimeSince() {
// noop
},
debug(...args: any[]) {
getLogger().debug(...args);
},
error(...args: any[]) {
getLogger().error(...args);
},
warn(...args: any[]) {
getLogger().warn(...args);
},
info(...args: any[]) {
getLogger().info(...args);
},
};
}

View File

@@ -0,0 +1,74 @@
/**
* 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 {FlipperServer, FlipperServerConfig} from 'flipper-common';
export function initializeRenderHost(
flipperServer: FlipperServer,
flipperServerConfig: FlipperServerConfig,
) {
window.FlipperRenderHostInstance = {
processId: 0,
isProduction: window.flipperConfig.debug !== true,
readTextFromClipboard() {
// TODO:
return undefined;
},
writeTextToClipboard(_text: string) {
// TODO:
},
async importFile() {
throw new Error('Not implemented');
},
async exportFile() {
throw new Error('Not implemented');
},
openLink(url: string) {
window.open(url, '_blank');
},
registerShortcut(_shortcut, _callback) {
// TODO:
return () => {};
},
hasFocus() {
return document.hasFocus();
},
onIpcEvent(event) {
console.warn('onIpcEvent not available', event);
},
sendIpcEvent(event, ..._args: any[]) {
console.warn('sendIpcEvent not available', event);
},
shouldUseDarkColors() {
return !!(
window.flipperConfig.theme === 'dark' ||
(window.flipperConfig.theme === 'system' &&
window.matchMedia?.('(prefers-color-scheme: dark)'))
);
},
restartFlipper() {
// TODO: restart server as well
window.location.reload();
},
loadDefaultPlugins: getDefaultPluginsIndex,
serverConfig: flipperServerConfig,
GK(gatekeeper) {
return flipperServerConfig.gatekeepers[gatekeeper] ?? false;
},
flipperServer,
};
}
function getDefaultPluginsIndex() {
// TODO:
return {};
// eslint-disable-next-line import/no-unresolved
// const index = require('../defaultPlugins');
// return index.default || index;
}

View File

@@ -5,6 +5,9 @@
"rootDir": "src"
},
"references": [
{
"path": "../flipper-common"
},
{
"path": "../flipper-ui-core"
}

View File

@@ -17,9 +17,6 @@ import {
ReleaseChannel,
Tristate,
} from 'flipper-common';
// TODO: those imports are only used for testing, require conditionally?
import {tmpdir} from 'os';
import {resolve} from 'path';
// Events that are emitted from the main.ts ovr the IPC process bridge in Electron
type MainProcessEvents = {
@@ -116,6 +113,9 @@ export function getRenderHostInstance(): RenderHost {
}
if (process.env.NODE_ENV === 'test') {
const {tmpdir} = require('os');
const {resolve} = require('path');
const rootPath = resolve(__dirname, '..', '..');
const stubConfig: FlipperServerConfig = {
env: {...process.env},

View File

@@ -17,7 +17,6 @@ import dispatcher from './dispatcher/index';
import TooltipProvider from './ui/components/TooltipProvider';
import {setPersistor} from './utils/persistor';
import React from 'react';
import path from 'path';
import {getStore} from './store';
import {cache} from '@emotion/css';
import {CacheProvider} from '@emotion/react';
@@ -142,24 +141,6 @@ class AppFrame extends React.Component<
}
}
function setProcessState(settings: Settings) {
const androidHome = settings.androidHome;
const idbPath = settings.idbPath;
if (!process.env.ANDROID_HOME && !process.env.ANDROID_SDK_ROOT) {
process.env.ANDROID_HOME = androidHome;
}
// emulator/emulator is more reliable than tools/emulator, so prefer it if
// it exists
process.env.PATH =
['emulator', 'tools', 'platform-tools']
.map((directory) => path.resolve(androidHome, directory))
.join(':') +
`:${idbPath}` +
`:${process.env.PATH}`;
}
function init(flipperServer: FlipperServer) {
const settings = getRenderHostInstance().serverConfig.settings;
const store = getStore();
@@ -173,7 +154,6 @@ function init(flipperServer: FlipperServer) {
// rehydrate app state before exposing init
const persistor = persistStore(store, undefined, () => {
// Make sure process state is set before dispatchers run
setProcessState(settings);
dispatcher(store, logger);
});

View File

@@ -10,6 +10,7 @@
window.flipperConfig = {
theme: 'light',
entryPoint: 'flipper-ui-browser/src/index-fast-refresh.bundle?platform=web&dev=true&minify=false',
debug: true,
}
</script>
<style>
@@ -62,7 +63,12 @@
(function() {
// FIXME: needed to make Metro work
window.global = window;
global.electronRequire = window.require;
// global.electronRequire = function(path) {
// // debugger;
// // throw(new Error("Tried to require: " + path));
// console.error("Tried to require " + path);
// return {};
// };
let suppressErrors = false;
@@ -87,6 +93,10 @@
box.textContent = text;
}
window.flipperShowError = openError;
window.flipperHideError = () => {
const box = document.querySelector('.__infinity-dev-box-error');
box.setAttribute('hidden', true);
}
// load correct theme (n.b. this doesn't handle system value specifically, will assume light in such cases)
try {

View File

@@ -2357,6 +2357,11 @@
dependencies:
"@sinonjs/commons" "^1.7.0"
"@socket.io/component-emitter@~3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.0.0.tgz#8863915676f837d9dad7b76f50cb500c1e9422e9"
integrity sha512-2pTGuibAXJswAPJjaKisthqS/NOK5ypG4LYT6tEAV0S/mxW0zOIvYvGK0V8w8+SHxAm6vRMSjqSalFXeBAqs+Q==
"@szmarczak/http-timer@^1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421"
@@ -4167,6 +4172,11 @@ babel-preset-jest@^26.6.2:
babel-plugin-jest-hoist "^26.6.2"
babel-preset-current-node-syntax "^1.0.0"
backo2@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947"
integrity sha1-MasayLEpNjRj41s+u2n038+6eUc=
bail@^1.0.0:
version "1.0.5"
resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.5.tgz#b6fa133404a392cbc1f8c4bf63f5953351e7a776"
@@ -5802,6 +5812,21 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1:
dependencies:
once "^1.4.0"
engine.io-client@~6.1.1:
version "6.1.1"
resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-6.1.1.tgz#800d4b9db5487d169686729e5bd887afa78d36b0"
integrity sha512-V05mmDo4gjimYW+FGujoGmmmxRaDsrVr7AXA3ZIfa04MWM1jOfZfUwou0oNqhNwy/votUDvGDt4JA4QF4e0b4g==
dependencies:
"@socket.io/component-emitter" "~3.0.0"
debug "~4.3.1"
engine.io-parser "~5.0.0"
has-cors "1.1.0"
parseqs "0.0.6"
parseuri "0.0.6"
ws "~8.2.3"
xmlhttprequest-ssl "~2.0.0"
yeast "0.1.2"
engine.io-parser@~5.0.0:
version "5.0.1"
resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.0.1.tgz#6695fc0f1e6d76ad4a48300ff80db5f6b3654939"
@@ -5809,6 +5834,22 @@ engine.io-parser@~5.0.0:
dependencies:
base64-arraybuffer "~1.0.1"
engine.io@~6.0.0:
version "6.0.1"
resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.0.1.tgz#4a37754c6067415e9bfbcc82e49e572437354615"
integrity sha512-Y53UaciUh2Rmx5MiogtMxOQcfh7pnemday+Bb4QDg0Wjmnvo/VTvuEyNGQgYmh8L7VOe8Je1QuiqjLNDelMqLA==
dependencies:
"@types/cookie" "^0.4.1"
"@types/cors" "^2.8.12"
"@types/node" ">=10.0.0"
accepts "~1.3.4"
base64id "2.0.0"
cookie "~0.4.1"
cors "~2.8.5"
debug "~4.3.1"
engine.io-parser "~5.0.0"
ws "~8.2.3"
engine.io@~6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.1.0.tgz#459eab0c3724899d7b63a20c3a6835cf92857939"
@@ -7106,6 +7147,11 @@ has-bigints@^1.0.1:
resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113"
integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==
has-cors@1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39"
integrity sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=
has-flag@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
@@ -10377,6 +10423,16 @@ parse5@6.0.1:
resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b"
integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==
parseqs@0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.6.tgz#8e4bb5a19d1cdc844a08ac974d34e273afa670d5"
integrity sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==
parseuri@0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/parseuri/-/parseuri-0.0.6.tgz#e1496e829e3ac2ff47f39a4dd044b32823c4a25a"
integrity sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==
parseurl@~1.3.3:
version "1.3.3"
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
@@ -12281,11 +12337,23 @@ snapdragon@^0.8.1:
source-map-resolve "^0.5.0"
use "^3.1.0"
socket.io-adapter@~2.3.3:
socket.io-adapter@~2.3.2, socket.io-adapter@~2.3.3:
version "2.3.3"
resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.3.3.tgz#4d6111e4d42e9f7646e365b4f578269821f13486"
integrity sha512-Qd/iwn3VskrpNO60BeRyCyr8ZWw9CPZyitW4AQwmRZ8zCiyDiL+znRnWX6tDHXnWn1sJrM1+b6Mn6wEDJJ4aYQ==
socket.io-client@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.4.0.tgz#d6568ebd79ac12e2d6b628e7e90e60f1d48d99ff"
integrity sha512-g7riSEJXi7qCFImPow98oT8X++MSsHz6MMFRXkWNJ6uEROSHOa3kxdrsYWMq85dO+09CFMkcqlpjvbVXQl4z6g==
dependencies:
"@socket.io/component-emitter" "~3.0.0"
backo2 "~1.0.2"
debug "~4.3.2"
engine.io-client "~6.1.1"
parseuri "0.0.6"
socket.io-parser "~4.1.1"
socket.io-parser@~4.0.4:
version "4.0.4"
resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.0.4.tgz#9ea21b0d61508d18196ef04a2c6b9ab630f4c2b0"
@@ -12295,7 +12363,27 @@ socket.io-parser@~4.0.4:
component-emitter "~1.3.0"
debug "~4.3.1"
socket.io@*, socket.io@^4.3.1, socket.io@^4.4.0:
socket.io-parser@~4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.1.1.tgz#0ad53d980781cab1eabe320417d8480c0133e62d"
integrity sha512-USQVLSkDWE5nbcY760ExdKaJxCE65kcsG/8k5FDGZVVxpD1pA7hABYXYkCUvxUuYYh/+uQw0N/fvBzfT8o07KA==
dependencies:
"@socket.io/component-emitter" "~3.0.0"
debug "~4.3.1"
socket.io@*, socket.io@^4.3.1:
version "4.3.1"
resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.3.1.tgz#c0aa14f3f916a8ab713e83a5bd20c16600245763"
integrity sha512-HC5w5Olv2XZ0XJ4gOLGzzHEuOCfj3G0SmoW3jLHYYh34EVsIr3EkW9h6kgfW+K3TFEcmYy8JcPWe//KUkBp5jA==
dependencies:
accepts "~1.3.4"
base64id "~2.0.0"
debug "~4.3.2"
engine.io "~6.0.0"
socket.io-adapter "~2.3.2"
socket.io-parser "~4.0.4"
socket.io@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.4.0.tgz#8140a0db2c22235f88a6dceb867e4d5c9bd70507"
integrity sha512-bnpJxswR9ov0Bw6ilhCvO38/1WPtE3eA2dtxi2Iq4/sFebiDJQzgKNYA7AuVVdGW09nrESXd90NbZqtDd9dzRQ==
@@ -13661,6 +13749,11 @@ xmldom@^0.5.0, "xmldom@github:xmldom/xmldom#0.7.0":
version "0.7.0"
resolved "https://codeload.github.com/xmldom/xmldom/tar.gz/c568938641cc1f121cef5b4df80fcfda1e489b6e"
xmlhttprequest-ssl@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz#91360c86b914e67f44dce769180027c0da618c67"
integrity sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==
xtend@^4.0.0, xtend@~4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
@@ -13785,6 +13878,11 @@ yazl@^2.5.1:
dependencies:
buffer-crc32 "~0.2.3"
yeast@0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419"
integrity sha1-AI4G2AlDIMNy28L47XagymyKxBk=
yn@3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"