diff --git a/.gitignore b/.gitignore index cd17637..ba37117 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ node_modules/ coverage/ *.map +dist/ typescript/*.js test/typescript/*.js package-lock.json diff --git a/dist/index.d.ts b/dist/index.d.ts deleted file mode 100644 index 7e7f95d..0000000 --- a/dist/index.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './themes'; -export * from './monitor'; -export * from './types'; diff --git a/dist/index.js b/dist/index.js deleted file mode 100644 index b68aa27..0000000 --- a/dist/index.js +++ /dev/null @@ -1,8 +0,0 @@ -"use strict"; -function __export(m) { - for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; -} -Object.defineProperty(exports, "__esModule", { value: true }); -__export(require("./themes")); -__export(require("./monitor")); -__export(require("./types")); diff --git a/dist/monitor.d.ts b/dist/monitor.d.ts deleted file mode 100644 index 9c19b4d..0000000 --- a/dist/monitor.d.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { ITheme, ThemeName } from './themes'; -import { EventName, IClient, IEventContext, IInitOptions } from './types'; -export declare class Monitor { - /** - * Attachment state. - */ - private state; - /** - * Attached Initialization Options object. - */ - private initOptions; - /** - * Current Color Theme. - */ - cct: ITheme; - /** - * Detailed output flag. - */ - detailed: boolean; - constructor(); - connect(client: IClient, dc: any, useCount: number): void; - disconnect(client: IClient, dc: any): void; - query(e: IEventContext): void; - task(e: IEventContext): void; - transact(e: IEventContext): void; - error(err: any, e: IEventContext): void; - attach(initOptions: IInitOptions, options?: { - events?: EventName[]; - override?: boolean; - }): void; - isAttached(): boolean; - detach(): void; - setTheme(t: ITheme | ThemeName): void; - setLog(log: any): void; - private print; -} diff --git a/dist/monitor.js b/dist/monitor.js deleted file mode 100644 index 4ff410d..0000000 --- a/dist/monitor.js +++ /dev/null @@ -1,363 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const themes_1 = require("./themes"); -const types_1 = require("./types"); -const utils_1 = require("./utils"); -const allThemes = new themes_1.Themes(); -// reusable error messages; -const errors = { - redirectParams(event) { - return `Invalid event '${event}' redirect parameters.`; - } -}; -// 9 spaces for the time offset: -const timeGap = ' '.repeat(9); -class Monitor { - constructor() { - this.state = {}; - this.initOptions = null; - this.detailed = true; - this.cct = null; - // this.cct = allThemes.dimmed; - } - /////////////////////////////////////////////// - // 'connect' event handler; - // parameters: - // - client - the only parameter for the event; - // - detailed - optional, indicates that user@database is to be reported; - connect(client, dc, useCount) { - const event = 'connect'; - const cp = client ? client.connectionParameters : null; - if (!cp) { - throw new TypeError(errors.redirectParams(event)); - } - if (this.detailed) { - const countInfo = typeof useCount === 'number' ? this.cct.cn('; useCount: ') + this.cct.value(useCount) : ''; - this.print(null, event, this.cct.cn('connect(') + this.cct.value(cp.user + '@' + cp.database) + this.cct.cn(')') + countInfo); - } - else { - this.print(null, event, this.cct.cn('connect')); - } - } - /////////////////////////////////////////////// - // 'connect' event handler; - // parameters: - // - client - the only parameter for the event; - // - detailed - optional, indicates that user@database is to be reported; - disconnect(client, dc) { - const event = 'disconnect'; - const cp = client ? client.connectionParameters : null; - if (!cp) { - throw new TypeError(errors.redirectParams(event)); - } - if (this.detailed) { - // report user@database details; - this.print(null, event, this.cct.cn('disconnect(') + this.cct.value(cp.user + '@' + cp.database) + this.cct.cn(')')); - } - else { - this.print(null, event, this.cct.cn('disconnect')); - } - } - /////////////////////////////////////////////// - // 'query' event handler; - // parameters: - // - e - the only parameter for the event; - // - detailed - optional, indicates that both task and transaction context are to be reported; - query(e) { - const event = 'query'; - if (!e || !('query' in e)) { - throw new TypeError(errors.redirectParams(event)); - } - let q = e.query; - let special, prepared; - if (typeof q === 'string') { - const qSmall = q.toLowerCase(); - const verbs = ['begin', 'commit', 'rollback', 'savepoint', 'release']; - for (let i = 0; i < verbs.length; i++) { - if (qSmall.indexOf(verbs[i]) === 0) { - special = true; - break; - } - } - } - else { - if (typeof q === 'object' && ('name' in q || 'text' in q)) { - // Either a Prepared Statement or a Parameterized Query; - prepared = true; - const msg = []; - if ('name' in q) { - msg.push(this.cct.query('name=') + '"' + this.cct.value(q.name) + '"'); - } - if ('text' in q) { - msg.push(this.cct.query('text=') + '"' + this.cct.value(q.text) + '"'); - } - if (Array.isArray(q.values) && q.values.length) { - msg.push(this.cct.query('values=') + this.cct.value(utils_1.toJson(q.values))); - } - q = msg.join(', '); - } - } - let qText = q; - if (!prepared) { - qText = special ? this.cct.special(q) : this.cct.query(q); - } - if (this.detailed && e.ctx) { - // task/transaction details are to be reported; - const sTag = utils_1.getTagName(e), prefix = e.ctx.isTX ? 'tx' : 'task'; - if (sTag) { - qText = this.cct.tx(prefix + '(') + this.cct.value(sTag) + this.cct.tx('): ') + qText; - } - else { - qText = this.cct.tx(prefix + ': ') + qText; - } - } - this.print(e, event, qText); - if (e.params) { - let p = e.params; - if (typeof p !== 'string') { - p = utils_1.toJson(p); - } - this.print(e, event, timeGap + this.cct.paramTitle('params: ') + this.cct.value(p), true); - } - } - /////////////////////////////////////////////// - // 'task' event handler; - // parameters: - // - e - the only parameter for the event; - task(e) { - const event = 'task'; - if (!e || !e.ctx) { - throw new TypeError(errors.redirectParams(event)); - } - let msg = this.cct.tx('task'); - const sTag = utils_1.getTagName(e); - if (sTag) { - msg += this.cct.tx('(') + this.cct.value(sTag) + this.cct.tx(')'); - } - if (e.ctx.finish) { - msg += this.cct.tx('/end'); - } - else { - msg += this.cct.tx('/start'); - } - if (e.ctx.finish) { - const duration = utils_1.formatDuration(e.ctx.finish.getDate() - e.ctx.start.getDate()); - msg += this.cct.tx('; duration: ') + this.cct.value(duration) + this.cct.tx(', success: ') + this.cct.value(!!e.ctx.success); - } - this.print(e, event, msg); - } - /////////////////////////////////////////////// - // 'transact' event handler; - // parameters: - // - e - the only parameter for the event; - transact(e) { - const event = 'transact'; - if (!e || !e.ctx) { - throw new TypeError(errors.redirectParams(event)); - } - let msg = this.cct.tx('tx'); - const sTag = utils_1.getTagName(e); - if (sTag) { - msg += this.cct.tx('(') + this.cct.value(sTag) + this.cct.tx(')'); - } - if (e.ctx.finish) { - msg += this.cct.tx('/end'); - } - else { - msg += this.cct.tx('/start'); - } - if (e.ctx.finish) { - const duration = utils_1.formatDuration(e.ctx.finish.getDate() - e.ctx.start.getDate()); - msg += this.cct.tx('; duration: ') + this.cct.value(duration) + this.cct.tx(', success: ') + this.cct.value(!!e.ctx.success); - } - this.print(e, event, msg); - } - /////////////////////////////////////////////// - // 'error' event handler; - // parameters: - // - err - error-text parameter for the original event; - // - e - error context object for the original event; - // - detailed - optional, indicates that transaction context is to be reported; - error(err, e) { - const event = 'error'; - const errMsg = err ? (err.message || err) : null; - if (!e || typeof e !== 'object') { - throw new TypeError(errors.redirectParams(event)); - } - this.print(e, event, this.cct.errorTitle('error: ') + this.cct.error(errMsg)); - let q = e.query; - if (q !== undefined && typeof q !== 'string') { - if (typeof q === 'object' && ('name' in q || 'text' in q)) { - const tmp = {}; - const names = ['name', 'text', 'values']; - names.forEach(n => { - if (n in q) { - tmp[n] = q[n]; - } - }); - q = tmp; - } - q = utils_1.toJson(q); - } - if (e.cn) { - // a connection issue; - this.print(e, event, timeGap + this.cct.paramTitle('connection: ') + this.cct.value(utils_1.toJson(e.cn)), true); - } - else { - if (q !== undefined) { - if (this.detailed && e.ctx) { - // transaction details are to be reported; - const sTag = utils_1.getTagName(e), prefix = e.ctx.isTX ? 'tx' : 'task'; - if (sTag) { - this.print(e, event, timeGap + this.cct.paramTitle(prefix + '(') + this.cct.value(sTag) + this.cct.paramTitle('): ') + this.cct.value(q), true); - } - else { - this.print(e, event, timeGap + this.cct.paramTitle(prefix + ': ') + this.cct.value(q), true); - } - } - else { - this.print(e, event, timeGap + this.cct.paramTitle('query: ') + this.cct.value(q), true); - } - } - } - if (e.params) { - this.print(e, event, timeGap + this.cct.paramTitle('params: ') + this.cct.value(utils_1.toJson(e.params)), true); - } - } - ///////////////////////////////////////////////////////// - // attaches to pg-promise initialization options object: - // - options - the options object; - // - events - optional, list of events to attach to; - // - override - optional, overrides the existing event handlers; - attach(initOptions, options) { - if (this.initOptions) { - throw new Error('Repeated attachments not supported, must call detach first.'); - } - if (!options || typeof options !== 'object') { - throw new TypeError(`Initialization object 'options' must be specified.`); - } - let events = options && options.events || []; - const override = options && options.override; - const hasFilter = Array.isArray(events); - if (!utils_1.isNull(events) && !hasFilter) { - throw new TypeError(`Invalid parameter 'events' passed.`); - } - events = events || []; - this.initOptions = initOptions; - const self = this; - const attach = (e) => { - if (!hasFilter || events.indexOf(e) !== -1) { - if (typeof initOptions[e] === 'function' && !override) { - self.state[e] = initOptions[e]; - initOptions[e] = function () { - if (typeof self.state[e] === 'function') { - self.state[e].apply(null, arguments); // call the original handler; - } - self[e].apply(null, arguments); - }; - } - else { - initOptions[e] = self[e]; - } - } - }; - types_1.eventNames.forEach(e => { - attach(e); - }); - } - isAttached() { - return !!this.initOptions; - } - ///////////////////////////////////////////////////////// - // detaches from all events to which was attached during - // the last `attach` call. - detach() { - if (!this.initOptions) { - throw new Error('Event monitor not attached.'); - } - const init = this.initOptions && this.initOptions; - // SEE: https://stackoverflow.com/questions/58380515/copying-a-same-key-property-error-in-typescript - types_1.eventNames.forEach(e => { - if (this.state[e]) { - init[e] = this.state[e]; - delete this.state[e]; - } - else { - delete init[e]; - } - }); - this.initOptions = null; - } - ////////////////////////////////////////////////////////////////// - // sets a new theme either by its name (from the predefined ones), - // or as a new object with all colors specified. - setTheme(t) { - const err = new TypeError('Invalid theme parameter specified.'); - if (!t) { - throw err; - } - if (typeof t === 'string') { - if (t in themes_1.Themes) { - this.cct = allThemes[t]; - } - else { - throw new TypeError(`Theme '${t}' does not exist.`); - } - } - else { - if (typeof t === 'object') { - themes_1.themeAttrs.forEach(a => { - if (!utils_1.hasOwnProperty(t, a)) { - throw new TypeError(`Invalid theme: property '${a}' is missing.`); - } - if (typeof t[a] !== 'function') { - throw new TypeError(`Theme property '${a}' is invalid.`); - } - }); - this.cct = t; - } - else { - throw err; - } - } - } - ////////////////////////////////////////////////////////////////// - // sets a custom log function to support the function attribution in Typescript. - setLog(log) { - module.exports.log = typeof log === 'function' ? log : null; - } - print(e, event, text, isExtraLine) { - let t = null, s = text; - if (!isExtraLine) { - t = new Date(); - s = this.cct.time(utils_1.formatTime(t)) + ' ' + text; - } - let display = true; - const log = module.exports.log; - if (typeof log === 'function') { - // the client expects log notifications; - const info = { - event: event, - time: t, - text: utils_1.removeColors(text).trim(), - ctx: null, - display: undefined - }; - if (e && e.ctx) { - info.ctx = e.ctx; - } - log(utils_1.removeColors(s), info); - display = info.display === undefined || !!info.display; - } - // istanbul ignore next: cannot test the next - // block without writing things into the console; - if (display) { - if (!process.stdout.isTTY) { - s = utils_1.removeColors(s); - } - // eslint-disable-next-line - console.log(s); - } - } -} -exports.Monitor = Monitor; diff --git a/dist/themes.d.ts b/dist/themes.d.ts deleted file mode 100644 index 763d208..0000000 --- a/dist/themes.d.ts +++ /dev/null @@ -1,71 +0,0 @@ -import * as color from 'cli-color'; -export interface ITheme { - /** - * timestamp color; - */ - time: color.Format; - /** - * color for any value; - */ - value: color.Format; - /** - * connect/disconnect color; - */ - cn: color.Format; - /** - * transaction start/finish color; - */ - tx: color.Format; - /** - * color for parameter titles: params/query/tx; - */ - paramTitle: color.Format; - /** - * color for error title: 'error'; - */ - errorTitle: color.Format; - /** - * color for regular queries; - */ - query: color.Format; - /** - * color for special queries: begin/commit/rollback; - */ - special: color.Format; - /** - * error message color; - */ - error: color.Format; -} -export declare const themeAttrs: (keyof ITheme)[]; -export declare class Themes { - /** - * dimmed palette (the default theme); - */ - dimmed: ITheme; - /** - * bright palette; - */ - bright: ITheme; - /** - * black + white + grey; - */ - monochrome: ITheme; - /** - * colors without distraction; - */ - minimalist: ITheme; - /** - * classy green; - */ - matrix: ITheme; - /** - * black + white + grey; - */ - invertedMonochrome: ITheme; - /** - * colorful contrast, with enforced white background - */ - invertedContrast: ITheme; -} -export declare type ThemeName = keyof Themes; diff --git a/dist/themes.js b/dist/themes.js deleted file mode 100644 index 457a027..0000000 --- a/dist/themes.js +++ /dev/null @@ -1,117 +0,0 @@ -"use strict"; -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; - result["default"] = mod; - return result; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const color = __importStar(require("cli-color")); -exports.themeAttrs = ['time', 'value', 'cn', 'tx', 'paramTitle', 'errorTitle', 'query', 'special', 'error']; -class Themes { - constructor() { - /** - * dimmed palette (the default theme); - */ - this.dimmed = { - time: color.bgWhite.black, - value: color.white, - cn: color.yellow, - tx: color.cyan, - paramTitle: color.magenta, - errorTitle: color.redBright, - query: color.whiteBright, - special: color.green, - error: color.red - }; - /** - * bright palette; - */ - this.bright = { - time: color.bgBlue.whiteBright, - value: color.white, - cn: color.yellowBright, - tx: color.cyanBright, - paramTitle: color.magentaBright, - errorTitle: color.redBright, - query: color.whiteBright, - special: color.greenBright, - error: color.redBright - }; - /** - * black + white + grey; - */ - this.monochrome = { - time: color.bgWhite.black, - value: color.whiteBright, - cn: color.white, - tx: color.white, - paramTitle: color.white, - errorTitle: color.white, - query: color.whiteBright, - special: color.whiteBright, - error: color.whiteBright - }; - /** - * colors without distraction; - */ - this.minimalist = { - time: color.bgWhite.black, - value: color.white, - cn: color.yellow, - tx: color.yellow, - paramTitle: color.cyan, - errorTitle: color.redBright, - query: color.whiteBright, - special: color.whiteBright, - error: color.red - }; - /** - * classy green; - */ - this.matrix = { - time: color.bgGreen.black, - value: color.white, - cn: color.green, - tx: color.green, - paramTitle: color.green, - errorTitle: color.green, - query: color.whiteBright, - special: color.whiteBright, - error: color.greenBright - }; - /////////////////////////////////////////// - // Themes for white or bright backgrounds - /////////////////////////////////////////// - /** - * black + white + grey; - */ - this.invertedMonochrome = { - time: color.bgWhite.black, - value: color.blackBright, - cn: color.black, - tx: color.black, - paramTitle: color.black, - errorTitle: color.black, - query: color.blackBright, - special: color.blackBright, - error: color.blackBright - }; - /** - * colorful contrast, with enforced white background - */ - this.invertedContrast = { - time: color.bgBlue.white, - value: color.bgWhiteBright.blueBright, - cn: color.bgWhiteBright.black, - tx: color.bgWhiteBright.black, - paramTitle: color.bgWhiteBright.magenta, - errorTitle: color.bgWhiteBright.red, - query: color.bgWhiteBright.green, - special: color.bgWhiteBright.cyan, - error: color.bgWhiteBright.redBright - }; - } -} -exports.Themes = Themes; diff --git a/dist/types.d.ts b/dist/types.d.ts deleted file mode 100644 index a58f74c..0000000 --- a/dist/types.d.ts +++ /dev/null @@ -1,29 +0,0 @@ -export interface IInitOptions { - connect?(client: IClient, dc: any, useCount: number): void; - disconnect?(client: IClient, dc: any): void; - query?(e: IEventContext): void; - task?(e: IEventContext): void; - transact?(e: IEventContext): void; - error?(err: any, e: IEventContext): void; -} -export declare type EventName = keyof IInitOptions; -export declare const eventNames: EventName[]; -export interface IClient { - connectionParameters: { - database: string; - user: string; - }; -} -export interface IEventContext { - cn?: any; - query?: any; - params?: any; - ctx?: ITaskContext; -} -export interface ITaskContext { - tag: any; - isTX: boolean; - start: Date; - finish: Date; - success?: boolean; -} diff --git a/dist/types.js b/dist/types.js deleted file mode 100644 index ad6ba7c..0000000 --- a/dist/types.js +++ /dev/null @@ -1,3 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.eventNames = ['connect', 'disconnect', 'query', 'task', 'transact', 'error']; diff --git a/dist/utils.d.ts b/dist/utils.d.ts deleted file mode 100644 index 54579b2..0000000 --- a/dist/utils.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { IEventContext } from './types'; -export declare function formatTime(t: Date): string; -export declare function formatDuration(d: number): string; -export declare function removeColors(text: string): string; -export declare function padZeros(value: number, n: number): string; -export declare function hasOwnProperty(obj: object, propName: string): boolean; -export declare function getTagName(e: IEventContext): string | undefined; -export declare function isNull(value: any): boolean; -export declare function toJson(data: any): string | undefined; diff --git a/dist/utils.js b/dist/utils.js deleted file mode 100644 index 4256c0e..0000000 --- a/dist/utils.js +++ /dev/null @@ -1,89 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -// formats time as '00:00:00'; -function formatTime(t) { - return padZeros(t.getHours(), 2) + ':' + padZeros(t.getMinutes(), 2) + ':' + padZeros(t.getSeconds(), 2); -} -exports.formatTime = formatTime; -// formats duration value (in milliseconds) as '00:00:00.000', -// shortened to just the values that are applicable. -function formatDuration(d) { - const hours = Math.floor(d / 3600000); - const minutes = Math.floor((d - hours * 3600000) / 60000); - const seconds = Math.floor((d - hours * 3600000 - minutes * 60000) / 1000); - const ms = d - hours * 3600000 - minutes * 60000 - seconds * 1000; - let s = '.' + padZeros(ms, 3); // milliseconds are shown always; - if (d >= 1000) { - // seconds are to be shown; - s = padZeros(seconds, 2) + s; - if (d >= 60000) { - // minutes are to be shown; - s = padZeros(minutes, 2) + ':' + s; - if (d >= 3600000) { - // hours are to be shown; - s = padZeros(hours, 2) + ':' + s; - } - } - } - return s; -} -exports.formatDuration = formatDuration; -// removes color elements from the text; -function removeColors(text) { - /*eslint no-control-regex: 0*/ - return text.replace(/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]/g, ''); -} -exports.removeColors = removeColors; -function padZeros(value, n) { - let str = value.toString(); - while (str.length < n) - str = '0' + str; - return str; -} -exports.padZeros = padZeros; -function hasOwnProperty(obj, propName) { - return Object.prototype.hasOwnProperty.call(obj, propName); -} -exports.hasOwnProperty = hasOwnProperty; -function getTagName(e) { - let sTag; - const tag = e.ctx && e.ctx.tag; - if (tag) { - switch (typeof tag) { - case 'string': - sTag = tag; - break; - case 'number': - if (Number.isFinite(tag)) { - sTag = tag.toString(); - } - break; - case 'object': - // A tag-object must have its own method toString(), in order to be converted automatically; - if (hasOwnProperty(tag, 'toString') && typeof tag.toString === 'function') { - sTag = tag.toString(); - } - break; - default: - break; - } - } - return sTag; -} -exports.getTagName = getTagName; -//////////////////////////////////////////// -// Simpler check for null/undefined; -function isNull(value) { - return value === null || value === undefined; -} -exports.isNull = isNull; -/////////////////////////////////////////////////////////////// -// Adds support for BigInt, to be rendered like in JavaScript, -// as an open value, with 'n' in the end. -function toJson(data) { - if (data !== undefined) { - return JSON.stringify(data, (_, v) => typeof v === 'bigint' ? `${v}#bigint` : v) - .replace(/"(-?\d+)#bigint"/g, (_, a) => a + 'n'); - } -} -exports.toJson = toJson;