Separate device in server and client version [2/n]
Summary:
This stack takes care of handling care of moving all device interactions over the (possible) async channel FlipperServer. The FlipperServer interface (see previous diff) allows listening to specific server events using `on`, and emit commands to be executed by the server by using `exec` (e.g. `exec('take-screenshot', serial) => Promise<buffer>`).
FlipperServerImpl implements this interface on the server side.
The device implementations are split as follows
```
server / backend process:
ServerDevice
- iOSDevice
- AndroidDevice
- MetroDevice
- DummyDevice
- Mac/Windows Device
frontend / ui:
BaseDevice: a normal connected, device, implements device apis as they already existed
- ArchivedDevice (note that this doesn't have a server counterpart)
- TestDevice (for unit tests, with stubbed backend communication)
```
All features of devices are for simplicity unified (since the deviations are small), where specific device types might not implement certain features like taking screenshots or running shell commands.
To avoid making this diff unnecessarily big, some open Todo's will be addressed later in this stack, and it shouldn't be landed alone.
Reviewed By: timur-valiev
Differential Revision: D30909346
fbshipit-source-id: cce0bee94fdd5db59bebe3577a6084219a038719
This commit is contained in:
committed by
Facebook GitHub Bot
parent
845d0755f1
commit
2d838efd4d
@@ -41,6 +41,7 @@
|
|||||||
"fs-extra": "^10.0.0",
|
"fs-extra": "^10.0.0",
|
||||||
"immer": "^9.0.6",
|
"immer": "^9.0.6",
|
||||||
"invariant": "^2.2.2",
|
"invariant": "^2.2.2",
|
||||||
|
"js-base64": "^3.7.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"lodash.memoize": "^4.1.2",
|
"lodash.memoize": "^4.1.2",
|
||||||
"open": "^8.2.1",
|
"open": "^8.2.1",
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
/* eslint-disable node/no-sync */
|
/* eslint-disable node/no-sync */
|
||||||
|
|
||||||
import {PluginDefinition} from './plugin';
|
import {PluginDefinition} from './plugin';
|
||||||
import BaseDevice from './server/devices/BaseDevice';
|
import BaseDevice from './devices/BaseDevice';
|
||||||
import {Logger} from './fb-interfaces/Logger';
|
import {Logger} from './fb-interfaces/Logger';
|
||||||
import {Store} from './reducers/index';
|
import {Store} from './reducers/index';
|
||||||
import {performance} from 'perf_hooks';
|
import {performance} from 'perf_hooks';
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
import {FlipperPlugin, FlipperDevicePlugin} from './plugin';
|
import {FlipperPlugin, FlipperDevicePlugin} from './plugin';
|
||||||
import {Logger} from './fb-interfaces/Logger';
|
import {Logger} from './fb-interfaces/Logger';
|
||||||
import BaseDevice from './server/devices/BaseDevice';
|
import BaseDevice from './devices/BaseDevice';
|
||||||
import {pluginKey as getPluginKey} from './utils/pluginKey';
|
import {pluginKey as getPluginKey} from './utils/pluginKey';
|
||||||
import Client from './Client';
|
import Client from './Client';
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {default as BaseDevice} from '../server/devices/BaseDevice';
|
|
||||||
import {createMockFlipperWithPlugin} from '../test-utils/createMockFlipperWithPlugin';
|
import {createMockFlipperWithPlugin} from '../test-utils/createMockFlipperWithPlugin';
|
||||||
import {
|
import {
|
||||||
TestUtils,
|
TestUtils,
|
||||||
@@ -17,7 +16,7 @@ import {
|
|||||||
PluginClient,
|
PluginClient,
|
||||||
} from 'flipper-plugin';
|
} from 'flipper-plugin';
|
||||||
import {handleClientConnected} from '../dispatcher/flipperServer';
|
import {handleClientConnected} from '../dispatcher/flipperServer';
|
||||||
import {destroyDevice} from '../reducers/connections';
|
import {TestDevice} from '../test-utils/TestDevice';
|
||||||
|
|
||||||
test('Devices can disconnect', async () => {
|
test('Devices can disconnect', async () => {
|
||||||
const deviceplugin = new _SandyPluginDefinition(
|
const deviceplugin = new _SandyPluginDefinition(
|
||||||
@@ -101,12 +100,8 @@ test('New device with same serial removes & cleans the old one', async () => {
|
|||||||
expect(instance.instanceApi.destroy).toBeCalledTimes(0);
|
expect(instance.instanceApi.destroy).toBeCalledTimes(0);
|
||||||
expect(store.getState().connections.devices).toEqual([device]);
|
expect(store.getState().connections.devices).toEqual([device]);
|
||||||
|
|
||||||
// calling destroy explicitly defeats the point of this test a bit,
|
|
||||||
// but we now print an error rather than proactively destroying the device,
|
|
||||||
// see https://github.com/facebook/flipper/issues/1989
|
|
||||||
destroyDevice(store, logger, device.serial);
|
|
||||||
// submit a new device with same serial
|
// submit a new device with same serial
|
||||||
const device2 = new BaseDevice(
|
const device2 = new TestDevice(
|
||||||
device.serial,
|
device.serial,
|
||||||
'physical',
|
'physical',
|
||||||
'MockAndroidDevice',
|
'MockAndroidDevice',
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React, {Component} from 'react';
|
import React, {Component} from 'react';
|
||||||
import BaseDevice from '../server/devices/BaseDevice';
|
import BaseDevice from '../devices/BaseDevice';
|
||||||
import {Button, Glyph, colors} from '../ui';
|
import {Button, Glyph, colors} from '../ui';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
import BaseDevice from './BaseDevice';
|
import BaseDevice from './BaseDevice';
|
||||||
import type {DeviceOS, DeviceType} from 'flipper-plugin';
|
import type {DeviceOS, DeviceType} from 'flipper-plugin';
|
||||||
import {DeviceShell} from './BaseDevice';
|
import {DeviceShell} from './BaseDevice';
|
||||||
import {SupportFormRequestDetailsState} from '../../reducers/supportForm';
|
import {SupportFormRequestDetailsState} from '../reducers/supportForm';
|
||||||
|
|
||||||
export default class ArchivedDevice extends BaseDevice {
|
export default class ArchivedDevice extends BaseDevice {
|
||||||
isArchived = true;
|
isArchived = true;
|
||||||
@@ -24,7 +24,28 @@ export default class ArchivedDevice extends BaseDevice {
|
|||||||
source?: string;
|
source?: string;
|
||||||
supportRequestDetails?: SupportFormRequestDetailsState;
|
supportRequestDetails?: SupportFormRequestDetailsState;
|
||||||
}) {
|
}) {
|
||||||
super(options.serial, options.deviceType, options.title, options.os);
|
super(
|
||||||
|
{
|
||||||
|
close() {},
|
||||||
|
exec(command, ..._args: any[]) {
|
||||||
|
throw new Error(
|
||||||
|
`[Archived device] Cannot invoke command ${command} on an archived device`,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
on(event) {
|
||||||
|
console.warn(
|
||||||
|
`Cannot subscribe to server events from an Archived device: ${event}`,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
off() {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deviceType: options.deviceType,
|
||||||
|
title: options.title,
|
||||||
|
os: options.os,
|
||||||
|
serial: options.serial,
|
||||||
|
},
|
||||||
|
);
|
||||||
this.icon = 'box';
|
this.icon = 'box';
|
||||||
this.connected.set(false);
|
this.connected.set(false);
|
||||||
this.source = options.source || '';
|
this.source = options.source || '';
|
||||||
@@ -18,9 +18,12 @@ import {
|
|||||||
createState,
|
createState,
|
||||||
getFlipperLib,
|
getFlipperLib,
|
||||||
DeviceOS,
|
DeviceOS,
|
||||||
|
DeviceDescription,
|
||||||
|
FlipperServer,
|
||||||
} from 'flipper-plugin';
|
} from 'flipper-plugin';
|
||||||
import {DeviceSpec, PluginDetails} from 'flipper-plugin-lib';
|
import {DeviceSpec, PluginDetails} from 'flipper-plugin-lib';
|
||||||
import {getPluginKey} from '../../utils/pluginKey';
|
import {getPluginKey} from '../utils/pluginKey';
|
||||||
|
import {Base64} from 'js-base64';
|
||||||
|
|
||||||
export type DeviceShell = {
|
export type DeviceShell = {
|
||||||
stdout: stream.Readable;
|
stdout: stream.Readable;
|
||||||
@@ -40,37 +43,40 @@ export type DeviceExport = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default class BaseDevice {
|
export default class BaseDevice {
|
||||||
|
description: DeviceDescription;
|
||||||
|
flipperServer: FlipperServer;
|
||||||
isArchived = false;
|
isArchived = false;
|
||||||
hasDevicePlugins = false; // true if there are device plugins for this device (not necessarily enabled)
|
hasDevicePlugins = false; // true if there are device plugins for this device (not necessarily enabled)
|
||||||
|
|
||||||
constructor(
|
constructor(flipperServer: FlipperServer, description: DeviceDescription) {
|
||||||
serial: string,
|
this.flipperServer = flipperServer;
|
||||||
deviceType: DeviceType,
|
this.description = description;
|
||||||
title: string,
|
|
||||||
os: DeviceOS,
|
|
||||||
specs: DeviceSpec[] = [],
|
|
||||||
) {
|
|
||||||
this.serial = serial;
|
|
||||||
this.title = title;
|
|
||||||
this.deviceType = deviceType;
|
|
||||||
this.os = os;
|
|
||||||
this.specs = specs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// operating system of this device
|
// operating system of this device
|
||||||
os: DeviceOS;
|
get os() {
|
||||||
|
return this.description.os;
|
||||||
|
}
|
||||||
|
|
||||||
// human readable name for this device
|
// human readable name for this device
|
||||||
title: string;
|
get title(): string {
|
||||||
|
return this.description.title;
|
||||||
|
}
|
||||||
|
|
||||||
// type of this device
|
// type of this device
|
||||||
deviceType: DeviceType;
|
get deviceType() {
|
||||||
|
return this.description.deviceType;
|
||||||
|
}
|
||||||
|
|
||||||
// serial number for this device
|
// serial number for this device
|
||||||
serial: string;
|
get serial() {
|
||||||
|
return this.description.serial;
|
||||||
|
}
|
||||||
|
|
||||||
// additional device specs used for plugin compatibility checks
|
// additional device specs used for plugin compatibility checks
|
||||||
specs: DeviceSpec[];
|
get specs(): DeviceSpec[] {
|
||||||
|
return this.description.specs ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
// possible src of icon to display next to the device title
|
// possible src of icon to display next to the device title
|
||||||
icon: string | null | undefined;
|
icon: string | null | undefined;
|
||||||
@@ -127,25 +133,16 @@ export default class BaseDevice {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
startLogging() {
|
private deviceLogEventHandler = (payload: {
|
||||||
// to be subclassed
|
serial: string;
|
||||||
|
entry: DeviceLogEntry;
|
||||||
|
}) => {
|
||||||
|
if (payload.serial === this.serial && this.logListeners.size > 0) {
|
||||||
|
this.addLogEntry(payload.entry);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
stopLogging() {
|
addLogEntry(entry: DeviceLogEntry) {
|
||||||
// to be subclassed
|
|
||||||
}
|
|
||||||
|
|
||||||
addLogListener(callback: DeviceLogListener): Symbol {
|
|
||||||
if (this.logListeners.size === 0) {
|
|
||||||
this.startLogging();
|
|
||||||
}
|
|
||||||
const id = Symbol();
|
|
||||||
this.logListeners.set(id, callback);
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
_notifyLogListeners(entry: DeviceLogEntry) {
|
|
||||||
if (this.logListeners.size > 0) {
|
|
||||||
this.logListeners.forEach((listener) => {
|
this.logListeners.forEach((listener) => {
|
||||||
// prevent breaking other listeners, if one listener doesn't work.
|
// prevent breaking other listeners, if one listener doesn't work.
|
||||||
try {
|
try {
|
||||||
@@ -155,10 +152,24 @@ export default class BaseDevice {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async startLogging() {
|
||||||
|
await this.flipperServer.exec('device-start-logging', this.serial);
|
||||||
|
this.flipperServer.on('device-log', this.deviceLogEventHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
addLogEntry(entry: DeviceLogEntry) {
|
stopLogging() {
|
||||||
this._notifyLogListeners(entry);
|
this.flipperServer.off('device-log', this.deviceLogEventHandler);
|
||||||
|
return this.flipperServer.exec('device-stop-logging', this.serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
addLogListener(callback: DeviceLogListener): Symbol {
|
||||||
|
if (this.logListeners.size === 0) {
|
||||||
|
this.startLogging();
|
||||||
|
}
|
||||||
|
const id = Symbol();
|
||||||
|
this.logListeners.set(id, callback);
|
||||||
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
removeLogListener(id: Symbol) {
|
removeLogListener(id: Symbol) {
|
||||||
@@ -172,22 +183,48 @@ export default class BaseDevice {
|
|||||||
throw new Error('unimplemented');
|
throw new Error('unimplemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
screenshot(): Promise<Buffer> {
|
async screenshotAvailable(): Promise<boolean> {
|
||||||
return Promise.reject(
|
if (this.isArchived) {
|
||||||
new Error('No screenshot support for current device'),
|
return false;
|
||||||
|
}
|
||||||
|
return this.flipperServer.exec('device-supports-screenshot', this.serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
async screenshot(): Promise<Buffer> {
|
||||||
|
if (this.isArchived) {
|
||||||
|
return Buffer.from([]);
|
||||||
|
}
|
||||||
|
return Buffer.from(
|
||||||
|
Base64.toUint8Array(
|
||||||
|
await this.flipperServer.exec('device-take-screenshot', this.serial),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async screenCaptureAvailable(): Promise<boolean> {
|
async screenCaptureAvailable(): Promise<boolean> {
|
||||||
|
if (this.isArchived) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return this.flipperServer.exec(
|
||||||
|
'device-supports-screencapture',
|
||||||
|
this.serial,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
async startScreenCapture(_destination: string): Promise<void> {
|
async startScreenCapture(destination: string): Promise<void> {
|
||||||
throw new Error('startScreenCapture not implemented on BaseDevice ');
|
return this.flipperServer.exec(
|
||||||
|
'device-start-screencapture',
|
||||||
|
this.serial,
|
||||||
|
destination,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async stopScreenCapture(): Promise<string | null> {
|
async stopScreenCapture(): Promise<string | null> {
|
||||||
return null;
|
return this.flipperServer.exec('device-stop-screencapture', this.serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
async executeShell(command: string): Promise<string> {
|
||||||
|
return this.flipperServer.exec('device-shell-exec', this.serial, command);
|
||||||
}
|
}
|
||||||
|
|
||||||
supportsPlugin(plugin: PluginDefinition | PluginDetails) {
|
supportsPlugin(plugin: PluginDefinition | PluginDetails) {
|
||||||
@@ -22,7 +22,7 @@ import {_SandyPluginDefinition as SandyPluginDefinition} from 'flipper-plugin';
|
|||||||
import MockFlipper from '../../test-utils/MockFlipper';
|
import MockFlipper from '../../test-utils/MockFlipper';
|
||||||
import Client from '../../Client';
|
import Client from '../../Client';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import BaseDevice from '../../server/devices/BaseDevice';
|
import BaseDevice from '../../devices/BaseDevice';
|
||||||
|
|
||||||
const pluginDetails1 = TestUtils.createMockPluginDetails({
|
const pluginDetails1 = TestUtils.createMockPluginDetails({
|
||||||
id: 'plugin1',
|
id: 'plugin1',
|
||||||
|
|||||||
@@ -10,16 +10,16 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {Store} from '../reducers/index';
|
import {Store} from '../reducers/index';
|
||||||
import {Logger} from '../fb-interfaces/Logger';
|
import {Logger} from '../fb-interfaces/Logger';
|
||||||
import {FlipperServer} from '../server/FlipperServer';
|
import {FlipperServerImpl} from '../server/FlipperServerImpl';
|
||||||
import {selectClient, selectDevice} from '../reducers/connections';
|
import {selectClient, selectDevice} from '../reducers/connections';
|
||||||
import Client from '../Client';
|
import Client from '../Client';
|
||||||
import {notification} from 'antd';
|
import {notification} from 'antd';
|
||||||
import BaseDevice from '../server/devices/BaseDevice';
|
import BaseDevice from '../devices/BaseDevice';
|
||||||
|
|
||||||
export default async (store: Store, logger: Logger) => {
|
export default async (store: Store, logger: Logger) => {
|
||||||
const {enableAndroid, androidHome, idbPath, enableIOS, enablePhysicalIOS} =
|
const {enableAndroid, androidHome, idbPath, enableIOS, enablePhysicalIOS} =
|
||||||
store.getState().settingsState;
|
store.getState().settingsState;
|
||||||
const server = new FlipperServer(
|
const server = new FlipperServerImpl(
|
||||||
{
|
{
|
||||||
enableAndroid,
|
enableAndroid,
|
||||||
androidHome,
|
androidHome,
|
||||||
@@ -69,23 +69,22 @@ export default async (store: Store, logger: Logger) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
server.on('device-connected', (device) => {
|
server.on('device-connected', (deviceInfo) => {
|
||||||
logger.track('usage', 'register-device', {
|
logger.track('usage', 'register-device', {
|
||||||
os: device.os,
|
os: deviceInfo.os,
|
||||||
name: device.title,
|
name: deviceInfo.title,
|
||||||
serial: device.serial,
|
serial: deviceInfo.serial,
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: fixed later in this stack
|
const device = new BaseDevice(server, deviceInfo);
|
||||||
(device as BaseDevice).loadDevicePlugins(
|
device.loadDevicePlugins(
|
||||||
store.getState().plugins.devicePlugins,
|
store.getState().plugins.devicePlugins,
|
||||||
store.getState().connections.enabledDevicePlugins,
|
store.getState().connections.enabledDevicePlugins,
|
||||||
);
|
);
|
||||||
|
|
||||||
store.dispatch({
|
store.dispatch({
|
||||||
type: 'REGISTER_DEVICE',
|
type: 'REGISTER_DEVICE',
|
||||||
// TODO: fixed later in this stack
|
payload: device,
|
||||||
payload: device as any,
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import {loadPlugin, switchPlugin} from '../reducers/pluginManager';
|
|||||||
import {startPluginDownload} from '../reducers/pluginDownloads';
|
import {startPluginDownload} from '../reducers/pluginDownloads';
|
||||||
import isProduction, {isTest} from '../utils/isProduction';
|
import isProduction, {isTest} from '../utils/isProduction';
|
||||||
import restart from '../utils/restartFlipper';
|
import restart from '../utils/restartFlipper';
|
||||||
import BaseDevice from '../server/devices/BaseDevice';
|
import BaseDevice from '../devices/BaseDevice';
|
||||||
import Client from '../Client';
|
import Client from '../Client';
|
||||||
import {RocketOutlined} from '@ant-design/icons';
|
import {RocketOutlined} from '@ant-design/icons';
|
||||||
import {showEmulatorLauncher} from '../sandy-chrome/appinspect/LaunchEmulator';
|
import {showEmulatorLauncher} from '../sandy-chrome/appinspect/LaunchEmulator';
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
// Used to register a shortcut. Don't have an alternative for that.
|
// Used to register a shortcut. Don't have an alternative for that.
|
||||||
// eslint-disable-next-line flipper/no-electron-remote-imports
|
// eslint-disable-next-line flipper/no-electron-remote-imports
|
||||||
import {remote} from 'electron';
|
import {remote} from 'electron';
|
||||||
import MetroDevice from '../server/devices/metro/MetroDevice';
|
|
||||||
import {Store} from '../reducers';
|
import {Store} from '../reducers';
|
||||||
|
|
||||||
type ShortcutEventCommand =
|
type ShortcutEventCommand =
|
||||||
@@ -47,9 +46,15 @@ export default (store: Store) => {
|
|||||||
.getState()
|
.getState()
|
||||||
.connections.devices.filter(
|
.connections.devices.filter(
|
||||||
(device) => device.os === 'Metro' && !device.isArchived,
|
(device) => device.os === 'Metro' && !device.isArchived,
|
||||||
) as MetroDevice[];
|
);
|
||||||
|
|
||||||
devices.forEach((device) => device.sendCommand(shortcut.command));
|
devices.forEach((device) =>
|
||||||
|
device.flipperServer.exec(
|
||||||
|
'metro-command',
|
||||||
|
device.serial,
|
||||||
|
shortcut.command,
|
||||||
|
),
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import {
|
|||||||
selectionChanged,
|
selectionChanged,
|
||||||
} from '../reducers/usageTracking';
|
} from '../reducers/usageTracking';
|
||||||
import produce from 'immer';
|
import produce from 'immer';
|
||||||
import BaseDevice from '../server/devices/BaseDevice';
|
import BaseDevice from '../devices/BaseDevice';
|
||||||
import {deconstructClientId} from '../utils/clientUtils';
|
import {deconstructClientId} from '../utils/clientUtils';
|
||||||
import {getCPUUsage} from 'process';
|
import {getCPUUsage} from 'process';
|
||||||
import {sideEffect} from '../utils/sideEffect';
|
import {sideEffect} from '../utils/sideEffect';
|
||||||
|
|||||||
@@ -34,14 +34,11 @@ export {getPluginKey} from './utils/pluginKey';
|
|||||||
export {Notification, Idler} from 'flipper-plugin';
|
export {Notification, Idler} from 'flipper-plugin';
|
||||||
export {IdlerImpl} from './utils/Idler';
|
export {IdlerImpl} from './utils/Idler';
|
||||||
export {Store, State as ReduxState} from './reducers/index';
|
export {Store, State as ReduxState} from './reducers/index';
|
||||||
export {default as BaseDevice} from './server/devices/BaseDevice';
|
export {default as BaseDevice} from './devices/BaseDevice';
|
||||||
export {default as isProduction} from './utils/isProduction';
|
export {default as isProduction} from './utils/isProduction';
|
||||||
export {DetailSidebar} from 'flipper-plugin';
|
export {DetailSidebar} from 'flipper-plugin';
|
||||||
export {default as Device} from './server/devices/BaseDevice';
|
export {default as Device} from './devices/BaseDevice';
|
||||||
export {default as AndroidDevice} from './server/devices/android/AndroidDevice';
|
export {default as ArchivedDevice} from './devices/ArchivedDevice';
|
||||||
export {default as ArchivedDevice} from './server/devices/ArchivedDevice';
|
|
||||||
export {default as IOSDevice} from './server/devices/ios/IOSDevice';
|
|
||||||
export {default as KaiOSDevice} from './server/devices/android/KaiOSDevice';
|
|
||||||
export {DeviceOS as OS} from 'flipper-plugin';
|
export {DeviceOS as OS} from 'flipper-plugin';
|
||||||
export {default as Button} from './ui/components/Button';
|
export {default as Button} from './ui/components/Button';
|
||||||
export {default as ToggleButton} from './ui/components/ToggleSwitch';
|
export {default as ToggleButton} from './ui/components/ToggleSwitch';
|
||||||
@@ -135,3 +132,4 @@ export {IDEFileResolver, IDEType} from './fb-stubs/IDEFileResolver';
|
|||||||
export {renderMockFlipperWithPlugin} from './test-utils/createMockFlipperWithPlugin';
|
export {renderMockFlipperWithPlugin} from './test-utils/createMockFlipperWithPlugin';
|
||||||
export {Tracked} from 'flipper-plugin'; // To be able to use it in legacy plugins
|
export {Tracked} from 'flipper-plugin'; // To be able to use it in legacy plugins
|
||||||
export {RequireLogin} from './ui/components/RequireLogin';
|
export {RequireLogin} from './ui/components/RequireLogin';
|
||||||
|
export {TestDevice} from './test-utils/TestDevice';
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {KeyboardActions} from './MenuBar';
|
|||||||
import {Logger} from './fb-interfaces/Logger';
|
import {Logger} from './fb-interfaces/Logger';
|
||||||
import Client from './Client';
|
import Client from './Client';
|
||||||
import {Component} from 'react';
|
import {Component} from 'react';
|
||||||
import BaseDevice from './server/devices/BaseDevice';
|
import BaseDevice from './devices/BaseDevice';
|
||||||
import {StaticView} from './reducers/connections';
|
import {StaticView} from './reducers/connections';
|
||||||
import {State as ReduxState} from './reducers';
|
import {State as ReduxState} from './reducers';
|
||||||
import {DEFAULT_MAX_QUEUE_SIZE} from './reducers/pluginMessageQueue';
|
import {DEFAULT_MAX_QUEUE_SIZE} from './reducers/pluginMessageQueue';
|
||||||
|
|||||||
@@ -9,10 +9,9 @@
|
|||||||
|
|
||||||
import reducer from '../connections';
|
import reducer from '../connections';
|
||||||
import {State, selectPlugin} from '../connections';
|
import {State, selectPlugin} from '../connections';
|
||||||
import BaseDevice from '../../server/devices/BaseDevice';
|
|
||||||
import MetroDevice from '../../server/devices/metro/MetroDevice';
|
|
||||||
import {_setFlipperLibImplementation} from 'flipper-plugin';
|
import {_setFlipperLibImplementation} from 'flipper-plugin';
|
||||||
import {createMockFlipperLib} from 'flipper-plugin/src/test-utils/test-utils';
|
import {createMockFlipperLib} from 'flipper-plugin/src/test-utils/test-utils';
|
||||||
|
import {TestDevice} from '../../test-utils/TestDevice';
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
_setFlipperLibImplementation(createMockFlipperLib());
|
_setFlipperLibImplementation(createMockFlipperLib());
|
||||||
@@ -23,8 +22,8 @@ afterEach(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('doing a double REGISTER_DEVICE keeps the last', () => {
|
test('doing a double REGISTER_DEVICE keeps the last', () => {
|
||||||
const device1 = new BaseDevice('serial', 'physical', 'title', 'Android');
|
const device1 = new TestDevice('serial', 'physical', 'title', 'Android');
|
||||||
const device2 = new BaseDevice('serial', 'physical', 'title2', 'Android');
|
const device2 = new TestDevice('serial', 'physical', 'title2', 'Android');
|
||||||
const initialState: State = reducer(undefined, {
|
const initialState: State = reducer(undefined, {
|
||||||
type: 'REGISTER_DEVICE',
|
type: 'REGISTER_DEVICE',
|
||||||
payload: device1,
|
payload: device1,
|
||||||
@@ -41,7 +40,12 @@ test('doing a double REGISTER_DEVICE keeps the last', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('register, remove, re-register a metro device works correctly', () => {
|
test('register, remove, re-register a metro device works correctly', () => {
|
||||||
const device1 = new MetroDevice('http://localhost:8081', undefined);
|
const device1 = new TestDevice(
|
||||||
|
'http://localhost:8081',
|
||||||
|
'emulator',
|
||||||
|
'React Native',
|
||||||
|
'Metro',
|
||||||
|
);
|
||||||
let state: State = reducer(undefined, {
|
let state: State = reducer(undefined, {
|
||||||
type: 'REGISTER_DEVICE',
|
type: 'REGISTER_DEVICE',
|
||||||
payload: device1,
|
payload: device1,
|
||||||
@@ -56,7 +60,12 @@ test('register, remove, re-register a metro device works correctly', () => {
|
|||||||
|
|
||||||
state = reducer(state, {
|
state = reducer(state, {
|
||||||
type: 'REGISTER_DEVICE',
|
type: 'REGISTER_DEVICE',
|
||||||
payload: new MetroDevice('http://localhost:8081', undefined),
|
payload: new TestDevice(
|
||||||
|
'http://localhost:8081',
|
||||||
|
'emulator',
|
||||||
|
'React Native',
|
||||||
|
'Metro',
|
||||||
|
),
|
||||||
});
|
});
|
||||||
expect(state.devices.length).toBe(1);
|
expect(state.devices.length).toBe(1);
|
||||||
expect(state.devices[0].displayTitle()).toBe('React Native');
|
expect(state.devices[0].displayTitle()).toBe('React Native');
|
||||||
@@ -70,19 +79,3 @@ test('selectPlugin sets deepLinkPayload correctly', () => {
|
|||||||
);
|
);
|
||||||
expect(state.deepLinkPayload).toBe('myPayload');
|
expect(state.deepLinkPayload).toBe('myPayload');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('UNREGISTER_DEVICE removes device', () => {
|
|
||||||
const device = new BaseDevice('serial', 'physical', 'title', 'Android');
|
|
||||||
const initialState: State = reducer(undefined, {
|
|
||||||
type: 'REGISTER_DEVICE',
|
|
||||||
payload: new BaseDevice('serial', 'physical', 'title', 'Android'),
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(initialState.devices).toEqual([device]);
|
|
||||||
const endState = reducer(initialState, {
|
|
||||||
type: 'UNREGISTER_DEVICES',
|
|
||||||
payload: new Set(['serial']),
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(endState.devices).toEqual([]);
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
import {createMockFlipperWithPlugin} from '../../test-utils/createMockFlipperWithPlugin';
|
import {createMockFlipperWithPlugin} from '../../test-utils/createMockFlipperWithPlugin';
|
||||||
import {Store} from '../../';
|
import {Store} from '../../';
|
||||||
import {destroyDevice, selectPlugin} from '../../reducers/connections';
|
import {selectPlugin} from '../../reducers/connections';
|
||||||
import {
|
import {
|
||||||
_SandyPluginDefinition,
|
_SandyPluginDefinition,
|
||||||
_SandyDevicePluginInstance,
|
_SandyDevicePluginInstance,
|
||||||
@@ -86,15 +86,3 @@ test('it should initialize device sandy plugins', async () => {
|
|||||||
expect(instanceApi.deactivateStub).toBeCalledTimes(1);
|
expect(instanceApi.deactivateStub).toBeCalledTimes(1);
|
||||||
expect(instanceApi.destroyStub).toBeCalledTimes(0);
|
expect(instanceApi.destroyStub).toBeCalledTimes(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it should cleanup if device is removed', async () => {
|
|
||||||
const {device, store, logger} = await createMockFlipperWithPlugin(TestPlugin);
|
|
||||||
const pluginInstance = device.sandyPluginStates.get(TestPlugin.id)!;
|
|
||||||
expect(pluginInstance.instanceApi.destroyStub).toHaveBeenCalledTimes(0);
|
|
||||||
|
|
||||||
// close device
|
|
||||||
destroyDevice(store, logger, device.serial);
|
|
||||||
expect(
|
|
||||||
(pluginInstance.instanceApi as PluginApi).destroyStub,
|
|
||||||
).toHaveBeenCalledTimes(1);
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -10,21 +10,19 @@
|
|||||||
import {ComponentType} from 'react';
|
import {ComponentType} from 'react';
|
||||||
import {produce} from 'immer';
|
import {produce} from 'immer';
|
||||||
|
|
||||||
import type BaseDevice from '../server/devices/BaseDevice';
|
import type BaseDevice from '../devices/BaseDevice';
|
||||||
import MacDevice from '../server/devices/desktop/MacDevice';
|
|
||||||
import type Client from '../Client';
|
import type Client from '../Client';
|
||||||
import type {UninitializedClient} from '../server/UninitializedClient';
|
import type {UninitializedClient} from '../server/UninitializedClient';
|
||||||
import {performance} from 'perf_hooks';
|
import {performance} from 'perf_hooks';
|
||||||
import type {Actions, Store} from '.';
|
import type {Actions} from '.';
|
||||||
import {WelcomeScreenStaticView} from '../sandy-chrome/WelcomeScreen';
|
import {WelcomeScreenStaticView} from '../sandy-chrome/WelcomeScreen';
|
||||||
import {isDevicePluginDefinition} from '../utils/pluginUtils';
|
import {isDevicePluginDefinition} from '../utils/pluginUtils';
|
||||||
import {getPluginKey} from '../utils/pluginKey';
|
import {getPluginKey} from '../utils/pluginKey';
|
||||||
|
|
||||||
import {deconstructClientId} from '../utils/clientUtils';
|
import {deconstructClientId} from '../utils/clientUtils';
|
||||||
import type {RegisterPluginAction} from './plugins';
|
import type {RegisterPluginAction} from './plugins';
|
||||||
import MetroDevice from '../server/devices/metro/MetroDevice';
|
import {DeviceOS, Logger} from 'flipper-plugin';
|
||||||
import {Logger} from 'flipper-plugin';
|
import {FlipperServerImpl} from '../server/FlipperServerImpl';
|
||||||
import {FlipperServer} from '../server/FlipperServer';
|
|
||||||
import {shallowEqual} from 'react-redux';
|
import {shallowEqual} from 'react-redux';
|
||||||
|
|
||||||
export type StaticViewProps = {logger: Logger};
|
export type StaticViewProps = {logger: Logger};
|
||||||
@@ -77,7 +75,7 @@ type StateV2 = {
|
|||||||
deepLinkPayload: unknown;
|
deepLinkPayload: unknown;
|
||||||
staticView: StaticView;
|
staticView: StaticView;
|
||||||
selectedAppPluginListRevision: number;
|
selectedAppPluginListRevision: number;
|
||||||
flipperServer: FlipperServer | undefined;
|
flipperServer: FlipperServerImpl | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateV1 = Omit<StateV2, 'enabledPlugins' | 'enabledDevicePlugins'> & {
|
type StateV1 = Omit<StateV2, 'enabledPlugins' | 'enabledDevicePlugins'> & {
|
||||||
@@ -91,10 +89,6 @@ type StateV0 = Omit<StateV1, 'enabledPlugins' | 'enabledDevicePlugins'> & {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type Action =
|
export type Action =
|
||||||
| {
|
|
||||||
type: 'UNREGISTER_DEVICES';
|
|
||||||
payload: Set<string>;
|
|
||||||
}
|
|
||||||
| {
|
| {
|
||||||
type: 'REGISTER_DEVICE';
|
type: 'REGISTER_DEVICE';
|
||||||
payload: BaseDevice;
|
payload: BaseDevice;
|
||||||
@@ -173,12 +167,12 @@ export type Action =
|
|||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: 'SET_FLIPPER_SERVER';
|
type: 'SET_FLIPPER_SERVER';
|
||||||
payload: FlipperServer;
|
payload: FlipperServerImpl;
|
||||||
}
|
}
|
||||||
| RegisterPluginAction;
|
| RegisterPluginAction;
|
||||||
|
|
||||||
const DEFAULT_PLUGIN = 'DeviceLogs';
|
const DEFAULT_PLUGIN = 'DeviceLogs';
|
||||||
const DEFAULT_DEVICE_BLACKLIST = [MacDevice, MetroDevice];
|
const DEFAULT_DEVICE_BLACKLIST: DeviceOS[] = ['MacOS', 'Metro', 'Windows'];
|
||||||
const INITAL_STATE: State = {
|
const INITAL_STATE: State = {
|
||||||
devices: [],
|
devices: [],
|
||||||
selectedDevice: null,
|
selectedDevice: null,
|
||||||
@@ -250,7 +244,13 @@ export default (state: State = INITAL_STATE, action: Actions): State => {
|
|||||||
(device) => device.serial === payload.serial,
|
(device) => device.serial === payload.serial,
|
||||||
);
|
);
|
||||||
if (existing !== -1) {
|
if (existing !== -1) {
|
||||||
newDevices[existing].destroy();
|
const d = newDevices[existing];
|
||||||
|
if (d.connected.get()) {
|
||||||
|
console.warn(
|
||||||
|
`Tried to replace still connected device '${d.serial}' with a new instance`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
d.destroy();
|
||||||
newDevices[existing] = payload;
|
newDevices[existing] = payload;
|
||||||
} else {
|
} else {
|
||||||
newDevices.push(payload);
|
newDevices.push(payload);
|
||||||
@@ -262,28 +262,6 @@ export default (state: State = INITAL_STATE, action: Actions): State => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: remove
|
|
||||||
case 'UNREGISTER_DEVICES': {
|
|
||||||
const deviceSerials = action.payload;
|
|
||||||
|
|
||||||
return updateSelection(
|
|
||||||
produce(state, (draft) => {
|
|
||||||
draft.devices = draft.devices.filter((device) => {
|
|
||||||
if (!deviceSerials.has(device.serial)) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
if (device.connected.get()) {
|
|
||||||
console.warn(
|
|
||||||
'Tried to unregister a device before it was destroyed',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'SELECT_PLUGIN': {
|
case 'SELECT_PLUGIN': {
|
||||||
const {payload} = action;
|
const {payload} = action;
|
||||||
const {selectedPlugin, selectedApp, deepLinkPayload} = payload;
|
const {selectedPlugin, selectedApp, deepLinkPayload} = payload;
|
||||||
@@ -581,9 +559,7 @@ export function getClientById(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function canBeDefaultDevice(device: BaseDevice) {
|
export function canBeDefaultDevice(device: BaseDevice) {
|
||||||
return !DEFAULT_DEVICE_BLACKLIST.some(
|
return !DEFAULT_DEVICE_BLACKLIST.includes(device.os);
|
||||||
(blacklistedDevice) => device instanceof blacklistedDevice,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -669,21 +645,3 @@ export function isPluginEnabled(
|
|||||||
const enabledAppPlugins = enabledPlugins[appInfo.app];
|
const enabledAppPlugins = enabledPlugins[appInfo.app];
|
||||||
return enabledAppPlugins && enabledAppPlugins.indexOf(pluginId) > -1;
|
return enabledAppPlugins && enabledAppPlugins.indexOf(pluginId) > -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: remove!
|
|
||||||
export function destroyDevice(store: Store, logger: Logger, serial: string) {
|
|
||||||
const device = store
|
|
||||||
.getState()
|
|
||||||
.connections.devices.find((device) => device.serial === serial);
|
|
||||||
if (device) {
|
|
||||||
device.destroy();
|
|
||||||
logger.track('usage', 'unregister-device', {
|
|
||||||
os: device.os,
|
|
||||||
serial,
|
|
||||||
});
|
|
||||||
store.dispatch({
|
|
||||||
type: 'UNREGISTER_DEVICES',
|
|
||||||
payload: new Set([serial]),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import ScreenCaptureButtons from '../../chrome/ScreenCaptureButtons';
|
|||||||
import MetroButton from '../../chrome/MetroButton';
|
import MetroButton from '../../chrome/MetroButton';
|
||||||
import {BookmarkSection} from './BookmarkSection';
|
import {BookmarkSection} from './BookmarkSection';
|
||||||
import Client from '../../Client';
|
import Client from '../../Client';
|
||||||
import BaseDevice from '../../server/devices/BaseDevice';
|
import BaseDevice from '../../devices/BaseDevice';
|
||||||
import {ExclamationCircleOutlined, FieldTimeOutlined} from '@ant-design/icons';
|
import {ExclamationCircleOutlined, FieldTimeOutlined} from '@ant-design/icons';
|
||||||
import {useSelector} from 'react-redux';
|
import {useSelector} from 'react-redux';
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import {
|
|||||||
selectClient,
|
selectClient,
|
||||||
selectDevice,
|
selectDevice,
|
||||||
} from '../../reducers/connections';
|
} from '../../reducers/connections';
|
||||||
import BaseDevice from '../../server/devices/BaseDevice';
|
import BaseDevice from '../../devices/BaseDevice';
|
||||||
import Client from '../../Client';
|
import Client from '../../Client';
|
||||||
import {State} from '../../reducers';
|
import {State} from '../../reducers';
|
||||||
import {brandColors, brandIcons, colors} from '../../ui/components/colors';
|
import {brandColors, brandIcons, colors} from '../../ui/components/colors';
|
||||||
|
|||||||
@@ -23,9 +23,8 @@ import {useDispatch, useStore} from '../../utils/useStore';
|
|||||||
import {getPluginTitle, getPluginTooltip} from '../../utils/pluginUtils';
|
import {getPluginTitle, getPluginTooltip} from '../../utils/pluginUtils';
|
||||||
import {selectPlugin} from '../../reducers/connections';
|
import {selectPlugin} from '../../reducers/connections';
|
||||||
import Client from '../../Client';
|
import Client from '../../Client';
|
||||||
import BaseDevice from '../../server/devices/BaseDevice';
|
import BaseDevice from '../../devices/BaseDevice';
|
||||||
import {DownloadablePluginDetails} from 'flipper-plugin-lib';
|
import {DownloadablePluginDetails} from 'flipper-plugin-lib';
|
||||||
import MetroDevice from '../../server/devices/metro/MetroDevice';
|
|
||||||
import {
|
import {
|
||||||
DownloadablePluginState,
|
DownloadablePluginState,
|
||||||
PluginDownloadStatus,
|
PluginDownloadStatus,
|
||||||
@@ -52,7 +51,7 @@ export const PluginList = memo(function PluginList({
|
|||||||
}: {
|
}: {
|
||||||
client: Client | null;
|
client: Client | null;
|
||||||
activeDevice: BaseDevice | null;
|
activeDevice: BaseDevice | null;
|
||||||
metroDevice: MetroDevice | null;
|
metroDevice: BaseDevice | null;
|
||||||
}) {
|
}) {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const connections = useStore((state) => state.connections);
|
const connections = useStore((state) => state.connections);
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
} from '../../../test-utils/createMockFlipperWithPlugin';
|
} from '../../../test-utils/createMockFlipperWithPlugin';
|
||||||
import {FlipperPlugin} from '../../../plugin';
|
import {FlipperPlugin} from '../../../plugin';
|
||||||
import MetroDevice from '../../../server/devices/metro/MetroDevice';
|
import MetroDevice from '../../../server/devices/metro/MetroDevice';
|
||||||
import BaseDevice from '../../../server/devices/BaseDevice';
|
import BaseDevice from '../../../devices/BaseDevice';
|
||||||
import {_SandyPluginDefinition} from 'flipper-plugin';
|
import {_SandyPluginDefinition} from 'flipper-plugin';
|
||||||
import {TestUtils} from 'flipper-plugin';
|
import {TestUtils} from 'flipper-plugin';
|
||||||
import {selectPlugin} from '../../../reducers/connections';
|
import {selectPlugin} from '../../../reducers/connections';
|
||||||
@@ -33,6 +33,7 @@ import {
|
|||||||
getMetroDevice,
|
getMetroDevice,
|
||||||
getPluginLists,
|
getPluginLists,
|
||||||
} from '../../../selectors/connections';
|
} from '../../../selectors/connections';
|
||||||
|
import {TestDevice} from '../../../test-utils/TestDevice';
|
||||||
|
|
||||||
const createMockPluginDetails = TestUtils.createMockPluginDetails;
|
const createMockPluginDetails = TestUtils.createMockPluginDetails;
|
||||||
|
|
||||||
@@ -67,7 +68,7 @@ describe('basic getActiveDevice', () => {
|
|||||||
|
|
||||||
describe('basic getActiveDevice with metro present', () => {
|
describe('basic getActiveDevice with metro present', () => {
|
||||||
let flipper: MockFlipperResult;
|
let flipper: MockFlipperResult;
|
||||||
let metro: MetroDevice;
|
let metro: BaseDevice;
|
||||||
let testDevice: BaseDevice;
|
let testDevice: BaseDevice;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
@@ -79,7 +80,12 @@ describe('basic getActiveDevice with metro present', () => {
|
|||||||
// flipper.store.dispatch(registerPlugins([LogsPlugin]))
|
// flipper.store.dispatch(registerPlugins([LogsPlugin]))
|
||||||
flipper.store.dispatch({
|
flipper.store.dispatch({
|
||||||
type: 'REGISTER_DEVICE',
|
type: 'REGISTER_DEVICE',
|
||||||
payload: new MetroDevice('http://localhost:8081', undefined),
|
payload: new TestDevice(
|
||||||
|
'http://localhost:8081',
|
||||||
|
'physical',
|
||||||
|
'metro',
|
||||||
|
'Metro',
|
||||||
|
),
|
||||||
});
|
});
|
||||||
metro = getMetroDevice(flipper.store.getState())!;
|
metro = getMetroDevice(flipper.store.getState())!;
|
||||||
metro.supportsPlugin = (p) => {
|
metro.supportsPlugin = (p) => {
|
||||||
@@ -88,7 +94,7 @@ describe('basic getActiveDevice with metro present', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('findMetroDevice', () => {
|
test('findMetroDevice', () => {
|
||||||
expect(metro).toBeInstanceOf(MetroDevice);
|
expect(metro.os).toBe('Metro');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('correct base selection state', () => {
|
test('correct base selection state', () => {
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import MetroDevice from '../server/devices/metro/MetroDevice';
|
|
||||||
import {State} from '../reducers';
|
import {State} from '../reducers';
|
||||||
import {
|
import {
|
||||||
computePluginLists,
|
computePluginLists,
|
||||||
@@ -36,9 +35,8 @@ export const getActiveClient = createSelector(
|
|||||||
|
|
||||||
export const getMetroDevice = createSelector(getDevices, (devices) => {
|
export const getMetroDevice = createSelector(getDevices, (devices) => {
|
||||||
return (
|
return (
|
||||||
(devices.find((device) => device.os === 'Metro' && !device.isArchived) as
|
devices.find((device) => device.os === 'Metro' && !device.isArchived) ??
|
||||||
| MetroDevice
|
null
|
||||||
| undefined) ?? null
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import {Logger} from '../fb-interfaces/Logger';
|
|||||||
import ServerController from './comms/ServerController';
|
import ServerController from './comms/ServerController';
|
||||||
import {UninitializedClient} from './UninitializedClient';
|
import {UninitializedClient} from './UninitializedClient';
|
||||||
import {addErrorNotification} from '../reducers/notifications';
|
import {addErrorNotification} from '../reducers/notifications';
|
||||||
import {CertificateExchangeMedium} from '../server/utils/CertificateProvider';
|
import {CertificateExchangeMedium} from './utils/CertificateProvider';
|
||||||
import {isLoggedIn} from '../fb-stubs/user';
|
import {isLoggedIn} from '../fb-stubs/user';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {Typography} from 'antd';
|
import {Typography} from 'antd';
|
||||||
@@ -27,8 +27,15 @@ import {AndroidDeviceManager} from './devices/android/androidDeviceManager';
|
|||||||
import {IOSDeviceManager} from './devices/ios/iOSDeviceManager';
|
import {IOSDeviceManager} from './devices/ios/iOSDeviceManager';
|
||||||
import metroDevice from './devices/metro/metroDeviceManager';
|
import metroDevice from './devices/metro/metroDeviceManager';
|
||||||
import desktopDevice from './devices/desktop/desktopDeviceManager';
|
import desktopDevice from './devices/desktop/desktopDeviceManager';
|
||||||
import BaseDevice from './devices/BaseDevice';
|
import {
|
||||||
import {FlipperServerEvents, FlipperServerState} from 'flipper-plugin';
|
FlipperServerEvents,
|
||||||
|
FlipperServerState,
|
||||||
|
FlipperServerCommands,
|
||||||
|
FlipperServer,
|
||||||
|
} from 'flipper-plugin';
|
||||||
|
import {ServerDevice} from './devices/ServerDevice';
|
||||||
|
import {Base64} from 'js-base64';
|
||||||
|
import MetroDevice from './devices/metro/MetroDevice';
|
||||||
|
|
||||||
export interface FlipperServerConfig {
|
export interface FlipperServerConfig {
|
||||||
enableAndroid: boolean;
|
enableAndroid: boolean;
|
||||||
@@ -60,14 +67,14 @@ const defaultConfig: FlipperServerConfig = {
|
|||||||
* The server should be largely treated as event emitter, by listening to the relevant events
|
* The server should be largely treated as event emitter, by listening to the relevant events
|
||||||
* using '.on'. All events are strongly typed.
|
* using '.on'. All events are strongly typed.
|
||||||
*/
|
*/
|
||||||
export class FlipperServer {
|
export class FlipperServerImpl implements FlipperServer {
|
||||||
public config: FlipperServerConfig;
|
public config: FlipperServerConfig;
|
||||||
|
|
||||||
private readonly events = new EventEmitter();
|
private readonly events = new EventEmitter();
|
||||||
// server handles the incoming RSocket / WebSocket connections from Flipper clients
|
// server handles the incoming RSocket / WebSocket connections from Flipper clients
|
||||||
readonly server: ServerController;
|
readonly server: ServerController;
|
||||||
readonly disposers: ((() => void) | void)[] = [];
|
readonly disposers: ((() => void) | void)[] = [];
|
||||||
private readonly devices = new Map<string, BaseDevice>();
|
private readonly devices = new Map<string, ServerDevice>();
|
||||||
state: FlipperServerState = 'pending';
|
state: FlipperServerState = 'pending';
|
||||||
android: AndroidDeviceManager;
|
android: AndroidDeviceManager;
|
||||||
ios: IOSDeviceManager;
|
ios: IOSDeviceManager;
|
||||||
@@ -197,6 +204,13 @@ export class FlipperServer {
|
|||||||
this.events.on(event, callback);
|
this.events.on(event, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
off<Event extends keyof FlipperServerEvents>(
|
||||||
|
event: Event,
|
||||||
|
callback: (payload: FlipperServerEvents[Event]) => void,
|
||||||
|
): void {
|
||||||
|
this.events.off(event, callback);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
@@ -207,31 +221,64 @@ export class FlipperServer {
|
|||||||
this.events.emit(event, payload);
|
this.events.emit(event, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
registerDevice(device: BaseDevice) {
|
exec<Event extends keyof FlipperServerCommands>(
|
||||||
|
event: Event,
|
||||||
|
...args: Parameters<FlipperServerCommands[Event]>
|
||||||
|
): ReturnType<FlipperServerCommands[Event]> {
|
||||||
|
console.debug(`[FlipperServer] command ${event}: `, args);
|
||||||
|
const handler: (...args: any[]) => Promise<any> =
|
||||||
|
this.commandHandler[event];
|
||||||
|
if (!handler) {
|
||||||
|
throw new Error(`Unimplemented server command: ${event}`);
|
||||||
|
}
|
||||||
|
return handler(...args) as any;
|
||||||
|
}
|
||||||
|
|
||||||
|
private commandHandler: FlipperServerCommands = {
|
||||||
|
'device-start-logging': async (serial: string) =>
|
||||||
|
this.getDevice(serial).startLogging(),
|
||||||
|
'device-stop-logging': async (serial: string) =>
|
||||||
|
this.getDevice(serial).stopLogging(),
|
||||||
|
'device-supports-screenshot': async (serial: string) =>
|
||||||
|
this.getDevice(serial).screenshotAvailable(),
|
||||||
|
'device-supports-screencapture': async (serial: string) =>
|
||||||
|
this.getDevice(serial).screenCaptureAvailable(),
|
||||||
|
'device-take-screenshot': async (serial: string) =>
|
||||||
|
Base64.fromUint8Array(await this.getDevice(serial).screenshot()),
|
||||||
|
'device-start-screencapture': async (serial, destination) =>
|
||||||
|
this.getDevice(serial).startScreenCapture(destination),
|
||||||
|
'device-stop-screencapture': async (serial: string) =>
|
||||||
|
this.getDevice(serial).stopScreenCapture(),
|
||||||
|
'device-shell-exec': async (serial: string, command: string) =>
|
||||||
|
this.getDevice(serial).executeShell(command),
|
||||||
|
'metro-command': async (serial: string, command: string) => {
|
||||||
|
const device = this.getDevice(serial);
|
||||||
|
if (!(device instanceof MetroDevice)) {
|
||||||
|
throw new Error('Not a Metro device: ' + serial);
|
||||||
|
}
|
||||||
|
device.sendCommand(command);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
registerDevice(device: ServerDevice) {
|
||||||
// destroy existing device
|
// destroy existing device
|
||||||
const existing = this.devices.get(device.serial);
|
const {serial} = device.info;
|
||||||
|
const existing = this.devices.get(serial);
|
||||||
if (existing) {
|
if (existing) {
|
||||||
// assert different kind of devices aren't accidentally reusing the same serial
|
// assert different kind of devices aren't accidentally reusing the same serial
|
||||||
if (Object.getPrototypeOf(existing) !== Object.getPrototypeOf(device)) {
|
if (Object.getPrototypeOf(existing) !== Object.getPrototypeOf(device)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Tried to register a new device type for existing serial '${
|
`Tried to register a new device type for existing serial '${serial}': Trying to replace existing '${
|
||||||
device.serial
|
|
||||||
}': Trying to replace existing '${
|
|
||||||
Object.getPrototypeOf(existing).constructor.name
|
Object.getPrototypeOf(existing).constructor.name
|
||||||
}' with a new '${Object.getPrototypeOf(device).constructor.name}`,
|
}' with a new '${Object.getPrototypeOf(device).constructor.name}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// devices should be recycled, unless they have lost connection
|
// clean up connection
|
||||||
if (existing.connected.get()) {
|
existing.disconnect();
|
||||||
throw new Error(
|
|
||||||
`Tried to replace still connected device '${device.serial}' with a new instance`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
existing.destroy();
|
|
||||||
}
|
}
|
||||||
// register new device
|
// register new device
|
||||||
this.devices.set(device.serial, device);
|
this.devices.set(device.info.serial, device);
|
||||||
this.emit('device-connected', device);
|
this.emit('device-connected', device.info);
|
||||||
}
|
}
|
||||||
|
|
||||||
unregisterDevice(serial: string) {
|
unregisterDevice(serial: string) {
|
||||||
@@ -240,10 +287,10 @@ export class FlipperServer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
device.disconnect(); // we'll only destroy upon replacement
|
device.disconnect(); // we'll only destroy upon replacement
|
||||||
this.emit('device-disconnected', device);
|
this.emit('device-disconnected', device.info);
|
||||||
}
|
}
|
||||||
|
|
||||||
getDevice(serial: string): BaseDevice {
|
getDevice(serial: string): ServerDevice {
|
||||||
const device = this.devices.get(serial);
|
const device = this.devices.get(serial);
|
||||||
if (!device) {
|
if (!device) {
|
||||||
throw new Error('No device with serial: ' + serial);
|
throw new Error('No device with serial: ' + serial);
|
||||||
@@ -255,14 +302,14 @@ export class FlipperServer {
|
|||||||
return Array.from(this.devices.keys());
|
return Array.from(this.devices.keys());
|
||||||
}
|
}
|
||||||
|
|
||||||
getDevices(): BaseDevice[] {
|
getDevices(): ServerDevice[] {
|
||||||
return Array.from(this.devices.values());
|
return Array.from(this.devices.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
public async close() {
|
public async close() {
|
||||||
this.server.close();
|
this.server.close();
|
||||||
for (const device of this.devices.values()) {
|
for (const device of this.devices.values()) {
|
||||||
device.destroy();
|
device.disconnect();
|
||||||
}
|
}
|
||||||
this.disposers.forEach((f) => f?.());
|
this.disposers.forEach((f) => f?.());
|
||||||
this.setServerState('closed');
|
this.setServerState('closed');
|
||||||
@@ -21,7 +21,7 @@ import invariant from 'invariant';
|
|||||||
import GK from '../../fb-stubs/GK';
|
import GK from '../../fb-stubs/GK';
|
||||||
import {buildClientId} from '../../utils/clientUtils';
|
import {buildClientId} from '../../utils/clientUtils';
|
||||||
import DummyDevice from '../../server/devices/DummyDevice';
|
import DummyDevice from '../../server/devices/DummyDevice';
|
||||||
import BaseDevice from '../../server/devices/BaseDevice';
|
import BaseDevice from '../../devices/BaseDevice';
|
||||||
import {sideEffect} from '../../utils/sideEffect';
|
import {sideEffect} from '../../utils/sideEffect';
|
||||||
import {
|
import {
|
||||||
appNameWithUpdateHint,
|
appNameWithUpdateHint,
|
||||||
@@ -36,7 +36,7 @@ import {
|
|||||||
createServer,
|
createServer,
|
||||||
TransportType,
|
TransportType,
|
||||||
} from './ServerFactory';
|
} from './ServerFactory';
|
||||||
import {FlipperServer} from '../FlipperServer';
|
import {FlipperServerImpl} from '../FlipperServerImpl';
|
||||||
import {isTest} from '../../utils/isProduction';
|
import {isTest} from '../../utils/isProduction';
|
||||||
import {timeout} from 'flipper-plugin';
|
import {timeout} from 'flipper-plugin';
|
||||||
|
|
||||||
@@ -77,11 +77,11 @@ class ServerController extends EventEmitter implements ServerEventsListener {
|
|||||||
certificateProvider: CertificateProvider;
|
certificateProvider: CertificateProvider;
|
||||||
connectionTracker: ConnectionTracker;
|
connectionTracker: ConnectionTracker;
|
||||||
|
|
||||||
flipperServer: FlipperServer;
|
flipperServer: FlipperServerImpl;
|
||||||
|
|
||||||
timeHandlers: Map<string, NodeJS.Timeout> = new Map();
|
timeHandlers: Map<string, NodeJS.Timeout> = new Map();
|
||||||
|
|
||||||
constructor(flipperServer: FlipperServer) {
|
constructor(flipperServer: FlipperServerImpl) {
|
||||||
super();
|
super();
|
||||||
this.flipperServer = flipperServer;
|
this.flipperServer = flipperServer;
|
||||||
this.connections = new Map();
|
this.connections = new Map();
|
||||||
@@ -247,6 +247,7 @@ class ServerController extends EventEmitter implements ServerEventsListener {
|
|||||||
if (transformedMedium === 'WWW' || transformedMedium === 'NONE') {
|
if (transformedMedium === 'WWW' || transformedMedium === 'NONE') {
|
||||||
this.flipperServer.registerDevice(
|
this.flipperServer.registerDevice(
|
||||||
new DummyDevice(
|
new DummyDevice(
|
||||||
|
this.flipperServer,
|
||||||
clientQuery.device_id,
|
clientQuery.device_id,
|
||||||
clientQuery.app +
|
clientQuery.app +
|
||||||
(transformedMedium === 'WWW' ? ' Server Exchanged' : ''),
|
(transformedMedium === 'WWW' ? ' Server Exchanged' : ''),
|
||||||
|
|||||||
@@ -76,11 +76,6 @@ class ServerWebSocketBrowser extends ServerWebSocketBase {
|
|||||||
Object.values(clients).map((p) =>
|
Object.values(clients).map((p) =>
|
||||||
p.then((c) => this.listener.onConnectionClosed(c.id)),
|
p.then((c) => this.listener.onConnectionClosed(c.id)),
|
||||||
);
|
);
|
||||||
// TODO: destroy device.
|
|
||||||
// This seems to be the only case in which a device gets destroyed when there's a disconnection
|
|
||||||
// or error on the transport layer.
|
|
||||||
//
|
|
||||||
// destroyDevice(this.store, this.logger, deviceId);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -8,13 +8,24 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {DeviceOS} from 'flipper-plugin';
|
import {DeviceOS} from 'flipper-plugin';
|
||||||
import BaseDevice from './BaseDevice';
|
import {FlipperServerImpl} from '../FlipperServerImpl';
|
||||||
|
import {ServerDevice} from './ServerDevice';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use this device when you do not have the actual uuid of the device. For example, it is currently used in the case when, we do certificate exchange through WWW mode. In this mode we do not know the device id of the app and we generate a fake one.
|
* Use this device when you do not have the actual uuid of the device. For example, it is currently used in the case when, we do certificate exchange through WWW mode. In this mode we do not know the device id of the app and we generate a fake one.
|
||||||
*/
|
*/
|
||||||
export default class DummyDevice extends BaseDevice {
|
export default class DummyDevice extends ServerDevice {
|
||||||
constructor(serial: string, title: string, os: DeviceOS) {
|
constructor(
|
||||||
super(serial, 'dummy', title, os);
|
flipperServer: FlipperServerImpl,
|
||||||
|
serial: string,
|
||||||
|
title: string,
|
||||||
|
os: DeviceOS,
|
||||||
|
) {
|
||||||
|
super(flipperServer, {
|
||||||
|
serial,
|
||||||
|
deviceType: 'dummy',
|
||||||
|
title,
|
||||||
|
os,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
72
desktop/app/src/server/devices/ServerDevice.tsx
Normal file
72
desktop/app/src/server/devices/ServerDevice.tsx
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
/**
|
||||||
|
* 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 {createState, DeviceDescription, DeviceLogEntry} from 'flipper-plugin';
|
||||||
|
import {FlipperServerImpl} from '../FlipperServerImpl';
|
||||||
|
|
||||||
|
export abstract class ServerDevice {
|
||||||
|
readonly info: DeviceDescription;
|
||||||
|
readonly connected = createState(true);
|
||||||
|
readonly flipperServer: FlipperServerImpl;
|
||||||
|
|
||||||
|
constructor(flipperServer: FlipperServerImpl, info: DeviceDescription) {
|
||||||
|
this.flipperServer = flipperServer;
|
||||||
|
this.info = info;
|
||||||
|
}
|
||||||
|
|
||||||
|
get serial(): string {
|
||||||
|
return this.info.serial;
|
||||||
|
}
|
||||||
|
|
||||||
|
addLogEntry(entry: DeviceLogEntry) {
|
||||||
|
this.flipperServer.emit('device-log', {
|
||||||
|
serial: this.serial,
|
||||||
|
entry,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The device might have no active connection
|
||||||
|
*/
|
||||||
|
disconnect(): void {}
|
||||||
|
|
||||||
|
startLogging() {
|
||||||
|
// to be subclassed
|
||||||
|
}
|
||||||
|
|
||||||
|
stopLogging() {
|
||||||
|
// to be subclassed
|
||||||
|
}
|
||||||
|
|
||||||
|
async screenshotAvailable(): Promise<boolean> {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
screenshot(): Promise<Buffer> {
|
||||||
|
return Promise.reject(
|
||||||
|
new Error('No screenshot support for current device'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async screenCaptureAvailable(): Promise<boolean> {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async startScreenCapture(_destination: string): Promise<void> {
|
||||||
|
throw new Error('startScreenCapture not implemented on BaseDevice ');
|
||||||
|
}
|
||||||
|
|
||||||
|
async stopScreenCapture(): Promise<string> {
|
||||||
|
throw new Error('stopScreenCapture not implemented on BaseDevice ');
|
||||||
|
}
|
||||||
|
|
||||||
|
async executeShell(_command: string): Promise<string> {
|
||||||
|
throw new Error('executeShell not implemented on BaseDevice');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,12 +7,11 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import BaseDevice from '../BaseDevice';
|
|
||||||
import * as DeviceTestPluginModule from '../../../test-utils/DeviceTestPlugin';
|
import * as DeviceTestPluginModule from '../../../test-utils/DeviceTestPlugin';
|
||||||
import {TestUtils, _SandyPluginDefinition} from 'flipper-plugin';
|
import {TestUtils, _SandyPluginDefinition} from 'flipper-plugin';
|
||||||
import ArchivedDevice from '../ArchivedDevice';
|
|
||||||
import DummyDevice from '../DummyDevice';
|
|
||||||
import {createMockFlipperWithPlugin} from '../../../test-utils/createMockFlipperWithPlugin';
|
import {createMockFlipperWithPlugin} from '../../../test-utils/createMockFlipperWithPlugin';
|
||||||
|
import {TestDevice} from '../../../test-utils/TestDevice';
|
||||||
|
import ArchivedDevice from '../../../devices/ArchivedDevice';
|
||||||
|
|
||||||
const physicalDevicePluginDetails = TestUtils.createMockPluginDetails({
|
const physicalDevicePluginDetails = TestUtils.createMockPluginDetails({
|
||||||
id: 'physicalDevicePlugin',
|
id: 'physicalDevicePlugin',
|
||||||
@@ -122,7 +121,7 @@ const androidOnlyDevicePlugin = new _SandyPluginDefinition(
|
|||||||
);
|
);
|
||||||
|
|
||||||
test('ios physical device compatibility', () => {
|
test('ios physical device compatibility', () => {
|
||||||
const device = new BaseDevice('serial', 'physical', 'test device', 'iOS');
|
const device = new TestDevice('serial', 'physical', 'test device', 'iOS');
|
||||||
expect(device.supportsPlugin(physicalDevicePlugin)).toBeTruthy();
|
expect(device.supportsPlugin(physicalDevicePlugin)).toBeTruthy();
|
||||||
expect(device.supportsPlugin(iosPhysicalDevicePlugin)).toBeTruthy();
|
expect(device.supportsPlugin(iosPhysicalDevicePlugin)).toBeTruthy();
|
||||||
expect(device.supportsPlugin(iosEmulatorDevicePlugin)).toBeFalsy();
|
expect(device.supportsPlugin(iosEmulatorDevicePlugin)).toBeFalsy();
|
||||||
@@ -146,7 +145,7 @@ test('archived device compatibility', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('android emulator device compatibility', () => {
|
test('android emulator device compatibility', () => {
|
||||||
const device = new BaseDevice('serial', 'emulator', 'test device', 'Android');
|
const device = new TestDevice('serial', 'emulator', 'test device', 'Android');
|
||||||
expect(device.supportsPlugin(physicalDevicePlugin)).toBeFalsy();
|
expect(device.supportsPlugin(physicalDevicePlugin)).toBeFalsy();
|
||||||
expect(device.supportsPlugin(iosPhysicalDevicePlugin)).toBeFalsy();
|
expect(device.supportsPlugin(iosPhysicalDevicePlugin)).toBeFalsy();
|
||||||
expect(device.supportsPlugin(iosEmulatorDevicePlugin)).toBeFalsy();
|
expect(device.supportsPlugin(iosEmulatorDevicePlugin)).toBeFalsy();
|
||||||
@@ -155,7 +154,7 @@ test('android emulator device compatibility', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('android KaiOS device compatibility', () => {
|
test('android KaiOS device compatibility', () => {
|
||||||
const device = new BaseDevice(
|
const device = new TestDevice(
|
||||||
'serial',
|
'serial',
|
||||||
'physical',
|
'physical',
|
||||||
'test device',
|
'test device',
|
||||||
@@ -170,7 +169,7 @@ test('android KaiOS device compatibility', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('android dummy device compatibility', () => {
|
test('android dummy device compatibility', () => {
|
||||||
const device = new DummyDevice('serial', 'test device', 'Android');
|
const device = new TestDevice('serial', 'dummy', 'test device', 'Android');
|
||||||
expect(device.supportsPlugin(physicalDevicePlugin)).toBeFalsy();
|
expect(device.supportsPlugin(physicalDevicePlugin)).toBeFalsy();
|
||||||
expect(device.supportsPlugin(iosPhysicalDevicePlugin)).toBeFalsy();
|
expect(device.supportsPlugin(iosPhysicalDevicePlugin)).toBeFalsy();
|
||||||
expect(device.supportsPlugin(iosEmulatorDevicePlugin)).toBeFalsy();
|
expect(device.supportsPlugin(iosEmulatorDevicePlugin)).toBeFalsy();
|
||||||
@@ -188,7 +187,7 @@ test('log listeners are resumed and suspended automatically - 1', async () => {
|
|||||||
type: 'info',
|
type: 'info',
|
||||||
tag: 'tag',
|
tag: 'tag',
|
||||||
} as const;
|
} as const;
|
||||||
const device = new BaseDevice('serial', 'physical', 'test device', 'Android');
|
const device = new TestDevice('serial', 'physical', 'test device', 'Android');
|
||||||
device.startLogging = jest.fn();
|
device.startLogging = jest.fn();
|
||||||
device.stopLogging = jest.fn();
|
device.stopLogging = jest.fn();
|
||||||
|
|
||||||
@@ -251,7 +250,7 @@ test('log listeners are resumed and suspended automatically - 2', async () => {
|
|||||||
type: 'info',
|
type: 'info',
|
||||||
tag: 'tag',
|
tag: 'tag',
|
||||||
} as const;
|
} as const;
|
||||||
const device = new BaseDevice('serial', 'physical', 'test device', 'Android');
|
const device = new TestDevice('serial', 'physical', 'test device', 'Android');
|
||||||
device.startLogging = jest.fn();
|
device.startLogging = jest.fn();
|
||||||
device.stopLogging = jest.fn();
|
device.stopLogging = jest.fn();
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import BaseDevice from '../BaseDevice';
|
|
||||||
import adb, {Client as ADBClient, PullTransfer} from 'adbkit';
|
import adb, {Client as ADBClient, PullTransfer} from 'adbkit';
|
||||||
import {Priority, Reader} from 'adbkit-logcat';
|
import {Priority, Reader} from 'adbkit-logcat';
|
||||||
import {createWriteStream} from 'fs';
|
import {createWriteStream} from 'fs';
|
||||||
@@ -16,18 +15,19 @@ import which from 'which';
|
|||||||
import {spawn} from 'child_process';
|
import {spawn} from 'child_process';
|
||||||
import {dirname, join} from 'path';
|
import {dirname, join} from 'path';
|
||||||
import {DeviceSpec} from 'flipper-plugin-lib';
|
import {DeviceSpec} from 'flipper-plugin-lib';
|
||||||
|
import {ServerDevice} from '../ServerDevice';
|
||||||
|
import {FlipperServerImpl} from '../../FlipperServerImpl';
|
||||||
|
|
||||||
const DEVICE_RECORDING_DIR = '/sdcard/flipper_recorder';
|
const DEVICE_RECORDING_DIR = '/sdcard/flipper_recorder';
|
||||||
|
|
||||||
export default class AndroidDevice extends BaseDevice {
|
export default class AndroidDevice extends ServerDevice {
|
||||||
adb: ADBClient;
|
adb: ADBClient;
|
||||||
abiList: Array<string> = [];
|
|
||||||
sdkVersion: string | undefined = undefined;
|
|
||||||
pidAppMapping: {[key: number]: string} = {};
|
pidAppMapping: {[key: number]: string} = {};
|
||||||
private recordingProcess?: Promise<string>;
|
private recordingProcess?: Promise<string>;
|
||||||
reader?: Reader;
|
reader?: Reader;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
flipperServer: FlipperServerImpl,
|
||||||
serial: string,
|
serial: string,
|
||||||
deviceType: DeviceType,
|
deviceType: DeviceType,
|
||||||
title: string,
|
title: string,
|
||||||
@@ -36,11 +36,17 @@ export default class AndroidDevice extends BaseDevice {
|
|||||||
sdkVersion: string,
|
sdkVersion: string,
|
||||||
specs: DeviceSpec[] = [],
|
specs: DeviceSpec[] = [],
|
||||||
) {
|
) {
|
||||||
super(serial, deviceType, title, 'Android', specs);
|
super(flipperServer, {
|
||||||
|
serial,
|
||||||
|
deviceType,
|
||||||
|
title,
|
||||||
|
os: 'Android',
|
||||||
|
specs,
|
||||||
|
abiList,
|
||||||
|
sdkVersion,
|
||||||
|
});
|
||||||
this.adb = adb;
|
this.adb = adb;
|
||||||
this.icon = 'mobile';
|
// TODO: this.icon = 'mobile';
|
||||||
this.abiList = abiList;
|
|
||||||
this.sdkVersion = sdkVersion;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
startLogging() {
|
startLogging() {
|
||||||
@@ -117,7 +123,7 @@ export default class AndroidDevice extends BaseDevice {
|
|||||||
}
|
}
|
||||||
|
|
||||||
clearLogs(): Promise<void> {
|
clearLogs(): Promise<void> {
|
||||||
return this.executeShell(['logcat', '-c']);
|
return this.executeShellOrDie(['logcat', '-c']);
|
||||||
}
|
}
|
||||||
|
|
||||||
navigateToLocation(location: string) {
|
navigateToLocation(location: string) {
|
||||||
@@ -126,9 +132,6 @@ export default class AndroidDevice extends BaseDevice {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async screenshot(): Promise<Buffer> {
|
async screenshot(): Promise<Buffer> {
|
||||||
if (this.isArchived) {
|
|
||||||
return Buffer.from([]);
|
|
||||||
}
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.adb
|
this.adb
|
||||||
.screencap(this.serial)
|
.screencap(this.serial)
|
||||||
@@ -148,11 +151,8 @@ export default class AndroidDevice extends BaseDevice {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async screenCaptureAvailable(): Promise<boolean> {
|
async screenCaptureAvailable(): Promise<boolean> {
|
||||||
if (this.isArchived) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
await this.executeShell(
|
await this.executeShellOrDie(
|
||||||
`[ ! -f /system/bin/screenrecord ] && echo "File does not exist"`,
|
`[ ! -f /system/bin/screenrecord ] && echo "File does not exist"`,
|
||||||
);
|
);
|
||||||
return true;
|
return true;
|
||||||
@@ -161,7 +161,14 @@ export default class AndroidDevice extends BaseDevice {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async executeShell(command: string | string[]): Promise<void> {
|
async executeShell(command: string): Promise<string> {
|
||||||
|
return await this.adb
|
||||||
|
.shell(this.serial, command)
|
||||||
|
.then(adb.util.readAll)
|
||||||
|
.then((output: Buffer) => output.toString().trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
private async executeShellOrDie(command: string | string[]): Promise<void> {
|
||||||
const output = await this.adb
|
const output = await this.adb
|
||||||
.shell(this.serial, command)
|
.shell(this.serial, command)
|
||||||
.then(adb.util.readAll)
|
.then(adb.util.readAll)
|
||||||
@@ -191,7 +198,7 @@ export default class AndroidDevice extends BaseDevice {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async startScreenCapture(destination: string) {
|
async startScreenCapture(destination: string) {
|
||||||
await this.executeShell(
|
await this.executeShellOrDie(
|
||||||
`mkdir -p "${DEVICE_RECORDING_DIR}" && echo -n > "${DEVICE_RECORDING_DIR}/.nomedia"`,
|
`mkdir -p "${DEVICE_RECORDING_DIR}" && echo -n > "${DEVICE_RECORDING_DIR}/.nomedia"`,
|
||||||
);
|
);
|
||||||
const recordingLocation = `${DEVICE_RECORDING_DIR}/video.mp4`;
|
const recordingLocation = `${DEVICE_RECORDING_DIR}/video.mp4`;
|
||||||
|
|||||||
@@ -10,9 +10,11 @@
|
|||||||
import {DeviceType} from 'flipper-plugin-lib';
|
import {DeviceType} from 'flipper-plugin-lib';
|
||||||
import AndroidDevice from './AndroidDevice';
|
import AndroidDevice from './AndroidDevice';
|
||||||
import {Client as ADBClient} from 'adbkit';
|
import {Client as ADBClient} from 'adbkit';
|
||||||
|
import {FlipperServerImpl} from '../../FlipperServerImpl';
|
||||||
|
|
||||||
export default class KaiOSDevice extends AndroidDevice {
|
export default class KaiOSDevice extends AndroidDevice {
|
||||||
constructor(
|
constructor(
|
||||||
|
flipperServer: FlipperServerImpl,
|
||||||
serial: string,
|
serial: string,
|
||||||
deviceType: DeviceType,
|
deviceType: DeviceType,
|
||||||
title: string,
|
title: string,
|
||||||
@@ -20,7 +22,9 @@ export default class KaiOSDevice extends AndroidDevice {
|
|||||||
abiList: Array<string>,
|
abiList: Array<string>,
|
||||||
sdkVersion: string,
|
sdkVersion: string,
|
||||||
) {
|
) {
|
||||||
super(serial, deviceType, title, adb, abiList, sdkVersion, ['KaiOS']);
|
super(flipperServer, serial, deviceType, title, adb, abiList, sdkVersion, [
|
||||||
|
'KaiOS',
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
async screenCaptureAvailable() {
|
async screenCaptureAvailable() {
|
||||||
|
|||||||
@@ -15,14 +15,14 @@ import which from 'which';
|
|||||||
import {promisify} from 'util';
|
import {promisify} from 'util';
|
||||||
import {Client as ADBClient, Device} from 'adbkit';
|
import {Client as ADBClient, Device} from 'adbkit';
|
||||||
import {join} from 'path';
|
import {join} from 'path';
|
||||||
import {FlipperServer} from '../../FlipperServer';
|
import {FlipperServerImpl} from '../../FlipperServerImpl';
|
||||||
import {notNull} from '../../utils/typeUtils';
|
import {notNull} from '../../utils/typeUtils';
|
||||||
|
|
||||||
export class AndroidDeviceManager {
|
export class AndroidDeviceManager {
|
||||||
// cache emulator path
|
// cache emulator path
|
||||||
private emulatorPath: string | undefined;
|
private emulatorPath: string | undefined;
|
||||||
|
|
||||||
constructor(public flipperServer: FlipperServer) {}
|
constructor(public flipperServer: FlipperServerImpl) {}
|
||||||
|
|
||||||
private createDevice(
|
private createDevice(
|
||||||
adbClient: ADBClient,
|
adbClient: ADBClient,
|
||||||
@@ -49,7 +49,15 @@ export class AndroidDeviceManager {
|
|||||||
);
|
);
|
||||||
const androidLikeDevice = new (
|
const androidLikeDevice = new (
|
||||||
isKaiOSDevice ? KaiOSDevice : AndroidDevice
|
isKaiOSDevice ? KaiOSDevice : AndroidDevice
|
||||||
)(device.id, type, name, adbClient, abiList, sdkVersion);
|
)(
|
||||||
|
this.flipperServer,
|
||||||
|
device.id,
|
||||||
|
type,
|
||||||
|
name,
|
||||||
|
adbClient,
|
||||||
|
abiList,
|
||||||
|
sdkVersion,
|
||||||
|
);
|
||||||
if (this.flipperServer.config.serverPorts) {
|
if (this.flipperServer.config.serverPorts) {
|
||||||
await androidLikeDevice
|
await androidLikeDevice
|
||||||
.reverse([
|
.reverse([
|
||||||
|
|||||||
@@ -7,13 +7,17 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import BaseDevice from '../BaseDevice';
|
import {FlipperServerImpl} from '../../FlipperServerImpl';
|
||||||
|
import {ServerDevice} from '../ServerDevice';
|
||||||
|
|
||||||
export default class MacDevice extends BaseDevice {
|
export default class MacDevice extends ServerDevice {
|
||||||
constructor() {
|
constructor(flipperServer: FlipperServerImpl) {
|
||||||
super('', 'physical', 'Mac', 'MacOS');
|
super(flipperServer, {
|
||||||
this.icon = 'app-apple';
|
serial: '',
|
||||||
|
deviceType: 'physical',
|
||||||
|
title: 'Mac',
|
||||||
|
os: 'MacOS',
|
||||||
|
});
|
||||||
|
// TODO: this.icon = 'app-apple';
|
||||||
}
|
}
|
||||||
|
|
||||||
teardown() {}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,13 +7,17 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import BaseDevice from '../BaseDevice';
|
import {FlipperServerImpl} from '../../FlipperServerImpl';
|
||||||
|
import {ServerDevice} from '../ServerDevice';
|
||||||
|
|
||||||
export default class WindowsDevice extends BaseDevice {
|
export default class WindowsDevice extends ServerDevice {
|
||||||
constructor() {
|
constructor(flipperServer: FlipperServerImpl) {
|
||||||
super('', 'physical', 'Windows', 'Windows');
|
super(flipperServer, {
|
||||||
this.icon = 'app-microsoft-windows';
|
serial: '',
|
||||||
|
deviceType: 'physical',
|
||||||
|
title: 'Windows',
|
||||||
|
os: 'Windows',
|
||||||
|
});
|
||||||
|
// TODO: this.icon = 'app-microsoft-windows';
|
||||||
}
|
}
|
||||||
|
|
||||||
teardown() {}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,14 +9,14 @@
|
|||||||
|
|
||||||
import MacDevice from './MacDevice';
|
import MacDevice from './MacDevice';
|
||||||
import WindowsDevice from './WindowsDevice';
|
import WindowsDevice from './WindowsDevice';
|
||||||
import {FlipperServer} from '../../FlipperServer';
|
import {FlipperServerImpl} from '../../FlipperServerImpl';
|
||||||
|
|
||||||
export default (flipperServer: FlipperServer) => {
|
export default (flipperServer: FlipperServerImpl) => {
|
||||||
let device;
|
let device;
|
||||||
if (process.platform === 'darwin') {
|
if (process.platform === 'darwin') {
|
||||||
device = new MacDevice();
|
device = new MacDevice(flipperServer);
|
||||||
} else if (process.platform === 'win32') {
|
} else if (process.platform === 'win32') {
|
||||||
device = new WindowsDevice();
|
device = new WindowsDevice(flipperServer);
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,11 +9,12 @@
|
|||||||
|
|
||||||
import {LogLevel, DeviceLogEntry, DeviceType, timeout} from 'flipper-plugin';
|
import {LogLevel, DeviceLogEntry, DeviceType, timeout} from 'flipper-plugin';
|
||||||
import child_process, {ChildProcess} from 'child_process';
|
import child_process, {ChildProcess} from 'child_process';
|
||||||
import BaseDevice from '../BaseDevice';
|
|
||||||
import JSONStream from 'JSONStream';
|
import JSONStream from 'JSONStream';
|
||||||
import {Transform} from 'stream';
|
import {Transform} from 'stream';
|
||||||
import {ERR_PHYSICAL_DEVICE_LOGS_WITHOUT_IDB, IOSBridge} from './IOSBridge';
|
import {ERR_PHYSICAL_DEVICE_LOGS_WITHOUT_IDB, IOSBridge} from './IOSBridge';
|
||||||
import split2 from 'split2';
|
import split2 from 'split2';
|
||||||
|
import {ServerDevice} from '../ServerDevice';
|
||||||
|
import {FlipperServerImpl} from '../../FlipperServerImpl';
|
||||||
|
|
||||||
type IOSLogLevel = 'Default' | 'Info' | 'Debug' | 'Error' | 'Fault';
|
type IOSLogLevel = 'Default' | 'Info' | 'Debug' | 'Error' | 'Fault';
|
||||||
|
|
||||||
@@ -38,7 +39,7 @@ type RawLogEntry = {
|
|||||||
// Mar 25 17:06:38 iPhone symptomsd(SymptomEvaluator)[125] <Notice>: Stuff
|
// Mar 25 17:06:38 iPhone symptomsd(SymptomEvaluator)[125] <Notice>: Stuff
|
||||||
const logRegex = /(^.{15}) ([^ ]+?) ([^\[]+?)\[(\d+?)\] <(\w+?)>: (.*)$/s;
|
const logRegex = /(^.{15}) ([^ ]+?) ([^\[]+?)\[(\d+?)\] <(\w+?)>: (.*)$/s;
|
||||||
|
|
||||||
export default class IOSDevice extends BaseDevice {
|
export default class IOSDevice extends ServerDevice {
|
||||||
log?: child_process.ChildProcessWithoutNullStreams;
|
log?: child_process.ChildProcessWithoutNullStreams;
|
||||||
buffer: string;
|
buffer: string;
|
||||||
private recordingProcess?: ChildProcess;
|
private recordingProcess?: ChildProcess;
|
||||||
@@ -46,13 +47,14 @@ export default class IOSDevice extends BaseDevice {
|
|||||||
private iOSBridge: IOSBridge;
|
private iOSBridge: IOSBridge;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
flipperServer: FlipperServerImpl,
|
||||||
iOSBridge: IOSBridge,
|
iOSBridge: IOSBridge,
|
||||||
serial: string,
|
serial: string,
|
||||||
deviceType: DeviceType,
|
deviceType: DeviceType,
|
||||||
title: string,
|
title: string,
|
||||||
) {
|
) {
|
||||||
super(serial, deviceType, title, 'iOS');
|
super(flipperServer, {serial, deviceType, title, os: 'iOS'});
|
||||||
this.icon = 'mobile';
|
// TODO: this.icon = 'mobile';
|
||||||
this.buffer = '';
|
this.buffer = '';
|
||||||
this.iOSBridge = iOSBridge;
|
this.iOSBridge = iOSBridge;
|
||||||
}
|
}
|
||||||
@@ -87,7 +89,10 @@ export default class IOSDevice extends BaseDevice {
|
|||||||
|
|
||||||
if (!this.log) {
|
if (!this.log) {
|
||||||
try {
|
try {
|
||||||
this.log = iOSBridge.startLogListener(this.serial, this.deviceType);
|
this.log = iOSBridge.startLogListener(
|
||||||
|
this.serial,
|
||||||
|
this.info.deviceType,
|
||||||
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.message === ERR_PHYSICAL_DEVICE_LOGS_WITHOUT_IDB) {
|
if (e.message === ERR_PHYSICAL_DEVICE_LOGS_WITHOUT_IDB) {
|
||||||
console.warn(e);
|
console.warn(e);
|
||||||
@@ -110,7 +115,7 @@ export default class IOSDevice extends BaseDevice {
|
|||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (this.deviceType === 'physical') {
|
if (this.info.deviceType === 'physical') {
|
||||||
this.log.stdout.pipe(split2('\0')).on('data', (line: string) => {
|
this.log.stdout.pipe(split2('\0')).on('data', (line: string) => {
|
||||||
const parsed = IOSDevice.parseLogLine(line);
|
const parsed = IOSDevice.parseLogLine(line);
|
||||||
if (parsed) {
|
if (parsed) {
|
||||||
@@ -205,7 +210,7 @@ export default class IOSDevice extends BaseDevice {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async screenCaptureAvailable() {
|
async screenCaptureAvailable() {
|
||||||
return this.deviceType === 'emulator' && this.connected.get();
|
return this.info.deviceType === 'emulator' && this.connected.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
async startScreenCapture(destination: string) {
|
async startScreenCapture(destination: string) {
|
||||||
@@ -216,7 +221,7 @@ export default class IOSDevice extends BaseDevice {
|
|||||||
this.recordingLocation = destination;
|
this.recordingLocation = destination;
|
||||||
}
|
}
|
||||||
|
|
||||||
async stopScreenCapture(): Promise<string | null> {
|
async stopScreenCapture(): Promise<string> {
|
||||||
if (this.recordingProcess && this.recordingLocation) {
|
if (this.recordingProcess && this.recordingLocation) {
|
||||||
const prom = new Promise<void>((resolve, _reject) => {
|
const prom = new Promise<void>((resolve, _reject) => {
|
||||||
this.recordingProcess!.on(
|
this.recordingProcess!.on(
|
||||||
@@ -228,7 +233,7 @@ export default class IOSDevice extends BaseDevice {
|
|||||||
this.recordingProcess!.kill('SIGINT');
|
this.recordingProcess!.kill('SIGINT');
|
||||||
});
|
});
|
||||||
|
|
||||||
const output: string | null = await timeout<void>(
|
const output: string = await timeout<void>(
|
||||||
5000,
|
5000,
|
||||||
prom,
|
prom,
|
||||||
'Timed out to stop a screen capture.',
|
'Timed out to stop a screen capture.',
|
||||||
@@ -241,15 +246,17 @@ export default class IOSDevice extends BaseDevice {
|
|||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
this.recordingLocation = undefined;
|
this.recordingLocation = undefined;
|
||||||
console.warn('Failed to terminate iOS screen recording:', e);
|
console.warn('Failed to terminate iOS screen recording:', e);
|
||||||
return null;
|
throw e;
|
||||||
});
|
});
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
return null;
|
throw new Error('No recording in progress');
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnect() {
|
disconnect() {
|
||||||
|
if (this.recordingProcess && this.recordingLocation) {
|
||||||
this.stopScreenCapture();
|
this.stopScreenCapture();
|
||||||
|
}
|
||||||
super.disconnect();
|
super.disconnect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import configureStore from 'redux-mock-store';
|
|||||||
import {State, createRootReducer} from '../../../../reducers/index';
|
import {State, createRootReducer} from '../../../../reducers/index';
|
||||||
import {getInstance} from '../../../../fb-stubs/Logger';
|
import {getInstance} from '../../../../fb-stubs/Logger';
|
||||||
import {IOSBridge} from '../IOSBridge';
|
import {IOSBridge} from '../IOSBridge';
|
||||||
import {FlipperServer} from '../../../FlipperServer';
|
import {FlipperServerImpl} from '../../../FlipperServerImpl';
|
||||||
|
|
||||||
const mockStore = configureStore<State, {}>([])(
|
const mockStore = configureStore<State, {}>([])(
|
||||||
createRootReducer()(undefined, {type: 'INIT'}),
|
createRootReducer()(undefined, {type: 'INIT'}),
|
||||||
@@ -59,14 +59,14 @@ test('test parseXcodeFromCoreSimPath from standard locations', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('test getAllPromisesForQueryingDevices when xcode detected', () => {
|
test('test getAllPromisesForQueryingDevices when xcode detected', () => {
|
||||||
const flipperServer = new FlipperServer({}, mockStore, getInstance());
|
const flipperServer = new FlipperServerImpl({}, mockStore, getInstance());
|
||||||
flipperServer.ios.iosBridge = {} as IOSBridge;
|
flipperServer.ios.iosBridge = {} as IOSBridge;
|
||||||
const promises = flipperServer.ios.getAllPromisesForQueryingDevices(true);
|
const promises = flipperServer.ios.getAllPromisesForQueryingDevices(true);
|
||||||
expect(promises.length).toEqual(3);
|
expect(promises.length).toEqual(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('test getAllPromisesForQueryingDevices when xcode is not detected', () => {
|
test('test getAllPromisesForQueryingDevices when xcode is not detected', () => {
|
||||||
const flipperServer = new FlipperServer({}, mockStore, getInstance());
|
const flipperServer = new FlipperServerImpl({}, mockStore, getInstance());
|
||||||
flipperServer.ios.iosBridge = {} as IOSBridge;
|
flipperServer.ios.iosBridge = {} as IOSBridge;
|
||||||
const promises = flipperServer.ios.getAllPromisesForQueryingDevices(false);
|
const promises = flipperServer.ios.getAllPromisesForQueryingDevices(false);
|
||||||
expect(promises.length).toEqual(1);
|
expect(promises.length).toEqual(1);
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import {
|
|||||||
IOSBridge,
|
IOSBridge,
|
||||||
makeIOSBridge,
|
makeIOSBridge,
|
||||||
} from './IOSBridge';
|
} from './IOSBridge';
|
||||||
import {FlipperServer} from '../../FlipperServer';
|
import {FlipperServerImpl} from '../../FlipperServerImpl';
|
||||||
|
|
||||||
type iOSSimulatorDevice = {
|
type iOSSimulatorDevice = {
|
||||||
state: 'Booted' | 'Shutdown' | 'Shutting Down';
|
state: 'Booted' | 'Shutdown' | 'Shutting Down';
|
||||||
@@ -67,7 +67,7 @@ export class IOSDeviceManager {
|
|||||||
private xcodeVersionMismatchFound = false;
|
private xcodeVersionMismatchFound = false;
|
||||||
public xcodeCommandLineToolsDetected = false;
|
public xcodeCommandLineToolsDetected = false;
|
||||||
|
|
||||||
constructor(private flipperServer: FlipperServer) {
|
constructor(private flipperServer: FlipperServerImpl) {
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
window.addEventListener('beforeunload', () => {
|
window.addEventListener('beforeunload', () => {
|
||||||
this.portForwarders.forEach((process) => process.kill());
|
this.portForwarders.forEach((process) => process.kill());
|
||||||
@@ -152,7 +152,8 @@ export class IOSDeviceManager {
|
|||||||
this.flipperServer
|
this.flipperServer
|
||||||
.getDevices()
|
.getDevices()
|
||||||
.filter(
|
.filter(
|
||||||
(device) => device.os === 'iOS' && device.deviceType === targetType,
|
(device) =>
|
||||||
|
device.info.os === 'iOS' && device.info.deviceType === targetType,
|
||||||
)
|
)
|
||||||
.map((device) => device.serial),
|
.map((device) => device.serial),
|
||||||
);
|
);
|
||||||
@@ -162,7 +163,13 @@ export class IOSDeviceManager {
|
|||||||
currentDeviceIDs.delete(udid);
|
currentDeviceIDs.delete(udid);
|
||||||
} else if (targetType === type) {
|
} else if (targetType === type) {
|
||||||
console.log(`[conn] detected new iOS device ${targetType} ${udid}`);
|
console.log(`[conn] detected new iOS device ${targetType} ${udid}`);
|
||||||
const iOSDevice = new IOSDevice(this.iosBridge, udid, type, name);
|
const iOSDevice = new IOSDevice(
|
||||||
|
this.flipperServer,
|
||||||
|
this.iosBridge,
|
||||||
|
udid,
|
||||||
|
type,
|
||||||
|
name,
|
||||||
|
);
|
||||||
this.flipperServer.registerDevice(iOSDevice);
|
this.flipperServer.registerDevice(iOSDevice);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,9 +8,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {LogLevel} from 'flipper-plugin';
|
import {LogLevel} from 'flipper-plugin';
|
||||||
import BaseDevice from '../BaseDevice';
|
|
||||||
import {EventEmitter} from 'events';
|
import {EventEmitter} from 'events';
|
||||||
import util from 'util';
|
import util from 'util';
|
||||||
|
import {FlipperServerImpl} from '../../FlipperServerImpl';
|
||||||
|
import {ServerDevice} from '../ServerDevice';
|
||||||
|
|
||||||
// From xplat/js/metro/packages/metro/src/lib/reporting.js
|
// From xplat/js/metro/packages/metro/src/lib/reporting.js
|
||||||
export type BundleDetails = {
|
export type BundleDetails = {
|
||||||
@@ -139,12 +140,21 @@ function getLoglevelFromMessageType(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class MetroDevice extends BaseDevice {
|
export default class MetroDevice extends ServerDevice {
|
||||||
ws?: WebSocket;
|
ws?: WebSocket;
|
||||||
metroEventEmitter = new EventEmitter();
|
metroEventEmitter = new EventEmitter();
|
||||||
|
|
||||||
constructor(serial: string, ws: WebSocket | undefined) {
|
constructor(
|
||||||
super(serial, 'emulator', 'React Native', 'Metro');
|
flipperServer: FlipperServerImpl,
|
||||||
|
serial: string,
|
||||||
|
ws: WebSocket | undefined,
|
||||||
|
) {
|
||||||
|
super(flipperServer, {
|
||||||
|
serial,
|
||||||
|
deviceType: 'emulator',
|
||||||
|
title: 'React Native',
|
||||||
|
os: 'Metro',
|
||||||
|
});
|
||||||
if (ws) {
|
if (ws) {
|
||||||
this.ws = ws;
|
this.ws = ws;
|
||||||
ws.onmessage = this._handleWSMessage;
|
ws.onmessage = this._handleWSMessage;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
import MetroDevice from './MetroDevice';
|
import MetroDevice from './MetroDevice';
|
||||||
import http from 'http';
|
import http from 'http';
|
||||||
import {parseEnvironmentVariableAsNumber} from '../../utils/environmentVariables';
|
import {parseEnvironmentVariableAsNumber} from '../../utils/environmentVariables';
|
||||||
import {FlipperServer} from '../../FlipperServer';
|
import {FlipperServerImpl} from '../../FlipperServerImpl';
|
||||||
|
|
||||||
const METRO_HOST = 'localhost';
|
const METRO_HOST = 'localhost';
|
||||||
const METRO_PORT = parseEnvironmentVariableAsNumber('METRO_SERVER_PORT', 8081);
|
const METRO_PORT = parseEnvironmentVariableAsNumber('METRO_SERVER_PORT', 8081);
|
||||||
@@ -46,13 +46,13 @@ async function isMetroRunning(): Promise<boolean> {
|
|||||||
|
|
||||||
async function registerMetroDevice(
|
async function registerMetroDevice(
|
||||||
ws: WebSocket | undefined,
|
ws: WebSocket | undefined,
|
||||||
flipperServer: FlipperServer,
|
flipperServer: FlipperServerImpl,
|
||||||
) {
|
) {
|
||||||
const metroDevice = new MetroDevice(METRO_URL, ws);
|
const metroDevice = new MetroDevice(flipperServer, METRO_URL, ws);
|
||||||
flipperServer.registerDevice(metroDevice);
|
flipperServer.registerDevice(metroDevice);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default (flipperServer: FlipperServer) => {
|
export default (flipperServer: FlipperServerImpl) => {
|
||||||
let timeoutHandle: NodeJS.Timeout;
|
let timeoutHandle: NodeJS.Timeout;
|
||||||
let ws: WebSocket | undefined;
|
let ws: WebSocket | undefined;
|
||||||
let unregistered = false;
|
let unregistered = false;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {createStore} from 'redux';
|
import {createStore} from 'redux';
|
||||||
import BaseDevice from '../server/devices/BaseDevice';
|
import BaseDevice from '../devices/BaseDevice';
|
||||||
import {createRootReducer} from '../reducers';
|
import {createRootReducer} from '../reducers';
|
||||||
import {Store} from '../reducers/index';
|
import {Store} from '../reducers/index';
|
||||||
import Client from '../Client';
|
import Client from '../Client';
|
||||||
@@ -24,8 +24,9 @@ import {getInstance} from '../fb-stubs/Logger';
|
|||||||
import {initializeFlipperLibImplementation} from '../utils/flipperLibImplementation';
|
import {initializeFlipperLibImplementation} from '../utils/flipperLibImplementation';
|
||||||
import pluginManager from '../dispatcher/pluginManager';
|
import pluginManager from '../dispatcher/pluginManager';
|
||||||
import {PluginDetails} from 'flipper-plugin-lib';
|
import {PluginDetails} from 'flipper-plugin-lib';
|
||||||
import ArchivedDevice from '../server/devices/ArchivedDevice';
|
import ArchivedDevice from '../devices/ArchivedDevice';
|
||||||
import {ClientQuery, DeviceOS} from 'flipper-plugin';
|
import {ClientQuery, DeviceOS} from 'flipper-plugin';
|
||||||
|
import {TestDevice} from './TestDevice';
|
||||||
|
|
||||||
export interface AppOptions {
|
export interface AppOptions {
|
||||||
plugins?: PluginDefinition[];
|
plugins?: PluginDefinition[];
|
||||||
@@ -129,7 +130,7 @@ export default class MockFlipper {
|
|||||||
title: 'archived device',
|
title: 'archived device',
|
||||||
os: 'Android',
|
os: 'Android',
|
||||||
})
|
})
|
||||||
: new BaseDevice(s, 'physical', 'MockAndroidDevice', os ?? 'Android');
|
: new TestDevice(s, 'physical', 'MockAndroidDevice', os ?? 'Android');
|
||||||
device.supportsPlugin = !isSupportedByPlugin
|
device.supportsPlugin = !isSupportedByPlugin
|
||||||
? () => true
|
? () => true
|
||||||
: isSupportedByPlugin;
|
: isSupportedByPlugin;
|
||||||
|
|||||||
38
desktop/app/src/test-utils/TestDevice.tsx
Normal file
38
desktop/app/src/test-utils/TestDevice.tsx
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* 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 {DeviceOS, DeviceType} from 'flipper-plugin';
|
||||||
|
import {DeviceSpec} from 'flipper-plugin-lib';
|
||||||
|
import BaseDevice from '../devices/BaseDevice';
|
||||||
|
|
||||||
|
export class TestDevice extends BaseDevice {
|
||||||
|
constructor(
|
||||||
|
serial: string,
|
||||||
|
deviceType: DeviceType,
|
||||||
|
title: string,
|
||||||
|
os: DeviceOS,
|
||||||
|
specs?: DeviceSpec[],
|
||||||
|
) {
|
||||||
|
super(
|
||||||
|
{
|
||||||
|
on: jest.fn(),
|
||||||
|
off: jest.fn(),
|
||||||
|
exec: jest.fn(),
|
||||||
|
close: jest.fn(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
serial,
|
||||||
|
deviceType,
|
||||||
|
title,
|
||||||
|
os,
|
||||||
|
specs,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,7 +21,7 @@ import {
|
|||||||
selectDevice,
|
selectDevice,
|
||||||
selectClient,
|
selectClient,
|
||||||
} from '../reducers/connections';
|
} from '../reducers/connections';
|
||||||
import BaseDevice from '../server/devices/BaseDevice';
|
import BaseDevice from '../devices/BaseDevice';
|
||||||
|
|
||||||
import {Store} from '../reducers/index';
|
import {Store} from '../reducers/index';
|
||||||
import Client from '../Client';
|
import Client from '../Client';
|
||||||
|
|||||||
@@ -9,8 +9,7 @@
|
|||||||
|
|
||||||
import {State} from '../../reducers/index';
|
import {State} from '../../reducers/index';
|
||||||
import configureStore from 'redux-mock-store';
|
import configureStore from 'redux-mock-store';
|
||||||
import {default as BaseDevice} from '../../server/devices/BaseDevice';
|
import {default as ArchivedDevice} from '../../devices/ArchivedDevice';
|
||||||
import {default as ArchivedDevice} from '../../server/devices/ArchivedDevice';
|
|
||||||
import {
|
import {
|
||||||
processStore,
|
processStore,
|
||||||
determinePluginsToProcess,
|
determinePluginsToProcess,
|
||||||
@@ -35,6 +34,7 @@ import {
|
|||||||
} from 'flipper-plugin';
|
} from 'flipper-plugin';
|
||||||
import {selectPlugin} from '../../reducers/connections';
|
import {selectPlugin} from '../../reducers/connections';
|
||||||
import {TestIdler} from '../Idler';
|
import {TestIdler} from '../Idler';
|
||||||
|
import {TestDevice} from '../..';
|
||||||
|
|
||||||
const testIdler = new TestIdler();
|
const testIdler = new TestIdler();
|
||||||
|
|
||||||
@@ -71,7 +71,7 @@ function generateNotifications(
|
|||||||
return {id, title, message, severity};
|
return {id, title, message, severity};
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateClientIdentifier(device: BaseDevice, app: string): string {
|
function generateClientIdentifier(device: TestDevice, app: string): string {
|
||||||
const {os, deviceType, serial} = device;
|
const {os, deviceType, serial} = device;
|
||||||
const identifier = `${app}#${os}#${deviceType}#${serial}`;
|
const identifier = `${app}#${os}#${deviceType}#${serial}`;
|
||||||
return identifier;
|
return identifier;
|
||||||
@@ -98,7 +98,7 @@ function generateClientFromClientWithSalt(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
function generateClientFromDevice(
|
function generateClientFromDevice(
|
||||||
device: BaseDevice,
|
device: TestDevice,
|
||||||
app: string,
|
app: string,
|
||||||
): ClientExport {
|
): ClientExport {
|
||||||
const {os, deviceType, serial} = device;
|
const {os, deviceType, serial} = device;
|
||||||
@@ -710,7 +710,7 @@ test('test processStore function for no selected plugins', async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('test determinePluginsToProcess for mutilple clients having plugins present', async () => {
|
test('test determinePluginsToProcess for mutilple clients having plugins present', async () => {
|
||||||
const device1 = new BaseDevice('serial1', 'emulator', 'TestiPhone', 'iOS');
|
const device1 = new TestDevice('serial1', 'emulator', 'TestiPhone', 'iOS');
|
||||||
const client1 = new Client(
|
const client1 = new Client(
|
||||||
generateClientIdentifier(device1, 'app'),
|
generateClientIdentifier(device1, 'app'),
|
||||||
{
|
{
|
||||||
@@ -797,7 +797,7 @@ test('test determinePluginsToProcess for mutilple clients having plugins present
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('test determinePluginsToProcess for no selected plugin present in any clients', async () => {
|
test('test determinePluginsToProcess for no selected plugin present in any clients', async () => {
|
||||||
const device1 = new BaseDevice('serial1', 'emulator', 'TestiPhone', 'iOS');
|
const device1 = new TestDevice('serial1', 'emulator', 'TestiPhone', 'iOS');
|
||||||
const client1 = new Client(
|
const client1 = new Client(
|
||||||
generateClientIdentifier(device1, 'app'),
|
generateClientIdentifier(device1, 'app'),
|
||||||
{
|
{
|
||||||
@@ -853,7 +853,7 @@ test('test determinePluginsToProcess for no selected plugin present in any clien
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('test determinePluginsToProcess for multiple clients on same device', async () => {
|
test('test determinePluginsToProcess for multiple clients on same device', async () => {
|
||||||
const device1 = new BaseDevice('serial1', 'emulator', 'TestiPhone', 'iOS');
|
const device1 = new TestDevice('serial1', 'emulator', 'TestiPhone', 'iOS');
|
||||||
const client1 = new Client(
|
const client1 = new Client(
|
||||||
generateClientIdentifier(device1, 'app'),
|
generateClientIdentifier(device1, 'app'),
|
||||||
{
|
{
|
||||||
@@ -913,8 +913,8 @@ test('test determinePluginsToProcess for multiple clients on same device', async
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('test determinePluginsToProcess for multiple clients on different device', async () => {
|
test('test determinePluginsToProcess for multiple clients on different device', async () => {
|
||||||
const device1 = new BaseDevice('serial1', 'emulator', 'TestiPhone', 'iOS');
|
const device1 = new TestDevice('serial1', 'emulator', 'TestiPhone', 'iOS');
|
||||||
const device2 = new BaseDevice('serial2', 'emulator', 'TestiPhone', 'iOS');
|
const device2 = new TestDevice('serial2', 'emulator', 'TestiPhone', 'iOS');
|
||||||
const client1Device1 = new Client(
|
const client1Device1 = new Client(
|
||||||
generateClientIdentifier(device1, 'app'),
|
generateClientIdentifier(device1, 'app'),
|
||||||
{
|
{
|
||||||
@@ -1006,7 +1006,7 @@ test('test determinePluginsToProcess for multiple clients on different device',
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('test determinePluginsToProcess to ignore archived clients', async () => {
|
test('test determinePluginsToProcess to ignore archived clients', async () => {
|
||||||
const selectedDevice = new BaseDevice(
|
const selectedDevice = new TestDevice(
|
||||||
'serial',
|
'serial',
|
||||||
'emulator',
|
'emulator',
|
||||||
'TestiPhone',
|
'TestiPhone',
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type Client from '../Client';
|
import type Client from '../Client';
|
||||||
import type BaseDevice from '../server/devices/BaseDevice';
|
import type BaseDevice from '../devices/BaseDevice';
|
||||||
|
|
||||||
/* A Client uniuely identifies an app running on some device.
|
/* A Client uniuely identifies an app running on some device.
|
||||||
|
|
||||||
|
|||||||
@@ -12,15 +12,15 @@ import path from 'path';
|
|||||||
import electron from 'electron';
|
import electron from 'electron';
|
||||||
import {getInstance as getLogger} from '../fb-stubs/Logger';
|
import {getInstance as getLogger} from '../fb-stubs/Logger';
|
||||||
import {Store, MiddlewareAPI} from '../reducers';
|
import {Store, MiddlewareAPI} from '../reducers';
|
||||||
import {DeviceExport} from '../server/devices/BaseDevice';
|
import {DeviceExport} from '../devices/BaseDevice';
|
||||||
import {State as PluginsState} from '../reducers/plugins';
|
import {State as PluginsState} from '../reducers/plugins';
|
||||||
import {PluginNotification} from '../reducers/notifications';
|
import {PluginNotification} from '../reducers/notifications';
|
||||||
import Client, {ClientExport} from '../Client';
|
import Client, {ClientExport} from '../Client';
|
||||||
import {getAppVersion} from './info';
|
import {getAppVersion} from './info';
|
||||||
import {pluginKey} from '../utils/pluginKey';
|
import {pluginKey} from '../utils/pluginKey';
|
||||||
import {DevicePluginMap, ClientPluginMap} from '../plugin';
|
import {DevicePluginMap, ClientPluginMap} from '../plugin';
|
||||||
import {default as BaseDevice} from '../server/devices/BaseDevice';
|
import {default as BaseDevice} from '../devices/BaseDevice';
|
||||||
import {default as ArchivedDevice} from '../server/devices/ArchivedDevice';
|
import {default as ArchivedDevice} from '../devices/ArchivedDevice';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import {v4 as uuidv4} from 'uuid';
|
import {v4 as uuidv4} from 'uuid';
|
||||||
import {remote, OpenDialogOptions} from 'electron';
|
import {remote, OpenDialogOptions} from 'electron';
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import type {Logger} from '../fb-interfaces/Logger';
|
|||||||
import type {Store} from '../reducers';
|
import type {Store} from '../reducers';
|
||||||
import createPaste from '../fb-stubs/createPaste';
|
import createPaste from '../fb-stubs/createPaste';
|
||||||
import GK from '../fb-stubs/GK';
|
import GK from '../fb-stubs/GK';
|
||||||
import type BaseDevice from '../server/devices/BaseDevice';
|
import type BaseDevice from '../devices/BaseDevice';
|
||||||
import {clipboard, shell} from 'electron';
|
import {clipboard, shell} from 'electron';
|
||||||
import constants from '../fb-stubs/constants';
|
import constants from '../fb-stubs/constants';
|
||||||
import {addNotification} from '../reducers/notifications';
|
import {addNotification} from '../reducers/notifications';
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
import type {PluginDefinition} from '../plugin';
|
import type {PluginDefinition} from '../plugin';
|
||||||
import type {State, Store} from '../reducers';
|
import type {State, Store} from '../reducers';
|
||||||
import type {State as PluginsState} from '../reducers/plugins';
|
import type {State as PluginsState} from '../reducers/plugins';
|
||||||
import type BaseDevice from '../server/devices/BaseDevice';
|
import type BaseDevice from '../devices/BaseDevice';
|
||||||
import type Client from '../Client';
|
import type Client from '../Client';
|
||||||
import type {
|
import type {
|
||||||
ActivatablePluginDetails,
|
ActivatablePluginDetails,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {State} from '../reducers/index';
|
import {State} from '../reducers/index';
|
||||||
import {DeviceExport} from '../server/devices/BaseDevice';
|
import {DeviceExport} from '../devices/BaseDevice';
|
||||||
|
|
||||||
export const stateSanitizer = (state: State) => {
|
export const stateSanitizer = (state: State) => {
|
||||||
if (state.connections && state.connections.devices) {
|
if (state.connections && state.connections.devices) {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import BaseDevice from '../server/devices/BaseDevice';
|
import BaseDevice from '../devices/BaseDevice';
|
||||||
import {reportPlatformFailures} from './metrics';
|
import {reportPlatformFailures} from './metrics';
|
||||||
import expandTilde from 'expand-tilde';
|
import expandTilde from 'expand-tilde';
|
||||||
import {remote} from 'electron';
|
import {remote} from 'electron';
|
||||||
|
|||||||
@@ -69,7 +69,6 @@ export interface RealFlipperDevice {
|
|||||||
deviceType: DeviceType;
|
deviceType: DeviceType;
|
||||||
addLogListener(callback: DeviceLogListener): Symbol;
|
addLogListener(callback: DeviceLogListener): Symbol;
|
||||||
removeLogListener(id: Symbol): void;
|
removeLogListener(id: Symbol): void;
|
||||||
addLogEntry(entry: DeviceLogEntry): void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SandyDevicePluginInstance extends BasePluginInstance {
|
export class SandyDevicePluginInstance extends BasePluginInstance {
|
||||||
|
|||||||
@@ -486,7 +486,9 @@ export function createMockBundledPluginDetails(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function createMockDevice(options?: StartPluginOptions): RealFlipperDevice {
|
function createMockDevice(options?: StartPluginOptions): RealFlipperDevice & {
|
||||||
|
addLogEntry(entry: DeviceLogEntry): void;
|
||||||
|
} {
|
||||||
const logListeners: (undefined | DeviceLogListener)[] = [];
|
const logListeners: (undefined | DeviceLogListener)[] = [];
|
||||||
return {
|
return {
|
||||||
os: 'Android',
|
os: 'Android',
|
||||||
|
|||||||
@@ -8,9 +8,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
DeviceSpec,
|
||||||
DeviceType as PluginDeviceType,
|
DeviceType as PluginDeviceType,
|
||||||
OS as PluginOS,
|
OS as PluginOS,
|
||||||
} from 'flipper-plugin-lib';
|
} from 'flipper-plugin-lib';
|
||||||
|
import {DeviceLogEntry} from '../plugin/DevicePlugin';
|
||||||
|
|
||||||
// In the future, this file would deserve it's own package, as it doesn't really relate to plugins.
|
// In the future, this file would deserve it's own package, as it doesn't really relate to plugins.
|
||||||
// Since flipper-plugin however is currently shared among server, client and defines a lot of base types, leaving it here for now.
|
// Since flipper-plugin however is currently shared among server, client and defines a lot of base types, leaving it here for now.
|
||||||
@@ -31,6 +33,10 @@ export type DeviceDescription = {
|
|||||||
readonly title: string;
|
readonly title: string;
|
||||||
readonly deviceType: DeviceType;
|
readonly deviceType: DeviceType;
|
||||||
readonly serial: string;
|
readonly serial: string;
|
||||||
|
// Android specific information
|
||||||
|
readonly specs?: DeviceSpec[];
|
||||||
|
readonly abiList?: string[];
|
||||||
|
readonly sdkVersion?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ClientQuery = {
|
export type ClientQuery = {
|
||||||
@@ -58,8 +64,39 @@ export type FlipperServerEvents = {
|
|||||||
'device-connected': DeviceDescription;
|
'device-connected': DeviceDescription;
|
||||||
'device-disconnected': DeviceDescription;
|
'device-disconnected': DeviceDescription;
|
||||||
'client-connected': ClientDescription;
|
'client-connected': ClientDescription;
|
||||||
|
'device-log': {
|
||||||
|
serial: string;
|
||||||
|
entry: DeviceLogEntry;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export type FlipperServerCommands = {
|
export type FlipperServerCommands = {
|
||||||
// TODO
|
'device-start-logging': (serial: string) => Promise<void>;
|
||||||
|
'device-stop-logging': (serial: string) => Promise<void>;
|
||||||
|
'device-supports-screenshot': (serial: string) => Promise<boolean>;
|
||||||
|
'device-supports-screencapture': (serial: string) => Promise<boolean>;
|
||||||
|
'device-take-screenshot': (serial: string) => Promise<string>; // base64 encoded buffer
|
||||||
|
'device-start-screencapture': (
|
||||||
|
serial: string,
|
||||||
|
destination: string,
|
||||||
|
) => Promise<void>;
|
||||||
|
'device-stop-screencapture': (serial: string) => Promise<string>; // file path
|
||||||
|
'device-shell-exec': (serial: string, command: string) => Promise<string>;
|
||||||
|
'metro-command': (serial: string, command: string) => Promise<void>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface FlipperServer {
|
||||||
|
on<Event extends keyof FlipperServerEvents>(
|
||||||
|
event: Event,
|
||||||
|
callback: (payload: FlipperServerEvents[Event]) => void,
|
||||||
|
): void;
|
||||||
|
off<Event extends keyof FlipperServerEvents>(
|
||||||
|
event: Event,
|
||||||
|
callback: (payload: FlipperServerEvents[Event]) => void,
|
||||||
|
): void;
|
||||||
|
exec<Event extends keyof FlipperServerCommands>(
|
||||||
|
event: Event,
|
||||||
|
...args: Parameters<FlipperServerCommands[Event]>
|
||||||
|
): ReturnType<FlipperServerCommands[Event]>;
|
||||||
|
close(): void;
|
||||||
|
}
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ export function devicePlugin(client: PluginClient<{}, {}>) {
|
|||||||
const device = client.device;
|
const device = client.device;
|
||||||
|
|
||||||
const executeShell = async (command: string) => {
|
const executeShell = async (command: string) => {
|
||||||
|
// TODO: fix
|
||||||
return new Promise<string>((resolve, reject) => {
|
return new Promise<string>((resolve, reject) => {
|
||||||
(device.realDevice as any).adb
|
(device.realDevice as any).adb
|
||||||
.shell(device.serial, command)
|
.shell(device.serial, command)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {BaseDevice} from 'flipper';
|
import {TestDevice} from 'flipper';
|
||||||
import {Crash, CrashLog} from '../index';
|
import {Crash, CrashLog} from '../index';
|
||||||
import {TestUtils} from 'flipper-plugin';
|
import {TestUtils} from 'flipper-plugin';
|
||||||
import {getPluginKey} from 'flipper';
|
import {getPluginKey} from 'flipper';
|
||||||
@@ -121,7 +121,7 @@ test('test the parsing of the Android crash log with os being iOS', () => {
|
|||||||
expect(crash.name).toEqual('Cannot figure out the cause');
|
expect(crash.name).toEqual('Cannot figure out the cause');
|
||||||
});
|
});
|
||||||
test('test the getter of pluginKey with proper input', () => {
|
test('test the getter of pluginKey with proper input', () => {
|
||||||
const device = new BaseDevice('serial', 'emulator', 'test device', 'iOS');
|
const device = new TestDevice('serial', 'emulator', 'test device', 'iOS');
|
||||||
const pluginKey = getPluginKey(null, device, 'CrashReporter');
|
const pluginKey = getPluginKey(null, device, 'CrashReporter');
|
||||||
expect(pluginKey).toEqual('serial#CrashReporter');
|
expect(pluginKey).toEqual('serial#CrashReporter');
|
||||||
});
|
});
|
||||||
@@ -134,7 +134,7 @@ test('test the getter of pluginKey with defined selected app', () => {
|
|||||||
expect(pluginKey).toEqual('selectedApp#CrashReporter');
|
expect(pluginKey).toEqual('selectedApp#CrashReporter');
|
||||||
});
|
});
|
||||||
test('test the getter of pluginKey with defined selected app and defined base device', () => {
|
test('test the getter of pluginKey with defined selected app and defined base device', () => {
|
||||||
const device = new BaseDevice('serial', 'emulator', 'test device', 'iOS');
|
const device = new TestDevice('serial', 'emulator', 'test device', 'iOS');
|
||||||
const pluginKey = getPluginKey('selectedApp', device, 'CrashReporter');
|
const pluginKey = getPluginKey('selectedApp', device, 'CrashReporter');
|
||||||
expect(pluginKey).toEqual('selectedApp#CrashReporter');
|
expect(pluginKey).toEqual('selectedApp#CrashReporter');
|
||||||
});
|
});
|
||||||
@@ -216,7 +216,7 @@ test('test parsing of path when a regex is not present', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('test shouldShowCrashNotification function for all correct inputs', () => {
|
test('test shouldShowCrashNotification function for all correct inputs', () => {
|
||||||
const device = new BaseDevice(
|
const device = new TestDevice(
|
||||||
'TH1S-15DEV1CE-1D',
|
'TH1S-15DEV1CE-1D',
|
||||||
'emulator',
|
'emulator',
|
||||||
'test device',
|
'test device',
|
||||||
@@ -231,7 +231,7 @@ test('test shouldShowCrashNotification function for all correct inputs', () => {
|
|||||||
expect(shouldShowNotification).toEqual(true);
|
expect(shouldShowNotification).toEqual(true);
|
||||||
});
|
});
|
||||||
test('test shouldShowiOSCrashNotification function for all correct inputs but incorrect id', () => {
|
test('test shouldShowiOSCrashNotification function for all correct inputs but incorrect id', () => {
|
||||||
const device = new BaseDevice(
|
const device = new TestDevice(
|
||||||
'TH1S-15DEV1CE-1D',
|
'TH1S-15DEV1CE-1D',
|
||||||
'emulator',
|
'emulator',
|
||||||
'test device',
|
'test device',
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {FlipperDevicePlugin, Device, KaiOSDevice} from 'flipper';
|
import {FlipperDevicePlugin, Device} from 'flipper';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
@@ -186,7 +186,7 @@ export default class AllocationsPlugin extends FlipperDevicePlugin<
|
|||||||
};
|
};
|
||||||
|
|
||||||
static supportsDevice(device: Device) {
|
static supportsDevice(device: Device) {
|
||||||
return device instanceof KaiOSDevice;
|
return device.description.specs?.includes('KaiOS') ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
onStartMonitor = async () => {
|
onStartMonitor = async () => {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import {FlipperDevicePlugin, Device, KaiOSDevice, sleep} from 'flipper';
|
import {FlipperDevicePlugin, Device, sleep} from 'flipper';
|
||||||
|
|
||||||
import {FlexColumn, Button, Toolbar, Panel} from 'flipper';
|
import {FlexColumn, Button, Toolbar, Panel} from 'flipper';
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@ export default class KaiOSGraphs extends FlipperDevicePlugin<State, any, any> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static supportsDevice(device: Device) {
|
static supportsDevice(device: Device) {
|
||||||
return device instanceof KaiOSDevice;
|
return device.description.specs?.includes('KaiOS') ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
@@ -116,13 +116,8 @@ export default class KaiOSGraphs extends FlipperDevicePlugin<State, any, any> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
executeShell = (command: string) => {
|
executeShell = async (command: string): Promise<string> => {
|
||||||
return (this.device as KaiOSDevice).adb
|
return this.device.executeShell(command);
|
||||||
.shell(this.device.serial, command)
|
|
||||||
.then(adb.util.readAll)
|
|
||||||
.then((output) => {
|
|
||||||
return output.toString().trim();
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
getMemory = () => {
|
getMemory = () => {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import {BaseDevice, AndroidDevice, IOSDevice, getAppPath} from 'flipper';
|
import {BaseDevice, getAppPath} from 'flipper';
|
||||||
import {AppMatchPattern} from '../types';
|
import {AppMatchPattern} from '../types';
|
||||||
|
|
||||||
let patternsPath: string | undefined;
|
let patternsPath: string | undefined;
|
||||||
@@ -34,9 +34,9 @@ export const getAppMatchPatterns = (
|
|||||||
const appName = extractAppNameFromSelectedApp(selectedApp);
|
const appName = extractAppNameFromSelectedApp(selectedApp);
|
||||||
if (appName === 'Facebook') {
|
if (appName === 'Facebook') {
|
||||||
let filename: string;
|
let filename: string;
|
||||||
if (device instanceof AndroidDevice) {
|
if (device.os === 'Android') {
|
||||||
filename = 'facebook-match-patterns-android.json';
|
filename = 'facebook-match-patterns-android.json';
|
||||||
} else if (device instanceof IOSDevice) {
|
} else if (device.os === 'iOS') {
|
||||||
filename = 'facebook-match-patterns-ios.json';
|
filename = 'facebook-match-patterns-ios.json';
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -3461,6 +3461,11 @@
|
|||||||
"@typescript-eslint/types" "4.31.0"
|
"@typescript-eslint/types" "4.31.0"
|
||||||
eslint-visitor-keys "^2.0.0"
|
eslint-visitor-keys "^2.0.0"
|
||||||
|
|
||||||
|
"@ungap/promise-all-settled@1.1.2":
|
||||||
|
version "1.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44"
|
||||||
|
integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==
|
||||||
|
|
||||||
"@yarnpkg/lockfile@^1.1.0":
|
"@yarnpkg/lockfile@^1.1.0":
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31"
|
resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31"
|
||||||
@@ -3654,7 +3659,7 @@ ansi-align@^3.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
string-width "^3.0.0"
|
string-width "^3.0.0"
|
||||||
|
|
||||||
ansi-colors@^4.1.1:
|
ansi-colors@4.1.1, ansi-colors@^4.1.1:
|
||||||
version "4.1.1"
|
version "4.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
|
resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
|
||||||
integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==
|
integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==
|
||||||
@@ -3786,6 +3791,14 @@ anymatch@^3.0.3:
|
|||||||
normalize-path "^3.0.0"
|
normalize-path "^3.0.0"
|
||||||
picomatch "^2.0.4"
|
picomatch "^2.0.4"
|
||||||
|
|
||||||
|
anymatch@~3.1.1:
|
||||||
|
version "3.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716"
|
||||||
|
integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==
|
||||||
|
dependencies:
|
||||||
|
normalize-path "^3.0.0"
|
||||||
|
picomatch "^2.0.4"
|
||||||
|
|
||||||
app-builder-bin@3.5.13:
|
app-builder-bin@3.5.13:
|
||||||
version "3.5.13"
|
version "3.5.13"
|
||||||
resolved "https://registry.yarnpkg.com/app-builder-bin/-/app-builder-bin-3.5.13.tgz#6dd7f4de34a4e408806f99b8c7d6ef1601305b7e"
|
resolved "https://registry.yarnpkg.com/app-builder-bin/-/app-builder-bin-3.5.13.tgz#6dd7f4de34a4e408806f99b8c7d6ef1601305b7e"
|
||||||
@@ -4058,11 +4071,11 @@ axe-core@^4.0.2:
|
|||||||
integrity sha512-1uIESzroqpaTzt9uX48HO+6gfnKu3RwvWdCcWSrX4csMInJfCo1yvKPNXCwXFRpJqRW25tiASb6No0YH57PXqg==
|
integrity sha512-1uIESzroqpaTzt9uX48HO+6gfnKu3RwvWdCcWSrX4csMInJfCo1yvKPNXCwXFRpJqRW25tiASb6No0YH57PXqg==
|
||||||
|
|
||||||
axios@^0.18.0, axios@^0.21.1, axios@^0.21.2:
|
axios@^0.18.0, axios@^0.21.1, axios@^0.21.2:
|
||||||
version "0.21.4"
|
version "0.21.1"
|
||||||
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575"
|
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8"
|
||||||
integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==
|
integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==
|
||||||
dependencies:
|
dependencies:
|
||||||
follow-redirects "^1.14.0"
|
follow-redirects "^1.10.0"
|
||||||
|
|
||||||
axobject-query@^2.2.0:
|
axobject-query@^2.2.0:
|
||||||
version "2.2.0"
|
version "2.2.0"
|
||||||
@@ -4332,6 +4345,11 @@ base@^0.11.1:
|
|||||||
mixin-deep "^1.2.0"
|
mixin-deep "^1.2.0"
|
||||||
pascalcase "^0.1.1"
|
pascalcase "^0.1.1"
|
||||||
|
|
||||||
|
binary-extensions@^2.0.0:
|
||||||
|
version "2.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
|
||||||
|
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
|
||||||
|
|
||||||
binaryextensions@^4.15.0:
|
binaryextensions@^4.15.0:
|
||||||
version "4.15.0"
|
version "4.15.0"
|
||||||
resolved "https://registry.yarnpkg.com/binaryextensions/-/binaryextensions-4.15.0.tgz#c63a502e0078ff1b0e9b00a9f74d3c2b0f8bd32e"
|
resolved "https://registry.yarnpkg.com/binaryextensions/-/binaryextensions-4.15.0.tgz#c63a502e0078ff1b0e9b00a9f74d3c2b0f8bd32e"
|
||||||
@@ -4422,7 +4440,7 @@ braces@^2.3.1:
|
|||||||
split-string "^3.0.2"
|
split-string "^3.0.2"
|
||||||
to-regex "^3.0.1"
|
to-regex "^3.0.1"
|
||||||
|
|
||||||
braces@^3.0.1:
|
braces@^3.0.1, braces@~3.0.2:
|
||||||
version "3.0.2"
|
version "3.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
|
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
|
||||||
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
|
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
|
||||||
@@ -4441,6 +4459,11 @@ browser-resolve@^1.11.3:
|
|||||||
dependencies:
|
dependencies:
|
||||||
resolve "1.1.7"
|
resolve "1.1.7"
|
||||||
|
|
||||||
|
browser-stdout@1.3.1:
|
||||||
|
version "1.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60"
|
||||||
|
integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==
|
||||||
|
|
||||||
browserslist@^4.16.5, browserslist@^4.16.6:
|
browserslist@^4.16.5, browserslist@^4.16.6:
|
||||||
version "4.17.0"
|
version "4.17.0"
|
||||||
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.17.0.tgz#1fcd81ec75b41d6d4994fb0831b92ac18c01649c"
|
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.17.0.tgz#1fcd81ec75b41d6d4994fb0831b92ac18c01649c"
|
||||||
@@ -4693,6 +4716,21 @@ chardet@^0.7.0:
|
|||||||
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
|
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
|
||||||
integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
|
integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
|
||||||
|
|
||||||
|
chokidar@3.5.1:
|
||||||
|
version "3.5.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a"
|
||||||
|
integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==
|
||||||
|
dependencies:
|
||||||
|
anymatch "~3.1.1"
|
||||||
|
braces "~3.0.2"
|
||||||
|
glob-parent "~5.1.0"
|
||||||
|
is-binary-path "~2.1.0"
|
||||||
|
is-glob "~4.0.1"
|
||||||
|
normalize-path "~3.0.0"
|
||||||
|
readdirp "~3.5.0"
|
||||||
|
optionalDependencies:
|
||||||
|
fsevents "~2.3.1"
|
||||||
|
|
||||||
chownr@^1.1.1:
|
chownr@^1.1.1:
|
||||||
version "1.1.4"
|
version "1.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b"
|
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b"
|
||||||
@@ -5279,9 +5317,9 @@ cssstyle@^2.3.0:
|
|||||||
cssom "~0.3.6"
|
cssom "~0.3.6"
|
||||||
|
|
||||||
csstype@^2.5.7, csstype@^2.6.7, csstype@^3.0.2, csstype@^3.0.5:
|
csstype@^2.5.7, csstype@^2.6.7, csstype@^3.0.2, csstype@^3.0.5:
|
||||||
version "3.0.8"
|
version "3.0.9"
|
||||||
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.8.tgz#d2266a792729fb227cd216fb572f43728e1ad340"
|
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.9.tgz#6410af31b26bd0520933d02cbc64fce9ce3fbf0b"
|
||||||
integrity sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw==
|
integrity sha512-rpw6JPxK6Rfg1zLOYCSwle2GFOOsnjmDYDaBwEcwoOg4qlsIVCN789VkBZDJAGi4T07gI4YSutR43t9Zz4Lzuw==
|
||||||
|
|
||||||
d3-array@2, d3-array@2.3.3, d3-array@^2.3.0:
|
d3-array@2, d3-array@2.3.3, d3-array@^2.3.0:
|
||||||
version "2.3.3"
|
version "2.3.3"
|
||||||
@@ -5375,6 +5413,13 @@ debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, d
|
|||||||
dependencies:
|
dependencies:
|
||||||
ms "2.1.2"
|
ms "2.1.2"
|
||||||
|
|
||||||
|
debug@4.3.1:
|
||||||
|
version "4.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee"
|
||||||
|
integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==
|
||||||
|
dependencies:
|
||||||
|
ms "2.1.2"
|
||||||
|
|
||||||
debug@^3.2.6, debug@^3.2.7:
|
debug@^3.2.6, debug@^3.2.7:
|
||||||
version "3.2.7"
|
version "3.2.7"
|
||||||
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
|
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
|
||||||
@@ -5387,6 +5432,11 @@ decamelize@^1.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
|
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
|
||||||
integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
|
integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
|
||||||
|
|
||||||
|
decamelize@^4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837"
|
||||||
|
integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==
|
||||||
|
|
||||||
decimal.js-light@^2.4.1:
|
decimal.js-light@^2.4.1:
|
||||||
version "2.5.1"
|
version "2.5.1"
|
||||||
resolved "https://registry.yarnpkg.com/decimal.js-light/-/decimal.js-light-2.5.1.tgz#134fd32508f19e208f4fb2f8dac0d2626a867934"
|
resolved "https://registry.yarnpkg.com/decimal.js-light/-/decimal.js-light-2.5.1.tgz#134fd32508f19e208f4fb2f8dac0d2626a867934"
|
||||||
@@ -5602,6 +5652,11 @@ diff-sequences@^26.6.2:
|
|||||||
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1"
|
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1"
|
||||||
integrity sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==
|
integrity sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==
|
||||||
|
|
||||||
|
diff@5.0.0:
|
||||||
|
version "5.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b"
|
||||||
|
integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==
|
||||||
|
|
||||||
diff@^4.0.1:
|
diff@^4.0.1:
|
||||||
version "4.0.2"
|
version "4.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
|
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
|
||||||
@@ -6728,6 +6783,14 @@ find-root@^1.1.0:
|
|||||||
resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4"
|
resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4"
|
||||||
integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==
|
integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==
|
||||||
|
|
||||||
|
find-up@5.0.0:
|
||||||
|
version "5.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc"
|
||||||
|
integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==
|
||||||
|
dependencies:
|
||||||
|
locate-path "^6.0.0"
|
||||||
|
path-exists "^4.0.0"
|
||||||
|
|
||||||
find-up@^2.0.0, find-up@^2.1.0:
|
find-up@^2.0.0, find-up@^2.1.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7"
|
resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7"
|
||||||
@@ -6772,6 +6835,11 @@ flat-cache@^3.0.4:
|
|||||||
flatted "^3.1.0"
|
flatted "^3.1.0"
|
||||||
rimraf "^3.0.2"
|
rimraf "^3.0.2"
|
||||||
|
|
||||||
|
flat@^5.0.2:
|
||||||
|
version "5.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241"
|
||||||
|
integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==
|
||||||
|
|
||||||
flatted@^3.1.0:
|
flatted@^3.1.0:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.1.1.tgz#c4b489e80096d9df1dfc97c79871aea7c617c469"
|
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.1.1.tgz#c4b489e80096d9df1dfc97c79871aea7c617c469"
|
||||||
@@ -6787,10 +6855,10 @@ flow-parser@0.*:
|
|||||||
resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.110.0.tgz#fa56d1fdc4bbeb488db8b9d0a685cb91939ab0ff"
|
resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.110.0.tgz#fa56d1fdc4bbeb488db8b9d0a685cb91939ab0ff"
|
||||||
integrity sha512-IVHejqogTgZL2e206twVsdfX5he6mXS5F0AY315ao+6rEifbElEoVWKLYdEBsVl7QMp4buPbMe5FqXSdYQMUSQ==
|
integrity sha512-IVHejqogTgZL2e206twVsdfX5he6mXS5F0AY315ao+6rEifbElEoVWKLYdEBsVl7QMp4buPbMe5FqXSdYQMUSQ==
|
||||||
|
|
||||||
follow-redirects@^1.14.0:
|
follow-redirects@^1.10.0:
|
||||||
version "1.14.3"
|
version "1.14.4"
|
||||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.3.tgz#6ada78118d8d24caee595595accdc0ac6abd022e"
|
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.4.tgz#838fdf48a8bbdd79e52ee51fb1c94e3ed98b9379"
|
||||||
integrity sha512-3MkHxknWMUtb23apkgz/83fDoe+y+qr0TdgacGIA7bew+QLBo3vdgEN2xEsuXNivpFy4CyDhBBZnNZOtalmenw==
|
integrity sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g==
|
||||||
|
|
||||||
for-in@^1.0.2:
|
for-in@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
@@ -6905,6 +6973,11 @@ fsevents@^2.1.2:
|
|||||||
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e"
|
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e"
|
||||||
integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==
|
integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==
|
||||||
|
|
||||||
|
fsevents@~2.3.1:
|
||||||
|
version "2.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
|
||||||
|
integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
|
||||||
|
|
||||||
function-bind@^1.1.1:
|
function-bind@^1.1.1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
|
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
|
||||||
@@ -6978,13 +7051,25 @@ github-slugger@^1.2.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
emoji-regex ">=6.0.0 <=6.1.1"
|
emoji-regex ">=6.0.0 <=6.1.1"
|
||||||
|
|
||||||
glob-parent@^5.1.2:
|
glob-parent@^5.1.2, glob-parent@~5.1.0:
|
||||||
version "5.1.2"
|
version "5.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
|
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
|
||||||
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
|
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
|
||||||
dependencies:
|
dependencies:
|
||||||
is-glob "^4.0.1"
|
is-glob "^4.0.1"
|
||||||
|
|
||||||
|
glob@7.1.6:
|
||||||
|
version "7.1.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
|
||||||
|
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
|
||||||
|
dependencies:
|
||||||
|
fs.realpath "^1.0.0"
|
||||||
|
inflight "^1.0.4"
|
||||||
|
inherits "2"
|
||||||
|
minimatch "^3.0.4"
|
||||||
|
once "^1.3.0"
|
||||||
|
path-is-absolute "^1.0.0"
|
||||||
|
|
||||||
glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.1.7:
|
glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.1.7:
|
||||||
version "7.1.7"
|
version "7.1.7"
|
||||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90"
|
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90"
|
||||||
@@ -7125,6 +7210,11 @@ graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.
|
|||||||
resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
|
resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
|
||||||
integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=
|
integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=
|
||||||
|
|
||||||
|
growl@1.10.5:
|
||||||
|
version "1.10.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e"
|
||||||
|
integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==
|
||||||
|
|
||||||
growly@^1.3.0:
|
growly@^1.3.0:
|
||||||
version "1.3.0"
|
version "1.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
|
resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
|
||||||
@@ -7193,6 +7283,11 @@ has@^1.0.3:
|
|||||||
dependencies:
|
dependencies:
|
||||||
function-bind "^1.1.1"
|
function-bind "^1.1.1"
|
||||||
|
|
||||||
|
he@1.2.0:
|
||||||
|
version "1.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
|
||||||
|
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
|
||||||
|
|
||||||
hermes-parser@0.4.7:
|
hermes-parser@0.4.7:
|
||||||
version "0.4.7"
|
version "0.4.7"
|
||||||
resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.4.7.tgz#410f5129d57183784d205a0538e6fbdcf614c9ea"
|
resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.4.7.tgz#410f5129d57183784d205a0538e6fbdcf614c9ea"
|
||||||
@@ -7530,6 +7625,13 @@ is-bigint@^1.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.1.tgz#6923051dfcbc764278540b9ce0e6b3213aa5ebc2"
|
resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.1.tgz#6923051dfcbc764278540b9ce0e6b3213aa5ebc2"
|
||||||
integrity sha512-J0ELF4yHFxHy0cmSxZuheDOz2luOdVvqjwmEcj8H/L1JHeuEDSDbeRP+Dk9kFVk5RTFzbucJ2Kb9F7ixY2QaCg==
|
integrity sha512-J0ELF4yHFxHy0cmSxZuheDOz2luOdVvqjwmEcj8H/L1JHeuEDSDbeRP+Dk9kFVk5RTFzbucJ2Kb9F7ixY2QaCg==
|
||||||
|
|
||||||
|
is-binary-path@~2.1.0:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
|
||||||
|
integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
|
||||||
|
dependencies:
|
||||||
|
binary-extensions "^2.0.0"
|
||||||
|
|
||||||
is-boolean-object@^1.1.0:
|
is-boolean-object@^1.1.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.0.tgz#e2aaad3a3a8fca34c28f6eee135b156ed2587ff0"
|
resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.0.tgz#e2aaad3a3a8fca34c28f6eee135b156ed2587ff0"
|
||||||
@@ -7667,7 +7769,7 @@ is-generator-fn@^2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118"
|
resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118"
|
||||||
integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==
|
integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==
|
||||||
|
|
||||||
is-glob@^4.0.0, is-glob@^4.0.1:
|
is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1:
|
||||||
version "4.0.1"
|
version "4.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc"
|
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc"
|
||||||
integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==
|
integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==
|
||||||
@@ -7744,7 +7846,7 @@ is-path-inside@^3.0.2:
|
|||||||
resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283"
|
resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283"
|
||||||
integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
|
integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
|
||||||
|
|
||||||
is-plain-obj@^2.0.0:
|
is-plain-obj@^2.0.0, is-plain-obj@^2.1.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287"
|
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287"
|
||||||
integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==
|
integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==
|
||||||
@@ -8673,6 +8775,13 @@ jest@^26.6.3:
|
|||||||
import-local "^3.0.2"
|
import-local "^3.0.2"
|
||||||
jest-cli "^26.6.3"
|
jest-cli "^26.6.3"
|
||||||
|
|
||||||
|
js-base64@^3.7.0:
|
||||||
|
version "3.7.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-3.7.0.tgz#74979859ac07f999d28280b0697e82715f7e36ab"
|
||||||
|
integrity sha512-hJiXqoqZKdNx7PNuqHx3ZOgwcvgCprV0cs9ZMeqERshhVZ3cmXc3HGR60mKsHHqVK18PCwGXnmPiPDbao7SOMQ==
|
||||||
|
dependencies:
|
||||||
|
mocha "^8.4.0"
|
||||||
|
|
||||||
js-message@1.0.5:
|
js-message@1.0.5:
|
||||||
version "1.0.5"
|
version "1.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/js-message/-/js-message-1.0.5.tgz#2300d24b1af08e89dd095bc1a4c9c9cfcb892d15"
|
resolved "https://registry.yarnpkg.com/js-message/-/js-message-1.0.5.tgz#2300d24b1af08e89dd095bc1a4c9c9cfcb892d15"
|
||||||
@@ -8690,6 +8799,13 @@ js-queue@2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||||
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
|
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
|
||||||
|
|
||||||
|
js-yaml@4.0.0, js-yaml@^4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.0.0.tgz#f426bc0ff4b4051926cd588c71113183409a121f"
|
||||||
|
integrity sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==
|
||||||
|
dependencies:
|
||||||
|
argparse "^2.0.1"
|
||||||
|
|
||||||
js-yaml@^3.13.1:
|
js-yaml@^3.13.1:
|
||||||
version "3.14.0"
|
version "3.14.0"
|
||||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482"
|
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482"
|
||||||
@@ -8698,13 +8814,6 @@ js-yaml@^3.13.1:
|
|||||||
argparse "^1.0.7"
|
argparse "^1.0.7"
|
||||||
esprima "^4.0.0"
|
esprima "^4.0.0"
|
||||||
|
|
||||||
js-yaml@^4.0.0:
|
|
||||||
version "4.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.0.0.tgz#f426bc0ff4b4051926cd588c71113183409a121f"
|
|
||||||
integrity sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==
|
|
||||||
dependencies:
|
|
||||||
argparse "^2.0.1"
|
|
||||||
|
|
||||||
jscodeshift@^0.6.3:
|
jscodeshift@^0.6.3:
|
||||||
version "0.6.4"
|
version "0.6.4"
|
||||||
resolved "https://registry.yarnpkg.com/jscodeshift/-/jscodeshift-0.6.4.tgz#e19ab86214edac86a75c4557fc88b3937d558a8e"
|
resolved "https://registry.yarnpkg.com/jscodeshift/-/jscodeshift-0.6.4.tgz#e19ab86214edac86a75c4557fc88b3937d558a8e"
|
||||||
@@ -9071,6 +9180,13 @@ locate-path@^5.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
p-locate "^4.1.0"
|
p-locate "^4.1.0"
|
||||||
|
|
||||||
|
locate-path@^6.0.0:
|
||||||
|
version "6.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286"
|
||||||
|
integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==
|
||||||
|
dependencies:
|
||||||
|
p-locate "^5.0.0"
|
||||||
|
|
||||||
lockfile@^1.0.4:
|
lockfile@^1.0.4:
|
||||||
version "1.0.4"
|
version "1.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/lockfile/-/lockfile-1.0.4.tgz#07f819d25ae48f87e538e6578b6964a4981a5609"
|
resolved "https://registry.yarnpkg.com/lockfile/-/lockfile-1.0.4.tgz#07f819d25ae48f87e538e6578b6964a4981a5609"
|
||||||
@@ -9173,6 +9289,13 @@ lodash@4.x, lodash@^4.0.1, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lo
|
|||||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||||
|
|
||||||
|
log-symbols@4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920"
|
||||||
|
integrity sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==
|
||||||
|
dependencies:
|
||||||
|
chalk "^4.0.0"
|
||||||
|
|
||||||
log-symbols@^4.1.0:
|
log-symbols@^4.1.0:
|
||||||
version "4.1.0"
|
version "4.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503"
|
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503"
|
||||||
@@ -9748,6 +9871,37 @@ mkdirp@^0.5.1, mkdirp@^0.5.4:
|
|||||||
dependencies:
|
dependencies:
|
||||||
minimist "^1.2.5"
|
minimist "^1.2.5"
|
||||||
|
|
||||||
|
mocha@^8.4.0:
|
||||||
|
version "8.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/mocha/-/mocha-8.4.0.tgz#677be88bf15980a3cae03a73e10a0fc3997f0cff"
|
||||||
|
integrity sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==
|
||||||
|
dependencies:
|
||||||
|
"@ungap/promise-all-settled" "1.1.2"
|
||||||
|
ansi-colors "4.1.1"
|
||||||
|
browser-stdout "1.3.1"
|
||||||
|
chokidar "3.5.1"
|
||||||
|
debug "4.3.1"
|
||||||
|
diff "5.0.0"
|
||||||
|
escape-string-regexp "4.0.0"
|
||||||
|
find-up "5.0.0"
|
||||||
|
glob "7.1.6"
|
||||||
|
growl "1.10.5"
|
||||||
|
he "1.2.0"
|
||||||
|
js-yaml "4.0.0"
|
||||||
|
log-symbols "4.0.0"
|
||||||
|
minimatch "3.0.4"
|
||||||
|
ms "2.1.3"
|
||||||
|
nanoid "3.1.20"
|
||||||
|
serialize-javascript "5.0.1"
|
||||||
|
strip-json-comments "3.1.1"
|
||||||
|
supports-color "8.1.1"
|
||||||
|
which "2.0.2"
|
||||||
|
wide-align "1.1.3"
|
||||||
|
workerpool "6.1.0"
|
||||||
|
yargs "16.2.0"
|
||||||
|
yargs-parser "20.2.4"
|
||||||
|
yargs-unparser "2.0.0"
|
||||||
|
|
||||||
mock-fs@^5.0.0:
|
mock-fs@^5.0.0:
|
||||||
version "5.0.0"
|
version "5.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-5.0.0.tgz#5574520ac824c01a10091bf951c66f677c71acaa"
|
resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-5.0.0.tgz#5574520ac824c01a10091bf951c66f677c71acaa"
|
||||||
@@ -9773,7 +9927,7 @@ ms@2.1.2:
|
|||||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
||||||
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||||
|
|
||||||
ms@^2.1.1:
|
ms@2.1.3, ms@^2.1.1:
|
||||||
version "2.1.3"
|
version "2.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
|
||||||
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
|
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
|
||||||
@@ -9783,6 +9937,11 @@ mute-stream@0.0.8:
|
|||||||
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d"
|
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d"
|
||||||
integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==
|
integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==
|
||||||
|
|
||||||
|
nanoid@3.1.20:
|
||||||
|
version "3.1.20"
|
||||||
|
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788"
|
||||||
|
integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==
|
||||||
|
|
||||||
nanomatch@^1.2.9:
|
nanomatch@^1.2.9:
|
||||||
version "1.2.13"
|
version "1.2.13"
|
||||||
resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119"
|
resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119"
|
||||||
@@ -9919,7 +10078,7 @@ normalize-path@^2.1.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
remove-trailing-separator "^1.0.1"
|
remove-trailing-separator "^1.0.1"
|
||||||
|
|
||||||
normalize-path@^3.0.0:
|
normalize-path@^3.0.0, normalize-path@~3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
|
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
|
||||||
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
|
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
|
||||||
@@ -10227,6 +10386,13 @@ p-limit@^2.0.0, p-limit@^2.2.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
p-try "^2.0.0"
|
p-try "^2.0.0"
|
||||||
|
|
||||||
|
p-limit@^3.0.2:
|
||||||
|
version "3.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b"
|
||||||
|
integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==
|
||||||
|
dependencies:
|
||||||
|
yocto-queue "^0.1.0"
|
||||||
|
|
||||||
p-locate@^2.0.0:
|
p-locate@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43"
|
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43"
|
||||||
@@ -10248,6 +10414,13 @@ p-locate@^4.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
p-limit "^2.2.0"
|
p-limit "^2.2.0"
|
||||||
|
|
||||||
|
p-locate@^5.0.0:
|
||||||
|
version "5.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834"
|
||||||
|
integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==
|
||||||
|
dependencies:
|
||||||
|
p-limit "^3.0.2"
|
||||||
|
|
||||||
p-map@^2.0.0:
|
p-map@^2.0.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175"
|
resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175"
|
||||||
@@ -10443,7 +10616,7 @@ picomatch@^2.0.4, picomatch@^2.0.5:
|
|||||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad"
|
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad"
|
||||||
integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==
|
integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==
|
||||||
|
|
||||||
picomatch@^2.2.3:
|
picomatch@^2.2.1, picomatch@^2.2.3:
|
||||||
version "2.3.0"
|
version "2.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972"
|
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972"
|
||||||
integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==
|
integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==
|
||||||
@@ -10749,6 +10922,13 @@ raf@^3.4.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
performance-now "^2.1.0"
|
performance-now "^2.1.0"
|
||||||
|
|
||||||
|
randombytes@^2.1.0:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
|
||||||
|
integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==
|
||||||
|
dependencies:
|
||||||
|
safe-buffer "^5.1.0"
|
||||||
|
|
||||||
range-parser@~1.2.1:
|
range-parser@~1.2.1:
|
||||||
version "1.2.1"
|
version "1.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
|
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
|
||||||
@@ -11502,6 +11682,13 @@ readdir-glob@^1.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
minimatch "^3.0.4"
|
minimatch "^3.0.4"
|
||||||
|
|
||||||
|
readdirp@~3.5.0:
|
||||||
|
version "3.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e"
|
||||||
|
integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==
|
||||||
|
dependencies:
|
||||||
|
picomatch "^2.2.1"
|
||||||
|
|
||||||
realpath-native@^2.0.0:
|
realpath-native@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-2.0.0.tgz#7377ac429b6e1fd599dc38d08ed942d0d7beb866"
|
resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-2.0.0.tgz#7377ac429b6e1fd599dc38d08ed942d0d7beb866"
|
||||||
@@ -11949,7 +12136,7 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
|
|||||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
||||||
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
|
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
|
||||||
|
|
||||||
safe-buffer@^5.0.1, safe-buffer@~5.2.0:
|
safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@~5.2.0:
|
||||||
version "5.2.1"
|
version "5.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
|
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
|
||||||
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
||||||
@@ -12087,6 +12274,13 @@ serialize-error@^5.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
type-fest "^0.8.0"
|
type-fest "^0.8.0"
|
||||||
|
|
||||||
|
serialize-javascript@5.0.1:
|
||||||
|
version "5.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4"
|
||||||
|
integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==
|
||||||
|
dependencies:
|
||||||
|
randombytes "^2.1.0"
|
||||||
|
|
||||||
serve-static@1.14.1:
|
serve-static@1.14.1:
|
||||||
version "1.14.1"
|
version "1.14.1"
|
||||||
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9"
|
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9"
|
||||||
@@ -12486,7 +12680,7 @@ string-natural-compare@^3.0.0, string-natural-compare@^3.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4"
|
resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4"
|
||||||
integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==
|
integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==
|
||||||
|
|
||||||
string-width@^2.0.0, string-width@^2.1.1:
|
"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.1:
|
||||||
version "2.1.1"
|
version "2.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
|
resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
|
||||||
integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==
|
integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==
|
||||||
@@ -12611,7 +12805,7 @@ strip-final-newline@^2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
|
resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
|
||||||
integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
|
integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
|
||||||
|
|
||||||
strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
|
strip-json-comments@3.1.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
|
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
|
||||||
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
|
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
|
||||||
@@ -12647,6 +12841,13 @@ sumchecker@^3.0.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
debug "^4.1.0"
|
debug "^4.1.0"
|
||||||
|
|
||||||
|
supports-color@8.1.1, supports-color@^8.1.0:
|
||||||
|
version "8.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
|
||||||
|
integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
|
||||||
|
dependencies:
|
||||||
|
has-flag "^4.0.0"
|
||||||
|
|
||||||
supports-color@^5.3.0:
|
supports-color@^5.3.0:
|
||||||
version "5.5.0"
|
version "5.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
|
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
|
||||||
@@ -12661,13 +12862,6 @@ supports-color@^7.0.0, supports-color@^7.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
has-flag "^4.0.0"
|
has-flag "^4.0.0"
|
||||||
|
|
||||||
supports-color@^8.1.0:
|
|
||||||
version "8.1.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
|
|
||||||
integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
|
|
||||||
dependencies:
|
|
||||||
has-flag "^4.0.0"
|
|
||||||
|
|
||||||
supports-hyperlinks@^2.0.0, supports-hyperlinks@^2.1.0:
|
supports-hyperlinks@^2.0.0, supports-hyperlinks@^2.1.0:
|
||||||
version "2.2.0"
|
version "2.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb"
|
resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb"
|
||||||
@@ -13523,6 +13717,13 @@ which-typed-array@^1.1.2:
|
|||||||
has-symbols "^1.0.1"
|
has-symbols "^1.0.1"
|
||||||
is-typed-array "^1.1.3"
|
is-typed-array "^1.1.3"
|
||||||
|
|
||||||
|
which@2.0.2, which@^2.0.1, which@^2.0.2:
|
||||||
|
version "2.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
|
||||||
|
integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
|
||||||
|
dependencies:
|
||||||
|
isexe "^2.0.0"
|
||||||
|
|
||||||
which@^1.2.9:
|
which@^1.2.9:
|
||||||
version "1.3.1"
|
version "1.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
|
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
|
||||||
@@ -13530,12 +13731,12 @@ which@^1.2.9:
|
|||||||
dependencies:
|
dependencies:
|
||||||
isexe "^2.0.0"
|
isexe "^2.0.0"
|
||||||
|
|
||||||
which@^2.0.1, which@^2.0.2:
|
wide-align@1.1.3:
|
||||||
version "2.0.2"
|
version "1.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
|
resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457"
|
||||||
integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
|
integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==
|
||||||
dependencies:
|
dependencies:
|
||||||
isexe "^2.0.0"
|
string-width "^1.0.2 || 2"
|
||||||
|
|
||||||
widest-line@^3.1.0:
|
widest-line@^3.1.0:
|
||||||
version "3.1.0"
|
version "3.1.0"
|
||||||
@@ -13549,6 +13750,11 @@ word-wrap@^1.2.3, word-wrap@~1.2.3:
|
|||||||
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
|
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
|
||||||
integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
|
integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
|
||||||
|
|
||||||
|
workerpool@6.1.0:
|
||||||
|
version "6.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.1.0.tgz#a8e038b4c94569596852de7a8ea4228eefdeb37b"
|
||||||
|
integrity sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==
|
||||||
|
|
||||||
wrap-ansi@^4.0.0:
|
wrap-ansi@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-4.0.0.tgz#b3570d7c70156159a2d42be5cc942e957f7b1131"
|
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-4.0.0.tgz#b3570d7c70156159a2d42be5cc942e957f7b1131"
|
||||||
@@ -13689,6 +13895,11 @@ yaml@^1.7.2:
|
|||||||
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e"
|
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e"
|
||||||
integrity sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==
|
integrity sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==
|
||||||
|
|
||||||
|
yargs-parser@20.2.4:
|
||||||
|
version "20.2.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54"
|
||||||
|
integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==
|
||||||
|
|
||||||
yargs-parser@20.x:
|
yargs-parser@20.x:
|
||||||
version "20.2.3"
|
version "20.2.3"
|
||||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.3.tgz#92419ba867b858c868acf8bae9bf74af0dd0ce26"
|
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.3.tgz#92419ba867b858c868acf8bae9bf74af0dd0ce26"
|
||||||
@@ -13707,6 +13918,29 @@ yargs-parser@^20.2.2:
|
|||||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.7.tgz#61df85c113edfb5a7a4e36eb8aa60ef423cbc90a"
|
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.7.tgz#61df85c113edfb5a7a4e36eb8aa60ef423cbc90a"
|
||||||
integrity sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==
|
integrity sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==
|
||||||
|
|
||||||
|
yargs-unparser@2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb"
|
||||||
|
integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==
|
||||||
|
dependencies:
|
||||||
|
camelcase "^6.0.0"
|
||||||
|
decamelize "^4.0.0"
|
||||||
|
flat "^5.0.2"
|
||||||
|
is-plain-obj "^2.1.0"
|
||||||
|
|
||||||
|
yargs@16.2.0, yargs@^16.2.0:
|
||||||
|
version "16.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66"
|
||||||
|
integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==
|
||||||
|
dependencies:
|
||||||
|
cliui "^7.0.2"
|
||||||
|
escalade "^3.1.1"
|
||||||
|
get-caller-file "^2.0.5"
|
||||||
|
require-directory "^2.1.1"
|
||||||
|
string-width "^4.2.0"
|
||||||
|
y18n "^5.0.5"
|
||||||
|
yargs-parser "^20.2.2"
|
||||||
|
|
||||||
yargs@^15.3.1, yargs@^15.4.1:
|
yargs@^15.3.1, yargs@^15.4.1:
|
||||||
version "15.4.1"
|
version "15.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8"
|
resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8"
|
||||||
@@ -13724,19 +13958,6 @@ yargs@^15.3.1, yargs@^15.4.1:
|
|||||||
y18n "^4.0.0"
|
y18n "^4.0.0"
|
||||||
yargs-parser "^18.1.2"
|
yargs-parser "^18.1.2"
|
||||||
|
|
||||||
yargs@^16.2.0:
|
|
||||||
version "16.2.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66"
|
|
||||||
integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==
|
|
||||||
dependencies:
|
|
||||||
cliui "^7.0.2"
|
|
||||||
escalade "^3.1.1"
|
|
||||||
get-caller-file "^2.0.5"
|
|
||||||
require-directory "^2.1.1"
|
|
||||||
string-width "^4.2.0"
|
|
||||||
y18n "^5.0.5"
|
|
||||||
yargs-parser "^20.2.2"
|
|
||||||
|
|
||||||
yargs@^17.0.1:
|
yargs@^17.0.1:
|
||||||
version "17.0.1"
|
version "17.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.0.1.tgz#6a1ced4ed5ee0b388010ba9fd67af83b9362e0bb"
|
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.0.1.tgz#6a1ced4ed5ee0b388010ba9fd67af83b9362e0bb"
|
||||||
@@ -13770,6 +13991,11 @@ yn@3.1.1:
|
|||||||
resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
|
resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
|
||||||
integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==
|
integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==
|
||||||
|
|
||||||
|
yocto-queue@^0.1.0:
|
||||||
|
version "0.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
|
||||||
|
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
|
||||||
|
|
||||||
zip-stream@^4.1.0:
|
zip-stream@^4.1.0:
|
||||||
version "4.1.0"
|
version "4.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-4.1.0.tgz#51dd326571544e36aa3f756430b313576dc8fc79"
|
resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-4.1.0.tgz#51dd326571544e36aa3f756430b313576dc8fc79"
|
||||||
|
|||||||
Reference in New Issue
Block a user