@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
|
@@ -0,0 +1,93 @@
|
|
|
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_1 = __importDefault(require("axios"));
|
|
8
|
+
const axios_mock_adapter_1 = __importDefault(require("axios-mock-adapter"));
|
|
9
|
+
const cli_highlight_1 = require("cli-highlight");
|
|
10
|
+
const form_data_1 = __importDefault(require("form-data"));
|
|
11
|
+
const _1 = require(".");
|
|
12
|
+
function h(content) {
|
|
13
|
+
return (0, cli_highlight_1.highlight)(content, { language: 'http' });
|
|
14
|
+
}
|
|
15
|
+
let instance;
|
|
16
|
+
let mock;
|
|
17
|
+
let clock;
|
|
18
|
+
jest.mock('os');
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
clock = (0, fake_timers_1.install)();
|
|
21
|
+
});
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
clock.uninstall();
|
|
24
|
+
});
|
|
25
|
+
describe('formData', () => {
|
|
26
|
+
beforeAll(() => {
|
|
27
|
+
instance = axios_1.default.create();
|
|
28
|
+
instance.interceptors.request.use(_1.formData);
|
|
29
|
+
mock = new axios_mock_adapter_1.default(instance);
|
|
30
|
+
});
|
|
31
|
+
it('should add headers for form-data requests', async () => {
|
|
32
|
+
let headers;
|
|
33
|
+
mock.onAny('/').reply((config) => {
|
|
34
|
+
({ headers } = config);
|
|
35
|
+
return [200];
|
|
36
|
+
});
|
|
37
|
+
const form = new form_data_1.default();
|
|
38
|
+
await instance.post('/', form);
|
|
39
|
+
expect(headers).toHaveProperty('Content-Type', expect.stringMatching(/^multipart\/form-data; boundary=/));
|
|
40
|
+
});
|
|
41
|
+
it('should not add headers to non form-data requests', async () => {
|
|
42
|
+
let headers;
|
|
43
|
+
mock.onAny('/').reply((config) => {
|
|
44
|
+
({ headers } = config);
|
|
45
|
+
return [200];
|
|
46
|
+
});
|
|
47
|
+
await instance.post('/', {});
|
|
48
|
+
expect(headers).toBeUndefined();
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
describe('requestLogger', () => {
|
|
52
|
+
beforeAll(() => {
|
|
53
|
+
instance = axios_1.default.create();
|
|
54
|
+
instance.interceptors.request.use(_1.requestLogger);
|
|
55
|
+
mock = new axios_mock_adapter_1.default(instance);
|
|
56
|
+
mock.onAny().reply(200);
|
|
57
|
+
});
|
|
58
|
+
it('should log requests', async () => {
|
|
59
|
+
jest.spyOn(_1.logger, 'verbose');
|
|
60
|
+
await instance.get('/');
|
|
61
|
+
expect(_1.logger.verbose).toHaveBeenCalledWith(`> 0 ${h('GET / HTTP/1.1')}`);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
describe('responseLogger', () => {
|
|
65
|
+
beforeAll(() => {
|
|
66
|
+
instance = axios_1.default.create();
|
|
67
|
+
instance.interceptors.response.use(_1.responseLogger);
|
|
68
|
+
mock = new axios_mock_adapter_1.default(instance);
|
|
69
|
+
mock.onAny().reply(200);
|
|
70
|
+
});
|
|
71
|
+
it('should log responses', async () => {
|
|
72
|
+
jest.spyOn(_1.logger, 'verbose');
|
|
73
|
+
await instance.get('/');
|
|
74
|
+
expect(_1.logger.verbose).toHaveBeenCalledWith(expect.any(String));
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
describe('configureAxios', () => {
|
|
78
|
+
beforeEach(() => {
|
|
79
|
+
jest.spyOn(axios_1.default.interceptors.request, 'use').mockImplementation();
|
|
80
|
+
jest.spyOn(axios_1.default.interceptors.response, 'use').mockImplementation();
|
|
81
|
+
});
|
|
82
|
+
it('should set the correct user agent', () => {
|
|
83
|
+
(0, _1.configureAxios)('TestClient', '1.2.3');
|
|
84
|
+
expect(axios_1.default.defaults.headers.common['user-agent']).toBe(`TestClient/1.2.3 (Linux x64; Node ${process.version})`);
|
|
85
|
+
});
|
|
86
|
+
it('should apply all interceptors', () => {
|
|
87
|
+
(0, _1.configureAxios)('TestClient', '1.2.3');
|
|
88
|
+
expect(axios_1.default.interceptors.request.use).toHaveBeenCalledWith(_1.formData);
|
|
89
|
+
expect(axios_1.default.interceptors.request.use).toHaveBeenCalledWith(_1.requestLogger);
|
|
90
|
+
expect(axios_1.default.interceptors.response.use).toHaveBeenCalledWith(_1.responseLogger);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
//# sourceMappingURL=interceptors.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interceptors.test.js","sourceRoot":"","sources":["interceptors.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { install, InstalledClock } from '@sinonjs/fake-timers';
|
|
2
|
+
import axios, { AxiosInstance } from 'axios';
|
|
3
|
+
import MockAdapter from 'axios-mock-adapter';
|
|
4
|
+
import { highlight } from 'cli-highlight';
|
|
5
|
+
import FormData from 'form-data';
|
|
6
|
+
|
|
7
|
+
import { configureAxios, formData, logger, requestLogger, responseLogger } from '.';
|
|
8
|
+
|
|
9
|
+
function h(content: string): string {
|
|
10
|
+
return highlight(content, { language: 'http' });
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let instance: AxiosInstance;
|
|
14
|
+
let mock: MockAdapter;
|
|
15
|
+
let clock: InstalledClock;
|
|
16
|
+
|
|
17
|
+
jest.mock('os');
|
|
18
|
+
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
clock = install();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
afterEach(() => {
|
|
24
|
+
clock.uninstall();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
describe('formData', () => {
|
|
28
|
+
beforeAll(() => {
|
|
29
|
+
instance = axios.create();
|
|
30
|
+
instance.interceptors.request.use(formData);
|
|
31
|
+
mock = new MockAdapter(instance);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should add headers for form-data requests', async () => {
|
|
35
|
+
let headers;
|
|
36
|
+
mock.onAny('/').reply((config) => {
|
|
37
|
+
({ headers } = config);
|
|
38
|
+
return [200];
|
|
39
|
+
});
|
|
40
|
+
const form = new FormData();
|
|
41
|
+
await instance.post('/', form);
|
|
42
|
+
expect(headers).toHaveProperty(
|
|
43
|
+
'Content-Type',
|
|
44
|
+
expect.stringMatching(/^multipart\/form-data; boundary=/),
|
|
45
|
+
);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('should not add headers to non form-data requests', async () => {
|
|
49
|
+
let headers;
|
|
50
|
+
mock.onAny('/').reply((config) => {
|
|
51
|
+
({ headers } = config);
|
|
52
|
+
return [200];
|
|
53
|
+
});
|
|
54
|
+
await instance.post('/', {});
|
|
55
|
+
expect(headers).toBeUndefined();
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe('requestLogger', () => {
|
|
60
|
+
beforeAll(() => {
|
|
61
|
+
instance = axios.create();
|
|
62
|
+
instance.interceptors.request.use(requestLogger);
|
|
63
|
+
mock = new MockAdapter(instance);
|
|
64
|
+
mock.onAny().reply(200);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('should log requests', async () => {
|
|
68
|
+
jest.spyOn(logger, 'verbose');
|
|
69
|
+
await instance.get('/');
|
|
70
|
+
expect(logger.verbose).toHaveBeenCalledWith(`> 0 ${h('GET / HTTP/1.1')}`);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe('responseLogger', () => {
|
|
75
|
+
beforeAll(() => {
|
|
76
|
+
instance = axios.create();
|
|
77
|
+
instance.interceptors.response.use(responseLogger);
|
|
78
|
+
mock = new MockAdapter(instance);
|
|
79
|
+
mock.onAny().reply(200);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should log responses', async () => {
|
|
83
|
+
jest.spyOn(logger, 'verbose');
|
|
84
|
+
await instance.get('/');
|
|
85
|
+
expect(logger.verbose).toHaveBeenCalledWith(expect.any(String));
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe('configureAxios', () => {
|
|
90
|
+
beforeEach(() => {
|
|
91
|
+
jest.spyOn(axios.interceptors.request, 'use').mockImplementation();
|
|
92
|
+
jest.spyOn(axios.interceptors.response, 'use').mockImplementation();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('should set the correct user agent', () => {
|
|
96
|
+
configureAxios('TestClient', '1.2.3');
|
|
97
|
+
expect(axios.defaults.headers.common['user-agent']).toBe(
|
|
98
|
+
`TestClient/1.2.3 (Linux x64; Node ${process.version})`,
|
|
99
|
+
);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('should apply all interceptors', () => {
|
|
103
|
+
configureAxios('TestClient', '1.2.3');
|
|
104
|
+
expect(axios.interceptors.request.use).toHaveBeenCalledWith(formData);
|
|
105
|
+
expect(axios.interceptors.request.use).toHaveBeenCalledWith(requestLogger);
|
|
106
|
+
expect(axios.interceptors.response.use).toHaveBeenCalledWith(responseLogger);
|
|
107
|
+
});
|
|
108
|
+
});
|
package/interceptors.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { arch, type } from 'os';
|
|
2
|
+
|
|
3
|
+
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios';
|
|
4
|
+
import { highlight } from 'cli-highlight';
|
|
5
|
+
import FormData from 'form-data';
|
|
6
|
+
|
|
7
|
+
import { logger } from './logger';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* This is used to set a request id, so the request and response can be matched in the logs.
|
|
11
|
+
*/
|
|
12
|
+
const id = Symbol('id');
|
|
13
|
+
|
|
14
|
+
declare module 'axios' {
|
|
15
|
+
interface AxiosRequestConfig {
|
|
16
|
+
[id]?: number;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* An {@link axios} request interceptor to add support for {@link form-data}.
|
|
22
|
+
*
|
|
23
|
+
* @param config The axios request configuration.
|
|
24
|
+
* @returns The config with additional `multipart/form-data` headers if appropriate.
|
|
25
|
+
*/
|
|
26
|
+
export function formData(config: AxiosRequestConfig): AxiosRequestConfig {
|
|
27
|
+
if (config.data instanceof FormData) {
|
|
28
|
+
Object.assign(config.headers, config.data.getHeaders());
|
|
29
|
+
}
|
|
30
|
+
return config;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* An {@link axios} request interceptor to log requests.
|
|
35
|
+
*
|
|
36
|
+
* @param config The axios request configuration.
|
|
37
|
+
* @returns The original config.
|
|
38
|
+
*/
|
|
39
|
+
export function requestLogger(config: AxiosRequestConfig): AxiosRequestConfig {
|
|
40
|
+
const time = Date.now();
|
|
41
|
+
logger.verbose(
|
|
42
|
+
`> ${time} ${highlight(`${config.method.toUpperCase()} ${axios.getUri(config)} HTTP/1.1`, {
|
|
43
|
+
language: 'http',
|
|
44
|
+
})}`,
|
|
45
|
+
);
|
|
46
|
+
// eslint-disable-next-line no-param-reassign
|
|
47
|
+
config[id] = time;
|
|
48
|
+
return config;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* An {@link axios} response interceptor to log responses.
|
|
53
|
+
*
|
|
54
|
+
* @param response The axios response.
|
|
55
|
+
* @returns The original response.
|
|
56
|
+
*/
|
|
57
|
+
export function responseLogger(response: AxiosResponse): AxiosResponse {
|
|
58
|
+
logger.verbose(
|
|
59
|
+
`< ${response.config[id]} ${highlight(`HTTP/1.1 ${response.status} ${response.statusText}`, {
|
|
60
|
+
language: 'http',
|
|
61
|
+
})}`,
|
|
62
|
+
);
|
|
63
|
+
return response;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Configure the default Axios instance.
|
|
68
|
+
*
|
|
69
|
+
* This applies the interceptors in this modules and sets the appropriate user agent string.
|
|
70
|
+
*
|
|
71
|
+
* @param name A PascalCase representation of the client.
|
|
72
|
+
* @param version The version of the client to represent.
|
|
73
|
+
*/
|
|
74
|
+
export function configureAxios(name: string, version: string): void {
|
|
75
|
+
const ua = `${name}/${version} (${type()} ${arch()}; Node ${process.version})`;
|
|
76
|
+
axios.defaults.headers.common['user-agent'] = ua;
|
|
77
|
+
|
|
78
|
+
axios.interceptors.request.use(formData);
|
|
79
|
+
axios.interceptors.request.use(requestLogger);
|
|
80
|
+
axios.interceptors.response.use(responseLogger);
|
|
81
|
+
}
|
package/jest.config.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('../../config/jest/config')(__dirname);
|
package/jest.setup.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/jest.setup.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
// Importing a module here will break mocks for that module. Only add imports from `./src/*` here
|
|
4
|
+
// that are strictly necessary.
|
|
5
|
+
const logger_1 = require("./logger");
|
|
6
|
+
const testFixtures_1 = require("./testFixtures");
|
|
7
|
+
(0, testFixtures_1.setFixtureBase)(__dirname);
|
|
8
|
+
(0, logger_1.setLogLevel)(0);
|
|
9
|
+
//# sourceMappingURL=jest.setup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jest.setup.js","sourceRoot":"","sources":["jest.setup.ts"],"names":[],"mappings":";;AAAA,iGAAiG;AACjG,+BAA+B;AAC/B,qCAAuC;AACvC,iDAAgD;AAEhD,IAAA,6BAAc,EAAC,SAAS,CAAC,CAAC;AAC1B,IAAA,oBAAW,EAAC,CAAC,CAAC,CAAC"}
|
package/jest.setup.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Importing a module here will break mocks for that module. Only add imports from `./src/*` here
|
|
2
|
+
// that are strictly necessary.
|
|
3
|
+
import { setLogLevel } from './logger';
|
|
4
|
+
import { setFixtureBase } from './testFixtures';
|
|
5
|
+
|
|
6
|
+
setFixtureBase(__dirname);
|
|
7
|
+
setLogLevel(0);
|
|
@@ -16,7 +16,7 @@ export declare const logger: winston.Logger;
|
|
|
16
16
|
/**
|
|
17
17
|
* Set the logging level using a string or numeric value.
|
|
18
18
|
*
|
|
19
|
-
* @param level
|
|
19
|
+
* @param level The logger level to set.
|
|
20
20
|
*/
|
|
21
21
|
export declare function setLogLevel(level?: number | string): void;
|
|
22
22
|
/**
|
|
@@ -24,7 +24,7 @@ export declare function setLogLevel(level?: number | string): void;
|
|
|
24
24
|
*
|
|
25
25
|
* Use this in conjunction with `yargs`.
|
|
26
26
|
*
|
|
27
|
-
* @param argv
|
|
27
|
+
* @param argv The processed command line arguments.
|
|
28
28
|
*/
|
|
29
29
|
export declare function configureLogger({ quiet, verbose }: ConfigureLoggerParams): void;
|
|
30
30
|
export {};
|
|
@@ -7,7 +7,7 @@ exports.configureLogger = exports.setLogLevel = exports.logger = void 0;
|
|
|
7
7
|
const os_1 = require("os");
|
|
8
8
|
const util_1 = require("util");
|
|
9
9
|
const axios_1 = __importDefault(require("axios"));
|
|
10
|
-
const chalk_1 = require("chalk");
|
|
10
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
11
11
|
const cli_highlight_1 = require("cli-highlight");
|
|
12
12
|
const winston_1 = __importDefault(require("winston"));
|
|
13
13
|
const levels = ['crit', 'error', 'warn', 'info', 'verbose', 'silly'];
|
|
@@ -20,7 +20,7 @@ function httpErrorToString(error) {
|
|
|
20
20
|
var _a, _b, _c;
|
|
21
21
|
const { config, request, response } = error;
|
|
22
22
|
return [
|
|
23
|
-
chalk_1.blue.bold('Request:'),
|
|
23
|
+
chalk_1.default.blue.bold('Request:'),
|
|
24
24
|
(0, cli_highlight_1.highlight)([
|
|
25
25
|
`${config.method.toUpperCase()} ${axios_1.default.getUri(config)} HTTP/${(_b = (_a = request.res) === null || _a === void 0 ? void 0 : _a.httpVersion) !== null && _b !== void 0 ? _b : '1.1'}`,
|
|
26
26
|
...Object.entries(config.headers)
|
|
@@ -29,7 +29,7 @@ function httpErrorToString(error) {
|
|
|
29
29
|
.sort(),
|
|
30
30
|
].join(os_1.EOL), { language: 'http', ignoreIllegals: true }),
|
|
31
31
|
'',
|
|
32
|
-
chalk_1.blue.bold('Response:'),
|
|
32
|
+
chalk_1.default.blue.bold('Response:'),
|
|
33
33
|
response
|
|
34
34
|
? (0, cli_highlight_1.highlight)([
|
|
35
35
|
`HTTP/${(_c = request.res) === null || _c === void 0 ? void 0 : _c.httpVersion} ${response.status} ${response.statusText}`,
|
|
@@ -41,7 +41,7 @@ function httpErrorToString(error) {
|
|
|
41
41
|
? JSON.stringify(response.data, null, 2)
|
|
42
42
|
: response.data,
|
|
43
43
|
].join(os_1.EOL), { language: 'http', ignoreIllegals: true })
|
|
44
|
-
: chalk_1.red.bold('No response from server'),
|
|
44
|
+
: chalk_1.default.red.bold('No response from server'),
|
|
45
45
|
].join(os_1.EOL);
|
|
46
46
|
}
|
|
47
47
|
function toString(info) {
|
|
@@ -73,7 +73,7 @@ exports.logger = winston_1.default.createLogger({
|
|
|
73
73
|
/**
|
|
74
74
|
* Set the logging level using a string or numeric value.
|
|
75
75
|
*
|
|
76
|
-
* @param level
|
|
76
|
+
* @param level The logger level to set.
|
|
77
77
|
*/
|
|
78
78
|
function setLogLevel(level = DEFAULT_LEVEL) {
|
|
79
79
|
exports.logger.level = Number.isNaN(Number(level))
|
|
@@ -87,7 +87,7 @@ exports.setLogLevel = setLogLevel;
|
|
|
87
87
|
*
|
|
88
88
|
* Use this in conjunction with `yargs`.
|
|
89
89
|
*
|
|
90
|
-
* @param argv
|
|
90
|
+
* @param argv The processed command line arguments.
|
|
91
91
|
*/
|
|
92
92
|
function configureLogger({ quiet = 0, verbose = 0 }) {
|
|
93
93
|
process.on('warning', (warning) => {
|
package/logger.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["logger.ts"],"names":[],"mappings":"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"}
|
package/logger.test.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/logger.test.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const logger_1 = require("./logger");
|
|
4
|
+
describe('setLogLevel', () => {
|
|
5
|
+
afterEach(() => {
|
|
6
|
+
(0, logger_1.setLogLevel)();
|
|
7
|
+
});
|
|
8
|
+
it('should set the level of the exported logger based on a string', () => {
|
|
9
|
+
(0, logger_1.setLogLevel)('verbose');
|
|
10
|
+
expect(logger_1.logger.level).toBe('verbose');
|
|
11
|
+
});
|
|
12
|
+
it('should set the level of the exported logger based on a number', () => {
|
|
13
|
+
(0, logger_1.setLogLevel)(1);
|
|
14
|
+
expect(logger_1.logger.level).toBe('error');
|
|
15
|
+
});
|
|
16
|
+
it('should set the level of the exported logger based on a numeric string', () => {
|
|
17
|
+
(0, logger_1.setLogLevel)('2');
|
|
18
|
+
expect(logger_1.logger.level).toBe('warn');
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
describe('configureLogger', () => {
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
(0, logger_1.setLogLevel)();
|
|
24
|
+
});
|
|
25
|
+
it('should configure a log level based on the quiet count of the argv', () => {
|
|
26
|
+
(0, logger_1.configureLogger)({ quiet: 1 });
|
|
27
|
+
expect(logger_1.logger.level).toBe('warn');
|
|
28
|
+
});
|
|
29
|
+
it('should configure a log level based on the verbose count of the argv', () => {
|
|
30
|
+
(0, logger_1.configureLogger)({ verbose: 1 });
|
|
31
|
+
expect(logger_1.logger.level).toBe('verbose');
|
|
32
|
+
});
|
|
33
|
+
it('should configure a log level based on the combined quiet and verbose count', () => {
|
|
34
|
+
(0, logger_1.configureLogger)({ quiet: 1, verbose: 1 });
|
|
35
|
+
expect(logger_1.logger.level).toBe('info');
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
//# sourceMappingURL=logger.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.test.js","sourceRoot":"","sources":["logger.test.ts"],"names":[],"mappings":";;AAAA,qCAAgE;AAEhE,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,SAAS,CAAC,GAAG,EAAE;QACb,IAAA,oBAAW,GAAE,CAAC;IAChB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+DAA+D,EAAE,GAAG,EAAE;QACvE,IAAA,oBAAW,EAAC,SAAS,CAAC,CAAC;QACvB,MAAM,CAAC,eAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+DAA+D,EAAE,GAAG,EAAE;QACvE,IAAA,oBAAW,EAAC,CAAC,CAAC,CAAC;QACf,MAAM,CAAC,eAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uEAAuE,EAAE,GAAG,EAAE;QAC/E,IAAA,oBAAW,EAAC,GAAG,CAAC,CAAC;QACjB,MAAM,CAAC,eAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAC/B,SAAS,CAAC,GAAG,EAAE;QACb,IAAA,oBAAW,GAAE,CAAC;IAChB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mEAAmE,EAAE,GAAG,EAAE;QAC3E,IAAA,wBAAe,EAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;QAC9B,MAAM,CAAC,eAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qEAAqE,EAAE,GAAG,EAAE;QAC7E,IAAA,wBAAe,EAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;QAChC,MAAM,CAAC,eAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4EAA4E,EAAE,GAAG,EAAE;QACpF,IAAA,wBAAe,EAAC,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;QAC1C,MAAM,CAAC,eAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
package/logger.test.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { configureLogger, logger, setLogLevel } from './logger';
|
|
2
|
+
|
|
3
|
+
describe('setLogLevel', () => {
|
|
4
|
+
afterEach(() => {
|
|
5
|
+
setLogLevel();
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
it('should set the level of the exported logger based on a string', () => {
|
|
9
|
+
setLogLevel('verbose');
|
|
10
|
+
expect(logger.level).toBe('verbose');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('should set the level of the exported logger based on a number', () => {
|
|
14
|
+
setLogLevel(1);
|
|
15
|
+
expect(logger.level).toBe('error');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should set the level of the exported logger based on a numeric string', () => {
|
|
19
|
+
setLogLevel('2');
|
|
20
|
+
expect(logger.level).toBe('warn');
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe('configureLogger', () => {
|
|
25
|
+
afterEach(() => {
|
|
26
|
+
setLogLevel();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should configure a log level based on the quiet count of the argv', () => {
|
|
30
|
+
configureLogger({ quiet: 1 });
|
|
31
|
+
expect(logger.level).toBe('warn');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should configure a log level based on the verbose count of the argv', () => {
|
|
35
|
+
configureLogger({ verbose: 1 });
|
|
36
|
+
expect(logger.level).toBe('verbose');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should configure a log level based on the combined quiet and verbose count', () => {
|
|
40
|
+
configureLogger({ quiet: 1, verbose: 1 });
|
|
41
|
+
expect(logger.level).toBe('info');
|
|
42
|
+
});
|
|
43
|
+
});
|
package/logger.ts
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { EOL } from 'os';
|
|
2
|
+
import { inspect } from 'util';
|
|
3
|
+
|
|
4
|
+
import axios, { AxiosError } from 'axios';
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import { highlight } from 'cli-highlight';
|
|
7
|
+
import { TransformableInfo } from 'logform';
|
|
8
|
+
import winston from 'winston';
|
|
9
|
+
|
|
10
|
+
interface ConfigureLoggerParams {
|
|
11
|
+
/**
|
|
12
|
+
* The negative verbosity count.
|
|
13
|
+
*/
|
|
14
|
+
quiet?: number;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The verbosity count.
|
|
18
|
+
*/
|
|
19
|
+
verbose?: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const levels = ['crit', 'error', 'warn', 'info', 'verbose', 'silly'] as const;
|
|
23
|
+
const DEFAULT_LEVEL = levels.indexOf('info');
|
|
24
|
+
const padding = Math.max(...levels.map(({ length }) => length));
|
|
25
|
+
|
|
26
|
+
function headerCase(header: string): string {
|
|
27
|
+
return header.replace(/(^|-)\w/g, (a) => a.toUpperCase());
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function httpErrorToString(error: AxiosError): string {
|
|
31
|
+
const { config, request, response } = error;
|
|
32
|
+
return [
|
|
33
|
+
chalk.blue.bold('Request:'),
|
|
34
|
+
highlight(
|
|
35
|
+
[
|
|
36
|
+
`${config.method.toUpperCase()} ${axios.getUri(config)} HTTP/${
|
|
37
|
+
request.res?.httpVersion ?? '1.1'
|
|
38
|
+
}`,
|
|
39
|
+
...Object.entries(config.headers)
|
|
40
|
+
.map(([key, value]) => [headerCase(key), value])
|
|
41
|
+
.map(([key, value]) => `${key}: ${key === 'Authorization' ? 'xxxxxxxxxx' : value}`)
|
|
42
|
+
.sort(),
|
|
43
|
+
].join(EOL),
|
|
44
|
+
{ language: 'http', ignoreIllegals: true },
|
|
45
|
+
),
|
|
46
|
+
'',
|
|
47
|
+
chalk.blue.bold('Response:'),
|
|
48
|
+
response
|
|
49
|
+
? highlight(
|
|
50
|
+
[
|
|
51
|
+
`HTTP/${request.res?.httpVersion} ${response.status} ${response.statusText}`,
|
|
52
|
+
...Object.entries(response.headers)
|
|
53
|
+
.map(([key, value]) => `${headerCase(key)}: ${value}`)
|
|
54
|
+
.sort(),
|
|
55
|
+
'',
|
|
56
|
+
response.data instanceof Object
|
|
57
|
+
? JSON.stringify(response.data, null, 2)
|
|
58
|
+
: response.data,
|
|
59
|
+
].join(EOL),
|
|
60
|
+
{ language: 'http', ignoreIllegals: true },
|
|
61
|
+
)
|
|
62
|
+
: chalk.red.bold('No response from server'),
|
|
63
|
+
].join(EOL);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function toString(info: TransformableInfo): string {
|
|
67
|
+
if (info instanceof Error) {
|
|
68
|
+
if (axios.isAxiosError(info)) {
|
|
69
|
+
return httpErrorToString(info);
|
|
70
|
+
}
|
|
71
|
+
return info.stack;
|
|
72
|
+
}
|
|
73
|
+
if (typeof info.message === 'string') {
|
|
74
|
+
return info.message;
|
|
75
|
+
}
|
|
76
|
+
return inspect(info.message, { colors: true });
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* The default logger for NodeJS Appsemble projects.
|
|
81
|
+
*/
|
|
82
|
+
export const logger = winston.createLogger({
|
|
83
|
+
level: levels[DEFAULT_LEVEL],
|
|
84
|
+
levels: Object.fromEntries(levels.map((level, index) => [level, index])),
|
|
85
|
+
format: winston.format.combine(
|
|
86
|
+
winston.format((info) => ({
|
|
87
|
+
...info,
|
|
88
|
+
lines: toString(info)
|
|
89
|
+
.split(/\r?\n/)
|
|
90
|
+
.map((line) => `${''.padEnd(padding - info.level.length)}${line}`),
|
|
91
|
+
}))(),
|
|
92
|
+
winston.format.colorize(),
|
|
93
|
+
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
|
|
94
|
+
winston.format.printf(({ level, lines, timestamp }) =>
|
|
95
|
+
lines.map((line: string) => `${timestamp} [${level}]: ${line}`).join(EOL),
|
|
96
|
+
),
|
|
97
|
+
),
|
|
98
|
+
transports: [new winston.transports.Console()],
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Set the logging level using a string or numeric value.
|
|
103
|
+
*
|
|
104
|
+
* @param level The logger level to set.
|
|
105
|
+
*/
|
|
106
|
+
export function setLogLevel(level: number | string = DEFAULT_LEVEL): void {
|
|
107
|
+
logger.level = Number.isNaN(Number(level))
|
|
108
|
+
? (level as string)
|
|
109
|
+
: levels[Math.min(Math.max(level as number, 0), levels.length - 1)];
|
|
110
|
+
logger.silly(`Logging level set to ${logger.level}`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Configure logging using named quiet and verbose parameters.
|
|
115
|
+
*
|
|
116
|
+
* Use this in conjunction with `yargs`.
|
|
117
|
+
*
|
|
118
|
+
* @param argv The processed command line arguments.
|
|
119
|
+
*/
|
|
120
|
+
export function configureLogger({ quiet = 0, verbose = 0 }: ConfigureLoggerParams): void {
|
|
121
|
+
process.on('warning', (warning) => {
|
|
122
|
+
logger.warn(warning);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
process.on('unhandledRejection', (error) => {
|
|
126
|
+
logger.error(error);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
setLogLevel(DEFAULT_LEVEL + verbose - quiet);
|
|
130
|
+
}
|
|
File without changes
|