@visulima/pail
Version:
Highly configurable Logger for Node.js, Edge and Browser.
443 lines (433 loc) • 13.1 kB
JavaScript
'use strict';
Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: 'Module' } });
const process$1 = require('node:process');
const formatLabel = require('./packem_shared/format-label-CiYBxDGf.cjs');
const node_string_decoder = require('node:string_decoder');
const pail_browser = require('./packem_shared/pail.browser-O53Q_cus.cjs');
const inspector = require('@visulima/inspector');
const constants = require('./packem_shared/constants-DHfYGxxG.cjs');
const writeStream = require('./packem_shared/write-stream-CkNf2Ju8.cjs');
const MessageFormatterProcessor = require('./packem_shared/MessageFormatterProcessor-t4t6CpzV.cjs');
const PrettyReporter = require('./packem_shared/PrettyReporter-K-EU3qWb.cjs');
var __defProp$5 = Object.defineProperty;
var __name$5 = (target, value) => __defProp$5(target, "name", { value, configurable: true });
class InteractiveManager {
static {
__name$5(this, "InteractiveManager");
}
#stream;
#isActive = false;
#isSuspended = false;
#lastLength = 0;
#outside = 0;
constructor(stdout, stderr) {
this.#stream = {
stderr,
stdout
};
}
/**
* Last printed rows count
*/
get lastLength() {
return this.#lastLength;
}
/**
* Rows count outside editable area
*/
get outside() {
return this.#outside;
}
/**
* Hook activity status
*/
get isHooked() {
return this.#isActive;
}
/**
* Suspend status for active hooks
*/
get isSuspended() {
return this.#isSuspended;
}
/**
* Removes from the bottom of output up the specified count of lines
*
* @param stream - Stream to remove lines from
* @param count - lines count to remove
*/
erase(stream, count = this.#lastLength) {
if (this.#stream[stream] === void 0) {
throw new TypeError(`Stream "${stream}" is not available`);
}
this.#stream[stream].erase(count);
}
/**
* Hook stdout and stderr streams
* @returns Success status
*/
hook() {
if (!this.#isActive) {
Object.values(this.#stream).forEach((hook) => hook.active());
this._clear(true);
}
return this.#isActive;
}
/**
* Resume suspend hooks
*
* @param stream - Stream to resume
* @param eraseRowCount - erase output rows count
*/
resume(stream, eraseRowCount) {
if (this.#isSuspended) {
this.#isSuspended = false;
if (eraseRowCount) {
this.erase(stream, eraseRowCount);
}
this.#lastLength = 0;
Object.values(this.#stream).forEach((hook) => hook.active());
}
}
/**
* Suspend active hooks for external output
*
* @param stream - Stream to suspend
* @param erase - erase output
*/
suspend(stream, erase = true) {
if (!this.#isSuspended) {
this.#isSuspended = true;
if (erase) {
this.erase(stream);
}
Object.values(this.#stream).forEach((hook) => hook.renew());
}
}
/**
* Unhooks both stdout and stderr streams and print their story of logs
*
* @param separateHistory - If `true`, will add an empty line to the history output for individual recorded lines and console logs
*
* @returns Success status
*/
unhook(separateHistory = true) {
if (this.#isActive) {
Object.values(this.#stream).forEach((hook) => hook.inactive(separateHistory));
this._clear();
}
return !this.#isActive;
}
/**
* Update output
*
* @param stream - Stream to write to
* @param rows - Text lines to write to standard output
* @param from - Index of the line starting from which the contents of the terminal are being overwritten
*/
update(stream, rows, from = 0) {
if (rows.length > 0) {
if (this.#stream[stream] === void 0) {
throw new TypeError(`Stream "${stream}" is not available`);
}
const hook = this.#stream[stream];
const { columns: width, rows: height } = formatLabel.terminalSize();
const position = from > height ? height - 1 : Math.max(0, Math.min(height - 1, from));
const actualLength = this.lastLength - position;
const outside = Math.max(actualLength - height, this.outside);
let output = rows.reduce(
(accumulator, row) => [
...accumulator,
formatLabel.wrapAnsi(row, width, {
hard: true,
trim: false,
wordWrap: true
})
],
[]
);
if (height <= actualLength) {
hook.erase(height);
if (position < outside) {
output = output.slice(outside - position + 1);
}
} else if (actualLength) {
hook.erase(actualLength);
}
hook.write(output.join("\n") + "\n");
this.#lastLength = outside ? outside + output.length + 1 : output.length;
this.#outside = Math.max(this.lastLength - height, this.outside);
}
}
_clear(status = false) {
this.#isActive = status;
this.#lastLength = 0;
this.#outside = 0;
}
}
var __defProp$4 = Object.defineProperty;
var __name$4 = (target, value) => __defProp$4(target, "name", { value, configurable: true });
const ESC = "\x1B[";
const eraseScreen = ESC + "2J";
const eraseLine = ESC + "2K";
const cursorLeft = ESC + "G";
const cursorUp = /* @__PURE__ */ __name$4((count = 1) => ESC + count + "A", "cursorUp");
const clearTerminal = process.platform === "win32" ? `${eraseScreen}${ESC}0f` : (
// 1. Erases the screen (Only done in case `2` is not supported)
// 2. Erases the whole screen including scrollback buffer
// 3. Moves cursor to the top-left position
// More info: https://www.real-world-systems.com/docs/ANSIcode.html
`${eraseScreen}${ESC}3J${ESC}H`
);
const cursorHide = ESC + "?25l";
const cursorShow = ESC + "?25h";
const eraseLines = /* @__PURE__ */ __name$4((count) => {
let clear = "";
for (let index = 0; index < count; index++) {
clear += eraseLine + (index < count - 1 ? cursorUp() : "");
}
if (count) {
clear += cursorLeft;
}
return clear;
}, "eraseLines");
var __defProp$3 = Object.defineProperty;
var __name$3 = (target, value) => __defProp$3(target, "name", { value, configurable: true });
class InteractiveStreamHook {
static {
__name$3(this, "InteractiveStreamHook");
}
static DRAIN = true;
#decoder = new node_string_decoder.StringDecoder();
#history = [];
#method;
#stream;
constructor(stream) {
this.#method = stream.write;
this.#stream = stream;
}
active() {
this.write(cursorHide);
this.#stream.write = (data, ...arguments_) => {
const callback = arguments_.at(-1);
this.#history.push(
this.#decoder.write(
typeof data === "string" ? Buffer.from(data, typeof arguments_[0] === "string" ? arguments_[0] : void 0) : Buffer.from(data)
)
);
if (typeof callback === "function") {
callback();
}
return InteractiveStreamHook.DRAIN;
};
}
erase(count) {
if (count > 0) {
this.write(eraseLines(count + 1));
}
}
inactive(separateHistory = false) {
if (this.#history.length > 0) {
if (separateHistory) {
this.write("\n");
}
this.#history.forEach((element) => {
this.write(element);
});
this.#history = [];
}
this.renew();
}
renew() {
this.#stream.write = this.#method;
this.write(cursorShow);
}
write(message) {
this.#method.apply(this.#stream, [message]);
}
}
var __defProp$2 = Object.defineProperty;
var __name$2 = (target, value) => __defProp$2(target, "name", { value, configurable: true });
class RawReporter {
static {
__name$2(this, "RawReporter");
}
#stdout;
#stderr;
#interactiveManager;
#interactive = false;
#inspectOptions;
constructor(inspectOptions = {}) {
this.#stdout = process$1.stdout;
this.#stderr = process$1.stderr;
this.#inspectOptions = inspectOptions;
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
setStdout(stdout_) {
this.#stdout = stdout_;
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
setStderr(stderr_) {
this.#stderr = stderr_;
}
setInteractiveManager(manager) {
this.#interactiveManager = manager;
}
setIsInteractive(interactive) {
this.#interactive = interactive;
}
log(meta) {
const { context, groups, message, type } = meta;
const items = [];
if (message !== constants.EMPTY_SYMBOL) {
const formattedMessage = typeof message === "string" ? message : inspector.inspect(message, this.#inspectOptions);
items.push(formattedMessage);
}
if (context) {
items.push(
...context.map((value) => {
if (typeof value === "object") {
return " " + inspector.inspect(value, this.#inspectOptions);
}
return " " + value;
})
);
}
const streamType = ["error", "trace", "warn"].includes(type.level) ? "stderr" : "stdout";
const stream = streamType === "stderr" ? this.#stderr : this.#stdout;
const groupSpaces = groups.map(() => " ").join("");
if (this.#interactive && this.#interactiveManager !== void 0 && stream.isTTY) {
this.#interactiveManager.update(streamType, (groupSpaces + items.join("")).split("\n"), 0);
} else {
writeStream.writeStream(groupSpaces + items.join(""), stream);
}
}
}
var __defProp$1 = Object.defineProperty;
var __name$1 = (target, value) => __defProp$1(target, "name", { value, configurable: true });
class PailServerImpl extends pail_browser.PailBrowserImpl {
constructor(options) {
const { interactive, rawReporter, reporters, stderr, stdout, ...rest } = options;
super(rest);
this.options = options;
this.interactive = interactive ?? false;
this.stdout = stdout;
this.stderr = stderr;
if (this.interactive) {
this.interactiveManager = new InteractiveManager(new InteractiveStreamHook(this.stdout), new InteractiveStreamHook(this.stderr));
}
if (Array.isArray(reporters)) {
this.registerReporters(reporters);
}
this.rawReporter = this.extendReporter(options.rawReporter ?? new RawReporter());
}
static {
__name$1(this, "PailServerImpl");
}
stdout;
stderr;
interactiveManager;
interactive;
// @ts-expect-error - this returns a different type
scope(...name) {
if (name.length === 0) {
throw new Error("No scope name was defined.");
}
this.scopeName = name.flat();
return this;
}
getInteractiveManager() {
return this.interactiveManager;
}
wrapStd() {
this._wrapStream(this.stdout, "log");
this._wrapStream(this.stderr, "log");
}
restoreStd() {
this._restoreStream(this.stdout);
this._restoreStream(this.stderr);
}
wrapAll() {
this.wrapConsole();
this.wrapStd();
}
restoreAll() {
this.restoreConsole();
this.restoreStd();
}
clear() {
this.stdout.write(clearTerminal);
this.stderr.write(clearTerminal);
}
extendReporter(reporter) {
if (typeof reporter.setStdout === "function") {
reporter.setStdout(this.stdout);
}
if (typeof reporter.setStderr === "function") {
reporter.setStderr(this.stderr);
}
if (typeof reporter.setLoggerTypes === "function") {
reporter.setLoggerTypes(this.types);
}
if (typeof reporter.setStringify === "function") {
reporter.setStringify(this.stringify);
}
if (typeof reporter.setIsInteractive === "function") {
reporter.setIsInteractive(this.interactive);
}
if (this.interactive && typeof reporter.setInteractiveManager === "function") {
reporter.setInteractiveManager(this.interactiveManager);
}
return reporter;
}
_wrapStream(stream, type) {
if (!stream) {
return;
}
if (!stream.__write) {
stream.__write = stream.write;
}
stream.write = (data) => {
this[type](String(data).trim());
};
}
// eslint-disable-next-line class-methods-use-this
_restoreStream(stream) {
if (!stream) {
return;
}
if (stream.__write) {
stream.write = stream.__write;
delete stream.__write;
}
}
}
const PailServer = PailServerImpl;
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
const _getDefaultLogLevel = /* @__PURE__ */ __name(() => {
if (process$1.env.NODE_ENV === "debug" || process$1.env.DEBUG !== void 0) {
return "debug";
}
if (process$1.env.NODE_ENV === "test") {
return "warning";
}
return "informational";
}, "_getDefaultLogLevel");
const createPail = /* @__PURE__ */ __name((options) => {
let logLevel = _getDefaultLogLevel();
if (process$1.env.PAIL_LOG_LEVEL !== void 0) {
logLevel = process$1.env.PAIL_LOG_LEVEL;
}
return new PailServer({
logLevel,
processors: [new MessageFormatterProcessor()],
reporters: [new PrettyReporter.PrettyReporter()],
stderr: process$1.stderr,
stdout: process$1.stdout,
...options
});
}, "createPail");
const pail = createPail();
exports.createPail = createPail;
exports.pail = pail;