@drunkcod/express-kit 0.0.13 → 0.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cjs/index.d.ts +3 -0
- package/lib/cjs/index.js +10 -0
- package/lib/cjs/loggable.d.ts +7 -1
- package/lib/cjs/loggable.js +16 -12
- package/lib/cjs/stopwatch.d.ts +21 -0
- package/lib/cjs/stopwatch.js +58 -0
- package/lib/index.d.ts +3 -0
- package/lib/index.js +9 -0
- package/lib/loggable.d.ts +7 -1
- package/lib/loggable.js +17 -13
- package/lib/stopwatch.d.ts +21 -0
- package/lib/stopwatch.js +53 -0
- package/package.json +2 -2
package/lib/cjs/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type express from 'express';
|
|
2
|
+
import { Timespan } from './stopwatch.js';
|
|
2
3
|
export * from '@drunkcod/express-async';
|
|
3
4
|
export * from './loggable.js';
|
|
5
|
+
export * from './stopwatch.js';
|
|
4
6
|
type AsyncFn<T> = () => Promise<T>;
|
|
5
7
|
type ExpressServer = ReturnType<express.Application['listen']>;
|
|
6
8
|
export type ErrorHandler = (error: Error, request: express.Request, response: express.Response, next: express.NextFunction) => void;
|
|
@@ -17,3 +19,4 @@ export declare function closeAsync(server: {
|
|
|
17
19
|
close: (cb: (error?: Error) => void) => void;
|
|
18
20
|
}): Promise<void>;
|
|
19
21
|
export declare function registerShutdown<Server extends ExpressServer = ExpressServer>(server: Server, shutdown?: () => Promise<void>): void;
|
|
22
|
+
export declare function requestTimingMiddleware(onRequestFinish: (req: express.Request, duration: Timespan) => void): (req: express.Request, res: express.Response, next: express.NextFunction) => void;
|
package/lib/cjs/index.js
CHANGED
|
@@ -19,8 +19,11 @@ exports.mergeCallsAsync = mergeCallsAsync;
|
|
|
19
19
|
exports.listenAsync = listenAsync;
|
|
20
20
|
exports.closeAsync = closeAsync;
|
|
21
21
|
exports.registerShutdown = registerShutdown;
|
|
22
|
+
exports.requestTimingMiddleware = requestTimingMiddleware;
|
|
23
|
+
const stopwatch_js_1 = require("./stopwatch.js");
|
|
22
24
|
__exportStar(require("@drunkcod/express-async"), exports);
|
|
23
25
|
__exportStar(require("./loggable.js"), exports);
|
|
26
|
+
__exportStar(require("./stopwatch.js"), exports);
|
|
24
27
|
function onceAsync(fn) {
|
|
25
28
|
let p;
|
|
26
29
|
return () => {
|
|
@@ -78,3 +81,10 @@ function registerShutdown(server, shutdown) {
|
|
|
78
81
|
process.on('SIGINT', onShutdown);
|
|
79
82
|
process.on('SIGTERM', onShutdown);
|
|
80
83
|
}
|
|
84
|
+
function requestTimingMiddleware(onRequestFinish) {
|
|
85
|
+
return (req, res, next) => {
|
|
86
|
+
const s = stopwatch_js_1.Stopwatch.startNew();
|
|
87
|
+
res.on('finish', () => onRequestFinish(req, s.elapsed));
|
|
88
|
+
next();
|
|
89
|
+
};
|
|
90
|
+
}
|
package/lib/cjs/loggable.d.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
export declare function at(message?: string): string;
|
|
2
|
-
|
|
2
|
+
type LoggableCause = {
|
|
3
|
+
message: unknown;
|
|
4
|
+
cause?: unknown;
|
|
5
|
+
stack?: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function asLoggableError(error: unknown): LoggableCause;
|
|
3
8
|
export declare function hasOwnJSON(x: object): x is {
|
|
4
9
|
toJSON(): unknown;
|
|
5
10
|
};
|
|
11
|
+
export {};
|
package/lib/cjs/loggable.js
CHANGED
|
@@ -18,26 +18,30 @@ function at(message) {
|
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
function asLoggableCause(cause) {
|
|
21
|
-
if (cause == null
|
|
22
|
-
return
|
|
21
|
+
if (cause == null)
|
|
22
|
+
return null;
|
|
23
|
+
if (typeof cause !== 'object')
|
|
24
|
+
return { message: cause };
|
|
23
25
|
if (hasOwnJSON(cause))
|
|
24
26
|
return asLoggableCause(cause.toJSON());
|
|
25
|
-
if (cause instanceof Error) {
|
|
27
|
+
if (cause instanceof Error || (0, argis_1.hasOwn)(cause, 'cause')) {
|
|
26
28
|
const { message, stack, cause: innerCause, ...rest } = cause;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
const r = { message, ...rest };
|
|
30
|
+
if (innerCause) {
|
|
31
|
+
const loggableInner = asLoggableCause(innerCause);
|
|
32
|
+
r.cause = loggableInner;
|
|
33
|
+
r.message ??= loggableInner.message;
|
|
34
|
+
}
|
|
35
|
+
if (stack)
|
|
36
|
+
r.stack = stack;
|
|
37
|
+
return r;
|
|
30
38
|
}
|
|
31
|
-
|
|
32
|
-
const { cause: innerCause, ...rest } = cause;
|
|
33
|
-
return { ...rest, cause: asLoggableCause(innerCause) };
|
|
34
|
-
}
|
|
35
|
-
return { ...cause };
|
|
39
|
+
return { message: cause.toString(), ...cause };
|
|
36
40
|
}
|
|
37
41
|
function asLoggableError(error) {
|
|
38
42
|
if (error instanceof Error)
|
|
39
43
|
return asLoggableCause(error);
|
|
40
|
-
const r =
|
|
44
|
+
const r = error && typeof error === 'object' ? asLoggableCause(error) : { message: error };
|
|
41
45
|
Error.captureStackTrace(r, asLoggableError);
|
|
42
46
|
return Object.defineProperty(r, 'stack', { enumerable: true });
|
|
43
47
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export declare class Stopwatch {
|
|
2
|
+
#private;
|
|
3
|
+
private constructor();
|
|
4
|
+
static startNew(): Stopwatch;
|
|
5
|
+
get elapsed(): Timespan;
|
|
6
|
+
}
|
|
7
|
+
export declare class Timespan {
|
|
8
|
+
#private;
|
|
9
|
+
constructor(value: bigint);
|
|
10
|
+
get hours(): number;
|
|
11
|
+
get minutes(): number;
|
|
12
|
+
get seconds(): number;
|
|
13
|
+
get milliseconds(): number;
|
|
14
|
+
static fromSeconds(seconds: number): Timespan;
|
|
15
|
+
static fromMilliseconds(ms: number): Timespan;
|
|
16
|
+
get totalMinutes(): number;
|
|
17
|
+
get totalSeconds(): number;
|
|
18
|
+
get totalMilliseconds(): number;
|
|
19
|
+
toString(): string;
|
|
20
|
+
toDuration(): string;
|
|
21
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Timespan = exports.Stopwatch = void 0;
|
|
4
|
+
class Stopwatch {
|
|
5
|
+
#startedAt;
|
|
6
|
+
constructor() {
|
|
7
|
+
this.#startedAt = process.hrtime.bigint();
|
|
8
|
+
}
|
|
9
|
+
static startNew() {
|
|
10
|
+
return new Stopwatch();
|
|
11
|
+
}
|
|
12
|
+
get elapsed() {
|
|
13
|
+
return new Timespan(process.hrtime.bigint() - this.#startedAt);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
exports.Stopwatch = Stopwatch;
|
|
17
|
+
class Timespan {
|
|
18
|
+
#value;
|
|
19
|
+
constructor(value) {
|
|
20
|
+
this.#value = value;
|
|
21
|
+
}
|
|
22
|
+
get hours() {
|
|
23
|
+
return Number(this.#value / 1000000000n / 60n / 60n);
|
|
24
|
+
}
|
|
25
|
+
get minutes() {
|
|
26
|
+
return Number((this.#value / 1000000000n / 60n) % 60n);
|
|
27
|
+
}
|
|
28
|
+
get seconds() {
|
|
29
|
+
return Number((this.#value / 1000000000n) % 60n);
|
|
30
|
+
}
|
|
31
|
+
get milliseconds() {
|
|
32
|
+
return Number((this.#value / 1000000n) % 1000n);
|
|
33
|
+
}
|
|
34
|
+
static fromSeconds(seconds) {
|
|
35
|
+
return new Timespan(BigInt(seconds * 1e9));
|
|
36
|
+
}
|
|
37
|
+
static fromMilliseconds(ms) {
|
|
38
|
+
return new Timespan(BigInt(ms * 1e6));
|
|
39
|
+
}
|
|
40
|
+
get totalMinutes() {
|
|
41
|
+
return Number(this.#value) * (1e-9 / 60.0);
|
|
42
|
+
}
|
|
43
|
+
get totalSeconds() {
|
|
44
|
+
return Number(this.#value) * 1e-9;
|
|
45
|
+
}
|
|
46
|
+
get totalMilliseconds() {
|
|
47
|
+
return Number(this.#value) * 1e-6;
|
|
48
|
+
}
|
|
49
|
+
toString() {
|
|
50
|
+
const { hours, minutes, seconds, milliseconds } = this;
|
|
51
|
+
const padz = (x) => (x < 10 ? '0' + x : x);
|
|
52
|
+
return `${padz(hours)}:${padz(minutes)}:${padz(seconds)}.${('00' + milliseconds).slice(-3)}`;
|
|
53
|
+
}
|
|
54
|
+
toDuration() {
|
|
55
|
+
return `${this.totalSeconds}s`;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
exports.Timespan = Timespan;
|
package/lib/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type express from 'express';
|
|
2
|
+
import { Timespan } from './stopwatch.js';
|
|
2
3
|
export * from '@drunkcod/express-async';
|
|
3
4
|
export * from './loggable.js';
|
|
5
|
+
export * from './stopwatch.js';
|
|
4
6
|
type AsyncFn<T> = () => Promise<T>;
|
|
5
7
|
type ExpressServer = ReturnType<express.Application['listen']>;
|
|
6
8
|
export type ErrorHandler = (error: Error, request: express.Request, response: express.Response, next: express.NextFunction) => void;
|
|
@@ -17,3 +19,4 @@ export declare function closeAsync(server: {
|
|
|
17
19
|
close: (cb: (error?: Error) => void) => void;
|
|
18
20
|
}): Promise<void>;
|
|
19
21
|
export declare function registerShutdown<Server extends ExpressServer = ExpressServer>(server: Server, shutdown?: () => Promise<void>): void;
|
|
22
|
+
export declare function requestTimingMiddleware(onRequestFinish: (req: express.Request, duration: Timespan) => void): (req: express.Request, res: express.Response, next: express.NextFunction) => void;
|
package/lib/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { Stopwatch } from './stopwatch.js';
|
|
1
2
|
export * from '@drunkcod/express-async';
|
|
2
3
|
export * from './loggable.js';
|
|
4
|
+
export * from './stopwatch.js';
|
|
3
5
|
export function onceAsync(fn) {
|
|
4
6
|
let p;
|
|
5
7
|
return () => {
|
|
@@ -57,3 +59,10 @@ export function registerShutdown(server, shutdown) {
|
|
|
57
59
|
process.on('SIGINT', onShutdown);
|
|
58
60
|
process.on('SIGTERM', onShutdown);
|
|
59
61
|
}
|
|
62
|
+
export function requestTimingMiddleware(onRequestFinish) {
|
|
63
|
+
return (req, res, next) => {
|
|
64
|
+
const s = Stopwatch.startNew();
|
|
65
|
+
res.on('finish', () => onRequestFinish(req, s.elapsed));
|
|
66
|
+
next();
|
|
67
|
+
};
|
|
68
|
+
}
|
package/lib/loggable.d.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
export declare function at(message?: string): string;
|
|
2
|
-
|
|
2
|
+
type LoggableCause = {
|
|
3
|
+
message: unknown;
|
|
4
|
+
cause?: unknown;
|
|
5
|
+
stack?: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function asLoggableError(error: unknown): LoggableCause;
|
|
3
8
|
export declare function hasOwnJSON(x: object): x is {
|
|
4
9
|
toJSON(): unknown;
|
|
5
10
|
};
|
|
11
|
+
export {};
|
package/lib/loggable.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { hasOwn } from
|
|
1
|
+
import { hasOwn } from '@drunkcod/argis';
|
|
2
2
|
export function at(message) {
|
|
3
3
|
const old = Error.stackTraceLimit;
|
|
4
4
|
try {
|
|
@@ -13,26 +13,30 @@ export function at(message) {
|
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
15
|
function asLoggableCause(cause) {
|
|
16
|
-
if (cause == null
|
|
17
|
-
return
|
|
16
|
+
if (cause == null)
|
|
17
|
+
return null;
|
|
18
|
+
if (typeof cause !== 'object')
|
|
19
|
+
return { message: cause };
|
|
18
20
|
if (hasOwnJSON(cause))
|
|
19
21
|
return asLoggableCause(cause.toJSON());
|
|
20
|
-
if (cause instanceof Error) {
|
|
22
|
+
if (cause instanceof Error || hasOwn(cause, 'cause')) {
|
|
21
23
|
const { message, stack, cause: innerCause, ...rest } = cause;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
const r = { message, ...rest };
|
|
25
|
+
if (innerCause) {
|
|
26
|
+
const loggableInner = asLoggableCause(innerCause);
|
|
27
|
+
r.cause = loggableInner;
|
|
28
|
+
r.message ??= loggableInner.message;
|
|
29
|
+
}
|
|
30
|
+
if (stack)
|
|
31
|
+
r.stack = stack;
|
|
32
|
+
return r;
|
|
25
33
|
}
|
|
26
|
-
|
|
27
|
-
const { cause: innerCause, ...rest } = cause;
|
|
28
|
-
return { ...rest, cause: asLoggableCause(innerCause) };
|
|
29
|
-
}
|
|
30
|
-
return { ...cause };
|
|
34
|
+
return { message: cause.toString(), ...cause };
|
|
31
35
|
}
|
|
32
36
|
export function asLoggableError(error) {
|
|
33
37
|
if (error instanceof Error)
|
|
34
38
|
return asLoggableCause(error);
|
|
35
|
-
const r =
|
|
39
|
+
const r = error && typeof error === 'object' ? asLoggableCause(error) : { message: error };
|
|
36
40
|
Error.captureStackTrace(r, asLoggableError);
|
|
37
41
|
return Object.defineProperty(r, 'stack', { enumerable: true });
|
|
38
42
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export declare class Stopwatch {
|
|
2
|
+
#private;
|
|
3
|
+
private constructor();
|
|
4
|
+
static startNew(): Stopwatch;
|
|
5
|
+
get elapsed(): Timespan;
|
|
6
|
+
}
|
|
7
|
+
export declare class Timespan {
|
|
8
|
+
#private;
|
|
9
|
+
constructor(value: bigint);
|
|
10
|
+
get hours(): number;
|
|
11
|
+
get minutes(): number;
|
|
12
|
+
get seconds(): number;
|
|
13
|
+
get milliseconds(): number;
|
|
14
|
+
static fromSeconds(seconds: number): Timespan;
|
|
15
|
+
static fromMilliseconds(ms: number): Timespan;
|
|
16
|
+
get totalMinutes(): number;
|
|
17
|
+
get totalSeconds(): number;
|
|
18
|
+
get totalMilliseconds(): number;
|
|
19
|
+
toString(): string;
|
|
20
|
+
toDuration(): string;
|
|
21
|
+
}
|
package/lib/stopwatch.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export class Stopwatch {
|
|
2
|
+
#startedAt;
|
|
3
|
+
constructor() {
|
|
4
|
+
this.#startedAt = process.hrtime.bigint();
|
|
5
|
+
}
|
|
6
|
+
static startNew() {
|
|
7
|
+
return new Stopwatch();
|
|
8
|
+
}
|
|
9
|
+
get elapsed() {
|
|
10
|
+
return new Timespan(process.hrtime.bigint() - this.#startedAt);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export class Timespan {
|
|
14
|
+
#value;
|
|
15
|
+
constructor(value) {
|
|
16
|
+
this.#value = value;
|
|
17
|
+
}
|
|
18
|
+
get hours() {
|
|
19
|
+
return Number(this.#value / 1000000000n / 60n / 60n);
|
|
20
|
+
}
|
|
21
|
+
get minutes() {
|
|
22
|
+
return Number((this.#value / 1000000000n / 60n) % 60n);
|
|
23
|
+
}
|
|
24
|
+
get seconds() {
|
|
25
|
+
return Number((this.#value / 1000000000n) % 60n);
|
|
26
|
+
}
|
|
27
|
+
get milliseconds() {
|
|
28
|
+
return Number((this.#value / 1000000n) % 1000n);
|
|
29
|
+
}
|
|
30
|
+
static fromSeconds(seconds) {
|
|
31
|
+
return new Timespan(BigInt(seconds * 1e9));
|
|
32
|
+
}
|
|
33
|
+
static fromMilliseconds(ms) {
|
|
34
|
+
return new Timespan(BigInt(ms * 1e6));
|
|
35
|
+
}
|
|
36
|
+
get totalMinutes() {
|
|
37
|
+
return Number(this.#value) * (1e-9 / 60.0);
|
|
38
|
+
}
|
|
39
|
+
get totalSeconds() {
|
|
40
|
+
return Number(this.#value) * 1e-9;
|
|
41
|
+
}
|
|
42
|
+
get totalMilliseconds() {
|
|
43
|
+
return Number(this.#value) * 1e-6;
|
|
44
|
+
}
|
|
45
|
+
toString() {
|
|
46
|
+
const { hours, minutes, seconds, milliseconds } = this;
|
|
47
|
+
const padz = (x) => (x < 10 ? '0' + x : x);
|
|
48
|
+
return `${padz(hours)}:${padz(minutes)}:${padz(seconds)}.${('00' + milliseconds).slice(-3)}`;
|
|
49
|
+
}
|
|
50
|
+
toDuration() {
|
|
51
|
+
return `${this.totalSeconds}s`;
|
|
52
|
+
}
|
|
53
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@drunkcod/express-kit",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.15",
|
|
5
5
|
"description": "Express4 utility things",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"types": "lib/index.d.ts",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"express-async"
|
|
40
40
|
],
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@drunkcod/argis": "^0.0.
|
|
42
|
+
"@drunkcod/argis": "^0.0.11",
|
|
43
43
|
"@drunkcod/express-async": "^0.0.13"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|