- 37개 파일 IP → zioinfo.co.kr 치환 (소스/매뉴얼/설정/하네스) - Manager DrConsole/NetworkConsole/CsapConsole 빌드 + /var/www/manager/ 배포 - 테스트: Manager HTTP 200, ITSM 신규 API 7개 전체 200 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
185 lines
5.9 KiB
Plaintext
185 lines
5.9 KiB
Plaintext
/**
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* @flow
|
|
* @format
|
|
* @oncall react_native
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
const throttle = require('lodash.throttle');
|
|
const readline = require('readline');
|
|
const tty = require('tty');
|
|
const util = require('util');
|
|
|
|
type UnderlyingStream = net$Socket | stream$Writable;
|
|
|
|
/**
|
|
* Clear some text that was previously printed on an interactive stream,
|
|
* without trailing newline character (so we have to move back to the
|
|
* beginning of the line).
|
|
*/
|
|
function clearStringBackwards(stream: tty.WriteStream, str: string): void {
|
|
readline.moveCursor(stream, -stream.columns, 0);
|
|
readline.clearLine(stream, 0);
|
|
let lineCount = (str.match(/\n/g) || []).length;
|
|
while (lineCount > 0) {
|
|
readline.moveCursor(stream, 0, -1);
|
|
readline.clearLine(stream, 0);
|
|
--lineCount;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Cut a string into an array of string of the specific maximum size. A newline
|
|
* ends a chunk immediately (it's not included in the "." RexExp operator), and
|
|
* is not included in the result.
|
|
* When counting we should ignore non-printable characters. In particular the
|
|
* ANSI escape sequences (regex: /\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?m/)
|
|
* (Not an exhaustive match, intended to match ANSI color escapes)
|
|
* https://en.wikipedia.org/wiki/ANSI_escape_code
|
|
*/
|
|
function chunkString(str: string, size: number): Array<string> {
|
|
const ANSI_COLOR = '\x1B\\[([0-9]{1,2}(;[0-9]{1,2})?)?m';
|
|
const SKIP_ANSI = `(?:${ANSI_COLOR})*`;
|
|
return str.match(new RegExp(`(?:${SKIP_ANSI}.){1,${size}}`, 'g')) || [];
|
|
}
|
|
|
|
/**
|
|
* Get the stream as a TTY if it effectively looks like a valid TTY.
|
|
*/
|
|
function getTTYStream(stream: UnderlyingStream): ?tty.WriteStream {
|
|
if (
|
|
stream instanceof tty.WriteStream &&
|
|
stream.isTTY &&
|
|
stream.columns >= 1
|
|
) {
|
|
return stream;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* We don't just print things to the console, sometimes we also want to show
|
|
* and update progress. This utility just ensures the output stays neat: no
|
|
* missing newlines, no mangled log lines.
|
|
*
|
|
* const terminal = Terminal.default;
|
|
* terminal.status('Updating... 38%');
|
|
* terminal.log('warning: Something happened.');
|
|
* terminal.status('Updating, done.');
|
|
* terminal.persistStatus();
|
|
*
|
|
* The final output:
|
|
*
|
|
* warning: Something happened.
|
|
* Updating, done.
|
|
*
|
|
* Without the status feature, we may get a mangled output:
|
|
*
|
|
* Updating... 38%warning: Something happened.
|
|
* Updating, done.
|
|
*
|
|
* This is meant to be user-readable and TTY-oriented. We use stdout by default
|
|
* because it's more about status information than diagnostics/errors (stderr).
|
|
*
|
|
* Do not add any higher-level functionality in this class such as "warning" and
|
|
* "error" printers, as it is not meant for formatting/reporting. It has the
|
|
* single responsibility of handling status messages.
|
|
*/
|
|
class Terminal {
|
|
_logLines: Array<string>;
|
|
_nextStatusStr: string;
|
|
_scheduleUpdate: () => void;
|
|
_statusStr: string;
|
|
_stream: UnderlyingStream;
|
|
|
|
constructor(stream: UnderlyingStream) {
|
|
this._logLines = [];
|
|
this._nextStatusStr = '';
|
|
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
|
|
this._scheduleUpdate = throttle(this._update, 33);
|
|
this._statusStr = '';
|
|
this._stream = stream;
|
|
}
|
|
|
|
/**
|
|
* Clear and write the new status, logging in bulk in-between. Doing this in a
|
|
* throttled way (in a different tick than the calls to `log()` and
|
|
* `status()`) prevents us from repeatedly rewriting the status in case
|
|
* `terminal.log()` is called several times.
|
|
*/
|
|
_update(): void {
|
|
const {_statusStr, _stream} = this;
|
|
const ttyStream = getTTYStream(_stream);
|
|
if (_statusStr === this._nextStatusStr && this._logLines.length === 0) {
|
|
return;
|
|
}
|
|
if (ttyStream != null) {
|
|
clearStringBackwards(ttyStream, _statusStr);
|
|
}
|
|
this._logLines.forEach(line => {
|
|
_stream.write(line);
|
|
_stream.write('\n');
|
|
});
|
|
this._logLines = [];
|
|
if (ttyStream != null) {
|
|
this._nextStatusStr = chunkString(
|
|
this._nextStatusStr,
|
|
ttyStream.columns,
|
|
).join('\n');
|
|
_stream.write(this._nextStatusStr);
|
|
}
|
|
this._statusStr = this._nextStatusStr;
|
|
}
|
|
|
|
/**
|
|
* Shows some text that is meant to be overriden later. Return the previous
|
|
* status that was shown and is no more. Calling `status()` with no argument
|
|
* removes the status altogether. The status is never shown in a
|
|
* non-interactive terminal: for example, if the output is redirected to a
|
|
* file, then we don't care too much about having a progress bar.
|
|
*/
|
|
status(format: string, ...args: Array<mixed>): string {
|
|
const {_nextStatusStr} = this;
|
|
this._nextStatusStr = util.format(format, ...args);
|
|
this._scheduleUpdate();
|
|
return _nextStatusStr;
|
|
}
|
|
|
|
/**
|
|
* Similar to `console.log`, except it moves the status/progress text out of
|
|
* the way correctly. In non-interactive terminals this is the same as
|
|
* `console.log`.
|
|
*/
|
|
log(format: string, ...args: Array<mixed>): void {
|
|
this._logLines.push(util.format(format, ...args));
|
|
this._scheduleUpdate();
|
|
}
|
|
|
|
/**
|
|
* Log the current status and start from scratch. This is useful if the last
|
|
* status was the last one of a series of updates.
|
|
*/
|
|
persistStatus(): void {
|
|
this.log(this._nextStatusStr);
|
|
this._nextStatusStr = '';
|
|
}
|
|
|
|
flush(): void {
|
|
// Useful if you're going to start calling console.log/console.error directly
|
|
// again; otherwise you could end up with mangled output when the queued
|
|
// update starts writing to stream after a delay.
|
|
/* $FlowFixMe(>=0.99.0 site=react_native_fb) This comment suppresses an
|
|
* error found when Flow v0.99 was deployed. To see the error, delete this
|
|
* comment and run Flow. */
|
|
this._scheduleUpdate.flush();
|
|
}
|
|
}
|
|
|
|
module.exports = Terminal;
|