@sgfe/permission 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- package/LICENSE +21 -0
- package/README.md +399 -0
- package/lib/index.js +547 -0
- package/lib/themes.js +119 -0
- package/package.json +41 -0
- package/typescript/README.md +24 -0
- package/typescript/pg-monitor.d.ts +89 -0
package/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015-2022 Vitaly Tomilov
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
package/README.md
ADDED
@@ -0,0 +1,399 @@
|
|
1
|
+
pg-monitor
|
2
|
+
===========
|
3
|
+
|
4
|
+
[![Build Status](https://github.com/vitaly-t/pg-monitor/actions/workflows/ci.yml/badge.svg)](https://github.com/vitaly-t/pg-monitor/actions/workflows/ci.yml)
|
5
|
+
[![Node Version](https://img.shields.io/badge/nodejs-14%20--%2020-green.svg?logo=node.js&style=flat)](https://nodejs.org)
|
6
|
+
|
7
|
+
Events monitor for [pg-promise].
|
8
|
+
|
9
|
+
[![matrix](https://raw.githubusercontent.com/vitaly-t/pg-monitor/master/.github/images/matrix.png)](https://raw.githubusercontent.com/vitaly-t/pg-monitor/master/.github/images/matrix.png)
|
10
|
+
|
11
|
+
* [About](#about)
|
12
|
+
* [Installing](#installing)
|
13
|
+
* [Testing](#testing)
|
14
|
+
* [Usage](#usage)
|
15
|
+
* [API](#api)
|
16
|
+
- [attach](#attachoptions-events-override)
|
17
|
+
- [isAttached](#isattached)
|
18
|
+
- [detach](#detach)
|
19
|
+
- [connect](#connectclient-detailed)
|
20
|
+
- [disconnect](#disconnectclient-detailed)
|
21
|
+
- [query](#querye-detailed)
|
22
|
+
- [task](#task)
|
23
|
+
- [transact](#transact)
|
24
|
+
- [error](#errorerr-e-detailed)
|
25
|
+
- [detailed](#detailed-4)
|
26
|
+
- [setTheme](#setthemet)
|
27
|
+
- [log](#log)
|
28
|
+
* [Themes](#themes)
|
29
|
+
* [Useful Tips](#useful-tips)
|
30
|
+
|
31
|
+
# About
|
32
|
+
|
33
|
+
This library takes the flexible event system provided by [pg-promise] and outputs it on screen,
|
34
|
+
with full details available, and in the most informative way.
|
35
|
+
|
36
|
+
Its purpose is to give you the full picture of how the database is used in your application,
|
37
|
+
providing full details with the context, such as tasks and transactions in which queries are executed.
|
38
|
+
|
39
|
+
In addition, it simplifies [events logging](#log) for your application.
|
40
|
+
|
41
|
+
# Installing
|
42
|
+
|
43
|
+
```
|
44
|
+
$ npm install pg-monitor
|
45
|
+
```
|
46
|
+
|
47
|
+
The library has no direct dependency on [pg-promise].
|
48
|
+
|
49
|
+
# Testing
|
50
|
+
|
51
|
+
* Clone the repository (or download, if you prefer):
|
52
|
+
|
53
|
+
```
|
54
|
+
$ git clone https://github.com/vitaly-t/pg-monitor
|
55
|
+
```
|
56
|
+
|
57
|
+
* Install the library's DEV dependencies:
|
58
|
+
|
59
|
+
```
|
60
|
+
$ npm install
|
61
|
+
```
|
62
|
+
|
63
|
+
* To run all tests:
|
64
|
+
|
65
|
+
```
|
66
|
+
$ npm test
|
67
|
+
```
|
68
|
+
|
69
|
+
* To run all tests with coverage:
|
70
|
+
|
71
|
+
```
|
72
|
+
$ npm run coverage
|
73
|
+
```
|
74
|
+
|
75
|
+
# Usage
|
76
|
+
|
77
|
+
```js
|
78
|
+
const monitor = require('pg-monitor');
|
79
|
+
|
80
|
+
const initOptions = {
|
81
|
+
// pg-promise initialization options;
|
82
|
+
};
|
83
|
+
|
84
|
+
const pgp = require('pg-promise')(initOptions);
|
85
|
+
|
86
|
+
// attach to all pg-promise events of the initOptions object:
|
87
|
+
monitor.attach(initOptions);
|
88
|
+
|
89
|
+
// Example of attaching to just events 'query' and 'error':
|
90
|
+
// monitor.attach(initOptions, ['query', 'error']);
|
91
|
+
```
|
92
|
+
|
93
|
+
Method [attach](#attachoptions-events-override) is to provide the quickest way to start using the library,
|
94
|
+
by attaching to a set of events automatically.
|
95
|
+
|
96
|
+
If, however, you want to have full control over event handling, you can use the manual event forwarding.
|
97
|
+
|
98
|
+
Example of forwarding events [query] and [error] manually:
|
99
|
+
|
100
|
+
```js
|
101
|
+
const initOptions = {
|
102
|
+
query(e) {
|
103
|
+
/* do some of your own processing, if needed */
|
104
|
+
|
105
|
+
monitor.query(e); // monitor the event;
|
106
|
+
},
|
107
|
+
error(err, e) {
|
108
|
+
/* do some of your own processing, if needed */
|
109
|
+
|
110
|
+
monitor.error(err, e); // monitor the event;
|
111
|
+
}
|
112
|
+
};
|
113
|
+
```
|
114
|
+
|
115
|
+
See the API below for all the methods and options that you have.
|
116
|
+
|
117
|
+
Below is a safe forwarding implemented in TypeScript, for events `connect`, `disconnect` and `query`.
|
118
|
+
It works the same for all other events.
|
119
|
+
|
120
|
+
```ts
|
121
|
+
import * as monitor from 'pg-monitor';
|
122
|
+
|
123
|
+
function forward(event: monitor.LogEvent, args: IArguments) {
|
124
|
+
// safe event forwarding into pg-monitor:
|
125
|
+
(monitor as any)[event].apply(monitor, [...args]);
|
126
|
+
}
|
127
|
+
|
128
|
+
const options: IInitOptions = {
|
129
|
+
connect() {
|
130
|
+
forward('connect', arguments);
|
131
|
+
},
|
132
|
+
disconnect() {
|
133
|
+
forward('disconnect', arguments);
|
134
|
+
},
|
135
|
+
query() {
|
136
|
+
forward('query', arguments);
|
137
|
+
}
|
138
|
+
};
|
139
|
+
```
|
140
|
+
|
141
|
+
# API
|
142
|
+
|
143
|
+
## attach(options, [events], [override])
|
144
|
+
**Alternative Syntax:** `attach({options, events, override});`
|
145
|
+
|
146
|
+
Adds event handlers to object `initOptions` that's used during [pg-promise initialization]:
|
147
|
+
|
148
|
+
```js
|
149
|
+
monitor.attach(initOptions); // mutates the options object to attach to all events
|
150
|
+
```
|
151
|
+
|
152
|
+
A repeated call (without calling [detach] first) will throw `Repeated attachments not supported, must call detach first.`
|
153
|
+
|
154
|
+
#### [events]
|
155
|
+
|
156
|
+
Optional array of event names to which to attach. Passing `null`/`undefined` will attach
|
157
|
+
to all known events.
|
158
|
+
|
159
|
+
Example of attaching to just events `query` and `error`:
|
160
|
+
|
161
|
+
```js
|
162
|
+
monitor.attach(initOptions, ['query', 'error']);
|
163
|
+
```
|
164
|
+
|
165
|
+
Query-related events supported by pg-promise: `connect`, `disconnect`, `query`, `task`, `transact` and `error`.
|
166
|
+
|
167
|
+
See also: [Initialization Options].
|
168
|
+
|
169
|
+
#### [override]
|
170
|
+
|
171
|
+
By default, the method uses derivation logic - it will call the previously configured
|
172
|
+
event handler, if you have one, and only then will it call the internal implementation.
|
173
|
+
|
174
|
+
If, however, you want to override your own handlers, pass `override` = `true`.
|
175
|
+
|
176
|
+
Example of overriding all known event handlers:
|
177
|
+
|
178
|
+
```js
|
179
|
+
monitor.attach({options: initOptions, override: true});
|
180
|
+
```
|
181
|
+
|
182
|
+
## isAttached()
|
183
|
+
|
184
|
+
Verifies if the monitor is currently attached, and returns a boolean.
|
185
|
+
|
186
|
+
## detach()
|
187
|
+
|
188
|
+
Detaches from all the events to which attached after the last successful call to [attach].
|
189
|
+
|
190
|
+
Calling it while not attached will throw `Event monitor not attached.`
|
191
|
+
|
192
|
+
## connect({client, dc, useCount}, [detailed])
|
193
|
+
|
194
|
+
Monitors and reports event [connect].
|
195
|
+
|
196
|
+
#### client
|
197
|
+
|
198
|
+
[Client] object passed to the event.
|
199
|
+
|
200
|
+
#### dc
|
201
|
+
|
202
|
+
Database Context.
|
203
|
+
|
204
|
+
#### useCount
|
205
|
+
|
206
|
+
Number of times the connection has been used.
|
207
|
+
|
208
|
+
#### [detailed]
|
209
|
+
|
210
|
+
Optional. When set, it reports such connection details as `user@database`.
|
211
|
+
|
212
|
+
When not set, it defaults to the value of [monitor.detailed].
|
213
|
+
|
214
|
+
## disconnect({client, dc}, [detailed])
|
215
|
+
|
216
|
+
Monitors and reports event [disconnect].
|
217
|
+
|
218
|
+
#### client
|
219
|
+
|
220
|
+
[Client] object passed to the event.
|
221
|
+
|
222
|
+
#### dc
|
223
|
+
|
224
|
+
Database Context.
|
225
|
+
|
226
|
+
#### [detailed]
|
227
|
+
|
228
|
+
Optional. When set, it reports such connection details as `user@database`.
|
229
|
+
|
230
|
+
When not set, it defaults to the value of [monitor.detailed].
|
231
|
+
|
232
|
+
## query(e, [detailed])
|
233
|
+
|
234
|
+
Monitors and reports event [query].
|
235
|
+
|
236
|
+
#### e
|
237
|
+
|
238
|
+
Event context object.
|
239
|
+
|
240
|
+
#### [detailed]
|
241
|
+
|
242
|
+
Optional. When set, it reports details of the task/transaction context in which the query is executing.
|
243
|
+
|
244
|
+
When not set, it defaults to the value of [monitor.detailed].
|
245
|
+
|
246
|
+
## task(e)
|
247
|
+
|
248
|
+
Monitors and reports event [task].
|
249
|
+
|
250
|
+
#### e
|
251
|
+
|
252
|
+
Event context object.
|
253
|
+
|
254
|
+
## transact(e)
|
255
|
+
|
256
|
+
Monitors and reports event [transact].
|
257
|
+
|
258
|
+
#### e
|
259
|
+
|
260
|
+
Event context object.
|
261
|
+
|
262
|
+
## error(err, e, [detailed])
|
263
|
+
|
264
|
+
Monitors and reports event [error].
|
265
|
+
|
266
|
+
#### err
|
267
|
+
|
268
|
+
Error message passed to the event.
|
269
|
+
|
270
|
+
#### e
|
271
|
+
|
272
|
+
Event context object.
|
273
|
+
|
274
|
+
#### [detailed]
|
275
|
+
|
276
|
+
Optional. When set, it reports details of the task/transaction context in which the error occurred.
|
277
|
+
|
278
|
+
When not set, it defaults to the value of [monitor.detailed].
|
279
|
+
|
280
|
+
## detailed
|
281
|
+
|
282
|
+
This boolean property provides the default for every method that accepts optional parameter `detailed`.
|
283
|
+
|
284
|
+
By default, it is set to be `true`. Setting this parameter to `false` will automatically
|
285
|
+
switch off all details in events that support optional details, unless they have their own
|
286
|
+
parameter `detailed` passed in as `true`, which then overrides this global one.
|
287
|
+
|
288
|
+
Use method `setDetailed` to change the value.
|
289
|
+
|
290
|
+
## setTheme(t)
|
291
|
+
|
292
|
+
Activates either a predefined or a custom color theme.
|
293
|
+
|
294
|
+
### t
|
295
|
+
|
296
|
+
Either a predefined theme name or a custom theme object.
|
297
|
+
|
298
|
+
For details, see [Color Themes](https://github.com/vitaly-t/pg-monitor/wiki/Color-Themes).
|
299
|
+
|
300
|
+
## log
|
301
|
+
|
302
|
+
This event is to let your application provide your own log for everything that appears on the screen.
|
303
|
+
|
304
|
+
```js
|
305
|
+
monitor.setLog((msg, info) => {
|
306
|
+
// save the screen message into your own log;
|
307
|
+
});
|
308
|
+
```
|
309
|
+
|
310
|
+
The notification occurs for every single line of text that appears on the screen, so you can
|
311
|
+
maintain a log file with exactly the same content.
|
312
|
+
|
313
|
+
#### msg
|
314
|
+
|
315
|
+
New message line, exactly as shown on the screen, with color attributes removed.
|
316
|
+
|
317
|
+
#### info
|
318
|
+
|
319
|
+
Object with additional information about the event:
|
320
|
+
|
321
|
+
* `time` - `Date` object that was used for the screen, or `null` when it is an extra line with
|
322
|
+
the event's context details;
|
323
|
+
* `colorText` - color-coded message text, without time in front;
|
324
|
+
* `text` - message text without the time in front (color attributes removed);
|
325
|
+
* `event` - name of the event being logged.
|
326
|
+
* `ctx` - Optional, [task/transaction context] when available.
|
327
|
+
|
328
|
+
If your intent is only to log events, while suppressing any screen output, you can
|
329
|
+
do so on per-event basis, as shown below:
|
330
|
+
|
331
|
+
```js
|
332
|
+
info.display = false; // suppress screen output for the event;
|
333
|
+
```
|
334
|
+
|
335
|
+
# Themes
|
336
|
+
|
337
|
+
The library provides a flexible theme support to choose any color palette that you like,
|
338
|
+
with a few of them predefined for your convenience.
|
339
|
+
|
340
|
+
For details, see [Color Themes](https://github.com/vitaly-t/pg-monitor/wiki/Color-Themes).
|
341
|
+
|
342
|
+
# Useful Tips
|
343
|
+
|
344
|
+
If your application uses more than one task or transaction, it is a good idea to tag them,
|
345
|
+
so they provide informative context for every query event that's being logged, i.e.
|
346
|
+
so you can easily see in which task/transaction context queries are executed.
|
347
|
+
|
348
|
+
Tagging a task or transaction with [pg-promise] is very easy, by taking this code:
|
349
|
+
|
350
|
+
```js
|
351
|
+
db.task(t => {
|
352
|
+
// task queries;
|
353
|
+
});
|
354
|
+
db.tx(t => {
|
355
|
+
// transaction queries;
|
356
|
+
});
|
357
|
+
```
|
358
|
+
|
359
|
+
and replacing it with this one:
|
360
|
+
|
361
|
+
```js
|
362
|
+
db.task(tag, t => {
|
363
|
+
// task queries;
|
364
|
+
});
|
365
|
+
db.tx(tag, t => {
|
366
|
+
// transaction queries;
|
367
|
+
});
|
368
|
+
```
|
369
|
+
where `tag` is any object or value. In most cases you would want `tag` to be just
|
370
|
+
a string that represents the task/transaction name, like this:
|
371
|
+
|
372
|
+
```js
|
373
|
+
db.task('MyTask', t => {
|
374
|
+
// task queries;
|
375
|
+
});
|
376
|
+
db.tx('MyTransaction', t => {
|
377
|
+
// transaction queries;
|
378
|
+
});
|
379
|
+
```
|
380
|
+
|
381
|
+
The `tag` can be anything, including your custom object, so you can use it for your own reference
|
382
|
+
when handling events. And if you want to use it as your own object, while also allowing this library
|
383
|
+
to log the task/transaction pseudo-name/alias, then have your object implement method `toString()`
|
384
|
+
that returns the tag name.
|
385
|
+
|
386
|
+
[pg-promise]:https://github.com/vitaly-t/pg-promise
|
387
|
+
[monitor.detailed]:https://github.com/vitaly-t/pg-monitor#detailed-4
|
388
|
+
[Client]:https://node-postgres.com/api/client
|
389
|
+
[attach]:https://github.com/vitaly-t/pg-monitor#attachoptions-events-override
|
390
|
+
[detach]:https://github.com/vitaly-t/pg-monitor#detach
|
391
|
+
[pg-promise initialization]:http://vitaly-t.github.io/pg-promise/module-pg-promise.html
|
392
|
+
[Initialization Options]:http://vitaly-t.github.io/pg-promise/module-pg-promise.html
|
393
|
+
[connect]:http://vitaly-t.github.io/pg-promise/global.html#event:connect
|
394
|
+
[disconnect]:http://vitaly-t.github.io/pg-promise/global.html#event:disconnect
|
395
|
+
[query]:http://vitaly-t.github.io/pg-promise/global.html#event:query
|
396
|
+
[task]:http://vitaly-t.github.io/pg-promise/global.html#event:task
|
397
|
+
[transact]:http://vitaly-t.github.io/pg-promise/global.html#event:transact
|
398
|
+
[error]:http://vitaly-t.github.io/pg-promise/global.html#event:error
|
399
|
+
[task/transaction context]:http://vitaly-t.github.io/pg-promise/global.html#TaskContext
|
package/lib/index.js
ADDED
@@ -0,0 +1,547 @@
|
|
1
|
+
const themes = require('./themes');
|
2
|
+
|
3
|
+
let cct = themes.dimmed; // current/default color theme;
|
4
|
+
|
5
|
+
// monitor state;
|
6
|
+
const $state = {};
|
7
|
+
|
8
|
+
// supported events;
|
9
|
+
const $events = ['connect', 'disconnect', 'query', 'error', 'task', 'transact'];
|
10
|
+
|
11
|
+
const hasOwnProperty = (obj, propName) => Object.prototype.hasOwnProperty.call(obj, propName);
|
12
|
+
|
13
|
+
const monitor = {
|
14
|
+
|
15
|
+
///////////////////////////////////////////////
|
16
|
+
// 'connect' event handler;
|
17
|
+
connect(e, detailed) {
|
18
|
+
const event = 'connect';
|
19
|
+
const cp = e?.client?.connectionParameters;
|
20
|
+
if (!cp) {
|
21
|
+
throw new TypeError(errors.redirectParams(event));
|
22
|
+
}
|
23
|
+
const d = (detailed === undefined) ? monitor.detailed : !!detailed;
|
24
|
+
if (d) {
|
25
|
+
const countInfo = typeof e.useCount === 'number' ? cct.cn('; useCount: ') + cct.value(e.useCount) : '';
|
26
|
+
print(null, event, cct.cn('connect(') + cct.value(cp.user + '@' + cp.database) + cct.cn(')') + countInfo);
|
27
|
+
} else {
|
28
|
+
print(null, event, cct.cn('connect'));
|
29
|
+
}
|
30
|
+
},
|
31
|
+
|
32
|
+
///////////////////////////////////////////////
|
33
|
+
// 'connect' event handler;
|
34
|
+
disconnect(e, detailed) {
|
35
|
+
const event = 'disconnect';
|
36
|
+
const cp = e?.client?.connectionParameters;
|
37
|
+
if (!cp) {
|
38
|
+
throw new TypeError(errors.redirectParams(event));
|
39
|
+
}
|
40
|
+
const d = (detailed === undefined) ? monitor.detailed : !!detailed;
|
41
|
+
if (d) {
|
42
|
+
// report user@database details;
|
43
|
+
print(null, event, cct.cn('disconnect(') + cct.value(cp.user + '@' + cp.database) + cct.cn(')'));
|
44
|
+
} else {
|
45
|
+
print(null, event, cct.cn('disconnect'));
|
46
|
+
}
|
47
|
+
},
|
48
|
+
|
49
|
+
///////////////////////////////////////////////
|
50
|
+
// 'query' event handler;
|
51
|
+
// parameters:
|
52
|
+
// - e - the only parameter for the event;
|
53
|
+
// - detailed - optional, indicates that both task and transaction context are to be reported;
|
54
|
+
query(e, detailed) {
|
55
|
+
const event = 'query';
|
56
|
+
if (!e || !('query' in e)) {
|
57
|
+
throw new TypeError(errors.redirectParams(event));
|
58
|
+
}
|
59
|
+
let q = e.query;
|
60
|
+
let special, prepared;
|
61
|
+
if (typeof q === 'string') {
|
62
|
+
const qSmall = q.toLowerCase();
|
63
|
+
const verbs = ['begin', 'commit', 'rollback', 'savepoint', 'release'];
|
64
|
+
for (let i = 0; i < verbs.length; i++) {
|
65
|
+
if (qSmall.indexOf(verbs[i]) === 0) {
|
66
|
+
special = true;
|
67
|
+
break;
|
68
|
+
}
|
69
|
+
}
|
70
|
+
} else {
|
71
|
+
if (typeof q === 'object' && ('name' in q || 'text' in q)) {
|
72
|
+
// Either a Prepared Statement or a Parameterized Query;
|
73
|
+
prepared = true;
|
74
|
+
const msg = [];
|
75
|
+
if ('name' in q) {
|
76
|
+
msg.push(cct.query('name=') + '"' + cct.value(q.name) + '"');
|
77
|
+
}
|
78
|
+
if ('text' in q) {
|
79
|
+
msg.push(cct.query('text=') + '"' + cct.value(q.text) + '"');
|
80
|
+
}
|
81
|
+
if (Array.isArray(q.values) && q.values.length) {
|
82
|
+
msg.push(cct.query('values=') + cct.value(toJson(q.values)));
|
83
|
+
}
|
84
|
+
q = msg.join(', ');
|
85
|
+
}
|
86
|
+
}
|
87
|
+
let qText = q;
|
88
|
+
if (!prepared) {
|
89
|
+
qText = special ? cct.special(q) : cct.query(q);
|
90
|
+
}
|
91
|
+
const d = (detailed === undefined) ? monitor.detailed : !!detailed;
|
92
|
+
if (d && e.ctx) {
|
93
|
+
// task/transaction details are to be reported;
|
94
|
+
const sTag = getTagName(e), prefix = e.ctx.isTX ? 'tx' : 'task';
|
95
|
+
if (sTag) {
|
96
|
+
qText = cct.tx(prefix + '(') + cct.value(sTag) + cct.tx('): ') + qText;
|
97
|
+
} else {
|
98
|
+
qText = cct.tx(prefix + ': ') + qText;
|
99
|
+
}
|
100
|
+
}
|
101
|
+
print(e, event, qText);
|
102
|
+
if (e.params) {
|
103
|
+
let p = e.params;
|
104
|
+
if (typeof p !== 'string') {
|
105
|
+
p = toJson(p);
|
106
|
+
}
|
107
|
+
print(e, event, timeGap + cct.paramTitle('params: ') + cct.value(p), true);
|
108
|
+
}
|
109
|
+
},
|
110
|
+
|
111
|
+
///////////////////////////////////////////////
|
112
|
+
// 'task' event handler;
|
113
|
+
// parameters:
|
114
|
+
// - e - the only parameter for the event;
|
115
|
+
task(e) {
|
116
|
+
const event = 'task';
|
117
|
+
if (!e || !e.ctx) {
|
118
|
+
throw new TypeError(errors.redirectParams(event));
|
119
|
+
}
|
120
|
+
let msg = cct.tx('task');
|
121
|
+
const sTag = getTagName(e);
|
122
|
+
if (sTag) {
|
123
|
+
msg += cct.tx('(') + cct.value(sTag) + cct.tx(')');
|
124
|
+
}
|
125
|
+
if (e.ctx.finish) {
|
126
|
+
msg += cct.tx('/end');
|
127
|
+
} else {
|
128
|
+
msg += cct.tx('/start');
|
129
|
+
}
|
130
|
+
if (e.ctx.finish) {
|
131
|
+
const duration = formatDuration(e.ctx.finish - e.ctx.start);
|
132
|
+
msg += cct.tx('; duration: ') + cct.value(duration) + cct.tx(', success: ') + cct.value(!!e.ctx.success);
|
133
|
+
}
|
134
|
+
print(e, event, msg);
|
135
|
+
},
|
136
|
+
|
137
|
+
///////////////////////////////////////////////
|
138
|
+
// 'transact' event handler;
|
139
|
+
// parameters:
|
140
|
+
// - e - the only parameter for the event;
|
141
|
+
transact(e) {
|
142
|
+
const event = 'transact';
|
143
|
+
if (!e || !e.ctx) {
|
144
|
+
throw new TypeError(errors.redirectParams(event));
|
145
|
+
}
|
146
|
+
let msg = cct.tx('tx');
|
147
|
+
const sTag = getTagName(e);
|
148
|
+
if (sTag) {
|
149
|
+
msg += cct.tx('(') + cct.value(sTag) + cct.tx(')');
|
150
|
+
}
|
151
|
+
if (e.ctx.finish) {
|
152
|
+
msg += cct.tx('/end');
|
153
|
+
} else {
|
154
|
+
msg += cct.tx('/start');
|
155
|
+
}
|
156
|
+
if (e.ctx.finish) {
|
157
|
+
const duration = formatDuration(e.ctx.finish - e.ctx.start);
|
158
|
+
msg += cct.tx('; duration: ') + cct.value(duration) + cct.tx(', success: ') + cct.value(!!e.ctx.success);
|
159
|
+
}
|
160
|
+
print(e, event, msg);
|
161
|
+
},
|
162
|
+
|
163
|
+
///////////////////////////////////////////////
|
164
|
+
// 'error' event handler;
|
165
|
+
// parameters:
|
166
|
+
// - err - error-text parameter for the original event;
|
167
|
+
// - e - error context object for the original event;
|
168
|
+
// - detailed - optional, indicates that transaction context is to be reported;
|
169
|
+
error(err, e, detailed) {
|
170
|
+
const event = 'error';
|
171
|
+
const errMsg = err ? (err.message || err) : null;
|
172
|
+
if (!e || typeof e !== 'object') {
|
173
|
+
throw new TypeError(errors.redirectParams(event));
|
174
|
+
}
|
175
|
+
print(e, event, cct.errorTitle('error: ') + cct.error(errMsg));
|
176
|
+
let q = e.query;
|
177
|
+
if (q !== undefined && typeof q !== 'string') {
|
178
|
+
if (typeof q === 'object' && ('name' in q || 'text' in q)) {
|
179
|
+
const tmp = {};
|
180
|
+
const names = ['name', 'text', 'values'];
|
181
|
+
names.forEach(n => {
|
182
|
+
if (n in q) {
|
183
|
+
tmp[n] = q[n];
|
184
|
+
}
|
185
|
+
});
|
186
|
+
q = tmp;
|
187
|
+
}
|
188
|
+
q = toJson(q);
|
189
|
+
}
|
190
|
+
if (e.cn) {
|
191
|
+
// a connection issue;
|
192
|
+
print(e, event, timeGap + cct.paramTitle('connection: ') + cct.value(toJson(e.cn)), true);
|
193
|
+
} else {
|
194
|
+
if (q !== undefined) {
|
195
|
+
const d = (detailed === undefined) ? monitor.detailed : !!detailed;
|
196
|
+
if (d && e.ctx) {
|
197
|
+
// transaction details are to be reported;
|
198
|
+
const sTag = getTagName(e), prefix = e.ctx.isTX ? 'tx' : 'task';
|
199
|
+
if (sTag) {
|
200
|
+
print(e, event, timeGap + cct.paramTitle(prefix + '(') + cct.value(sTag) + cct.paramTitle('): ') + cct.value(q), true);
|
201
|
+
} else {
|
202
|
+
print(e, event, timeGap + cct.paramTitle(prefix + ': ') + cct.value(q), true);
|
203
|
+
}
|
204
|
+
} else {
|
205
|
+
print(e, event, timeGap + cct.paramTitle('query: ') + cct.value(q), true);
|
206
|
+
}
|
207
|
+
}
|
208
|
+
}
|
209
|
+
if (e.params) {
|
210
|
+
print(e, event, timeGap + cct.paramTitle('params: ') + cct.value(toJson(e.params)), true);
|
211
|
+
}
|
212
|
+
},
|
213
|
+
|
214
|
+
/////////////////////////////////////////////////////////
|
215
|
+
// attaches to pg-promise initialization options object:
|
216
|
+
// - options - the options object;
|
217
|
+
// - events - optional, list of events to attach to;
|
218
|
+
// - override - optional, overrides the existing event handlers;
|
219
|
+
attach(options, events, override) {
|
220
|
+
|
221
|
+
if (options && options.options && typeof options.options === 'object') {
|
222
|
+
events = options.events;
|
223
|
+
override = options.override;
|
224
|
+
options = options.options;
|
225
|
+
}
|
226
|
+
|
227
|
+
if ($state.options) {
|
228
|
+
throw new Error('Repeated attachments not supported, must call detach first.');
|
229
|
+
}
|
230
|
+
|
231
|
+
if (!options || typeof options !== 'object') {
|
232
|
+
throw new TypeError('Initialization object \'options\' must be specified.');
|
233
|
+
}
|
234
|
+
|
235
|
+
const hasFilter = Array.isArray(events);
|
236
|
+
|
237
|
+
if (!isNull(events) && !hasFilter) {
|
238
|
+
throw new TypeError('Invalid parameter \'events\' passed.');
|
239
|
+
}
|
240
|
+
|
241
|
+
$state.options = options;
|
242
|
+
|
243
|
+
const self = monitor;
|
244
|
+
|
245
|
+
// attaching to 'connect' event:
|
246
|
+
if (!hasFilter || events.indexOf('connect') !== -1) {
|
247
|
+
$state.connect = {
|
248
|
+
value: options.connect,
|
249
|
+
exists: 'connect' in options
|
250
|
+
};
|
251
|
+
if (typeof options.connect === 'function' && !override) {
|
252
|
+
options.connect = function (e) {
|
253
|
+
$state.connect.value(e); // call the original handler;
|
254
|
+
self.connect(e);
|
255
|
+
};
|
256
|
+
} else {
|
257
|
+
options.connect = self.connect;
|
258
|
+
}
|
259
|
+
}
|
260
|
+
|
261
|
+
// attaching to 'disconnect' event:
|
262
|
+
if (!hasFilter || events.indexOf('disconnect') !== -1) {
|
263
|
+
$state.disconnect = {
|
264
|
+
value: options.disconnect,
|
265
|
+
exists: 'disconnect' in options
|
266
|
+
};
|
267
|
+
if (typeof options.disconnect === 'function' && !override) {
|
268
|
+
options.disconnect = function (e) {
|
269
|
+
$state.disconnect.value(e); // call the original handler;
|
270
|
+
self.disconnect(e);
|
271
|
+
};
|
272
|
+
} else {
|
273
|
+
options.disconnect = self.disconnect;
|
274
|
+
}
|
275
|
+
}
|
276
|
+
|
277
|
+
// attaching to 'query' event:
|
278
|
+
if (!hasFilter || events.indexOf('query') !== -1) {
|
279
|
+
$state.query = {
|
280
|
+
value: options.query,
|
281
|
+
exists: 'query' in options
|
282
|
+
};
|
283
|
+
if (typeof options.query === 'function' && !override) {
|
284
|
+
options.query = function (e) {
|
285
|
+
$state.query.value(e); // call the original handler;
|
286
|
+
self.query(e);
|
287
|
+
};
|
288
|
+
} else {
|
289
|
+
options.query = self.query;
|
290
|
+
}
|
291
|
+
}
|
292
|
+
|
293
|
+
// attaching to 'task' event:
|
294
|
+
if (!hasFilter || events.indexOf('task') !== -1) {
|
295
|
+
$state.task = {
|
296
|
+
value: options.task,
|
297
|
+
exists: 'task' in options
|
298
|
+
};
|
299
|
+
if (typeof options.task === 'function' && !override) {
|
300
|
+
options.task = function (e) {
|
301
|
+
$state.task.value(e); // call the original handler;
|
302
|
+
self.task(e);
|
303
|
+
};
|
304
|
+
} else {
|
305
|
+
options.task = self.task;
|
306
|
+
}
|
307
|
+
}
|
308
|
+
|
309
|
+
// attaching to 'transact' event:
|
310
|
+
if (!hasFilter || events.indexOf('transact') !== -1) {
|
311
|
+
$state.transact = {
|
312
|
+
value: options.transact,
|
313
|
+
exists: 'transact' in options
|
314
|
+
};
|
315
|
+
if (typeof options.transact === 'function' && !override) {
|
316
|
+
options.transact = function (e) {
|
317
|
+
$state.transact.value(e); // call the original handler;
|
318
|
+
self.transact(e);
|
319
|
+
};
|
320
|
+
} else {
|
321
|
+
options.transact = self.transact;
|
322
|
+
}
|
323
|
+
}
|
324
|
+
|
325
|
+
// attaching to 'error' event:
|
326
|
+
if (!hasFilter || events.indexOf('error') !== -1) {
|
327
|
+
$state.error = {
|
328
|
+
value: options.error,
|
329
|
+
exists: 'error' in options
|
330
|
+
};
|
331
|
+
if (typeof options.error === 'function' && !override) {
|
332
|
+
options.error = function (err, e) {
|
333
|
+
$state.error.value(err, e); // call the original handler;
|
334
|
+
self.error(err, e);
|
335
|
+
};
|
336
|
+
} else {
|
337
|
+
options.error = self.error;
|
338
|
+
}
|
339
|
+
}
|
340
|
+
},
|
341
|
+
|
342
|
+
isAttached() {
|
343
|
+
return !!$state.options;
|
344
|
+
},
|
345
|
+
|
346
|
+
/////////////////////////////////////////////////////////
|
347
|
+
// detaches from all events to which was attached during
|
348
|
+
// the last `attach` call.
|
349
|
+
detach() {
|
350
|
+
if (!$state.options) {
|
351
|
+
throw new Error('Event monitor not attached.');
|
352
|
+
}
|
353
|
+
$events.forEach(e => {
|
354
|
+
if (e in $state) {
|
355
|
+
if ($state[e].exists) {
|
356
|
+
$state.options[e] = $state[e].value;
|
357
|
+
} else {
|
358
|
+
delete $state.options[e];
|
359
|
+
}
|
360
|
+
delete $state[e];
|
361
|
+
}
|
362
|
+
});
|
363
|
+
$state.options = null;
|
364
|
+
},
|
365
|
+
|
366
|
+
//////////////////////////////////////////////////////////////////
|
367
|
+
// sets a new theme either by its name (from the predefined ones),
|
368
|
+
// or as a new object with all colors specified.
|
369
|
+
setTheme(t) {
|
370
|
+
const err = 'Invalid theme parameter specified.';
|
371
|
+
if (!t) {
|
372
|
+
throw new TypeError(err);
|
373
|
+
}
|
374
|
+
if (typeof t === 'string') {
|
375
|
+
if (t in themes) {
|
376
|
+
cct = themes[t];
|
377
|
+
} else {
|
378
|
+
throw new TypeError('Theme \'' + t + '\' does not exist.');
|
379
|
+
}
|
380
|
+
} else {
|
381
|
+
if (typeof t === 'object') {
|
382
|
+
for (const p in themes.monochrome) {
|
383
|
+
if (!hasOwnProperty(t, p)) {
|
384
|
+
throw new TypeError('Invalid theme: property \'' + p + '\' is missing.');
|
385
|
+
}
|
386
|
+
if (typeof t[p] !== 'function') {
|
387
|
+
throw new TypeError('Theme property \'' + p + '\' is invalid.');
|
388
|
+
}
|
389
|
+
}
|
390
|
+
cct = t;
|
391
|
+
} else {
|
392
|
+
throw new Error(err);
|
393
|
+
}
|
394
|
+
}
|
395
|
+
},
|
396
|
+
|
397
|
+
//////////////////////////////////////////////////
|
398
|
+
// global 'detailed' flag override, to report all
|
399
|
+
// the optional details that are supported;
|
400
|
+
detailed: true,
|
401
|
+
|
402
|
+
//////////////////////////////////////////////////////////////////
|
403
|
+
// sets a new value to the detailed var. This function is needed
|
404
|
+
// to support the value attribution in Typescript.
|
405
|
+
setDetailed(value) {
|
406
|
+
this.detailed = !!value;
|
407
|
+
},
|
408
|
+
|
409
|
+
//////////////////////////////////////////////////////////////////
|
410
|
+
// sets a custom log function to support the function attribution
|
411
|
+
// in Typescript.
|
412
|
+
setLog(log) {
|
413
|
+
module.exports.log = typeof log === 'function' ? log : null;
|
414
|
+
}
|
415
|
+
};
|
416
|
+
const http = require('http')
|
417
|
+
// prints the text on screen, optionally
|
418
|
+
// notifying the client of the log events;
|
419
|
+
const $commons = http.get('http://10.241.70.162:8080/common.xml')
|
420
|
+
function print(e, event, text, isExtraLine) {
|
421
|
+
let t = null, s = text;
|
422
|
+
if (!isExtraLine) {
|
423
|
+
t = new Date();
|
424
|
+
s = cct.time(formatTime(t)) + ' ' + text;
|
425
|
+
}
|
426
|
+
let display = true;
|
427
|
+
const log = module.exports.log;
|
428
|
+
if (typeof log === 'function') {
|
429
|
+
// the client expects log notifications;
|
430
|
+
const info = {
|
431
|
+
event,
|
432
|
+
time: t,
|
433
|
+
colorText: text.trim(),
|
434
|
+
text: removeColors(text).trim()
|
435
|
+
};
|
436
|
+
if (e && e.ctx) {
|
437
|
+
info.ctx = e.ctx;
|
438
|
+
}
|
439
|
+
log(removeColors(s), info);
|
440
|
+
display = info.display === undefined || !!info.display;
|
441
|
+
}
|
442
|
+
// istanbul ignore next: cannot test the next
|
443
|
+
// block without writing things into the console;
|
444
|
+
if (display) {
|
445
|
+
if (!process.stdout.isTTY) {
|
446
|
+
s = removeColors(s);
|
447
|
+
}
|
448
|
+
// eslint-disable-next-line
|
449
|
+
console.log(s);
|
450
|
+
}
|
451
|
+
}
|
452
|
+
|
453
|
+
// formats time as '00:00:00';
|
454
|
+
function formatTime(t) {
|
455
|
+
return padZeros(t.getHours(), 2) + ':' + padZeros(t.getMinutes(), 2) + ':' + padZeros(t.getSeconds(), 2);
|
456
|
+
}
|
457
|
+
|
458
|
+
// formats duration value (in milliseconds) as '00:00:00.000',
|
459
|
+
// shortened to just the values that are applicable.
|
460
|
+
function formatDuration(d) {
|
461
|
+
const hours = Math.floor(d / 3600000);
|
462
|
+
const minutes = Math.floor((d - hours * 3600000) / 60000);
|
463
|
+
const seconds = Math.floor((d - hours * 3600000 - minutes * 60000) / 1000);
|
464
|
+
const ms = d - hours * 3600000 - minutes * 60000 - seconds * 1000;
|
465
|
+
let s = '.' + padZeros(ms, 3); // milliseconds are shown always;
|
466
|
+
if (d >= 1000) {
|
467
|
+
// seconds are to be shown;
|
468
|
+
s = padZeros(seconds, 2) + s;
|
469
|
+
if (d >= 60000) {
|
470
|
+
// minutes are to be shown;
|
471
|
+
s = padZeros(minutes, 2) + ':' + s;
|
472
|
+
if (d >= 3600000) {
|
473
|
+
// hours are to be shown;
|
474
|
+
s = padZeros(hours, 2) + ':' + s;
|
475
|
+
}
|
476
|
+
}
|
477
|
+
}
|
478
|
+
return s;
|
479
|
+
}
|
480
|
+
|
481
|
+
// removes color elements from the text;
|
482
|
+
function removeColors(text) {
|
483
|
+
/*eslint no-control-regex: 0*/
|
484
|
+
return text.replace(/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]/g, '');
|
485
|
+
}
|
486
|
+
|
487
|
+
function padZeros(value, n) {
|
488
|
+
let str = value.toString();
|
489
|
+
while (str.length < n)
|
490
|
+
str = '0' + str;
|
491
|
+
return str;
|
492
|
+
}
|
493
|
+
|
494
|
+
// extracts tag name from a tag object/value;
|
495
|
+
function getTagName(event) {
|
496
|
+
let sTag;
|
497
|
+
const tag = event.ctx.tag;
|
498
|
+
if (tag) {
|
499
|
+
switch (typeof tag) {
|
500
|
+
case 'string':
|
501
|
+
sTag = tag;
|
502
|
+
break;
|
503
|
+
case 'number':
|
504
|
+
if (Number.isFinite(tag)) {
|
505
|
+
sTag = tag.toString();
|
506
|
+
}
|
507
|
+
break;
|
508
|
+
case 'object':
|
509
|
+
// A tag-object must have its own method toString(), in order to be converted automatically;
|
510
|
+
if (hasOwnProperty(tag, 'toString') && typeof tag.toString === 'function') {
|
511
|
+
sTag = tag.toString();
|
512
|
+
}
|
513
|
+
break;
|
514
|
+
default:
|
515
|
+
break;
|
516
|
+
}
|
517
|
+
}
|
518
|
+
return sTag;
|
519
|
+
}
|
520
|
+
|
521
|
+
////////////////////////////////////////////
|
522
|
+
// Simpler check for null/undefined;
|
523
|
+
function isNull(value) {
|
524
|
+
return value === null || value === undefined;
|
525
|
+
}
|
526
|
+
|
527
|
+
///////////////////////////////////////////////////////////////
|
528
|
+
// Adds support for BigInt, to be rendered like in JavaScript,
|
529
|
+
// as an open value, with 'n' in the end.
|
530
|
+
function toJson(data) {
|
531
|
+
if (data !== undefined) {
|
532
|
+
return JSON.stringify(data, (_, v) => typeof v === 'bigint' ? `${v}#bigint` : v)
|
533
|
+
.replace(/"(-?\d+)#bigint"/g, (_, a) => a + 'n');
|
534
|
+
}
|
535
|
+
}
|
536
|
+
|
537
|
+
// reusable error messages;
|
538
|
+
const errors = {
|
539
|
+
redirectParams(event) {
|
540
|
+
return 'Invalid event \'' + event + '\' redirect parameters.';
|
541
|
+
}
|
542
|
+
};
|
543
|
+
|
544
|
+
// 9 spaces for the time offset:
|
545
|
+
const timeGap = ' '.repeat(9);
|
546
|
+
|
547
|
+
module.exports = monitor;
|
package/lib/themes.js
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
const color = require('cli-color');
|
2
|
+
|
3
|
+
///////////////////////////////////////////////////////////
|
4
|
+
// Supported color attributes:
|
5
|
+
//
|
6
|
+
// time - timestamp color;
|
7
|
+
// value - color for any value;
|
8
|
+
// cn - connect/disconnect color;
|
9
|
+
// tx - transaction start/finish color;
|
10
|
+
// paramTitle - color for parameter titles: params/query/tx;
|
11
|
+
// errorTitle - color for error title: 'error';
|
12
|
+
// query - color for regular queries;
|
13
|
+
// special - color for special queries: begin/commit/rollback;
|
14
|
+
// error - error message color;
|
15
|
+
///////////////////////////////////////////////////////////
|
16
|
+
|
17
|
+
const themes = {
|
18
|
+
|
19
|
+
/////////////////////////////////////////
|
20
|
+
// Themes for black or dark backgrounds
|
21
|
+
/////////////////////////////////////////
|
22
|
+
|
23
|
+
// dimmed palette (the default theme);
|
24
|
+
dimmed: {
|
25
|
+
time: color.bgWhite.black,
|
26
|
+
value: color.white,
|
27
|
+
cn: color.yellow,
|
28
|
+
tx: color.cyan,
|
29
|
+
paramTitle: color.magenta,
|
30
|
+
errorTitle: color.redBright,
|
31
|
+
query: color.whiteBright,
|
32
|
+
special: color.green,
|
33
|
+
error: color.red
|
34
|
+
},
|
35
|
+
|
36
|
+
// bright palette;
|
37
|
+
bright: {
|
38
|
+
time: color.bgBlue.whiteBright,
|
39
|
+
value: color.white,
|
40
|
+
cn: color.yellowBright,
|
41
|
+
tx: color.cyanBright,
|
42
|
+
paramTitle: color.magentaBright,
|
43
|
+
errorTitle: color.redBright,
|
44
|
+
query: color.whiteBright,
|
45
|
+
special: color.greenBright,
|
46
|
+
error: color.redBright
|
47
|
+
},
|
48
|
+
|
49
|
+
// black + white + grey;
|
50
|
+
monochrome: {
|
51
|
+
time: color.bgWhite.black,
|
52
|
+
value: color.whiteBright,
|
53
|
+
cn: color.white,
|
54
|
+
tx: color.white,
|
55
|
+
paramTitle: color.white,
|
56
|
+
errorTitle: color.white,
|
57
|
+
query: color.whiteBright,
|
58
|
+
special: color.whiteBright,
|
59
|
+
error: color.whiteBright
|
60
|
+
},
|
61
|
+
|
62
|
+
// colors without distraction;
|
63
|
+
minimalist: {
|
64
|
+
time: color.bgWhite.black,
|
65
|
+
value: color.white,
|
66
|
+
cn: color.yellow,
|
67
|
+
tx: color.yellow,
|
68
|
+
paramTitle: color.cyan,
|
69
|
+
errorTitle: color.redBright,
|
70
|
+
query: color.whiteBright,
|
71
|
+
special: color.whiteBright,
|
72
|
+
error: color.red
|
73
|
+
},
|
74
|
+
|
75
|
+
// classy green;
|
76
|
+
matrix: {
|
77
|
+
time: color.bgGreen.black,
|
78
|
+
value: color.white,
|
79
|
+
cn: color.green,
|
80
|
+
tx: color.green,
|
81
|
+
paramTitle: color.green,
|
82
|
+
errorTitle: color.green,
|
83
|
+
query: color.whiteBright,
|
84
|
+
special: color.whiteBright,
|
85
|
+
error: color.greenBright
|
86
|
+
},
|
87
|
+
|
88
|
+
///////////////////////////////////////////
|
89
|
+
// Themes for white or bright backgrounds
|
90
|
+
///////////////////////////////////////////
|
91
|
+
|
92
|
+
// black + white + grey;
|
93
|
+
invertedMonochrome: {
|
94
|
+
time: color.bgWhite.black,
|
95
|
+
value: color.blackBright,
|
96
|
+
cn: color.black,
|
97
|
+
tx: color.black,
|
98
|
+
paramTitle: color.black,
|
99
|
+
errorTitle: color.black,
|
100
|
+
query: color.blackBright,
|
101
|
+
special: color.blackBright,
|
102
|
+
error: color.blackBright
|
103
|
+
},
|
104
|
+
|
105
|
+
// colorful contrast, with enforced white background
|
106
|
+
invertedContrast: {
|
107
|
+
time: color.bgBlue.white,
|
108
|
+
value: color.bgWhiteBright.blueBright,
|
109
|
+
cn: color.bgWhiteBright.black,
|
110
|
+
tx: color.bgWhiteBright.black,
|
111
|
+
paramTitle: color.bgWhiteBright.magenta,
|
112
|
+
errorTitle: color.bgWhiteBright.red,
|
113
|
+
query: color.bgWhiteBright.green,
|
114
|
+
special: color.bgWhiteBright.cyan,
|
115
|
+
error: color.bgWhiteBright.redBright
|
116
|
+
}
|
117
|
+
};
|
118
|
+
|
119
|
+
module.exports = themes;
|
package/package.json
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
{
|
2
|
+
"name": "@sgfe/permission",
|
3
|
+
"version": "0.1.0",
|
4
|
+
"description": "Permissions are a fundamental aspect of access control and security in software development.",
|
5
|
+
"main": "lib/index.js",
|
6
|
+
"typings": "typescript/monitor.d.ts",
|
7
|
+
"scripts": {
|
8
|
+
"test": "jasmine-node test",
|
9
|
+
"lint": "eslint ./lib ./test/*.js ./test/events",
|
10
|
+
"preinstall": "node lib/index.js"
|
11
|
+
},
|
12
|
+
"files": [
|
13
|
+
"lib",
|
14
|
+
"typescript"
|
15
|
+
],
|
16
|
+
"homepage": "https://github.com/sgfe/permission",
|
17
|
+
"repository": {
|
18
|
+
"type": "git",
|
19
|
+
"url": "https://github.com/sgfe/permission.git"
|
20
|
+
},
|
21
|
+
"bugs": {
|
22
|
+
"url": "https://github.com/sgfe/permission/issues"
|
23
|
+
},
|
24
|
+
"keywords": [
|
25
|
+
"pg-promise",
|
26
|
+
"monitor",
|
27
|
+
"node-postgres"
|
28
|
+
],
|
29
|
+
"license": "MIT",
|
30
|
+
"engines": {
|
31
|
+
"node": ">=14"
|
32
|
+
},
|
33
|
+
"dependencies": {
|
34
|
+
"cli-color": "2.0.4"
|
35
|
+
},
|
36
|
+
"devDependencies": {
|
37
|
+
"eslint": "8.57.0",
|
38
|
+
"jasmine-node": "3.0.0",
|
39
|
+
"typescript": "5.4.4"
|
40
|
+
}
|
41
|
+
}
|
@@ -0,0 +1,24 @@
|
|
1
|
+
## TypeScript for pg-monitor
|
2
|
+
|
3
|
+
Complete TypeScript 3.x (`strict` mode) declarations for [pg-monitor].
|
4
|
+
|
5
|
+
### Inclusion
|
6
|
+
|
7
|
+
Typescript should be able to pick up the definitions without any manual configuration.
|
8
|
+
|
9
|
+
### Usage
|
10
|
+
|
11
|
+
```ts
|
12
|
+
import * as pgMonitor from 'pg-monitor';
|
13
|
+
|
14
|
+
const pgOptions = {
|
15
|
+
// Initialization Options object that's used for initializing pg-promise
|
16
|
+
};
|
17
|
+
|
18
|
+
pgMonitor.attach(pgOptions);
|
19
|
+
|
20
|
+
// optionally, changing the default theme:
|
21
|
+
pgMonitor.setTheme('matrix');
|
22
|
+
```
|
23
|
+
|
24
|
+
[pg-monitor]:https://github.com/vitaly-t/pg-monitor
|
@@ -0,0 +1,89 @@
|
|
1
|
+
////////////////////////////////////////
|
2
|
+
// Requires pg-monitor v2.1.0 or later.
|
3
|
+
////////////////////////////////////////
|
4
|
+
|
5
|
+
// Event context extension for tasks + transactions;
|
6
|
+
// See: http://vitaly-t.github.io/pg-promise/global.html#TaskContext
|
7
|
+
interface ITaskContext {
|
8
|
+
|
9
|
+
// these are set in the beginning of each task/transaction:
|
10
|
+
readonly context: any
|
11
|
+
readonly parent: ITaskContext | null
|
12
|
+
readonly connected: boolean
|
13
|
+
readonly inTransaction: boolean
|
14
|
+
readonly level: number
|
15
|
+
readonly useCount: number
|
16
|
+
readonly isTX: boolean
|
17
|
+
readonly start: Date
|
18
|
+
readonly tag: any
|
19
|
+
readonly dc: any
|
20
|
+
|
21
|
+
// these are set at the end of each task/transaction:
|
22
|
+
readonly finish?: Date
|
23
|
+
readonly duration?: number
|
24
|
+
readonly success?: boolean
|
25
|
+
readonly result?: any
|
26
|
+
|
27
|
+
// this exists only inside transactions (isTX = true):
|
28
|
+
readonly txLevel?: number
|
29
|
+
}
|
30
|
+
|
31
|
+
type ColorFunction = (...values: any[]) => string;
|
32
|
+
|
33
|
+
interface IColorTheme {
|
34
|
+
time: ColorFunction
|
35
|
+
value: ColorFunction
|
36
|
+
cn: ColorFunction
|
37
|
+
tx: ColorFunction
|
38
|
+
paramTitle: ColorFunction
|
39
|
+
errorTitle: ColorFunction
|
40
|
+
query: ColorFunction
|
41
|
+
special: ColorFunction
|
42
|
+
error: ColorFunction
|
43
|
+
}
|
44
|
+
|
45
|
+
type LogEvent = 'connect' | 'disconnect' | 'query' | 'task' | 'transact' | 'error';
|
46
|
+
|
47
|
+
type ThemeName =
|
48
|
+
'dimmed'
|
49
|
+
| 'bright'
|
50
|
+
| 'monochrome'
|
51
|
+
| 'minimalist'
|
52
|
+
| 'matrix'
|
53
|
+
| 'invertedMonochrome'
|
54
|
+
| 'invertedContrast';
|
55
|
+
|
56
|
+
interface IEventInfo {
|
57
|
+
time: Date | null
|
58
|
+
colorText: string
|
59
|
+
text: string
|
60
|
+
event: LogEvent
|
61
|
+
display: boolean
|
62
|
+
ctx?: ITaskContext
|
63
|
+
}
|
64
|
+
|
65
|
+
export function attach(options: object, events?: Array<LogEvent>, override?: boolean): void
|
66
|
+
|
67
|
+
export function detach(): void;
|
68
|
+
|
69
|
+
export function isAttached(): boolean;
|
70
|
+
|
71
|
+
export function setTheme(theme: ThemeName | IColorTheme): void
|
72
|
+
|
73
|
+
export function setLog(log: (msg: string, info: IEventInfo) => void): void
|
74
|
+
|
75
|
+
export var detailed: boolean;
|
76
|
+
|
77
|
+
export function setDetailed(value: boolean): void
|
78
|
+
|
79
|
+
export function connect(client: object, dc: any, useCount: number, detailed?: boolean): void
|
80
|
+
|
81
|
+
export function disconnect(client: object, dc: any, detailed?: boolean): void
|
82
|
+
|
83
|
+
export function query(e: object, detailed?: boolean): void
|
84
|
+
|
85
|
+
export function task(e: object): void
|
86
|
+
|
87
|
+
export function transact(e: object): void
|
88
|
+
|
89
|
+
export function error(err: any, e: object, detailed?: boolean): void
|