@vida-global/core 1.4.4 → 1.4.6
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/AGENTS.md +13 -0
- package/lib/http/README.md +10 -0
- package/lib/http/client.js +55 -12
- package/lib/http/error.js +19 -1
- package/lib/logger/README.md +33 -2
- package/lib/logger/index.js +99 -20
- package/lib/logger/serverMiddleware.js +39 -0
- package/lib/server/server.js +8 -62
- package/lib/server/serverController.js +129 -7
- package/package.json +5 -5
- package/test/activeRecord/db/schema.test.js +217 -0
- package/test/activeRecord/utils.test.js +72 -0
- package/test/http/client.test.js +72 -3
- package/test/logger/serverMiddleware.test.js +73 -0
- package/test/server/apiDocsGenerator.test.js +38 -0
- package/test/server/server.test.js +186 -4
- package/test/server/serverController.test.js +237 -5
package/AGENTS.md
ADDED
package/lib/http/README.md
CHANGED
|
@@ -30,3 +30,13 @@ const body = {baz: 1, ban: 2};
|
|
|
30
30
|
const response = await client.post("/foo/bar", { body });
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
+
## Aborting requests
|
|
34
|
+
All request methods accept `timeout` (ms) and/or a caller-supplied `signal` (`AbortSignal`). When both are provided, they are composed via `AbortSignal.any` — whichever fires first aborts the request. If the request is aborted (by the timeout or the external signal), an `HttpAbortError` is thrown with `reason` set to either `'timed out'` or `'aborted'`.
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
await client.post("/foo/bar", { body, timeout: 5000 });
|
|
38
|
+
|
|
39
|
+
const ctrl = new AbortController();
|
|
40
|
+
await client.get("/foo/bar", { signal: ctrl.signal });
|
|
41
|
+
```
|
|
42
|
+
|
package/lib/http/client.js
CHANGED
|
@@ -1,38 +1,67 @@
|
|
|
1
|
-
const { logger }
|
|
2
|
-
const { HttpError } = require('./error');
|
|
1
|
+
const { logger } = require('../logger');
|
|
2
|
+
const { HttpError, HttpAbortError } = require('./error');
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
class HttpClient {
|
|
6
|
-
async get(endpoint, { requestParams, headers }={}) {
|
|
6
|
+
async get(endpoint, { requestParams, headers, timeout, signal }={}) {
|
|
7
7
|
return await this.#makeRequest(endpoint, "GET", arguments[1]);
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
async post(endpoint, { body, headers }={}) {
|
|
11
|
+
async post(endpoint, { body, headers, timeout, signal }={}) {
|
|
12
12
|
return await this.#makeRequest(endpoint, "POST", arguments[1]);
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
async put(endpoint, { body, headers }={}) {
|
|
16
|
+
async put(endpoint, { body, headers, timeout, signal }={}) {
|
|
17
17
|
return await this.#makeRequest(endpoint, "PUT", arguments[1]);
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
async delete(endpoint, { requestParams, headers }={}) {
|
|
21
|
+
async delete(endpoint, { requestParams, headers, timeout, signal }={}) {
|
|
22
22
|
return await this.#makeRequest(endpoint, "DELETE", arguments[1]);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
async #makeRequest(endpoint, method, { requestParams, body, headers }={}) {
|
|
26
|
+
async #makeRequest(endpoint, method, { requestParams, body, headers, timeout, signal }={}) {
|
|
27
27
|
this.#logRequest(method, endpoint);
|
|
28
28
|
|
|
29
29
|
endpoint = `${this.urlRoot}${endpoint}`;
|
|
30
30
|
const url = new URL(endpoint);
|
|
31
31
|
const requestData = structuredClone(requestParams || body || {});
|
|
32
|
-
const payload = this.#prepareRequestPayload(url, requestData, headers, method);
|
|
32
|
+
const payload = this.#prepareRequestPayload(url, requestData, headers, method, timeout, signal);
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
try {
|
|
35
|
+
const res = await fetch(url.toString(), payload);
|
|
36
|
+
return await this.#handleRequestResponse(res, payload);
|
|
37
|
+
} catch (err) {
|
|
38
|
+
this.#handleRequestError(err, payload, timeout, signal);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
#handleRequestError(err, payload, timeout, externalSignal) {
|
|
44
|
+
if (this.#isAbortError(err)) {
|
|
45
|
+
const msg = this.#classifyAbortReason(timeout, externalSignal)
|
|
46
|
+
throw new HttpAbortError(msg, payload);
|
|
47
|
+
}
|
|
48
|
+
throw err;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
#isAbortError(err) {
|
|
53
|
+
return err instanceof DOMException && (err.name === 'AbortError' || err.name === 'TimeoutError');
|
|
54
|
+
}
|
|
35
55
|
|
|
56
|
+
|
|
57
|
+
#classifyAbortReason(timeout, externalSignal) {
|
|
58
|
+
if (externalSignal?.aborted) return 'aborted';
|
|
59
|
+
if (timeout) return 'timed out';
|
|
60
|
+
return 'aborted';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
async #handleRequestResponse(res, payload) {
|
|
36
65
|
if (res.status && res.status < 300) {
|
|
37
66
|
const json = await res.json();
|
|
38
67
|
return {data: json, status: res.status};
|
|
@@ -42,7 +71,7 @@ class HttpClient {
|
|
|
42
71
|
}
|
|
43
72
|
|
|
44
73
|
|
|
45
|
-
#prepareRequestPayload(url, requestData, headers, method) {
|
|
74
|
+
#prepareRequestPayload(url, requestData, headers, method, timeout, signal) {
|
|
46
75
|
const payload = {};
|
|
47
76
|
|
|
48
77
|
headers ||= {};
|
|
@@ -50,10 +79,23 @@ class HttpClient {
|
|
|
50
79
|
payload.method = method;
|
|
51
80
|
this.#addRequestData(payload, requestData, method, url, payload.headers);
|
|
52
81
|
|
|
82
|
+
const composedSignal = this.#composeSignal(signal, timeout);
|
|
83
|
+
if (composedSignal) payload.signal = composedSignal;
|
|
84
|
+
|
|
53
85
|
return payload;
|
|
54
86
|
}
|
|
55
87
|
|
|
56
88
|
|
|
89
|
+
#composeSignal(externalSignal, timeoutMs) {
|
|
90
|
+
const signals = [];
|
|
91
|
+
if (externalSignal) signals.push(externalSignal);
|
|
92
|
+
if (timeoutMs) signals.push(AbortSignal.timeout(timeoutMs));
|
|
93
|
+
if (signals.length === 0) return undefined;
|
|
94
|
+
if (signals.length === 1) return signals[0];
|
|
95
|
+
return AbortSignal.any(signals);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
|
|
57
99
|
#addRequestData(payload, requestData, method, url, headers) {
|
|
58
100
|
if (method == "POST" || method == "PUT") {
|
|
59
101
|
payload.body = JSON.stringify(requestData || {});
|
|
@@ -123,7 +165,8 @@ class HttpClient {
|
|
|
123
165
|
}
|
|
124
166
|
|
|
125
167
|
|
|
126
|
-
module.exports = {
|
|
168
|
+
module.exports = {
|
|
127
169
|
HttpClient,
|
|
128
|
-
HttpError
|
|
170
|
+
HttpError,
|
|
171
|
+
HttpAbortError
|
|
129
172
|
}
|
package/lib/http/error.js
CHANGED
|
@@ -29,6 +29,24 @@ class HttpError extends Error {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
|
|
32
|
+
class HttpAbortError extends Error {
|
|
33
|
+
#requestPayload;
|
|
34
|
+
#reason;
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
constructor(reason, requestPayload) {
|
|
38
|
+
super(`HTTP request ${reason}`);
|
|
39
|
+
this.#reason = reason;
|
|
40
|
+
this.#requestPayload = requestPayload;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
get reason() { return this.#reason; }
|
|
45
|
+
get requestPayload() { return structuredClone(this.#requestPayload); }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
|
|
32
49
|
module.exports = {
|
|
33
|
-
HttpError
|
|
50
|
+
HttpError,
|
|
51
|
+
HttpAbortError
|
|
34
52
|
}
|
package/lib/logger/README.md
CHANGED
|
@@ -1,2 +1,33 @@
|
|
|
1
|
-
|
|
2
|
-
A
|
|
1
|
+
# Logger
|
|
2
|
+
A simple logger that support multiple scopes and log levels. Set the `LOG_LEVEL` environment variable to determine what log levels are output.
|
|
3
|
+
|
|
4
|
+
## Scopes
|
|
5
|
+
```
|
|
6
|
+
logger.addScope('foo');
|
|
7
|
+
logger.addScope('bar');
|
|
8
|
+
|
|
9
|
+
logger.foo.debug('my log');
|
|
10
|
+
// [2026-04-30 16:16:12.797 -0700][DEBUG][FOO] my log
|
|
11
|
+
|
|
12
|
+
logger.bar.verbose('my log');
|
|
13
|
+
// [2026-04-30 16:16:12.797 -0700][VERBOSE][BAR] my log
|
|
14
|
+
|
|
15
|
+
logger.bar.info('my log');
|
|
16
|
+
// [2026-04-30 16:16:12.797 -0700][INFO][BAR] my log
|
|
17
|
+
|
|
18
|
+
logger.bar.warn('my log');
|
|
19
|
+
// [2026-04-30 16:16:12.797 -0700][WARN][BAR] my log
|
|
20
|
+
|
|
21
|
+
logger.bar.error('my log');
|
|
22
|
+
// [2026-04-30 16:16:12.797 -0700][ERROR][BAR] my log
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## IDs
|
|
26
|
+
```
|
|
27
|
+
logger.addScope('http');
|
|
28
|
+
const requestId = 123;
|
|
29
|
+
const requestLogger = logger.http.createChild(requestId);
|
|
30
|
+
|
|
31
|
+
requestLogger.debug('my log');
|
|
32
|
+
// [2026-04-30 16:16:12.797 -0700][DEBUG][HTTP][123] my log
|
|
33
|
+
``
|
package/lib/logger/index.js
CHANGED
|
@@ -1,25 +1,104 @@
|
|
|
1
|
-
const
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
1
|
+
const winston = require('winston');
|
|
2
|
+
const { colorize, combine, printf, timestamp } = winston.format;
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Logger {
|
|
6
|
+
#id;
|
|
7
|
+
#logger;
|
|
8
|
+
#scope;
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
constructor(scope, id) {
|
|
12
|
+
this.#id = id;
|
|
13
|
+
this.#scope = scope || 'API';
|
|
14
|
+
this.#logger = winston.createLogger(this.loggerOptions);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
get scope() { return this.#scope; }
|
|
19
|
+
get id() { return this.#id; }
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
get loggerOptions() {
|
|
23
|
+
const format = combine(...this.formatters);
|
|
24
|
+
const level = this.level;
|
|
25
|
+
const transports = this.transports;
|
|
26
|
+
|
|
27
|
+
return { format, level, transports };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
get formatters() {
|
|
32
|
+
const colors = {
|
|
33
|
+
debug: 'magenta',
|
|
34
|
+
verbose: 'green',
|
|
35
|
+
info: 'cyan',
|
|
36
|
+
warn: 'yellow',
|
|
37
|
+
error: 'red',
|
|
38
|
+
}
|
|
39
|
+
const messageFormatter = winston.format(this.messageFormatter.bind(this));
|
|
40
|
+
return [
|
|
41
|
+
timestamp({format: 'YYYY-MM-DD HH:mm:ss.SSS ZZ'}),
|
|
42
|
+
messageFormatter(),
|
|
43
|
+
colorize({all: true, colors}),
|
|
44
|
+
printf(({ message }) => message)
|
|
45
|
+
];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
messageFormatter(info) {
|
|
50
|
+
const level = info.level.toUpperCase()
|
|
51
|
+
|
|
52
|
+
let prefix = `[${info.timestamp}][${level}][${this.scope.toUpperCase()}]`;
|
|
53
|
+
if (this.id) prefix = `${prefix}[${this.id}]`;
|
|
54
|
+
|
|
55
|
+
info.message = `${prefix} ${info.message}`;
|
|
56
|
+
return info;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
get level() {
|
|
61
|
+
return process.env.LOG_LEVEL || 'info';
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
get transports() {
|
|
66
|
+
const transports = [ new winston.transports.Console() ];
|
|
67
|
+
return transports;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
addScope(scope) {
|
|
72
|
+
const existing = this[scope];
|
|
73
|
+
if (existing && !existing instanceof Logger) {
|
|
74
|
+
throw `Property already exists at ${scope}`;
|
|
18
75
|
}
|
|
19
|
-
|
|
76
|
+
|
|
77
|
+
const logger = new Logger(scope, this.id);
|
|
78
|
+
Object.defineProperty(this, scope, { get: () => logger });
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
createChild(id) {
|
|
83
|
+
return new Logger(this.scope, id);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
debug(msg) { this.#logger.debug(msg) }
|
|
88
|
+
error(msg) { this.#logger.error(msg) }
|
|
89
|
+
info(msg) { this.#logger.info(msg) }
|
|
90
|
+
silly(msg) { this.#logger.silly(msg) }
|
|
91
|
+
verbose(msg) { this.#logger.verbose(msg) }
|
|
92
|
+
warn(msg) { this.#logger.warn(msg) }
|
|
93
|
+
|
|
94
|
+
get _winstonLogger() {
|
|
95
|
+
this.warn('Directly accessing the underlying winston logger should be avoided unless necessary');
|
|
96
|
+
return this.#logger
|
|
97
|
+
}
|
|
20
98
|
}
|
|
21
99
|
|
|
22
|
-
|
|
100
|
+
|
|
101
|
+
const logger = new Logger();
|
|
23
102
|
|
|
24
103
|
|
|
25
|
-
module.exports = { logger };
|
|
104
|
+
module.exports = { logger, Logger };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const { logger } = require('./index');
|
|
2
|
+
const expressWinston = require('express-winston');
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
const msgFormatter = (req, res) => {
|
|
6
|
+
let msg;
|
|
7
|
+
if (req.controller) {
|
|
8
|
+
msg = `${req.controller.constructor.name}#${req.action}`;
|
|
9
|
+
} else {
|
|
10
|
+
msg = res.statusCode >= 500 ? 'request errored' : 'request completed';
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const headerPcs = res._header.split("\n").map(h => h.split(/:(.*)/));
|
|
14
|
+
const headers = Object.fromEntries(headerPcs);
|
|
15
|
+
const responseTime = headers['X-Response-Time'];
|
|
16
|
+
|
|
17
|
+
const requestDetails = `req: ${req.method} ${req.url.split('?')[0]} for ${req.ip}`;
|
|
18
|
+
const responseDetails = `res: statusCode=${res.statusCode} responseTime=${responseTime}`;
|
|
19
|
+
|
|
20
|
+
msg = `${msg}\n ${requestDetails}\n ${responseDetails}`;
|
|
21
|
+
if (res.error) {
|
|
22
|
+
msg = `${msg}\n${res.error.stack}`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return msg;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
logger.addScope('http');
|
|
30
|
+
const middleware = expressWinston.logger({
|
|
31
|
+
level: 'debug',
|
|
32
|
+
msg: msgFormatter,
|
|
33
|
+
winstonInstance: logger.http._winstonLogger,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
module.exports = {
|
|
38
|
+
middleware
|
|
39
|
+
}
|
package/lib/server/server.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
const { ControllerImporter } = require('./controllerImporter');
|
|
2
2
|
const express = require('express');
|
|
3
|
-
const httpLogger = require('pino-http')
|
|
4
3
|
const { logger } = require('../logger');
|
|
4
|
+
const loggingMiddleware = require('../logger/serverMiddleware');
|
|
5
5
|
const mustacheExpress = require('mustache-express');
|
|
6
|
-
const { pino } = require('pino');
|
|
7
6
|
const responseTime = require('response-time');
|
|
8
7
|
const IoServer = require("socket.io")
|
|
9
8
|
const { Server } = require('http');
|
|
10
9
|
const { SystemController } = require('./systemController');
|
|
10
|
+
const requestID = require( 'express-request-id');
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class VidaServer {
|
|
@@ -31,7 +31,7 @@ class VidaServer {
|
|
|
31
31
|
await this.registerControllers();
|
|
32
32
|
|
|
33
33
|
this.#httpServer.listen(this.#port, this.#host, () => {
|
|
34
|
-
|
|
34
|
+
logger.info(`Server is running on port ${this.#port}`);
|
|
35
35
|
if (callback) callback();
|
|
36
36
|
});
|
|
37
37
|
}
|
|
@@ -39,7 +39,7 @@ class VidaServer {
|
|
|
39
39
|
|
|
40
40
|
get host() { return this.#host; }
|
|
41
41
|
get port() { return this.#port; }
|
|
42
|
-
get logger() { return logger; }
|
|
42
|
+
get logger() { return logger.http; }
|
|
43
43
|
|
|
44
44
|
|
|
45
45
|
/***********************************************************************************************
|
|
@@ -72,11 +72,12 @@ class VidaServer {
|
|
|
72
72
|
setupMiddleware() {
|
|
73
73
|
this.use(this.jsonParsingMiddleware);
|
|
74
74
|
this.use(this.octetStreamParsingMiddleware);
|
|
75
|
-
this.use(responseTime())
|
|
75
|
+
this.use(responseTime({suffix: true}))
|
|
76
76
|
this.use(express.static('public'))
|
|
77
77
|
this.use(this.loggingMiddleware);
|
|
78
78
|
this.use('/static', express.static(this.staticFilesDirectory));
|
|
79
79
|
this.use(this.connectionAbortedMiddleware);
|
|
80
|
+
this.use(requestID());
|
|
80
81
|
|
|
81
82
|
this.#expressServer.engine('html', mustacheExpress());
|
|
82
83
|
}
|
|
@@ -105,15 +106,7 @@ class VidaServer {
|
|
|
105
106
|
|
|
106
107
|
|
|
107
108
|
get loggingMiddleware() {
|
|
108
|
-
|
|
109
|
-
logger: this.middlewareLogger,
|
|
110
|
-
customLogLevel: this.requestLogLevel.bind(this),
|
|
111
|
-
customSuccessMessage: this.requestLogMessage.bind(this),
|
|
112
|
-
customErrorMessage: this.requestLogMessage.bind(this),
|
|
113
|
-
customErrorObject: this.requestLogDetails.bind(this),
|
|
114
|
-
customSuccessObject: this.requestLogDetails.bind(this),
|
|
115
|
-
wrapSerializers: false,
|
|
116
|
-
})
|
|
109
|
+
return loggingMiddleware.middleware
|
|
117
110
|
}
|
|
118
111
|
|
|
119
112
|
|
|
@@ -135,53 +128,6 @@ class VidaServer {
|
|
|
135
128
|
use() { this.#expressServer.use(...arguments); }
|
|
136
129
|
|
|
137
130
|
|
|
138
|
-
/***********************************************************************************************
|
|
139
|
-
* LOGGING
|
|
140
|
-
***********************************************************************************************/
|
|
141
|
-
requestLogDetails(req, res, err) {
|
|
142
|
-
const details = {
|
|
143
|
-
req: `${req.method} ${req.url}`,
|
|
144
|
-
res: `statusCode=${res.statusCode}, responseTime=${res.get('X-Response-Time')}`,
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
if (res.statusCode >= 500 && res.error) {
|
|
148
|
-
details.error = this.requestLogErrorDetails(req, res, res.error);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return details;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
requestLogErrorDetails(req, res, err) {
|
|
156
|
-
if (typeof err == 'string') return {message: err};
|
|
157
|
-
return {
|
|
158
|
-
type: err.constructor.name,
|
|
159
|
-
message: err.message,
|
|
160
|
-
stack: err.stack
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
requestLogMessage(req, res) {
|
|
166
|
-
const prefix = '[VidaServer]';
|
|
167
|
-
if (req.controller) {
|
|
168
|
-
return `${prefix} ${req.controller.constructor.name}#${req.action}`;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
return `${prefix} ${res.status >= 500 ? 'request errored' : 'request completed'}`;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
requestLogLevel(req, res) {
|
|
176
|
-
return 'debug';
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
get middlewareLogger() {
|
|
181
|
-
return logger;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
|
|
185
131
|
/***********************************************************************************************
|
|
186
132
|
* SETTINGS
|
|
187
133
|
***********************************************************************************************/
|
|
@@ -234,7 +180,7 @@ class VidaServer {
|
|
|
234
180
|
const method = action.method.toLowerCase();
|
|
235
181
|
const requestHandler = this.requestHandler(action.action, controllerCls)
|
|
236
182
|
if (process.env.NODE_ENV != 'test') {
|
|
237
|
-
logger.
|
|
183
|
+
logger.verbose(`ROUTE: ${method.toUpperCase().padEnd(6)} ${action.path}`);
|
|
238
184
|
}
|
|
239
185
|
this['_'+method](action.path, requestHandler);
|
|
240
186
|
}
|