UNPKG

@visulima/pail

Version:

Highly configurable Logger for Node.js, Edge and Browser.

443 lines (433 loc) 13.1 kB
'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;