mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-04-19 12:57:17 +00:00
Improve Serial Monitor Performances (#524)
Co-authored-by: Alberto Iannaccone <a.iannaccone@arduino.cc>
This commit is contained in:
parent
7f8b227c39
commit
54a67fc67c
@ -18,7 +18,7 @@
|
||||
"test:watch": "mocha --watch --watch-files lib \"./lib/test/**/*.test.js\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@grpc/grpc-js": "^1.1.1",
|
||||
"@grpc/grpc-js": "^1.3.7",
|
||||
"@theia/application-package": "next",
|
||||
"@theia/core": "next",
|
||||
"@theia/editor": "next",
|
||||
@ -64,6 +64,7 @@
|
||||
"fuzzy": "^0.1.3",
|
||||
"glob": "^7.1.6",
|
||||
"google-protobuf": "^3.11.4",
|
||||
"grpc": "^1.24.11",
|
||||
"hash.js": "^1.1.7",
|
||||
"is-valid-path": "^0.1.1",
|
||||
"js-yaml": "^3.13.1",
|
||||
@ -78,6 +79,7 @@
|
||||
"react-disable": "^0.1.0",
|
||||
"react-select": "^3.0.4",
|
||||
"react-tabs": "^3.1.2",
|
||||
"react-window": "^1.8.6",
|
||||
"semver": "^7.3.2",
|
||||
"string-natural-compare": "^2.0.3",
|
||||
"temp": "^0.9.1",
|
||||
@ -89,6 +91,7 @@
|
||||
"@types/chai": "^4.2.7",
|
||||
"@types/chai-string": "^1.4.2",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/react-window": "^1.8.5",
|
||||
"chai": "^4.2.0",
|
||||
"chai-string": "^1.5.0",
|
||||
"decompress": "^4.2.0",
|
||||
@ -97,6 +100,7 @@
|
||||
"download": "^7.1.0",
|
||||
"grpc_tools_node_protoc_ts": "^4.1.0",
|
||||
"mocha": "^7.0.0",
|
||||
"mockdate": "^3.0.5",
|
||||
"moment": "^2.24.0",
|
||||
"protoc": "^1.0.4",
|
||||
"shelljs": "^0.8.3",
|
||||
|
@ -400,24 +400,14 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
bind(MonitorService)
|
||||
.toDynamicValue((context) => {
|
||||
const connection = context.container.get(WebSocketConnectionProvider);
|
||||
const client = context.container.get(MonitorServiceClientImpl);
|
||||
const client =
|
||||
context.container.get<MonitorServiceClient>(MonitorServiceClient);
|
||||
return connection.createProxy(MonitorServicePath, client);
|
||||
})
|
||||
.inSingletonScope();
|
||||
bind(MonitorConnection).toSelf().inSingletonScope();
|
||||
// Serial monitor service client to receive and delegate notifications from the backend.
|
||||
bind(MonitorServiceClientImpl).toSelf().inSingletonScope();
|
||||
bind(MonitorServiceClient)
|
||||
.toDynamicValue((context) => {
|
||||
const client = context.container.get(MonitorServiceClientImpl);
|
||||
WebSocketConnectionProvider.createProxy(
|
||||
context.container,
|
||||
MonitorServicePath,
|
||||
client
|
||||
);
|
||||
return client;
|
||||
})
|
||||
.inSingletonScope();
|
||||
bind(MonitorServiceClient).to(MonitorServiceClientImpl).inSingletonScope();
|
||||
|
||||
bind(WorkspaceService).toSelf().inSingletonScope();
|
||||
rebind(TheiaWorkspaceService).toService(WorkspaceService);
|
||||
|
@ -8,6 +8,7 @@ import {
|
||||
MonitorConfig,
|
||||
MonitorError,
|
||||
Status,
|
||||
MonitorServiceClient,
|
||||
} from '../../common/protocol/monitor-service';
|
||||
import { BoardsServiceProvider } from '../boards/boards-service-provider';
|
||||
import {
|
||||
@ -16,7 +17,6 @@ import {
|
||||
BoardsService,
|
||||
AttachedBoardsChangeEvent,
|
||||
} from '../../common/protocol/boards-service';
|
||||
import { MonitorServiceClientImpl } from './monitor-service-client-impl';
|
||||
import { BoardsConfig } from '../boards/boards-config';
|
||||
import { MonitorModel } from './monitor-model';
|
||||
import { NotificationCenter } from '../notification-center';
|
||||
@ -29,8 +29,8 @@ export class MonitorConnection {
|
||||
@inject(MonitorService)
|
||||
protected readonly monitorService: MonitorService;
|
||||
|
||||
@inject(MonitorServiceClientImpl)
|
||||
protected readonly monitorServiceClient: MonitorServiceClientImpl;
|
||||
@inject(MonitorServiceClient)
|
||||
protected readonly monitorServiceClient: MonitorServiceClient;
|
||||
|
||||
@inject(BoardsService)
|
||||
protected readonly boardsService: BoardsService;
|
||||
@ -59,7 +59,7 @@ export class MonitorConnection {
|
||||
/**
|
||||
* This emitter forwards all read events **iff** the connection is established.
|
||||
*/
|
||||
protected readonly onReadEmitter = new Emitter<{ message: string }>();
|
||||
protected readonly onReadEmitter = new Emitter<{ messages: string[] }>();
|
||||
|
||||
/**
|
||||
* Array for storing previous monitor errors received from the server, and based on the number of elements in this array,
|
||||
@ -71,112 +71,15 @@ export class MonitorConnection {
|
||||
|
||||
@postConstruct()
|
||||
protected init(): void {
|
||||
this.monitorServiceClient.onError(async (error) => {
|
||||
let shouldReconnect = false;
|
||||
if (this.state) {
|
||||
const { code, config } = error;
|
||||
const { board, port } = config;
|
||||
const options = { timeout: 3000 };
|
||||
switch (code) {
|
||||
case MonitorError.ErrorCodes.CLIENT_CANCEL: {
|
||||
console.debug(
|
||||
`Connection was canceled by client: ${MonitorConnection.State.toString(
|
||||
this.state
|
||||
)}.`
|
||||
);
|
||||
break;
|
||||
}
|
||||
case MonitorError.ErrorCodes.DEVICE_BUSY: {
|
||||
this.messageService.warn(
|
||||
`Connection failed. Serial port is busy: ${Port.toString(port)}.`,
|
||||
options
|
||||
);
|
||||
shouldReconnect = this.autoConnect;
|
||||
this.monitorErrors.push(error);
|
||||
break;
|
||||
}
|
||||
case MonitorError.ErrorCodes.DEVICE_NOT_CONFIGURED: {
|
||||
this.messageService.info(
|
||||
`Disconnected ${Board.toString(board, {
|
||||
useFqbn: false,
|
||||
})} from ${Port.toString(port)}.`,
|
||||
options
|
||||
);
|
||||
break;
|
||||
}
|
||||
case undefined: {
|
||||
this.messageService.error(
|
||||
`Unexpected error. Reconnecting ${Board.toString(
|
||||
board
|
||||
)} on port ${Port.toString(port)}.`,
|
||||
options
|
||||
);
|
||||
console.error(JSON.stringify(error));
|
||||
shouldReconnect = this.connected && this.autoConnect;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const oldState = this.state;
|
||||
this.state = undefined;
|
||||
this.onConnectionChangedEmitter.fire(this.state);
|
||||
if (shouldReconnect) {
|
||||
if (this.monitorErrors.length >= 10) {
|
||||
this.messageService.warn(
|
||||
`Failed to reconnect ${Board.toString(board, {
|
||||
useFqbn: false,
|
||||
})} to the the serial-monitor after 10 consecutive attempts. The ${Port.toString(
|
||||
port
|
||||
)} serial port is busy. after 10 consecutive attempts.`
|
||||
);
|
||||
this.monitorErrors.length = 0;
|
||||
} else {
|
||||
const attempts = this.monitorErrors.length || 1;
|
||||
if (this.reconnectTimeout !== undefined) {
|
||||
// Clear the previous timer.
|
||||
window.clearTimeout(this.reconnectTimeout);
|
||||
}
|
||||
const timeout = attempts * 1000;
|
||||
this.messageService.warn(
|
||||
`Reconnecting ${Board.toString(board, {
|
||||
useFqbn: false,
|
||||
})} to ${Port.toString(port)} in ${attempts} seconds...`,
|
||||
{ timeout }
|
||||
);
|
||||
this.reconnectTimeout = window.setTimeout(
|
||||
() => this.connect(oldState.config),
|
||||
timeout
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
this.monitorServiceClient.onMessage(this.handleMessage.bind(this));
|
||||
this.monitorServiceClient.onError(this.handleError.bind(this));
|
||||
this.boardsServiceProvider.onBoardsConfigChanged(
|
||||
this.handleBoardConfigChange.bind(this)
|
||||
);
|
||||
this.notificationCenter.onAttachedBoardsChanged((event) => {
|
||||
if (this.autoConnect && this.connected) {
|
||||
const { boardsConfig } = this.boardsServiceProvider;
|
||||
if (
|
||||
this.boardsServiceProvider.canUploadTo(boardsConfig, {
|
||||
silent: false,
|
||||
})
|
||||
) {
|
||||
const { attached } = AttachedBoardsChangeEvent.diff(event);
|
||||
if (
|
||||
attached.boards.some(
|
||||
(board) =>
|
||||
!!board.port && BoardsConfig.Config.sameAs(boardsConfig, board)
|
||||
)
|
||||
) {
|
||||
const { selectedBoard: board, selectedPort: port } = boardsConfig;
|
||||
const { baudRate } = this.monitorModel;
|
||||
this.disconnect().then(() =>
|
||||
this.connect({ board, port, baudRate })
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
this.notificationCenter.onAttachedBoardsChanged(
|
||||
this.handleAttachedBoardsChanged.bind(this)
|
||||
);
|
||||
|
||||
// Handles the `baudRate` changes by reconnecting if required.
|
||||
this.monitorModel.onChange(({ property }) => {
|
||||
if (property === 'baudRate' && this.autoConnect && this.connected) {
|
||||
@ -186,6 +89,14 @@ export class MonitorConnection {
|
||||
});
|
||||
}
|
||||
|
||||
async handleMessage(port: string): Promise<void> {
|
||||
const w = new WebSocket(`ws://localhost:${port}`);
|
||||
w.onmessage = (res) => {
|
||||
const messages = JSON.parse(res.data);
|
||||
this.onReadEmitter.fire({ messages });
|
||||
};
|
||||
}
|
||||
|
||||
get connected(): boolean {
|
||||
return !!this.state;
|
||||
}
|
||||
@ -217,6 +128,109 @@ export class MonitorConnection {
|
||||
}
|
||||
}
|
||||
|
||||
handleError(error: MonitorError): void {
|
||||
let shouldReconnect = false;
|
||||
if (this.state) {
|
||||
const { code, config } = error;
|
||||
const { board, port } = config;
|
||||
const options = { timeout: 3000 };
|
||||
switch (code) {
|
||||
case MonitorError.ErrorCodes.CLIENT_CANCEL: {
|
||||
console.debug(
|
||||
`Connection was canceled by client: ${MonitorConnection.State.toString(
|
||||
this.state
|
||||
)}.`
|
||||
);
|
||||
break;
|
||||
}
|
||||
case MonitorError.ErrorCodes.DEVICE_BUSY: {
|
||||
this.messageService.warn(
|
||||
`Connection failed. Serial port is busy: ${Port.toString(port)}.`,
|
||||
options
|
||||
);
|
||||
shouldReconnect = this.autoConnect;
|
||||
this.monitorErrors.push(error);
|
||||
break;
|
||||
}
|
||||
case MonitorError.ErrorCodes.DEVICE_NOT_CONFIGURED: {
|
||||
this.messageService.info(
|
||||
`Disconnected ${Board.toString(board, {
|
||||
useFqbn: false,
|
||||
})} from ${Port.toString(port)}.`,
|
||||
options
|
||||
);
|
||||
break;
|
||||
}
|
||||
case undefined: {
|
||||
this.messageService.error(
|
||||
`Unexpected error. Reconnecting ${Board.toString(
|
||||
board
|
||||
)} on port ${Port.toString(port)}.`,
|
||||
options
|
||||
);
|
||||
console.error(JSON.stringify(error));
|
||||
shouldReconnect = this.connected && this.autoConnect;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const oldState = this.state;
|
||||
this.state = undefined;
|
||||
this.onConnectionChangedEmitter.fire(this.state);
|
||||
if (shouldReconnect) {
|
||||
if (this.monitorErrors.length >= 10) {
|
||||
this.messageService.warn(
|
||||
`Failed to reconnect ${Board.toString(board, {
|
||||
useFqbn: false,
|
||||
})} to the the serial-monitor after 10 consecutive attempts. The ${Port.toString(
|
||||
port
|
||||
)} serial port is busy. after 10 consecutive attempts.`
|
||||
);
|
||||
this.monitorErrors.length = 0;
|
||||
} else {
|
||||
const attempts = this.monitorErrors.length || 1;
|
||||
if (this.reconnectTimeout !== undefined) {
|
||||
// Clear the previous timer.
|
||||
window.clearTimeout(this.reconnectTimeout);
|
||||
}
|
||||
const timeout = attempts * 1000;
|
||||
this.messageService.warn(
|
||||
`Reconnecting ${Board.toString(board, {
|
||||
useFqbn: false,
|
||||
})} to ${Port.toString(port)} in ${attempts} seconds...`,
|
||||
{ timeout }
|
||||
);
|
||||
this.reconnectTimeout = window.setTimeout(
|
||||
() => this.connect(oldState.config),
|
||||
timeout
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleAttachedBoardsChanged(event: AttachedBoardsChangeEvent): void {
|
||||
if (this.autoConnect && this.connected) {
|
||||
const { boardsConfig } = this.boardsServiceProvider;
|
||||
if (
|
||||
this.boardsServiceProvider.canUploadTo(boardsConfig, {
|
||||
silent: false,
|
||||
})
|
||||
) {
|
||||
const { attached } = AttachedBoardsChangeEvent.diff(event);
|
||||
if (
|
||||
attached.boards.some(
|
||||
(board) =>
|
||||
!!board.port && BoardsConfig.Config.sameAs(boardsConfig, board)
|
||||
)
|
||||
) {
|
||||
const { selectedBoard: board, selectedPort: port } = boardsConfig;
|
||||
const { baudRate } = this.monitorModel;
|
||||
this.disconnect().then(() => this.connect({ board, port, baudRate }));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async connect(config: MonitorConfig): Promise<Status> {
|
||||
if (this.connected) {
|
||||
const disconnectStatus = await this.disconnect();
|
||||
@ -231,15 +245,6 @@ export class MonitorConnection {
|
||||
);
|
||||
const connectStatus = await this.monitorService.connect(config);
|
||||
if (Status.isOK(connectStatus)) {
|
||||
const requestMessage = () => {
|
||||
this.monitorService.request().then(({ message }) => {
|
||||
if (this.connected) {
|
||||
this.onReadEmitter.fire({ message });
|
||||
requestMessage();
|
||||
}
|
||||
});
|
||||
};
|
||||
requestMessage();
|
||||
this.state = { config };
|
||||
console.info(
|
||||
`<<< Serial monitor connection created for ${Board.toString(
|
||||
@ -300,7 +305,7 @@ export class MonitorConnection {
|
||||
return this.onConnectionChangedEmitter.event;
|
||||
}
|
||||
|
||||
get onRead(): Event<{ message: string }> {
|
||||
get onRead(): Event<{ messages: string[] }> {
|
||||
return this.onReadEmitter.event;
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,14 @@ export class MonitorServiceClientImpl implements MonitorServiceClient {
|
||||
protected readonly onErrorEmitter = new Emitter<MonitorError>();
|
||||
readonly onError = this.onErrorEmitter.event;
|
||||
|
||||
protected readonly onMessageEmitter = new Emitter<string>();
|
||||
readonly onMessage = this.onMessageEmitter.event;
|
||||
|
||||
notifyError(error: MonitorError): void {
|
||||
this.onErrorEmitter.fire(error);
|
||||
}
|
||||
|
||||
notifyMessage(message: string): void {
|
||||
this.onMessageEmitter.fire(message);
|
||||
}
|
||||
}
|
||||
|
68
arduino-ide-extension/src/browser/monitor/monitor-utils.ts
Normal file
68
arduino-ide-extension/src/browser/monitor/monitor-utils.ts
Normal file
@ -0,0 +1,68 @@
|
||||
import { Line, SerialMonitorOutput } from './serial-monitor-send-output';
|
||||
|
||||
export function messagesToLines(
|
||||
messages: string[],
|
||||
prevLines: Line[] = [],
|
||||
charCount = 0,
|
||||
separator = '\n'
|
||||
): [Line[], number] {
|
||||
const linesToAdd: Line[] = prevLines.length
|
||||
? [prevLines[prevLines.length - 1]]
|
||||
: [{ message: '', lineLen: 0 }];
|
||||
|
||||
for (const message of messages) {
|
||||
const messageLen = message.length;
|
||||
charCount += messageLen;
|
||||
const lastLine = linesToAdd[linesToAdd.length - 1];
|
||||
|
||||
// if the previous messages ends with "separator" add a new line
|
||||
if (lastLine.message.charAt(lastLine.message.length - 1) === separator) {
|
||||
linesToAdd.push({
|
||||
message,
|
||||
timestamp: new Date(),
|
||||
lineLen: messageLen,
|
||||
});
|
||||
} else {
|
||||
// concatenate to the last line
|
||||
linesToAdd[linesToAdd.length - 1].message += message;
|
||||
linesToAdd[linesToAdd.length - 1].lineLen += messageLen;
|
||||
if (!linesToAdd[linesToAdd.length - 1].timestamp) {
|
||||
linesToAdd[linesToAdd.length - 1].timestamp = new Date();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
prevLines.splice(prevLines.length - 1, 1, ...linesToAdd);
|
||||
return [prevLines, charCount];
|
||||
}
|
||||
|
||||
export function truncateLines(
|
||||
lines: Line[],
|
||||
charCount: number,
|
||||
maxCharacters: number = SerialMonitorOutput.MAX_CHARACTERS
|
||||
): [Line[], number] {
|
||||
let charsToDelete = charCount - maxCharacters;
|
||||
let lineIndex = 0;
|
||||
while (charsToDelete > 0 || lineIndex > 0) {
|
||||
const firstLineLength = lines[lineIndex]?.lineLen;
|
||||
|
||||
if (charsToDelete >= firstLineLength) {
|
||||
// every time a full line to delete is found, move the index.
|
||||
lineIndex++;
|
||||
charsToDelete -= firstLineLength;
|
||||
charCount -= firstLineLength;
|
||||
continue;
|
||||
}
|
||||
|
||||
// delete all previous lines
|
||||
lines.splice(0, lineIndex);
|
||||
lineIndex = 0;
|
||||
|
||||
const newFirstLine = lines[0]?.message?.substring(charsToDelete);
|
||||
const deletedCharsCount = firstLineLength - newFirstLine.length;
|
||||
charCount -= deletedCharsCount;
|
||||
charsToDelete -= deletedCharsCount;
|
||||
lines[0].message = newFirstLine;
|
||||
}
|
||||
return [lines, charCount];
|
||||
}
|
@ -1,26 +1,20 @@
|
||||
import * as React from 'react';
|
||||
import * as dateFormat from 'dateformat';
|
||||
import { postConstruct, injectable, inject } from 'inversify';
|
||||
import { OptionsType } from 'react-select/src/types';
|
||||
import { isOSX } from '@theia/core/lib/common/os';
|
||||
import { Event, Emitter } from '@theia/core/lib/common/event';
|
||||
import { Key, KeyCode } from '@theia/core/lib/browser/keys';
|
||||
import {
|
||||
DisposableCollection,
|
||||
Disposable,
|
||||
} from '@theia/core/lib/common/disposable';
|
||||
import { Emitter } from '@theia/core/lib/common/event';
|
||||
import { Disposable } from '@theia/core/lib/common/disposable';
|
||||
import {
|
||||
ReactWidget,
|
||||
Message,
|
||||
Widget,
|
||||
MessageLoop,
|
||||
} from '@theia/core/lib/browser/widgets';
|
||||
import { Board, Port } from '../../common/protocol/boards-service';
|
||||
import { MonitorConfig } from '../../common/protocol/monitor-service';
|
||||
import { ArduinoSelect } from '../widgets/arduino-select';
|
||||
import { MonitorModel } from './monitor-model';
|
||||
import { MonitorConnection } from './monitor-connection';
|
||||
import { MonitorServiceClientImpl } from './monitor-service-client-impl';
|
||||
import { SerialMonitorSendInput } from './serial-monitor-send-input';
|
||||
import { SerialMonitorOutput } from './serial-monitor-send-output';
|
||||
|
||||
@injectable()
|
||||
export class MonitorWidget extends ReactWidget {
|
||||
@ -32,9 +26,6 @@ export class MonitorWidget extends ReactWidget {
|
||||
@inject(MonitorConnection)
|
||||
protected readonly monitorConnection: MonitorConnection;
|
||||
|
||||
@inject(MonitorServiceClientImpl)
|
||||
protected readonly monitorServiceClient: MonitorServiceClientImpl;
|
||||
|
||||
protected widgetHeight: number;
|
||||
|
||||
/**
|
||||
@ -122,7 +113,9 @@ export class MonitorWidget extends ReactWidget {
|
||||
);
|
||||
};
|
||||
|
||||
protected get lineEndings(): OptionsType<SelectOption<MonitorModel.EOL>> {
|
||||
protected get lineEndings(): OptionsType<
|
||||
SerialMonitorOutput.SelectOption<MonitorModel.EOL>
|
||||
> {
|
||||
return [
|
||||
{
|
||||
label: 'No Line Ending',
|
||||
@ -143,7 +136,9 @@ export class MonitorWidget extends ReactWidget {
|
||||
];
|
||||
}
|
||||
|
||||
protected get baudRates(): OptionsType<SelectOption<MonitorConfig.BaudRate>> {
|
||||
protected get baudRates(): OptionsType<
|
||||
SerialMonitorOutput.SelectOption<MonitorConfig.BaudRate>
|
||||
> {
|
||||
const baudRates: Array<MonitorConfig.BaudRate> = [
|
||||
300, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200,
|
||||
];
|
||||
@ -196,6 +191,7 @@ export class MonitorWidget extends ReactWidget {
|
||||
monitorModel={this.monitorModel}
|
||||
monitorConnection={this.monitorConnection}
|
||||
clearConsoleEvent={this.clearOutputEmitter.event}
|
||||
height={Math.floor(this.widgetHeight - 50)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -208,187 +204,14 @@ export class MonitorWidget extends ReactWidget {
|
||||
}
|
||||
|
||||
protected readonly onChangeLineEnding = (
|
||||
option: SelectOption<MonitorModel.EOL>
|
||||
option: SerialMonitorOutput.SelectOption<MonitorModel.EOL>
|
||||
) => {
|
||||
this.monitorModel.lineEnding = option.value;
|
||||
};
|
||||
|
||||
protected readonly onChangeBaudRate = (
|
||||
option: SelectOption<MonitorConfig.BaudRate>
|
||||
option: SerialMonitorOutput.SelectOption<MonitorConfig.BaudRate>
|
||||
) => {
|
||||
this.monitorModel.baudRate = option.value;
|
||||
};
|
||||
}
|
||||
|
||||
export namespace SerialMonitorSendInput {
|
||||
export interface Props {
|
||||
readonly monitorConfig?: MonitorConfig;
|
||||
readonly onSend: (text: string) => void;
|
||||
readonly resolveFocus: (element: HTMLElement | undefined) => void;
|
||||
}
|
||||
export interface State {
|
||||
text: string;
|
||||
}
|
||||
}
|
||||
|
||||
export class SerialMonitorSendInput extends React.Component<
|
||||
SerialMonitorSendInput.Props,
|
||||
SerialMonitorSendInput.State
|
||||
> {
|
||||
constructor(props: Readonly<SerialMonitorSendInput.Props>) {
|
||||
super(props);
|
||||
this.state = { text: '' };
|
||||
this.onChange = this.onChange.bind(this);
|
||||
this.onSend = this.onSend.bind(this);
|
||||
this.onKeyDown = this.onKeyDown.bind(this);
|
||||
}
|
||||
|
||||
render(): React.ReactNode {
|
||||
return (
|
||||
<input
|
||||
ref={this.setRef}
|
||||
type="text"
|
||||
className={`theia-input ${this.props.monitorConfig ? '' : 'warning'}`}
|
||||
placeholder={this.placeholder}
|
||||
value={this.state.text}
|
||||
onChange={this.onChange}
|
||||
onKeyDown={this.onKeyDown}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
protected get placeholder(): string {
|
||||
const { monitorConfig } = this.props;
|
||||
if (!monitorConfig) {
|
||||
return 'Not connected. Select a board and a port to connect automatically.';
|
||||
}
|
||||
const { board, port } = monitorConfig;
|
||||
return `Message (${
|
||||
isOSX ? '⌘' : 'Ctrl'
|
||||
}+Enter to send message to '${Board.toString(board, {
|
||||
useFqbn: false,
|
||||
})}' on '${Port.toString(port)}')`;
|
||||
}
|
||||
|
||||
protected setRef = (element: HTMLElement | null) => {
|
||||
if (this.props.resolveFocus) {
|
||||
this.props.resolveFocus(element || undefined);
|
||||
}
|
||||
};
|
||||
|
||||
protected onChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
this.setState({ text: event.target.value });
|
||||
}
|
||||
|
||||
protected onSend(): void {
|
||||
this.props.onSend(this.state.text);
|
||||
this.setState({ text: '' });
|
||||
}
|
||||
|
||||
protected onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
||||
const keyCode = KeyCode.createKeyCode(event.nativeEvent);
|
||||
if (keyCode) {
|
||||
const { key, meta, ctrl } = keyCode;
|
||||
if (key === Key.ENTER && ((isOSX && meta) || (!isOSX && ctrl))) {
|
||||
this.onSend();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export namespace SerialMonitorOutput {
|
||||
export interface Props {
|
||||
readonly monitorModel: MonitorModel;
|
||||
readonly monitorConnection: MonitorConnection;
|
||||
readonly clearConsoleEvent: Event<void>;
|
||||
}
|
||||
export interface State {
|
||||
content: string;
|
||||
timestamp: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
export class SerialMonitorOutput extends React.Component<
|
||||
SerialMonitorOutput.Props,
|
||||
SerialMonitorOutput.State
|
||||
> {
|
||||
/**
|
||||
* Do not touch it. It is used to be able to "follow" the serial monitor log.
|
||||
*/
|
||||
protected anchor: HTMLElement | null;
|
||||
protected toDisposeBeforeUnmount = new DisposableCollection();
|
||||
|
||||
constructor(props: Readonly<SerialMonitorOutput.Props>) {
|
||||
super(props);
|
||||
this.state = {
|
||||
content: '',
|
||||
timestamp: this.props.monitorModel.timestamp,
|
||||
};
|
||||
}
|
||||
|
||||
render(): React.ReactNode {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div style={{ whiteSpace: 'pre', fontFamily: 'monospace' }}>
|
||||
{this.state.content}
|
||||
</div>
|
||||
<div
|
||||
style={{ float: 'left', clear: 'both' }}
|
||||
ref={(element) => {
|
||||
this.anchor = element;
|
||||
}}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
this.scrollToBottom();
|
||||
this.toDisposeBeforeUnmount.pushAll([
|
||||
this.props.monitorConnection.onRead(({ message }) => {
|
||||
const rawLines = message.split('\n');
|
||||
const lines: string[] = [];
|
||||
const timestamp = () =>
|
||||
this.state.timestamp
|
||||
? `${dateFormat(new Date(), 'H:M:ss.l')} -> `
|
||||
: '';
|
||||
for (let i = 0; i < rawLines.length; i++) {
|
||||
if (i === 0 && this.state.content.length !== 0) {
|
||||
lines.push(rawLines[i]);
|
||||
} else {
|
||||
lines.push(timestamp() + rawLines[i]);
|
||||
}
|
||||
}
|
||||
const content = this.state.content + lines.join('\n');
|
||||
this.setState({ content });
|
||||
}),
|
||||
this.props.clearConsoleEvent(() => this.setState({ content: '' })),
|
||||
this.props.monitorModel.onChange(({ property }) => {
|
||||
if (property === 'timestamp') {
|
||||
const { timestamp } = this.props.monitorModel;
|
||||
this.setState({ timestamp });
|
||||
}
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
componentDidUpdate(): void {
|
||||
this.scrollToBottom();
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
// TODO: "Your preferred browser's local storage is almost full." Discard `content` before saving layout?
|
||||
this.toDisposeBeforeUnmount.dispose();
|
||||
}
|
||||
|
||||
protected scrollToBottom(): void {
|
||||
if (this.props.monitorModel.autoscroll && this.anchor) {
|
||||
this.anchor.scrollIntoView();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface SelectOption<T> {
|
||||
readonly label: string;
|
||||
readonly value: T;
|
||||
}
|
||||
|
@ -0,0 +1,81 @@
|
||||
import * as React from 'react';
|
||||
import { Key, KeyCode } from '@theia/core/lib/browser/keys';
|
||||
import { Board, Port } from '../../common/protocol/boards-service';
|
||||
import { MonitorConfig } from '../../common/protocol/monitor-service';
|
||||
import { isOSX } from '@theia/core/lib/common/os';
|
||||
|
||||
export namespace SerialMonitorSendInput {
|
||||
export interface Props {
|
||||
readonly monitorConfig?: MonitorConfig;
|
||||
readonly onSend: (text: string) => void;
|
||||
readonly resolveFocus: (element: HTMLElement | undefined) => void;
|
||||
}
|
||||
export interface State {
|
||||
text: string;
|
||||
}
|
||||
}
|
||||
|
||||
export class SerialMonitorSendInput extends React.Component<
|
||||
SerialMonitorSendInput.Props,
|
||||
SerialMonitorSendInput.State
|
||||
> {
|
||||
constructor(props: Readonly<SerialMonitorSendInput.Props>) {
|
||||
super(props);
|
||||
this.state = { text: '' };
|
||||
this.onChange = this.onChange.bind(this);
|
||||
this.onSend = this.onSend.bind(this);
|
||||
this.onKeyDown = this.onKeyDown.bind(this);
|
||||
}
|
||||
|
||||
render(): React.ReactNode {
|
||||
return (
|
||||
<input
|
||||
ref={this.setRef}
|
||||
type="text"
|
||||
className={`theia-input ${this.props.monitorConfig ? '' : 'warning'}`}
|
||||
placeholder={this.placeholder}
|
||||
value={this.state.text}
|
||||
onChange={this.onChange}
|
||||
onKeyDown={this.onKeyDown}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
protected get placeholder(): string {
|
||||
const { monitorConfig } = this.props;
|
||||
if (!monitorConfig) {
|
||||
return 'Not connected. Select a board and a port to connect automatically.';
|
||||
}
|
||||
const { board, port } = monitorConfig;
|
||||
return `Message (${
|
||||
isOSX ? '⌘' : 'Ctrl'
|
||||
}+Enter to send message to '${Board.toString(board, {
|
||||
useFqbn: false,
|
||||
})}' on '${Port.toString(port)}')`;
|
||||
}
|
||||
|
||||
protected setRef = (element: HTMLElement | null) => {
|
||||
if (this.props.resolveFocus) {
|
||||
this.props.resolveFocus(element || undefined);
|
||||
}
|
||||
};
|
||||
|
||||
protected onChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
this.setState({ text: event.target.value });
|
||||
}
|
||||
|
||||
protected onSend(): void {
|
||||
this.props.onSend(this.state.text);
|
||||
this.setState({ text: '' });
|
||||
}
|
||||
|
||||
protected onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
||||
const keyCode = KeyCode.createKeyCode(event.nativeEvent);
|
||||
if (keyCode) {
|
||||
const { key, meta, ctrl } = keyCode;
|
||||
if (key === Key.ENTER && ((isOSX && meta) || (!isOSX && ctrl))) {
|
||||
this.onSend();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,144 @@
|
||||
import * as React from 'react';
|
||||
import { Event } from '@theia/core/lib/common/event';
|
||||
import { DisposableCollection } from '@theia/core/lib/common/disposable';
|
||||
import { areEqual, FixedSizeList as List } from 'react-window';
|
||||
import { MonitorModel } from './monitor-model';
|
||||
import { MonitorConnection } from './monitor-connection';
|
||||
import dateFormat = require('dateformat');
|
||||
import { messagesToLines, truncateLines } from './monitor-utils';
|
||||
|
||||
export type Line = { message: string; timestamp?: Date; lineLen: number };
|
||||
|
||||
export class SerialMonitorOutput extends React.Component<
|
||||
SerialMonitorOutput.Props,
|
||||
SerialMonitorOutput.State
|
||||
> {
|
||||
/**
|
||||
* Do not touch it. It is used to be able to "follow" the serial monitor log.
|
||||
*/
|
||||
protected toDisposeBeforeUnmount = new DisposableCollection();
|
||||
private listRef: React.RefObject<any>;
|
||||
|
||||
constructor(props: Readonly<SerialMonitorOutput.Props>) {
|
||||
super(props);
|
||||
this.listRef = React.createRef();
|
||||
this.state = {
|
||||
lines: [],
|
||||
timestamp: this.props.monitorModel.timestamp,
|
||||
charCount: 0,
|
||||
};
|
||||
}
|
||||
|
||||
render(): React.ReactNode {
|
||||
return (
|
||||
<List
|
||||
className="serial-monitor-messages"
|
||||
height={this.props.height}
|
||||
itemData={
|
||||
{
|
||||
lines: this.state.lines,
|
||||
timestamp: this.state.timestamp,
|
||||
} as any
|
||||
}
|
||||
itemCount={this.state.lines.length}
|
||||
itemSize={18}
|
||||
width={'100%'}
|
||||
ref={this.listRef}
|
||||
>
|
||||
{Row}
|
||||
</List>
|
||||
);
|
||||
}
|
||||
|
||||
shouldComponentUpdate(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
this.scrollToBottom();
|
||||
this.toDisposeBeforeUnmount.pushAll([
|
||||
this.props.monitorConnection.onRead(({ messages }) => {
|
||||
const [newLines, totalCharCount] = messagesToLines(
|
||||
messages,
|
||||
this.state.lines,
|
||||
this.state.charCount
|
||||
);
|
||||
const [lines, charCount] = truncateLines(newLines, totalCharCount);
|
||||
|
||||
this.setState({
|
||||
lines,
|
||||
charCount,
|
||||
});
|
||||
this.scrollToBottom();
|
||||
}),
|
||||
this.props.clearConsoleEvent(() => this.setState({ lines: [] })),
|
||||
this.props.monitorModel.onChange(({ property }) => {
|
||||
if (property === 'timestamp') {
|
||||
const { timestamp } = this.props.monitorModel;
|
||||
this.setState({ timestamp });
|
||||
}
|
||||
if (property === 'autoscroll') {
|
||||
this.scrollToBottom();
|
||||
}
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
// TODO: "Your preferred browser's local storage is almost full." Discard `content` before saving layout?
|
||||
this.toDisposeBeforeUnmount.dispose();
|
||||
}
|
||||
|
||||
scrollToBottom = ((): void => {
|
||||
if (this.listRef.current && this.props.monitorModel.autoscroll) {
|
||||
this.listRef.current.scrollToItem(this.state.lines.length, 'end');
|
||||
}
|
||||
}).bind(this);
|
||||
}
|
||||
|
||||
const _Row = ({
|
||||
index,
|
||||
style,
|
||||
data,
|
||||
}: {
|
||||
index: number;
|
||||
style: any;
|
||||
data: { lines: Line[]; timestamp: boolean };
|
||||
}) => {
|
||||
const timestamp =
|
||||
(data.timestamp &&
|
||||
`${dateFormat(data.lines[index].timestamp, 'H:M:ss.l')} -> `) ||
|
||||
'';
|
||||
return (
|
||||
(data.lines[index].lineLen && (
|
||||
<div style={style}>
|
||||
{timestamp}
|
||||
{data.lines[index].message}
|
||||
</div>
|
||||
)) ||
|
||||
null
|
||||
);
|
||||
};
|
||||
const Row = React.memo(_Row, areEqual);
|
||||
|
||||
export namespace SerialMonitorOutput {
|
||||
export interface Props {
|
||||
readonly monitorModel: MonitorModel;
|
||||
readonly monitorConnection: MonitorConnection;
|
||||
readonly clearConsoleEvent: Event<void>;
|
||||
readonly height: number;
|
||||
}
|
||||
|
||||
export interface State {
|
||||
lines: Line[];
|
||||
timestamp: boolean;
|
||||
charCount: number;
|
||||
}
|
||||
|
||||
export interface SelectOption<T> {
|
||||
readonly label: string;
|
||||
readonly value: T;
|
||||
}
|
||||
|
||||
export const MAX_CHARACTERS = 1_000_000;
|
||||
}
|
@ -9,6 +9,11 @@
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.serial-monitor-messages {
|
||||
white-space: 'pre';
|
||||
font-family: monospace
|
||||
}
|
||||
|
||||
.serial-monitor .head {
|
||||
display: flex;
|
||||
padding: 5px;
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { JsonRpcServer } from '@theia/core/lib/common/messaging/proxy-factory';
|
||||
import { Board, Port } from './boards-service';
|
||||
import { Event } from '@theia/core/lib/common/event';
|
||||
|
||||
export interface Status {}
|
||||
export type OK = Status;
|
||||
@ -23,7 +24,6 @@ export interface MonitorService extends JsonRpcServer<MonitorServiceClient> {
|
||||
connect(config: MonitorConfig): Promise<Status>;
|
||||
disconnect(): Promise<Status>;
|
||||
send(message: string): Promise<Status>;
|
||||
request(): Promise<{ message: string }>;
|
||||
}
|
||||
|
||||
export interface MonitorConfig {
|
||||
@ -60,7 +60,10 @@ export namespace MonitorConfig {
|
||||
|
||||
export const MonitorServiceClient = Symbol('MonitorServiceClient');
|
||||
export interface MonitorServiceClient {
|
||||
onError: Event<MonitorError>;
|
||||
onMessage: Event<string>;
|
||||
notifyError(event: MonitorError): void;
|
||||
notifyMessage(message: string): void;
|
||||
}
|
||||
|
||||
export interface MonitorError {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { ClientDuplexStream } from '@grpc/grpc-js';
|
||||
import { TextDecoder, TextEncoder } from 'util';
|
||||
import { TextEncoder } from 'util';
|
||||
import { injectable, inject, named } from 'inversify';
|
||||
import { Struct } from 'google-protobuf/google/protobuf/struct_pb';
|
||||
import { Emitter } from '@theia/core/lib/common/event';
|
||||
@ -18,6 +18,7 @@ import {
|
||||
} from '../cli-protocol/cc/arduino/cli/monitor/v1/monitor_pb';
|
||||
import { MonitorClientProvider } from './monitor-client-provider';
|
||||
import { Board, Port } from '../../common/protocol/boards-service';
|
||||
import * as WebSocket from 'ws';
|
||||
|
||||
interface ErrorWithCode extends Error {
|
||||
readonly code: number;
|
||||
@ -122,14 +123,55 @@ export class MonitorServiceImpl implements MonitorService {
|
||||
}).bind(this)
|
||||
);
|
||||
|
||||
const ws = new WebSocket.Server({ port: 0 });
|
||||
const address: any = ws.address();
|
||||
this.client?.notifyMessage(address.port);
|
||||
let wsConn: WebSocket | null = null;
|
||||
ws.on('connection', (ws) => {
|
||||
wsConn = ws;
|
||||
});
|
||||
|
||||
const flushMessagesToFrontend = () => {
|
||||
if (this.messages.length) {
|
||||
wsConn?.send(JSON.stringify(this.messages));
|
||||
this.messages = [];
|
||||
}
|
||||
};
|
||||
|
||||
// empty the queue every 16ms (~60fps)
|
||||
setInterval(flushMessagesToFrontend, 32);
|
||||
|
||||
// converts 'ab\nc\nd' => [ab\n,c\n,d]
|
||||
const stringToArray = (string: string, separator = '\n') => {
|
||||
const retArray: string[] = [];
|
||||
|
||||
let prevChar = separator;
|
||||
|
||||
for (let i = 0; i < string.length; i++) {
|
||||
const currChar = string[i];
|
||||
|
||||
if (prevChar === separator) {
|
||||
retArray.push(currChar);
|
||||
} else {
|
||||
const lastWord = retArray[retArray.length - 1];
|
||||
retArray[retArray.length - 1] = lastWord + currChar;
|
||||
}
|
||||
|
||||
prevChar = currChar;
|
||||
}
|
||||
return retArray;
|
||||
};
|
||||
|
||||
duplex.on(
|
||||
'data',
|
||||
((resp: StreamingOpenResponse) => {
|
||||
const raw = resp.getData();
|
||||
const message =
|
||||
typeof raw === 'string' ? raw : new TextDecoder('utf8').decode(raw);
|
||||
this.messages.push(message);
|
||||
this.onMessageDidReadEmitter.fire();
|
||||
|
||||
// split the message if it contains more lines
|
||||
const messages = stringToArray(message);
|
||||
this.messages.push(...messages);
|
||||
}).bind(this)
|
||||
);
|
||||
|
||||
@ -207,19 +249,6 @@ export class MonitorServiceImpl implements MonitorService {
|
||||
});
|
||||
}
|
||||
|
||||
async request(): Promise<{ message: string }> {
|
||||
const message = this.messages.shift();
|
||||
if (message) {
|
||||
return { message };
|
||||
}
|
||||
return new Promise<{ message: string }>((resolve) => {
|
||||
const toDispose = this.onMessageDidReadEmitter.event(() => {
|
||||
toDispose.dispose();
|
||||
resolve(this.request());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
protected mapType(
|
||||
type?: MonitorConfig.ConnectionType
|
||||
): GrpcMonitorConfig.TargetType {
|
||||
|
171
arduino-ide-extension/src/test/browser/monitor-utils.test.ts
Normal file
171
arduino-ide-extension/src/test/browser/monitor-utils.test.ts
Normal file
@ -0,0 +1,171 @@
|
||||
import { expect } from 'chai';
|
||||
import {
|
||||
messagesToLines,
|
||||
truncateLines,
|
||||
} from '../../browser/monitor/monitor-utils';
|
||||
import { Line } from '../../browser/monitor/serial-monitor-send-output';
|
||||
import { set, reset } from 'mockdate';
|
||||
|
||||
type TestLine = {
|
||||
messages: string[];
|
||||
prevLines?: { lines: Line[]; charCount: number };
|
||||
expected: { lines: Line[]; charCount: number };
|
||||
expectedTruncated?: {
|
||||
lines: Line[];
|
||||
charCount: number;
|
||||
maxCharacters?: number;
|
||||
};
|
||||
};
|
||||
|
||||
const date = new Date();
|
||||
const testLines: TestLine[] = [
|
||||
{
|
||||
messages: ['Hello'],
|
||||
expected: { lines: [{ message: 'Hello', lineLen: 5 }], charCount: 5 },
|
||||
},
|
||||
{
|
||||
messages: ['Hello', 'Dog!'],
|
||||
expected: { lines: [{ message: 'HelloDog!', lineLen: 9 }], charCount: 9 },
|
||||
},
|
||||
{
|
||||
messages: ['Hello\n', 'Dog!'],
|
||||
expected: {
|
||||
lines: [
|
||||
{ message: 'Hello\n', lineLen: 6 },
|
||||
{ message: 'Dog!', lineLen: 4 },
|
||||
],
|
||||
charCount: 10,
|
||||
},
|
||||
},
|
||||
{
|
||||
messages: ['Dog!'],
|
||||
prevLines: { lines: [{ message: 'Hello\n', lineLen: 6 }], charCount: 6 },
|
||||
expected: {
|
||||
lines: [
|
||||
{ message: 'Hello\n', lineLen: 6 },
|
||||
{ message: 'Dog!', lineLen: 4 },
|
||||
],
|
||||
charCount: 10,
|
||||
},
|
||||
},
|
||||
{
|
||||
messages: [' Dog!\n', " Who's a good ", 'boy?\n', "You're a good boy!"],
|
||||
prevLines: { lines: [{ message: 'Hello', lineLen: 5 }], charCount: 5 },
|
||||
expected: {
|
||||
lines: [
|
||||
{ message: 'Hello Dog!\n', lineLen: 11 },
|
||||
{ message: " Who's a good boy?\n", lineLen: 19 },
|
||||
{ message: "You're a good boy!", lineLen: 8 },
|
||||
],
|
||||
charCount: 48,
|
||||
},
|
||||
expectedTruncated: {
|
||||
maxCharacters: 20,
|
||||
charCount: 20,
|
||||
lines: [
|
||||
{ message: '?\n', lineLen: 2 },
|
||||
{ message: "You're a good boy!", lineLen: 8 },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
messages: ['boy?\n', "You're a good boy!"],
|
||||
prevLines: {
|
||||
lines: [
|
||||
{ message: 'Hello Dog!\n', lineLen: 11 },
|
||||
{ message: " Who's a good ", lineLen: 14 },
|
||||
],
|
||||
charCount: 25,
|
||||
},
|
||||
expected: {
|
||||
lines: [
|
||||
{ message: 'Hello Dog!\n', lineLen: 11 },
|
||||
{ message: " Who's a good boy?\n", lineLen: 19 },
|
||||
{ message: "You're a good boy!", lineLen: 8 },
|
||||
],
|
||||
charCount: 48,
|
||||
},
|
||||
expectedTruncated: {
|
||||
maxCharacters: 20,
|
||||
charCount: 20,
|
||||
lines: [
|
||||
{ message: '?\n', lineLen: 2 },
|
||||
{ message: "You're a good boy!", lineLen: 8 },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
messages: ["Who's a good boy?\n", 'Yo'],
|
||||
prevLines: {
|
||||
lines: [{ message: 'Hello Dog!\n', lineLen: 11 }],
|
||||
charCount: 11,
|
||||
},
|
||||
expected: {
|
||||
lines: [
|
||||
{ message: 'Hello Dog!\n', lineLen: 11 },
|
||||
{ message: "Who's a good boy?\n", lineLen: 18 },
|
||||
{ message: 'Yo', lineLen: 2 },
|
||||
],
|
||||
charCount: 31,
|
||||
},
|
||||
expectedTruncated: {
|
||||
maxCharacters: 20,
|
||||
charCount: 20,
|
||||
lines: [
|
||||
{ message: "Who's a good boy?\n", lineLen: 18 },
|
||||
{ message: 'Yo', lineLen: 2 },
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
testLines.forEach((t) =>
|
||||
[...t.expected.lines, ...(t.prevLines?.lines || [])].forEach(
|
||||
(l) => (l.timestamp = date)
|
||||
)
|
||||
);
|
||||
|
||||
describe.only('Monitor Utils', () => {
|
||||
beforeEach(() => {
|
||||
set(date);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
reset();
|
||||
});
|
||||
|
||||
testLines.forEach((testLine) => {
|
||||
context('when converting messages', () => {
|
||||
it('should give the right result', () => {
|
||||
const [newLines, addedCharCount] = messagesToLines(
|
||||
testLine.messages,
|
||||
testLine.prevLines?.lines,
|
||||
testLine.prevLines?.charCount
|
||||
);
|
||||
newLines.forEach((line, index) => {
|
||||
expect(line.message).to.equal(testLine.expected.lines[index].message);
|
||||
expect(line.timestamp).to.deep.equal(
|
||||
testLine.expected.lines[index].timestamp
|
||||
);
|
||||
});
|
||||
expect(addedCharCount).to.equal(testLine.expected.charCount);
|
||||
|
||||
const [truncatedLines, totalCharCount] = truncateLines(
|
||||
newLines,
|
||||
addedCharCount,
|
||||
testLine.expectedTruncated?.maxCharacters
|
||||
);
|
||||
let charCount = 0;
|
||||
if (testLine.expectedTruncated) {
|
||||
truncatedLines.forEach((line, index) => {
|
||||
expect(line.message).to.equal(
|
||||
testLine.expectedTruncated?.lines[index].message
|
||||
);
|
||||
charCount += line.message.length;
|
||||
});
|
||||
expect(totalCharCount).to.equal(charCount);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
323
yarn.lock
323
yarn.lock
@ -826,6 +826,13 @@
|
||||
"@babel/types" "^7.4.4"
|
||||
esutils "^2.0.2"
|
||||
|
||||
"@babel/runtime@^7.0.0":
|
||||
version "7.15.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.4.tgz#fd17d16bfdf878e6dd02d19753a39fa8a8d9c84a"
|
||||
integrity sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@babel/runtime@^7.10.0", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7":
|
||||
version "7.13.10"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.13.10.tgz#47d42a57b6095f4468da440388fdbad8bebf0d7d"
|
||||
@ -1048,14 +1055,12 @@
|
||||
unique-filename "^1.1.1"
|
||||
which "^1.3.1"
|
||||
|
||||
"@grpc/grpc-js@^1.1.1":
|
||||
version "1.2.11"
|
||||
resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.2.11.tgz#68faa56bded64844294dc6429185503376f05ff1"
|
||||
integrity sha512-DZqx3nHBm2OGY7NKq4sppDEfx4nBAsQH/d/H/yxo/+BwpVLWLGs+OorpwQ+Fqd6EgpDEoi4MhqndjGUeLl/5GA==
|
||||
"@grpc/grpc-js@^1.3.7":
|
||||
version "1.3.7"
|
||||
resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.3.7.tgz#58b687aff93b743aafde237fd2ee9a3259d7f2d8"
|
||||
integrity sha512-CKQVuwuSPh40tgOkR7c0ZisxYRiN05PcKPW72mQL5y++qd7CwBRoaJZvU5xfXnCJDFBmS3qZGQ71Frx6Ofo2XA==
|
||||
dependencies:
|
||||
"@types/node" ">=12.12.47"
|
||||
google-auth-library "^6.1.1"
|
||||
semver "^6.2.0"
|
||||
|
||||
"@lerna/add@3.21.0":
|
||||
version "3.21.0"
|
||||
@ -1742,6 +1747,21 @@
|
||||
npmlog "^4.1.2"
|
||||
write-file-atomic "^2.3.0"
|
||||
|
||||
"@mapbox/node-pre-gyp@^1.0.4":
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.5.tgz#2a0b32fcb416fb3f2250fd24cb2a81421a4f5950"
|
||||
integrity sha512-4srsKPXWlIxp5Vbqz5uLfBN+du2fJChBoYn/f2h991WLdk7jUvcSk/McVLSv/X+xQIPI8eGD5GjrnygdyHnhPA==
|
||||
dependencies:
|
||||
detect-libc "^1.0.3"
|
||||
https-proxy-agent "^5.0.0"
|
||||
make-dir "^3.1.0"
|
||||
node-fetch "^2.6.1"
|
||||
nopt "^5.0.0"
|
||||
npmlog "^4.1.2"
|
||||
rimraf "^3.0.2"
|
||||
semver "^7.3.4"
|
||||
tar "^6.1.0"
|
||||
|
||||
"@mrmlnc/readdir-enhanced@^2.2.1":
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde"
|
||||
@ -2675,6 +2695,14 @@
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/bytebuffer@^5.0.40":
|
||||
version "5.0.42"
|
||||
resolved "https://registry.yarnpkg.com/@types/bytebuffer/-/bytebuffer-5.0.42.tgz#1c602a77942d34c5c0879ad75c58d5d8c07dfb3b"
|
||||
integrity sha512-lEgKojWUAc/MG2t649oZS5AfYFP2xRNPoDuwDBlBMjHXd8MaGPgFgtCXUK7inZdBOygmVf10qxc1Us8GXC96aw==
|
||||
dependencies:
|
||||
"@types/long" "*"
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/caseless@*":
|
||||
version "0.12.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.2.tgz#f65d3d6389e01eeb458bd54dc8f52b95a9463bc8"
|
||||
@ -2834,6 +2862,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.168.tgz#fe24632e79b7ade3f132891afff86caa5e5ce008"
|
||||
integrity sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q==
|
||||
|
||||
"@types/long@*":
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.1.tgz#459c65fa1867dafe6a8f322c4c51695663cc55e9"
|
||||
integrity sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==
|
||||
|
||||
"@types/mime-types@^2.1.0":
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/mime-types/-/mime-types-2.1.0.tgz#9ca52cda363f699c69466c2a6ccdaad913ea7a73"
|
||||
@ -2995,6 +3028,13 @@
|
||||
"@types/prop-types" "*"
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-window@^1.8.5":
|
||||
version "1.8.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-window/-/react-window-1.8.5.tgz#285fcc5cea703eef78d90f499e1457e9b5c02fc1"
|
||||
integrity sha512-V9q3CvhC9Jk9bWBOysPGaWy/Z0lxYcTXLtLipkt2cnRj1JOSFNF7wqGpkScSXMgBwC+fnVRg/7shwgddBG5ICw==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react@*":
|
||||
version "17.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.3.tgz#ba6e215368501ac3826951eef2904574c262cc79"
|
||||
@ -3443,13 +3483,6 @@ abbrev@1:
|
||||
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
|
||||
integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
|
||||
|
||||
abort-controller@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392"
|
||||
integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==
|
||||
dependencies:
|
||||
event-target-shim "^5.0.0"
|
||||
|
||||
accepts@~1.3.7:
|
||||
version "1.3.7"
|
||||
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd"
|
||||
@ -4047,7 +4080,7 @@ arrify@^1.0.0, arrify@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
|
||||
integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=
|
||||
|
||||
arrify@^2.0.0, arrify@^2.0.1:
|
||||
arrify@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa"
|
||||
integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==
|
||||
@ -4057,6 +4090,14 @@ asap@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
|
||||
integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=
|
||||
|
||||
ascli@~1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/ascli/-/ascli-1.0.1.tgz#bcfa5974a62f18e81cabaeb49732ab4a88f906bc"
|
||||
integrity sha1-vPpZdKYvGOgcq660lzKrSoj5Brw=
|
||||
dependencies:
|
||||
colour "~0.7.1"
|
||||
optjs "~3.2.2"
|
||||
|
||||
asn1.js@^5.2.0:
|
||||
version "5.4.1"
|
||||
resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07"
|
||||
@ -5023,11 +5064,6 @@ big.js@^5.2.2:
|
||||
resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
|
||||
integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==
|
||||
|
||||
bignumber.js@^9.0.0:
|
||||
version "9.0.1"
|
||||
resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.1.tgz#8d7ba124c882bfd8e43260c67475518d0689e4e5"
|
||||
integrity sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==
|
||||
|
||||
binary-extensions@^1.0.0:
|
||||
version "1.13.1"
|
||||
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65"
|
||||
@ -5279,11 +5315,6 @@ buffer-crc32@~0.2.3:
|
||||
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
|
||||
integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=
|
||||
|
||||
buffer-equal-constant-time@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
|
||||
integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=
|
||||
|
||||
buffer-fill@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c"
|
||||
@ -5346,6 +5377,13 @@ byte-size@^5.0.1:
|
||||
resolved "https://registry.yarnpkg.com/byte-size/-/byte-size-5.0.1.tgz#4b651039a5ecd96767e71a3d7ed380e48bed4191"
|
||||
integrity sha512-/XuKeqWocKsYa/cBY1YbSJSWWqTi4cFgr9S6OyM7PBaPbr9zvNGwWP33vt0uqGhwDdN+y3yhbXVILEUpnwEWGw==
|
||||
|
||||
bytebuffer@~5:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/bytebuffer/-/bytebuffer-5.0.1.tgz#582eea4b1a873b6d020a48d58df85f0bba6cfddd"
|
||||
integrity sha1-WC7qSxqHO20CCkjVjfhfC7ps/d0=
|
||||
dependencies:
|
||||
long "~3"
|
||||
|
||||
bytes@3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6"
|
||||
@ -5515,7 +5553,7 @@ camelcase-keys@^6.2.2:
|
||||
map-obj "^4.0.0"
|
||||
quick-lru "^4.0.1"
|
||||
|
||||
camelcase@^2.0.0:
|
||||
camelcase@^2.0.0, camelcase@^2.0.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f"
|
||||
integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=
|
||||
@ -5846,6 +5884,15 @@ cli-width@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6"
|
||||
integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==
|
||||
|
||||
cliui@^3.0.3:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d"
|
||||
integrity sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=
|
||||
dependencies:
|
||||
string-width "^1.0.1"
|
||||
strip-ansi "^3.0.1"
|
||||
wrap-ansi "^2.0.0"
|
||||
|
||||
cliui@^4.0.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49"
|
||||
@ -6012,6 +6059,11 @@ colors@~1.1.2:
|
||||
resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63"
|
||||
integrity sha1-FopHAXVran9RoSzgyXv6KMCE7WM=
|
||||
|
||||
colour@~0.7.1:
|
||||
version "0.7.1"
|
||||
resolved "https://registry.yarnpkg.com/colour/-/colour-0.7.1.tgz#9cb169917ec5d12c0736d3e8685746df1cadf778"
|
||||
integrity sha1-nLFpkX7F0SwHNtPoaFdG3xyt93g=
|
||||
|
||||
columnify@^1.5.4:
|
||||
version "1.5.4"
|
||||
resolved "https://registry.yarnpkg.com/columnify/-/columnify-1.5.4.tgz#4737ddf1c7b69a8a7c340570782e947eec8e78bb"
|
||||
@ -7154,13 +7206,6 @@ ecc-jsbn@~0.1.1:
|
||||
jsbn "~0.1.0"
|
||||
safer-buffer "^2.1.0"
|
||||
|
||||
ecdsa-sig-formatter@1.0.11, ecdsa-sig-formatter@^1.0.11:
|
||||
version "1.0.11"
|
||||
resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf"
|
||||
integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==
|
||||
dependencies:
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
editions@^2.2.0:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/editions/-/editions-2.3.1.tgz#3bc9962f1978e801312fbd0aebfed63b49bfe698"
|
||||
@ -7677,11 +7722,6 @@ event-stream@=3.3.4:
|
||||
stream-combiner "~0.0.4"
|
||||
through "~2.3.1"
|
||||
|
||||
event-target-shim@^5.0.0:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789"
|
||||
integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==
|
||||
|
||||
eventemitter3@^3.1.0:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7"
|
||||
@ -8002,11 +8042,6 @@ fast-safe-stringify@^2.0.7:
|
||||
resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743"
|
||||
integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==
|
||||
|
||||
fast-text-encoding@^1.0.0:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz#ec02ac8e01ab8a319af182dae2681213cfe9ce53"
|
||||
integrity sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig==
|
||||
|
||||
fastparse@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.2.tgz#91728c5a5942eced8531283c79441ee4122c35a9"
|
||||
@ -8533,25 +8568,6 @@ gauge@~2.7.3:
|
||||
strip-ansi "^3.0.1"
|
||||
wide-align "^1.1.0"
|
||||
|
||||
gaxios@^4.0.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-4.2.0.tgz#33bdc4fc241fc33b8915a4b8c07cfb368b932e46"
|
||||
integrity sha512-Ms7fNifGv0XVU+6eIyL9LB7RVESeML9+cMvkwGS70xyD6w2Z80wl6RiqiJ9k1KFlJCUTQqFFc8tXmPQfSKUe8g==
|
||||
dependencies:
|
||||
abort-controller "^3.0.0"
|
||||
extend "^3.0.2"
|
||||
https-proxy-agent "^5.0.0"
|
||||
is-stream "^2.0.0"
|
||||
node-fetch "^2.3.0"
|
||||
|
||||
gcp-metadata@^4.2.0:
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-4.2.1.tgz#31849fbcf9025ef34c2297c32a89a1e7e9f2cd62"
|
||||
integrity sha512-tSk+REe5iq/N+K+SK1XjZJUrFPuDqGZVzCy2vocIHIGmPlTGsa8owXMJwGkrXr73NO0AzhPW4MF2DEHz7P2AVw==
|
||||
dependencies:
|
||||
gaxios "^4.0.0"
|
||||
json-bigint "^1.0.0"
|
||||
|
||||
genfun@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/genfun/-/genfun-5.0.0.tgz#9dd9710a06900a5c4a5bf57aca5da4e52fe76537"
|
||||
@ -8822,6 +8838,18 @@ glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, gl
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
glob@^7.0.5:
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
|
||||
integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
|
||||
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"
|
||||
|
||||
global-agent@^2.0.2:
|
||||
version "2.1.12"
|
||||
resolved "https://registry.yarnpkg.com/global-agent/-/global-agent-2.1.12.tgz#e4ae3812b731a9e81cbf825f9377ef450a8e4195"
|
||||
@ -8940,28 +8968,6 @@ globby@^9.2.0:
|
||||
pify "^4.0.1"
|
||||
slash "^2.0.0"
|
||||
|
||||
google-auth-library@^6.1.1:
|
||||
version "6.1.6"
|
||||
resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-6.1.6.tgz#deacdcdb883d9ed6bac78bb5d79a078877fdf572"
|
||||
integrity sha512-Q+ZjUEvLQj/lrVHF/IQwRo6p3s8Nc44Zk/DALsN+ac3T4HY/g/3rrufkgtl+nZ1TW7DNAw5cTChdVp4apUXVgQ==
|
||||
dependencies:
|
||||
arrify "^2.0.0"
|
||||
base64-js "^1.3.0"
|
||||
ecdsa-sig-formatter "^1.0.11"
|
||||
fast-text-encoding "^1.0.0"
|
||||
gaxios "^4.0.0"
|
||||
gcp-metadata "^4.2.0"
|
||||
gtoken "^5.0.4"
|
||||
jws "^4.0.0"
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
google-p12-pem@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/google-p12-pem/-/google-p12-pem-3.0.3.tgz#673ac3a75d3903a87f05878f3c75e06fc151669e"
|
||||
integrity sha512-wS0ek4ZtFx/ACKYF3JhyGe5kzH7pgiQ7J5otlumqR9psmWMYc+U9cErKlCYVYHoUaidXHdZ2xbo34kB+S+24hA==
|
||||
dependencies:
|
||||
node-forge "^0.10.0"
|
||||
|
||||
google-protobuf@3.12.4:
|
||||
version "3.12.4"
|
||||
resolved "https://registry.yarnpkg.com/google-protobuf/-/google-protobuf-3.12.4.tgz#fd89b7e5052cdb35a80f9b455612851d542a5c9f"
|
||||
@ -9073,6 +9079,18 @@ grpc-tools@^1.9.0:
|
||||
dependencies:
|
||||
node-pre-gyp "^0.15.0"
|
||||
|
||||
grpc@^1.24.11:
|
||||
version "1.24.11"
|
||||
resolved "https://registry.yarnpkg.com/grpc/-/grpc-1.24.11.tgz#7039da9f6f22ce35168535a6d5dda618398a5966"
|
||||
integrity sha512-8/AQdFCzCeCDWW3SoaMNp6ccbRvTQEH1O1u1uFtt29eWsg5gSZCJ3m6fbkduEIh3smY7WAPP+LgVJ5n3nZRxcA==
|
||||
dependencies:
|
||||
"@mapbox/node-pre-gyp" "^1.0.4"
|
||||
"@types/bytebuffer" "^5.0.40"
|
||||
lodash.camelcase "^4.3.0"
|
||||
lodash.clone "^4.5.0"
|
||||
nan "^2.13.2"
|
||||
protobufjs "^5.0.3"
|
||||
|
||||
grpc_tools_node_protoc_ts@^4.1.0:
|
||||
version "4.1.5"
|
||||
resolved "https://registry.yarnpkg.com/grpc_tools_node_protoc_ts/-/grpc_tools_node_protoc_ts-4.1.5.tgz#ad540a51867ff407196538d2d6370b27d6d3cfc8"
|
||||
@ -9082,15 +9100,6 @@ grpc_tools_node_protoc_ts@^4.1.0:
|
||||
handlebars "4.7.4"
|
||||
handlebars-helpers "0.10.0"
|
||||
|
||||
gtoken@^5.0.4:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-5.2.1.tgz#4dae1fea17270f457954b4a45234bba5fc796d16"
|
||||
integrity sha512-OY0BfPKe3QnMsY9MzTHTSKn+Vl2l1CcLe6BwDEQj00mbbkl5nyQ/7EUREstg4fQNZ8iYE7br4JJ7TdKeDOPWmw==
|
||||
dependencies:
|
||||
gaxios "^4.0.0"
|
||||
google-p12-pem "^3.0.3"
|
||||
jws "^4.0.0"
|
||||
|
||||
gulp-header@^1.7.1:
|
||||
version "1.8.12"
|
||||
resolved "https://registry.yarnpkg.com/gulp-header/-/gulp-header-1.8.12.tgz#ad306be0066599127281c4f8786660e705080a84"
|
||||
@ -9788,6 +9797,11 @@ inversify@^5.0.1:
|
||||
resolved "https://registry.yarnpkg.com/inversify/-/inversify-5.0.5.tgz#bd1f8e6d8e0f739331acd8ba9bc954635aae0bbf"
|
||||
integrity sha512-60QsfPz8NAU/GZqXu8hJ+BhNf/C/c+Hp0eDc6XMIJTxBiP36AQyyQKpBkOVTLWBFDQWYVHpbbEuIsHu9dLuJDA==
|
||||
|
||||
invert-kv@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6"
|
||||
integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY=
|
||||
|
||||
invert-kv@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02"
|
||||
@ -10491,13 +10505,6 @@ jsesc@~0.5.0:
|
||||
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
|
||||
integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=
|
||||
|
||||
json-bigint@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/json-bigint/-/json-bigint-1.0.0.tgz#ae547823ac0cad8398667f8cd9ef4730f5b01ff1"
|
||||
integrity sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==
|
||||
dependencies:
|
||||
bignumber.js "^9.0.0"
|
||||
|
||||
json-buffer@3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898"
|
||||
@ -10602,23 +10609,6 @@ just-extend@^4.0.2:
|
||||
resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.1.1.tgz#158f1fdb01f128c411dc8b286a7b4837b3545282"
|
||||
integrity sha512-aWgeGFW67BP3e5181Ep1Fv2v8z//iBJfrvyTnq8wG86vEESwmonn1zPBJ0VfmT9CJq2FIT0VsETtrNFm2a+SHA==
|
||||
|
||||
jwa@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/jwa/-/jwa-2.0.0.tgz#a7e9c3f29dae94027ebcaf49975c9345593410fc"
|
||||
integrity sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==
|
||||
dependencies:
|
||||
buffer-equal-constant-time "1.0.1"
|
||||
ecdsa-sig-formatter "1.0.11"
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
jws@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/jws/-/jws-4.0.0.tgz#2d4e8cf6a318ffaa12615e9dec7e86e6c97310f4"
|
||||
integrity sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==
|
||||
dependencies:
|
||||
jwa "^2.0.0"
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
jwt-decode@^3.1.2:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-3.1.2.tgz#3fb319f3675a2df0c2895c8f5e9fa4b67b04ed59"
|
||||
@ -10685,6 +10675,13 @@ lazy-cache@^2.0.1, lazy-cache@^2.0.2:
|
||||
dependencies:
|
||||
set-getter "^0.1.0"
|
||||
|
||||
lcid@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835"
|
||||
integrity sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=
|
||||
dependencies:
|
||||
invert-kv "^1.0.0"
|
||||
|
||||
lcid@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/lcid/-/lcid-2.0.0.tgz#6ef5d2df60e52f82eb228a4c373e8d1f397253cf"
|
||||
@ -10964,6 +10961,11 @@ lodash.camelcase@^4.3.0:
|
||||
resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
|
||||
integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY=
|
||||
|
||||
lodash.clone@^4.5.0:
|
||||
version "4.5.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.clone/-/lodash.clone-4.5.0.tgz#195870450f5a13192478df4bc3d23d2dea1907b6"
|
||||
integrity sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y=
|
||||
|
||||
lodash.clonedeep@^4.5.0:
|
||||
version "4.5.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
|
||||
@ -11115,6 +11117,11 @@ logging-helpers@^1.0.0:
|
||||
isobject "^3.0.0"
|
||||
log-utils "^0.2.1"
|
||||
|
||||
long@~3:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/long/-/long-3.2.0.tgz#d821b7138ca1cb581c172990ef14db200b5c474b"
|
||||
integrity sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=
|
||||
|
||||
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
|
||||
@ -11371,6 +11378,11 @@ mem@^4.0.0:
|
||||
mimic-fn "^2.0.0"
|
||||
p-is-promise "^2.0.0"
|
||||
|
||||
"memoize-one@>=3.1.1 <6":
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e"
|
||||
integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==
|
||||
|
||||
memoize-one@^5.0.0:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.1.1.tgz#047b6e3199b508eaec03504de71229b8eb1d75c0"
|
||||
@ -11724,6 +11736,11 @@ mocha@^7.0.0:
|
||||
yargs-parser "13.1.2"
|
||||
yargs-unparser "1.6.0"
|
||||
|
||||
mockdate@^3.0.5:
|
||||
version "3.0.5"
|
||||
resolved "https://registry.yarnpkg.com/mockdate/-/mockdate-3.0.5.tgz#789be686deb3149e7df2b663d2bc4392bc3284fb"
|
||||
integrity sha512-iniQP4rj1FhBdBYS/+eQv7j1tadJ9lJtdzgOpvsOHng/GbcDh2Fhdeq+ZRldrPYdXvCyfFUmFeEwEGXZB5I/AQ==
|
||||
|
||||
modify-values@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022"
|
||||
@ -11844,6 +11861,11 @@ nan@^2.0.0, nan@^2.12.1, nan@^2.14.0:
|
||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19"
|
||||
integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==
|
||||
|
||||
nan@^2.13.2:
|
||||
version "2.15.0"
|
||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee"
|
||||
integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==
|
||||
|
||||
nanomatch@^1.2.9:
|
||||
version "1.2.13"
|
||||
resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119"
|
||||
@ -11967,16 +11989,11 @@ node-fetch-npm@^2.0.2:
|
||||
json-parse-better-errors "^1.0.0"
|
||||
safe-buffer "^5.1.1"
|
||||
|
||||
node-fetch@^2.3.0, node-fetch@^2.5.0, node-fetch@^2.6.0, node-fetch@^2.6.1:
|
||||
node-fetch@^2.5.0, node-fetch@^2.6.0, node-fetch@^2.6.1:
|
||||
version "2.6.1"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
|
||||
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
|
||||
|
||||
node-forge@^0.10.0:
|
||||
version "0.10.0"
|
||||
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3"
|
||||
integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==
|
||||
|
||||
node-gyp@^5.0.2:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-5.1.1.tgz#eb915f7b631c937d282e33aed44cb7a025f62a3e"
|
||||
@ -12533,6 +12550,11 @@ optionator@^0.9.1:
|
||||
type-check "^0.4.0"
|
||||
word-wrap "^1.2.3"
|
||||
|
||||
optjs@~3.2.2:
|
||||
version "3.2.2"
|
||||
resolved "https://registry.yarnpkg.com/optjs/-/optjs-3.2.2.tgz#69a6ce89c442a44403141ad2f9b370bd5bb6f4ee"
|
||||
integrity sha1-aabOicRCpEQDFBrS+bNwvVu29O4=
|
||||
|
||||
ora@^0.2.3:
|
||||
version "0.2.3"
|
||||
resolved "https://registry.yarnpkg.com/ora/-/ora-0.2.3.tgz#37527d220adcd53c39b73571d754156d5db657a4"
|
||||
@ -12565,6 +12587,13 @@ os-homedir@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
|
||||
integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M=
|
||||
|
||||
os-locale@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9"
|
||||
integrity sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=
|
||||
dependencies:
|
||||
lcid "^1.0.0"
|
||||
|
||||
os-locale@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a"
|
||||
@ -13551,6 +13580,16 @@ proto-list@~1.2.1:
|
||||
resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849"
|
||||
integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=
|
||||
|
||||
protobufjs@^5.0.3:
|
||||
version "5.0.3"
|
||||
resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-5.0.3.tgz#e4dfe9fb67c90b2630d15868249bcc4961467a17"
|
||||
integrity sha512-55Kcx1MhPZX0zTbVosMQEO5R6/rikNXd9b6RQK4KSPcrSIIwoXTtebIczUrXlwaSrbz4x8XUVThGPob1n8I4QA==
|
||||
dependencies:
|
||||
ascli "~1"
|
||||
bytebuffer "~5"
|
||||
glob "^7.0.5"
|
||||
yargs "^3.10.0"
|
||||
|
||||
protoc@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/protoc/-/protoc-1.0.4.tgz#fd0ba07132c459df80c6135889bd5cc92f0afec2"
|
||||
@ -13913,6 +13952,14 @@ react-virtualized@^9.20.0:
|
||||
prop-types "^15.7.2"
|
||||
react-lifecycles-compat "^3.0.4"
|
||||
|
||||
react-window@^1.8.6:
|
||||
version "1.8.6"
|
||||
resolved "https://registry.yarnpkg.com/react-window/-/react-window-1.8.6.tgz#d011950ac643a994118632665aad0c6382e2a112"
|
||||
integrity sha512-8VwEEYyjz6DCnGBsd+MgkD0KJ2/OXFULyDtorIiTz+QzwoP94tBoA7CnbtyXMm+cCeAUER5KJcPtWl9cpKbOBg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.0.0"
|
||||
memoize-one ">=3.1.1 <6"
|
||||
|
||||
react@^16.8.0:
|
||||
version "16.14.0"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d"
|
||||
@ -15703,6 +15750,18 @@ tar@^6.0.2:
|
||||
mkdirp "^1.0.3"
|
||||
yallist "^4.0.0"
|
||||
|
||||
tar@^6.1.0:
|
||||
version "6.1.11"
|
||||
resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621"
|
||||
integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==
|
||||
dependencies:
|
||||
chownr "^2.0.0"
|
||||
fs-minipass "^2.0.0"
|
||||
minipass "^3.0.0"
|
||||
minizlib "^2.1.1"
|
||||
mkdirp "^1.0.3"
|
||||
yallist "^4.0.0"
|
||||
|
||||
temp-dir@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-1.0.0.tgz#0a7c0ea26d3a39afa7e0ebea9c1fc0bc4daa011d"
|
||||
@ -16815,6 +16874,11 @@ winchan@^0.2.2:
|
||||
resolved "https://registry.yarnpkg.com/winchan/-/winchan-0.2.2.tgz#6766917b88e5e1cb75f455ffc7cc13f51e5c834e"
|
||||
integrity sha512-pvN+IFAbRP74n/6mc6phNyCH8oVkzXsto4KCHPJ2AScniAnA1AmeLI03I2BzjePpaClGSI4GUMowzsD3qz5PRQ==
|
||||
|
||||
window-size@^0.1.4:
|
||||
version "0.1.4"
|
||||
resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.4.tgz#f8e1aa1ee5a53ec5bf151ffa09742a6ad7697876"
|
||||
integrity sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY=
|
||||
|
||||
windows-release@^3.1.0:
|
||||
version "3.3.3"
|
||||
resolved "https://registry.yarnpkg.com/windows-release/-/windows-release-3.3.3.tgz#1c10027c7225743eec6b89df160d64c2e0293999"
|
||||
@ -17030,7 +17094,7 @@ xterm@~4.11.0:
|
||||
resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.11.0.tgz#d7dabc7af5299579e4663fedf2b3a179af9aaff9"
|
||||
integrity sha512-NeJH909WTO2vth/ZlC0gkP3AGzupbvVHVlmtrpBw56/sGFXaF9bNdKgqKa3tf8qbGvXMzL2JhCcHVklqFztIRw==
|
||||
|
||||
y18n@^3.2.1:
|
||||
y18n@^3.2.0, y18n@^3.2.1:
|
||||
version "3.2.2"
|
||||
resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.2.tgz#85c901bd6470ce71fc4bb723ad209b70f7f28696"
|
||||
integrity sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==
|
||||
@ -17173,6 +17237,19 @@ yargs@^15.3.1:
|
||||
y18n "^4.0.0"
|
||||
yargs-parser "^18.1.2"
|
||||
|
||||
yargs@^3.10.0:
|
||||
version "3.32.0"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.32.0.tgz#03088e9ebf9e756b69751611d2a5ef591482c995"
|
||||
integrity sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU=
|
||||
dependencies:
|
||||
camelcase "^2.0.1"
|
||||
cliui "^3.0.3"
|
||||
decamelize "^1.1.1"
|
||||
os-locale "^1.4.0"
|
||||
string-width "^1.0.1"
|
||||
window-size "^0.1.4"
|
||||
y18n "^3.2.0"
|
||||
|
||||
yauzl@^2.10.0, yauzl@^2.4.2:
|
||||
version "2.10.0"
|
||||
resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"
|
||||
|
Loading…
x
Reference in New Issue
Block a user