@appsemble/node-utils 0.20.8 → 0.20.11
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/{dist/AppsembleError.d.ts → AppsembleError.d.ts} +1 -1
- package/{dist/AppsembleError.js → AppsembleError.js} +1 -1
- package/AppsembleError.js.map +1 -0
- package/AppsembleError.test.d.ts +1 -0
- package/AppsembleError.test.js +8 -0
- package/AppsembleError.test.js.map +1 -0
- package/AppsembleError.test.ts +6 -0
- package/AppsembleError.ts +16 -0
- package/__fixtures__/hello.txt +1 -0
- package/__fixtures__/invalid-json.json +3 -0
- package/__fixtures__/json.json +1 -0
- package/__fixtures__/test/bar.txt +0 -0
- package/__fixtures__/test/foo.txt +0 -0
- package/__fixtures__/test/z/baz.txt +0 -0
- package/__fixtures__/test/z/fooz.txt +0 -0
- package/__fixtures__/yaml.yaml +1 -0
- package/__mocks__/os.d.ts +5 -0
- package/__mocks__/os.js +8 -0
- package/__mocks__/os.js.map +1 -0
- package/__mocks__/os.ts +10 -0
- package/{dist/basicAuth.d.ts → basicAuth.d.ts} +2 -2
- package/{dist/basicAuth.js → basicAuth.js} +2 -2
- package/basicAuth.js.map +1 -0
- package/basicAuth.test.d.ts +1 -0
- package/basicAuth.test.js +8 -0
- package/basicAuth.test.js.map +1 -0
- package/basicAuth.test.ts +6 -0
- package/basicAuth.ts +10 -0
- package/changed/added/.gitkeep +0 -0
- package/changed/changed/.gitkeep +0 -0
- package/changed/deprecated/.gitkeep +0 -0
- package/changed/fixed/.gitkeep +0 -0
- package/changed/removed/.gitkeep +0 -0
- package/changed/security/.gitkeep +0 -0
- package/{dist/formData.d.ts → formData.d.ts} +1 -1
- package/{dist/formData.js → formData.js} +1 -1
- package/formData.js.map +1 -0
- package/formData.test.d.ts +1 -0
- package/formData.test.js +94 -0
- package/formData.test.js.map +1 -0
- package/formData.test.ts +113 -0
- package/formData.ts +37 -0
- package/{dist/fs.d.ts → fs.d.ts} +11 -10
- package/{dist/fs.js → fs.js} +16 -16
- package/fs.js.map +1 -0
- package/fs.test.d.ts +1 -0
- package/fs.test.js +76 -0
- package/fs.test.js.map +1 -0
- package/fs.test.ts +120 -0
- package/fs.ts +144 -0
- package/{dist/getWorkspaces.d.ts → getWorkspaces.d.ts} +1 -1
- package/{dist/getWorkspaces.js → getWorkspaces.js} +3 -3
- package/getWorkspaces.js.map +1 -0
- package/getWorkspaces.ts +23 -0
- package/{dist/handleError.d.ts → handleError.d.ts} +3 -3
- package/{dist/handleError.js → handleError.js} +3 -3
- package/handleError.js.map +1 -0
- package/handleError.test.d.ts +1 -0
- package/handleError.test.js +34 -0
- package/handleError.test.js.map +1 -0
- package/handleError.test.ts +38 -0
- package/handleError.ts +26 -0
- package/{dist/index.d.ts → index.d.ts} +0 -0
- package/{dist/index.js → index.js} +0 -0
- package/index.js.map +1 -0
- package/index.ts +11 -0
- package/{dist/interceptors.d.ts → interceptors.d.ts} +5 -5
- package/{dist/interceptors.js → interceptors.js} +5 -5
- package/interceptors.js.map +1 -0
- package/interceptors.test.d.ts +1 -0
- package/interceptors.test.js +93 -0
- package/interceptors.test.js.map +1 -0
- package/interceptors.test.ts +108 -0
- package/interceptors.ts +81 -0
- package/jest.config.js +1 -0
- package/jest.setup.d.ts +1 -0
- package/jest.setup.js +9 -0
- package/jest.setup.js.map +1 -0
- package/jest.setup.ts +7 -0
- package/{dist/logger.d.ts → logger.d.ts} +2 -2
- package/{dist/logger.js → logger.js} +6 -6
- package/logger.js.map +1 -0
- package/logger.test.d.ts +1 -0
- package/logger.test.js +38 -0
- package/logger.test.js.map +1 -0
- package/logger.test.ts +43 -0
- package/logger.ts +130 -0
- package/{dist/loggerMiddleware.d.ts → loggerMiddleware.d.ts} +0 -0
- package/{dist/loggerMiddleware.js → loggerMiddleware.js} +18 -15
- package/loggerMiddleware.js.map +1 -0
- package/loggerMiddleware.test.d.ts +1 -0
- package/loggerMiddleware.test.js +130 -0
- package/loggerMiddleware.test.js.map +1 -0
- package/loggerMiddleware.test.ts +185 -0
- package/loggerMiddleware.ts +93 -0
- package/package.json +6 -5
- package/{dist/readFileOrString.d.ts → readFileOrString.d.ts} +1 -1
- package/{dist/readFileOrString.js → readFileOrString.js} +3 -3
- package/readFileOrString.js.map +1 -0
- package/readFileOrString.test.d.ts +1 -0
- package/readFileOrString.test.js +13 -0
- package/readFileOrString.test.js.map +1 -0
- package/readFileOrString.test.ts +12 -0
- package/readFileOrString.ts +20 -0
- package/{dist/testFixtures.d.ts → testFixtures.d.ts} +6 -5
- package/{dist/testFixtures.js → testFixtures.js} +5 -4
- package/testFixtures.js.map +1 -0
- package/testFixtures.ts +58 -0
- package/tsconfig.json +6 -0
- package/dist/AppsembleError.js.map +0 -1
- package/dist/basicAuth.js.map +0 -1
- package/dist/formData.js.map +0 -1
- package/dist/fs.js.map +0 -1
- package/dist/getWorkspaces.js.map +0 -1
- package/dist/handleError.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/interceptors.js.map +0 -1
- package/dist/logger.js.map +0 -1
- package/dist/loggerMiddleware.js.map +0 -1
- package/dist/readFileOrString.js.map +0 -1
- package/dist/testFixtures.js.map +0 -1
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.loggerMiddleware = void 0;
|
|
4
|
-
const chalk_1 = require("chalk");
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
5
8
|
const logger_1 = require("./logger");
|
|
6
9
|
function rangeFormat(value, map) {
|
|
7
10
|
return Object.entries(map).find(([v]) => v === 'default' || value < Number(v))[1];
|
|
@@ -15,8 +18,8 @@ function loggerMiddleware() {
|
|
|
15
18
|
return (ctx, next) => {
|
|
16
19
|
const { href, res } = ctx;
|
|
17
20
|
const start = Date.now();
|
|
18
|
-
const method =
|
|
19
|
-
logger_1.logger.info(`${method} ${href} — ${
|
|
21
|
+
const method = chalk_1.default.bold(ctx.method);
|
|
22
|
+
logger_1.logger.info(`${method} ${href} — ${chalk_1.default.white(ctx.ip)}`);
|
|
20
23
|
function logResponse() {
|
|
21
24
|
res.removeListener('finish', logResponse);
|
|
22
25
|
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
@@ -25,16 +28,16 @@ function loggerMiddleware() {
|
|
|
25
28
|
const s = `${status} ${message}`;
|
|
26
29
|
const duration = Date.now() - start;
|
|
27
30
|
const formatDuration = rangeFormat(duration, {
|
|
28
|
-
100: chalk_1.green,
|
|
29
|
-
1000: chalk_1.yellow,
|
|
30
|
-
default: chalk_1.red,
|
|
31
|
+
100: chalk_1.default.green,
|
|
32
|
+
1000: chalk_1.default.yellow,
|
|
33
|
+
default: chalk_1.default.red,
|
|
31
34
|
});
|
|
32
35
|
const formatStatus = rangeFormat(status, {
|
|
33
|
-
200: chalk_1.red,
|
|
34
|
-
300: chalk_1.green,
|
|
35
|
-
400: chalk_1.cyan,
|
|
36
|
-
500: chalk_1.yellow,
|
|
37
|
-
default: chalk_1.red,
|
|
36
|
+
200: chalk_1.default.red,
|
|
37
|
+
300: chalk_1.default.green,
|
|
38
|
+
400: chalk_1.default.cyan,
|
|
39
|
+
500: chalk_1.default.yellow,
|
|
40
|
+
default: chalk_1.default.red,
|
|
38
41
|
});
|
|
39
42
|
const level = rangeFormat(status, {
|
|
40
43
|
200: 'error',
|
|
@@ -49,11 +52,11 @@ function loggerMiddleware() {
|
|
|
49
52
|
res.removeListener('close', logCancel);
|
|
50
53
|
const duration = Date.now() - start;
|
|
51
54
|
const formatDuration = rangeFormat(duration, {
|
|
52
|
-
100: chalk_1.green,
|
|
53
|
-
1000: chalk_1.yellow,
|
|
54
|
-
default: chalk_1.red,
|
|
55
|
+
100: chalk_1.default.green,
|
|
56
|
+
1000: chalk_1.default.yellow,
|
|
57
|
+
default: chalk_1.default.red,
|
|
55
58
|
});
|
|
56
|
-
logger_1.logger.warn(`${method} ${href} ${
|
|
59
|
+
logger_1.logger.warn(`${method} ${href} ${chalk_1.default.grey('Cancelled')} ${formatDuration(`${duration}ms`)}`);
|
|
57
60
|
}
|
|
58
61
|
res.once('finish', logResponse);
|
|
59
62
|
res.once('close', logCancel);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loggerMiddleware.js","sourceRoot":"","sources":["loggerMiddleware.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const fake_timers_1 = require("@sinonjs/fake-timers");
|
|
7
|
+
const axios_test_instance_1 = require("axios-test-instance");
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const koa_1 = __importDefault(require("koa"));
|
|
10
|
+
const logger_1 = require("./logger");
|
|
11
|
+
const loggerMiddleware_1 = require("./loggerMiddleware");
|
|
12
|
+
class TestError extends Error {
|
|
13
|
+
constructor(message) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.name = 'TestError';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
let app;
|
|
19
|
+
let clock;
|
|
20
|
+
beforeEach(async () => {
|
|
21
|
+
jest.spyOn(logger_1.logger, 'info').mockImplementation(() => logger_1.logger);
|
|
22
|
+
jest.spyOn(logger_1.logger, 'log').mockImplementation(() => logger_1.logger);
|
|
23
|
+
clock = (0, fake_timers_1.install)();
|
|
24
|
+
app = new koa_1.default();
|
|
25
|
+
app.use(async (ctx, next) => {
|
|
26
|
+
Object.defineProperty(ctx.request, 'origin', {
|
|
27
|
+
value: 'https://example.com:1337',
|
|
28
|
+
});
|
|
29
|
+
try {
|
|
30
|
+
await next();
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
if (!(error instanceof TestError)) {
|
|
34
|
+
throw error;
|
|
35
|
+
}
|
|
36
|
+
ctx.status = 400;
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
app.use((0, loggerMiddleware_1.loggerMiddleware)());
|
|
40
|
+
app.silent = true;
|
|
41
|
+
axios_test_instance_1.request.defaults.maxRedirects = 0;
|
|
42
|
+
await (0, axios_test_instance_1.setTestApp)(app);
|
|
43
|
+
});
|
|
44
|
+
afterEach(() => {
|
|
45
|
+
clock.uninstall();
|
|
46
|
+
});
|
|
47
|
+
it('should log requests', async () => {
|
|
48
|
+
await axios_test_instance_1.request.get('/pizza');
|
|
49
|
+
expect(logger_1.logger.info).toHaveBeenCalledWith(`${chalk_1.default.bold('GET')} https://example.com:1337/pizza — ${chalk_1.default.white('127.0.0.1')}`);
|
|
50
|
+
});
|
|
51
|
+
it('should log success responses as info', async () => {
|
|
52
|
+
app.use((ctx) => {
|
|
53
|
+
clock.tick(1);
|
|
54
|
+
ctx.status = 200;
|
|
55
|
+
});
|
|
56
|
+
await axios_test_instance_1.request.get('/fries');
|
|
57
|
+
expect(logger_1.logger.log).toHaveBeenCalledWith('info', `${chalk_1.default.bold('GET')} https://example.com:1337/fries ${chalk_1.default.green('200 OK')} ${chalk_1.default.green('1ms')}`);
|
|
58
|
+
});
|
|
59
|
+
it('should log redirect responses as info', async () => {
|
|
60
|
+
app.use((ctx) => {
|
|
61
|
+
clock.tick(33);
|
|
62
|
+
ctx.redirect('/');
|
|
63
|
+
});
|
|
64
|
+
await axios_test_instance_1.request.get('/fries');
|
|
65
|
+
expect(logger_1.logger.log).toHaveBeenCalledWith('info', `${chalk_1.default.bold('GET')} https://example.com:1337/fries ${chalk_1.default.cyan('302 Found → /')} ${chalk_1.default.green('33ms')}`);
|
|
66
|
+
});
|
|
67
|
+
it('should log bad responses as warn', async () => {
|
|
68
|
+
app.use((ctx) => {
|
|
69
|
+
clock.tick(3);
|
|
70
|
+
ctx.status = 400;
|
|
71
|
+
});
|
|
72
|
+
await axios_test_instance_1.request.get('/burrito');
|
|
73
|
+
expect(logger_1.logger.log).toHaveBeenCalledWith('warn', `${chalk_1.default.bold('GET')} https://example.com:1337/burrito ${chalk_1.default.yellow('400 Bad Request')} ${chalk_1.default.green('3ms')}`);
|
|
74
|
+
});
|
|
75
|
+
it('should log error responses as error', async () => {
|
|
76
|
+
app.use((ctx) => {
|
|
77
|
+
clock.tick(53);
|
|
78
|
+
ctx.status = 503;
|
|
79
|
+
});
|
|
80
|
+
await axios_test_instance_1.request.get('/wrap');
|
|
81
|
+
expect(logger_1.logger.log).toHaveBeenCalledWith('error', `${chalk_1.default.bold('GET')} https://example.com:1337/wrap ${chalk_1.default.red('503 Service Unavailable')} ${chalk_1.default.green('53ms')}`);
|
|
82
|
+
});
|
|
83
|
+
it('should log long request lengths yellow', async () => {
|
|
84
|
+
app.use((ctx) => {
|
|
85
|
+
clock.tick(400);
|
|
86
|
+
ctx.status = 200;
|
|
87
|
+
});
|
|
88
|
+
await axios_test_instance_1.request.get('/banana');
|
|
89
|
+
expect(logger_1.logger.log).toHaveBeenCalledWith('info', `${chalk_1.default.bold('GET')} https://example.com:1337/banana ${chalk_1.default.green('200 OK')} ${chalk_1.default.yellow('400ms')}`);
|
|
90
|
+
});
|
|
91
|
+
it('should log extremely long request lengths red', async () => {
|
|
92
|
+
app.use((ctx) => {
|
|
93
|
+
clock.tick(1337);
|
|
94
|
+
ctx.status = 200;
|
|
95
|
+
});
|
|
96
|
+
await axios_test_instance_1.request.get('/pepperoni');
|
|
97
|
+
expect(logger_1.logger.log).toHaveBeenCalledWith('info', `${chalk_1.default.bold('GET')} https://example.com:1337/pepperoni ${chalk_1.default.green('200 OK')} ${chalk_1.default.red('1337ms')}`);
|
|
98
|
+
});
|
|
99
|
+
it('should log errors as internal server errors and rethrow', async () => {
|
|
100
|
+
const spy = jest.fn();
|
|
101
|
+
const error = new Error('fail');
|
|
102
|
+
let context;
|
|
103
|
+
app.on('error', spy);
|
|
104
|
+
app.use((ctx) => {
|
|
105
|
+
clock.tick(86);
|
|
106
|
+
context = ctx;
|
|
107
|
+
throw error;
|
|
108
|
+
});
|
|
109
|
+
await axios_test_instance_1.request.get('/taco');
|
|
110
|
+
expect(spy).toHaveBeenCalledWith(error, context);
|
|
111
|
+
expect(logger_1.logger.log).toHaveBeenCalledWith('error', `${chalk_1.default.bold('GET')} https://example.com:1337/taco ${chalk_1.default.red('500 Internal Server Error')} ${chalk_1.default.green('86ms')}`);
|
|
112
|
+
});
|
|
113
|
+
it('should append the response length if it is defined', async () => {
|
|
114
|
+
app.use((ctx) => {
|
|
115
|
+
clock.tick(1);
|
|
116
|
+
ctx.status = 200;
|
|
117
|
+
ctx.body = '{}';
|
|
118
|
+
});
|
|
119
|
+
await axios_test_instance_1.request.get('/fries');
|
|
120
|
+
expect(logger_1.logger.log).toHaveBeenCalledWith('info', `${chalk_1.default.bold('GET')} https://example.com:1337/fries ${chalk_1.default.green('200 OK')} ${chalk_1.default.green('1ms')}`);
|
|
121
|
+
});
|
|
122
|
+
it('should log handled errors correctly', async () => {
|
|
123
|
+
app.use(() => {
|
|
124
|
+
clock.tick(15);
|
|
125
|
+
throw new TestError();
|
|
126
|
+
});
|
|
127
|
+
await axios_test_instance_1.request.get('potatoes');
|
|
128
|
+
expect(logger_1.logger.log).toHaveBeenCalledWith('warn', `${chalk_1.default.bold('GET')} https://example.com:1337/potatoes ${chalk_1.default.yellow('400 Bad Request')} ${chalk_1.default.green('15ms')}`);
|
|
129
|
+
});
|
|
130
|
+
//# sourceMappingURL=loggerMiddleware.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loggerMiddleware.test.js","sourceRoot":"","sources":["loggerMiddleware.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { install, InstalledClock } from '@sinonjs/fake-timers';
|
|
2
|
+
import { request, setTestApp } from 'axios-test-instance';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import Koa from 'koa';
|
|
5
|
+
|
|
6
|
+
import { logger } from './logger';
|
|
7
|
+
import { loggerMiddleware } from './loggerMiddleware';
|
|
8
|
+
|
|
9
|
+
class TestError extends Error {
|
|
10
|
+
constructor(message?: string) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.name = 'TestError';
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let app: Koa;
|
|
17
|
+
let clock: InstalledClock;
|
|
18
|
+
|
|
19
|
+
beforeEach(async () => {
|
|
20
|
+
jest.spyOn(logger, 'info').mockImplementation(() => logger);
|
|
21
|
+
jest.spyOn(logger, 'log').mockImplementation(() => logger);
|
|
22
|
+
clock = install();
|
|
23
|
+
app = new Koa();
|
|
24
|
+
app.use(async (ctx, next) => {
|
|
25
|
+
Object.defineProperty(ctx.request, 'origin', {
|
|
26
|
+
value: 'https://example.com:1337',
|
|
27
|
+
});
|
|
28
|
+
try {
|
|
29
|
+
await next();
|
|
30
|
+
} catch (error: unknown) {
|
|
31
|
+
if (!(error instanceof TestError)) {
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
ctx.status = 400;
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
app.use(loggerMiddleware());
|
|
38
|
+
app.silent = true;
|
|
39
|
+
request.defaults.maxRedirects = 0;
|
|
40
|
+
await setTestApp(app);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
afterEach(() => {
|
|
44
|
+
clock.uninstall();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should log requests', async () => {
|
|
48
|
+
await request.get('/pizza');
|
|
49
|
+
expect(logger.info).toHaveBeenCalledWith(
|
|
50
|
+
`${chalk.bold('GET')} https://example.com:1337/pizza — ${chalk.white('127.0.0.1')}`,
|
|
51
|
+
);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should log success responses as info', async () => {
|
|
55
|
+
app.use((ctx) => {
|
|
56
|
+
clock.tick(1);
|
|
57
|
+
ctx.status = 200;
|
|
58
|
+
});
|
|
59
|
+
await request.get('/fries');
|
|
60
|
+
expect(logger.log).toHaveBeenCalledWith(
|
|
61
|
+
'info',
|
|
62
|
+
`${chalk.bold('GET')} https://example.com:1337/fries ${chalk.green('200 OK')} ${chalk.green(
|
|
63
|
+
'1ms',
|
|
64
|
+
)}`,
|
|
65
|
+
);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should log redirect responses as info', async () => {
|
|
69
|
+
app.use((ctx) => {
|
|
70
|
+
clock.tick(33);
|
|
71
|
+
ctx.redirect('/');
|
|
72
|
+
});
|
|
73
|
+
await request.get('/fries');
|
|
74
|
+
expect(logger.log).toHaveBeenCalledWith(
|
|
75
|
+
'info',
|
|
76
|
+
`${chalk.bold('GET')} https://example.com:1337/fries ${chalk.cyan(
|
|
77
|
+
'302 Found → /',
|
|
78
|
+
)} ${chalk.green('33ms')}`,
|
|
79
|
+
);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should log bad responses as warn', async () => {
|
|
83
|
+
app.use((ctx) => {
|
|
84
|
+
clock.tick(3);
|
|
85
|
+
ctx.status = 400;
|
|
86
|
+
});
|
|
87
|
+
await request.get('/burrito');
|
|
88
|
+
expect(logger.log).toHaveBeenCalledWith(
|
|
89
|
+
'warn',
|
|
90
|
+
`${chalk.bold('GET')} https://example.com:1337/burrito ${chalk.yellow(
|
|
91
|
+
'400 Bad Request',
|
|
92
|
+
)} ${chalk.green('3ms')}`,
|
|
93
|
+
);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('should log error responses as error', async () => {
|
|
97
|
+
app.use((ctx) => {
|
|
98
|
+
clock.tick(53);
|
|
99
|
+
ctx.status = 503;
|
|
100
|
+
});
|
|
101
|
+
await request.get('/wrap');
|
|
102
|
+
expect(logger.log).toHaveBeenCalledWith(
|
|
103
|
+
'error',
|
|
104
|
+
`${chalk.bold('GET')} https://example.com:1337/wrap ${chalk.red(
|
|
105
|
+
'503 Service Unavailable',
|
|
106
|
+
)} ${chalk.green('53ms')}`,
|
|
107
|
+
);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('should log long request lengths yellow', async () => {
|
|
111
|
+
app.use((ctx) => {
|
|
112
|
+
clock.tick(400);
|
|
113
|
+
ctx.status = 200;
|
|
114
|
+
});
|
|
115
|
+
await request.get('/banana');
|
|
116
|
+
expect(logger.log).toHaveBeenCalledWith(
|
|
117
|
+
'info',
|
|
118
|
+
`${chalk.bold('GET')} https://example.com:1337/banana ${chalk.green('200 OK')} ${chalk.yellow(
|
|
119
|
+
'400ms',
|
|
120
|
+
)}`,
|
|
121
|
+
);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('should log extremely long request lengths red', async () => {
|
|
125
|
+
app.use((ctx) => {
|
|
126
|
+
clock.tick(1337);
|
|
127
|
+
ctx.status = 200;
|
|
128
|
+
});
|
|
129
|
+
await request.get('/pepperoni');
|
|
130
|
+
expect(logger.log).toHaveBeenCalledWith(
|
|
131
|
+
'info',
|
|
132
|
+
`${chalk.bold('GET')} https://example.com:1337/pepperoni ${chalk.green('200 OK')} ${chalk.red(
|
|
133
|
+
'1337ms',
|
|
134
|
+
)}`,
|
|
135
|
+
);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('should log errors as internal server errors and rethrow', async () => {
|
|
139
|
+
const spy = jest.fn();
|
|
140
|
+
const error = new Error('fail');
|
|
141
|
+
let context;
|
|
142
|
+
app.on('error', spy);
|
|
143
|
+
app.use((ctx) => {
|
|
144
|
+
clock.tick(86);
|
|
145
|
+
context = ctx;
|
|
146
|
+
throw error;
|
|
147
|
+
});
|
|
148
|
+
await request.get('/taco');
|
|
149
|
+
expect(spy).toHaveBeenCalledWith(error, context);
|
|
150
|
+
expect(logger.log).toHaveBeenCalledWith(
|
|
151
|
+
'error',
|
|
152
|
+
`${chalk.bold('GET')} https://example.com:1337/taco ${chalk.red(
|
|
153
|
+
'500 Internal Server Error',
|
|
154
|
+
)} ${chalk.green('86ms')}`,
|
|
155
|
+
);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('should append the response length if it is defined', async () => {
|
|
159
|
+
app.use((ctx) => {
|
|
160
|
+
clock.tick(1);
|
|
161
|
+
ctx.status = 200;
|
|
162
|
+
ctx.body = '{}';
|
|
163
|
+
});
|
|
164
|
+
await request.get('/fries');
|
|
165
|
+
expect(logger.log).toHaveBeenCalledWith(
|
|
166
|
+
'info',
|
|
167
|
+
`${chalk.bold('GET')} https://example.com:1337/fries ${chalk.green('200 OK')} ${chalk.green(
|
|
168
|
+
'1ms',
|
|
169
|
+
)}`,
|
|
170
|
+
);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('should log handled errors correctly', async () => {
|
|
174
|
+
app.use(() => {
|
|
175
|
+
clock.tick(15);
|
|
176
|
+
throw new TestError();
|
|
177
|
+
});
|
|
178
|
+
await request.get('potatoes');
|
|
179
|
+
expect(logger.log).toHaveBeenCalledWith(
|
|
180
|
+
'warn',
|
|
181
|
+
`${chalk.bold('GET')} https://example.com:1337/potatoes ${chalk.yellow(
|
|
182
|
+
'400 Bad Request',
|
|
183
|
+
)} ${chalk.green('15ms')}`,
|
|
184
|
+
);
|
|
185
|
+
});
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { ParameterizedContext } from 'koa';
|
|
3
|
+
import * as compose from 'koa-compose';
|
|
4
|
+
|
|
5
|
+
import { logger } from './logger';
|
|
6
|
+
|
|
7
|
+
interface RangeMap<T> {
|
|
8
|
+
[key: number]: T;
|
|
9
|
+
default: T;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function rangeFormat<T>(value: number, map: RangeMap<T>): T {
|
|
13
|
+
return Object.entries(map).find(([v]) => v === 'default' || value < Number(v))[1];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Koa middleware for logging requests using the Appsemble logger.
|
|
18
|
+
*
|
|
19
|
+
* @returns Koa middleware for logging requests using the Appsemble logger.
|
|
20
|
+
*/
|
|
21
|
+
export function loggerMiddleware(): compose.Middleware<ParameterizedContext> {
|
|
22
|
+
return (ctx, next) => {
|
|
23
|
+
const { href, res } = ctx;
|
|
24
|
+
const start = Date.now();
|
|
25
|
+
const method = chalk.bold(ctx.method);
|
|
26
|
+
logger.info(`${method} ${href} — ${chalk.white(ctx.ip)}`);
|
|
27
|
+
|
|
28
|
+
function logResponse(): void {
|
|
29
|
+
res.removeListener('finish', logResponse);
|
|
30
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
31
|
+
res.removeListener('close', logCancel);
|
|
32
|
+
|
|
33
|
+
const {
|
|
34
|
+
message,
|
|
35
|
+
response: {
|
|
36
|
+
header: { location },
|
|
37
|
+
},
|
|
38
|
+
status,
|
|
39
|
+
} = ctx;
|
|
40
|
+
const s = `${status} ${message}`;
|
|
41
|
+
const duration = Date.now() - start;
|
|
42
|
+
|
|
43
|
+
const formatDuration = rangeFormat(duration, {
|
|
44
|
+
100: chalk.green,
|
|
45
|
+
1000: chalk.yellow,
|
|
46
|
+
default: chalk.red,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const formatStatus = rangeFormat(status, {
|
|
50
|
+
200: chalk.red,
|
|
51
|
+
300: chalk.green,
|
|
52
|
+
400: chalk.cyan,
|
|
53
|
+
500: chalk.yellow,
|
|
54
|
+
default: chalk.red,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const level = rangeFormat(status, {
|
|
58
|
+
200: 'error',
|
|
59
|
+
400: 'info',
|
|
60
|
+
500: 'warn',
|
|
61
|
+
default: 'error',
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
logger.log(
|
|
65
|
+
level,
|
|
66
|
+
`${method} ${href} ${formatStatus(location ? `${s} → ${location}` : s)} ${formatDuration(
|
|
67
|
+
`${duration}ms`,
|
|
68
|
+
)}`,
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function logCancel(): void {
|
|
73
|
+
res.removeListener('finish', logResponse);
|
|
74
|
+
res.removeListener('close', logCancel);
|
|
75
|
+
|
|
76
|
+
const duration = Date.now() - start;
|
|
77
|
+
const formatDuration = rangeFormat(duration, {
|
|
78
|
+
100: chalk.green,
|
|
79
|
+
1000: chalk.yellow,
|
|
80
|
+
default: chalk.red,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
logger.warn(
|
|
84
|
+
`${method} ${href} ${chalk.grey('Cancelled')} ${formatDuration(`${duration}ms`)}`,
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
res.once('finish', logResponse);
|
|
89
|
+
res.once('close', logCancel);
|
|
90
|
+
|
|
91
|
+
return next();
|
|
92
|
+
};
|
|
93
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@appsemble/node-utils",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.11",
|
|
4
4
|
"description": "NodeJS utilities used by Appsemble internally.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"app",
|
|
@@ -20,16 +20,17 @@
|
|
|
20
20
|
"license": "LGPL-3.0-only",
|
|
21
21
|
"author": "Appsemble <info@appsemble.com> (https://appsemble.com)",
|
|
22
22
|
"sideEffects": false,
|
|
23
|
-
"main": "dist",
|
|
24
23
|
"files": [
|
|
25
|
-
"
|
|
24
|
+
"**/*.d.ts",
|
|
25
|
+
"**/*.js",
|
|
26
|
+
"!*.config.js"
|
|
26
27
|
],
|
|
27
28
|
"scripts": {
|
|
28
|
-
"prepack": "tsc
|
|
29
|
+
"prepack": "tsc --noEmit false",
|
|
29
30
|
"test": "jest"
|
|
30
31
|
},
|
|
31
32
|
"dependencies": {
|
|
32
|
-
"@appsemble/utils": "0.20.
|
|
33
|
+
"@appsemble/utils": "0.20.11",
|
|
33
34
|
"@types/koa": "^2.0.0",
|
|
34
35
|
"axios": "^0.27.0",
|
|
35
36
|
"chalk": "^4.0.0",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* If the string represents an existing path, read the file. Otherwise, return the string itself.
|
|
4
4
|
*
|
|
5
|
-
* @param string
|
|
5
|
+
* @param string The string to handle.
|
|
6
6
|
* @returns The handled string.
|
|
7
7
|
*/
|
|
8
8
|
export declare function readFileOrString(string: Buffer | string): Promise<Buffer | string>;
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.readFileOrString = void 0;
|
|
4
|
-
const
|
|
4
|
+
const promises_1 = require("fs/promises");
|
|
5
5
|
const _1 = require(".");
|
|
6
6
|
/**
|
|
7
7
|
* If the string represents an existing path, read the file. Otherwise, return the string itself.
|
|
8
8
|
*
|
|
9
|
-
* @param string
|
|
9
|
+
* @param string The string to handle.
|
|
10
10
|
* @returns The handled string.
|
|
11
11
|
*/
|
|
12
12
|
async function readFileOrString(string) {
|
|
13
13
|
try {
|
|
14
|
-
return await
|
|
14
|
+
return await (0, promises_1.readFile)(string);
|
|
15
15
|
}
|
|
16
16
|
catch (error) {
|
|
17
17
|
if ((0, _1.isErrno)(error, 'ENOENT')) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readFileOrString.js","sourceRoot":"","sources":["readFileOrString.ts"],"names":[],"mappings":";;;AAAA,0CAAuC;AAEvC,wBAA4B;AAE5B;;;;;GAKG;AACI,KAAK,UAAU,gBAAgB,CAAC,MAAuB;IAC5D,IAAI;QACF,OAAO,MAAM,IAAA,mBAAQ,EAAC,MAAM,CAAC,CAAC;KAC/B;IAAC,OAAO,KAAc,EAAE;QACvB,IAAI,IAAA,UAAO,EAAC,KAAK,EAAE,QAAQ,CAAC,EAAE;YAC5B,OAAO,MAAM,CAAC;SACf;QACD,MAAM,KAAK,CAAC;KACb;AACH,CAAC;AATD,4CASC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const readFileOrString_1 = require("./readFileOrString");
|
|
4
|
+
const testFixtures_1 = require("./testFixtures");
|
|
5
|
+
it('should return file content if it resolves to a file', async () => {
|
|
6
|
+
const result = await (0, readFileOrString_1.readFileOrString)((0, testFixtures_1.resolveFixture)('hello.txt'));
|
|
7
|
+
expect(Buffer.from('Hello world!\n').equals(result)).toBe(true);
|
|
8
|
+
});
|
|
9
|
+
it('should return the input if it doesn’t resolve to a file', async () => {
|
|
10
|
+
const result = await (0, readFileOrString_1.readFileOrString)('hello.txt');
|
|
11
|
+
expect(result).toBe('hello.txt');
|
|
12
|
+
});
|
|
13
|
+
//# sourceMappingURL=readFileOrString.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readFileOrString.test.js","sourceRoot":"","sources":["readFileOrString.test.ts"],"names":[],"mappings":";;AAAA,yDAAsD;AACtD,iDAAgD;AAEhD,EAAE,CAAC,qDAAqD,EAAE,KAAK,IAAI,EAAE;IACnE,MAAM,MAAM,GAAG,MAAM,IAAA,mCAAgB,EAAC,IAAA,6BAAc,EAAC,WAAW,CAAC,CAAC,CAAC;IACnE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,MAAM,CAAC,MAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC5E,CAAC,CAAC,CAAC;AAEH,EAAE,CAAC,yDAAyD,EAAE,KAAK,IAAI,EAAE;IACvE,MAAM,MAAM,GAAG,MAAM,IAAA,mCAAgB,EAAC,WAAW,CAAC,CAAC;IACnD,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;AACnC,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { readFileOrString } from './readFileOrString';
|
|
2
|
+
import { resolveFixture } from './testFixtures';
|
|
3
|
+
|
|
4
|
+
it('should return file content if it resolves to a file', async () => {
|
|
5
|
+
const result = await readFileOrString(resolveFixture('hello.txt'));
|
|
6
|
+
expect(Buffer.from('Hello world!\n').equals(result as Buffer)).toBe(true);
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('should return the input if it doesn’t resolve to a file', async () => {
|
|
10
|
+
const result = await readFileOrString('hello.txt');
|
|
11
|
+
expect(result).toBe('hello.txt');
|
|
12
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { readFile } from 'fs/promises';
|
|
2
|
+
|
|
3
|
+
import { isErrno } from '.';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* If the string represents an existing path, read the file. Otherwise, return the string itself.
|
|
7
|
+
*
|
|
8
|
+
* @param string The string to handle.
|
|
9
|
+
* @returns The handled string.
|
|
10
|
+
*/
|
|
11
|
+
export async function readFileOrString(string: Buffer | string): Promise<Buffer | string> {
|
|
12
|
+
try {
|
|
13
|
+
return await readFile(string);
|
|
14
|
+
} catch (error: unknown) {
|
|
15
|
+
if (isErrno(error, 'ENOENT')) {
|
|
16
|
+
return string;
|
|
17
|
+
}
|
|
18
|
+
throw error;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
+
/// <reference types="node" />
|
|
2
3
|
import { ReadStream } from 'fs';
|
|
3
4
|
/**
|
|
4
5
|
* Set the test fixture base path. This folder should contain a folder named `__fixtures__`.
|
|
5
6
|
*
|
|
6
7
|
* This is typically called in `jest.setup.ts`.
|
|
7
8
|
*
|
|
8
|
-
* @param dir
|
|
9
|
+
* @param dir The base directory
|
|
9
10
|
* @example
|
|
10
11
|
* ```ts
|
|
11
12
|
* setFixtureBase(__dirname);
|
|
@@ -15,7 +16,7 @@ export declare function setFixtureBase(dir: string): void;
|
|
|
15
16
|
/**
|
|
16
17
|
* Resolve the path to a test fixture.
|
|
17
18
|
*
|
|
18
|
-
* @param path
|
|
19
|
+
* @param path The path to resolve relative to the fixture base. It will be normalized for the
|
|
19
20
|
* operating system.
|
|
20
21
|
* @returns The full path to the fixture path.
|
|
21
22
|
*/
|
|
@@ -23,9 +24,9 @@ export declare function resolveFixture(path: string): string;
|
|
|
23
24
|
/**
|
|
24
25
|
* Read a test fixture.
|
|
25
26
|
*
|
|
26
|
-
* @param path
|
|
27
|
+
* @param path The path to resolve relative to the fixture base. It will be normalized for the
|
|
27
28
|
* operating system.
|
|
28
|
-
* @param encoding
|
|
29
|
+
* @param encoding The text encoding of the file.
|
|
29
30
|
* @returns The file contents as a string or buffer.
|
|
30
31
|
*/
|
|
31
32
|
export declare function readFixture(path: string): Promise<Buffer>;
|
|
@@ -33,7 +34,7 @@ export declare function readFixture(path: string, encoding: BufferEncoding): Pro
|
|
|
33
34
|
/**
|
|
34
35
|
* Create a read stream for a fixture.
|
|
35
36
|
*
|
|
36
|
-
* @param path
|
|
37
|
+
* @param path The path to resolve relative to the fixture base. It will be normalized for the
|
|
37
38
|
* operating system.
|
|
38
39
|
* @returns A filesystem read stream for the fixture.
|
|
39
40
|
*/
|