beavuck-time 3.1.0 → 3.1.1
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/build/__tests__/api.test.js +74 -0
- package/build/__tests__/config/corsOptions.test.js +34 -0
- package/build/__tests__/middlewares/corsMiddleware.test.js +63 -0
- package/build/__tests__/middlewares/errorHandler.test.js +80 -0
- package/build/__tests__/utils/stringUtil.test.js +15 -0
- package/build/__tests__/utils/urlUtil.test.js +46 -0
- package/openapi/swagger.json +1 -1
- package/package.json +18 -15
|
@@ -0,0 +1,74 @@
|
|
|
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 supertest_1 = __importDefault(require("supertest"));
|
|
7
|
+
const api_1 = require("../api");
|
|
8
|
+
const http_status_codes_1 = require("http-status-codes");
|
|
9
|
+
const isoTimestamp_1 = require("../types/isoTimestamp");
|
|
10
|
+
function getSomeTrustedOrigin() {
|
|
11
|
+
return process.env.BEAVUCK_TIME_TRUSTED_ORIGINS?.split(',')[0] ?? '';
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Increments the callsMadeSoFar counter and returns the response and trusted origin
|
|
15
|
+
*/
|
|
16
|
+
async function makeSuccessfulCallToNowEndpoint() {
|
|
17
|
+
const trustedOrigin = getSomeTrustedOrigin();
|
|
18
|
+
const res = await (0, supertest_1.default)(api_1.api).get('/now').set('Origin', trustedOrigin);
|
|
19
|
+
return { res, trustedOrigin };
|
|
20
|
+
}
|
|
21
|
+
describe('App Initialization', () => {
|
|
22
|
+
it('should disable x-powered-by header', async () => {
|
|
23
|
+
const res = await (0, supertest_1.default)(api_1.api).get('/now');
|
|
24
|
+
expect(res.header['x-powered-by']).toBeUndefined();
|
|
25
|
+
});
|
|
26
|
+
it('should allow requests from trusted origins', async () => {
|
|
27
|
+
const { res, trustedOrigin } = await makeSuccessfulCallToNowEndpoint();
|
|
28
|
+
expect(res.header['access-control-allow-origin']).toBe(trustedOrigin);
|
|
29
|
+
expect(res.status).toBe(http_status_codes_1.StatusCodes.OK);
|
|
30
|
+
});
|
|
31
|
+
it('should block requests from non-trusted origins', async () => {
|
|
32
|
+
const res = await (0, supertest_1.default)(api_1.api).get('/now').set('Origin', 'https://non-trusted.com');
|
|
33
|
+
expect(res.header['access-control-allow-origin']).toBeUndefined();
|
|
34
|
+
expect(res.status).toBe(http_status_codes_1.StatusCodes.FORBIDDEN);
|
|
35
|
+
});
|
|
36
|
+
it('should block requests with no origin header', async () => {
|
|
37
|
+
const res = await (0, supertest_1.default)(api_1.api).get('/now');
|
|
38
|
+
expect(res.header['access-control-allow-origin']).toBeUndefined();
|
|
39
|
+
expect(res.status).toBe(http_status_codes_1.StatusCodes.FORBIDDEN);
|
|
40
|
+
});
|
|
41
|
+
it('should allow requests with no origin header but a referrer header identical to server url', async () => {
|
|
42
|
+
const REFERRER = process.env.BEAVUCK_TIME_HOST_URL;
|
|
43
|
+
const res = await (0, supertest_1.default)(api_1.api).get('/now').set('Referrer', REFERRER);
|
|
44
|
+
expect(res.header['access-control-allow-origin']).toBeUndefined();
|
|
45
|
+
expect(res.status).toBe(http_status_codes_1.StatusCodes.OK);
|
|
46
|
+
});
|
|
47
|
+
it('should allow requests with no origin header but a referer (sic) header identical to server url', async () => {
|
|
48
|
+
const REFERER = process.env.BEAVUCK_TIME_HOST_URL;
|
|
49
|
+
const res = await (0, supertest_1.default)(api_1.api).get('/now').set('Referer', REFERER);
|
|
50
|
+
expect(res.header['access-control-allow-origin']).toBeUndefined();
|
|
51
|
+
expect(res.status).toBe(http_status_codes_1.StatusCodes.OK);
|
|
52
|
+
});
|
|
53
|
+
it('should handle errors using errorHandler middleware', async () => {
|
|
54
|
+
const res = await (0, supertest_1.default)(api_1.api).get('/nope').set('Origin', getSomeTrustedOrigin());
|
|
55
|
+
expect(res.status).toBe(http_status_codes_1.StatusCodes.NOT_FOUND);
|
|
56
|
+
});
|
|
57
|
+
it('should return the current time in ISO format', async () => {
|
|
58
|
+
const { res } = await makeSuccessfulCallToNowEndpoint();
|
|
59
|
+
expect(res.status).toBe(http_status_codes_1.StatusCodes.OK);
|
|
60
|
+
expect(res.body).toHaveProperty('now');
|
|
61
|
+
expect(res.body.now).toMatch(isoTimestamp_1.RFC_3339_FORMAT);
|
|
62
|
+
});
|
|
63
|
+
it('should return 500 if HOST_URL is not set', async () => {
|
|
64
|
+
const originalHostUrl = process.env.BEAVUCK_TIME_HOST_URL;
|
|
65
|
+
delete process.env.BEAVUCK_TIME_HOST_URL;
|
|
66
|
+
const res = await (0, supertest_1.default)(api_1.api).get('/now');
|
|
67
|
+
expect(res.status).toBe(http_status_codes_1.StatusCodes.INTERNAL_SERVER_ERROR);
|
|
68
|
+
process.env.BEAVUCK_TIME_HOST_URL = originalHostUrl;
|
|
69
|
+
});
|
|
70
|
+
it('should block requests with non-trusted referrer header if origin is not OK', async () => {
|
|
71
|
+
const res = await (0, supertest_1.default)(api_1.api).get('/now').set('Referrer', 'https://non-trusted.com');
|
|
72
|
+
expect(res.status).toBe(http_status_codes_1.StatusCodes.FORBIDDEN);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/__tests__/config/corsOptions.test.ts
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const corsOptions_1 = require("../../config/corsOptions");
|
|
5
|
+
function setTrustedOrigins(...origins) {
|
|
6
|
+
process.env.BEAVUCK_TIME_TRUSTED_ORIGINS = origins.join(',');
|
|
7
|
+
(0, corsOptions_1.initTrustedOrigins)();
|
|
8
|
+
}
|
|
9
|
+
describe('isOriginTrusted', () => {
|
|
10
|
+
it('returns true for an exact match', () => {
|
|
11
|
+
setTrustedOrigins('https://app.example.com');
|
|
12
|
+
expect((0, corsOptions_1.isOriginTrusted)('https://app.example.com')).toBe(true);
|
|
13
|
+
});
|
|
14
|
+
it('returns true for a wildcard pattern matching the origin host', () => {
|
|
15
|
+
setTrustedOrigins('https://app.example.com/*');
|
|
16
|
+
expect((0, corsOptions_1.isOriginTrusted)('https://app.example.com')).toBe(true);
|
|
17
|
+
});
|
|
18
|
+
it('does not allow a domain that merely shares a prefix with a trusted origin', () => {
|
|
19
|
+
setTrustedOrigins('https://app.example.com');
|
|
20
|
+
expect((0, corsOptions_1.isOriginTrusted)('https://app.example.com.evil.com')).toBe(false);
|
|
21
|
+
});
|
|
22
|
+
it('does not allow a domain that shares a prefix with a wildcard trusted origin', () => {
|
|
23
|
+
setTrustedOrigins('https://app.example.com/*');
|
|
24
|
+
expect((0, corsOptions_1.isOriginTrusted)('https://app.example.com.evil.com')).toBe(false);
|
|
25
|
+
});
|
|
26
|
+
it('returns false for an untrusted origin', () => {
|
|
27
|
+
setTrustedOrigins('https://app.example.com');
|
|
28
|
+
expect((0, corsOptions_1.isOriginTrusted)('https://other.com')).toBe(false);
|
|
29
|
+
});
|
|
30
|
+
it('returns false when the trusted origin pattern is malformed', () => {
|
|
31
|
+
setTrustedOrigins('not-a-url/*');
|
|
32
|
+
expect((0, corsOptions_1.isOriginTrusted)('not-a-url')).toBe(false);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/__tests__/middlewares/corsMiddleware.test.ts
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const cors_1 = __importDefault(require("cors"));
|
|
8
|
+
const corsMiddleware_1 = require("../../middlewares/corsMiddleware");
|
|
9
|
+
const corsError_1 = require("../../errors/corsError");
|
|
10
|
+
const beavuckTimeServerError_1 = require("../../errors/beavuckTimeServerError");
|
|
11
|
+
const logger_1 = require("../../config/logger");
|
|
12
|
+
jest.mock('cors');
|
|
13
|
+
jest.mock('../../config/logger');
|
|
14
|
+
const mockCors = cors_1.default;
|
|
15
|
+
describe('corsMiddleware', () => {
|
|
16
|
+
let req;
|
|
17
|
+
let res;
|
|
18
|
+
let next;
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
req = { headers: {} };
|
|
21
|
+
res = {};
|
|
22
|
+
next = jest.fn();
|
|
23
|
+
mockCors.mockReturnValue((_req, _res, n) => n());
|
|
24
|
+
process.env.BEAVUCK_TIME_HOST_URL = 'http://localhost:3000';
|
|
25
|
+
});
|
|
26
|
+
afterEach(() => {
|
|
27
|
+
jest.clearAllMocks();
|
|
28
|
+
});
|
|
29
|
+
it('delegates to cors() when origin header is present', () => {
|
|
30
|
+
req.headers.origin = 'http://trusted.com';
|
|
31
|
+
(0, corsMiddleware_1.corsMiddleware)(req, res, next);
|
|
32
|
+
expect(mockCors).toHaveBeenCalled();
|
|
33
|
+
expect(next).toHaveBeenCalledWith();
|
|
34
|
+
});
|
|
35
|
+
it('logs the sanitized origin on CORS requests', () => {
|
|
36
|
+
req.headers.origin = 'http://evil.com\x01injected';
|
|
37
|
+
(0, corsMiddleware_1.corsMiddleware)(req, res, next);
|
|
38
|
+
expect(logger_1.logger.debug).toHaveBeenCalledWith('CORS request from http://evil.cominjected');
|
|
39
|
+
});
|
|
40
|
+
it('calls next with BeavuckTimeServerError when HOST_URL is not set', () => {
|
|
41
|
+
delete process.env.BEAVUCK_TIME_HOST_URL;
|
|
42
|
+
(0, corsMiddleware_1.corsMiddleware)(req, res, next);
|
|
43
|
+
expect(next).toHaveBeenCalledWith(expect.any(beavuckTimeServerError_1.BeavuckTimeServerError));
|
|
44
|
+
});
|
|
45
|
+
it('calls next without error when referrer matches the host', () => {
|
|
46
|
+
req.headers.referer = 'http://localhost:3000/page';
|
|
47
|
+
(0, corsMiddleware_1.corsMiddleware)(req, res, next);
|
|
48
|
+
expect(next).toHaveBeenCalledWith();
|
|
49
|
+
});
|
|
50
|
+
it('calls next with CorsError when neither origin nor referrer is trusted', () => {
|
|
51
|
+
req.headers.referer = 'http://untrusted.com';
|
|
52
|
+
(0, corsMiddleware_1.corsMiddleware)(req, res, next);
|
|
53
|
+
expect(next).toHaveBeenCalledWith(expect.any(corsError_1.CorsError));
|
|
54
|
+
});
|
|
55
|
+
it('sanitizes control characters from the CorsError message', () => {
|
|
56
|
+
req.headers.referer = 'http://evil.com\x0ainjected';
|
|
57
|
+
(0, corsMiddleware_1.corsMiddleware)(req, res, next);
|
|
58
|
+
const error = next.mock.calls[0][0];
|
|
59
|
+
// eslint-disable-next-line no-control-regex
|
|
60
|
+
expect(error.message).not.toMatch(/\x0a/);
|
|
61
|
+
expect(error.message).toContain('http://evil.cominjected');
|
|
62
|
+
});
|
|
63
|
+
});
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/__tests__/middlewares/errorHandler.test.ts
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const errorHandler_1 = require("../../middlewares/errorHandler");
|
|
5
|
+
const beavuckTimeClientError_1 = require("../../errors/beavuckTimeClientError");
|
|
6
|
+
const beavuckTimeServerError_1 = require("../../errors/beavuckTimeServerError");
|
|
7
|
+
const corsError_1 = require("../../errors/corsError");
|
|
8
|
+
const http_status_codes_1 = require("http-status-codes");
|
|
9
|
+
const logger_1 = require("../../config/logger");
|
|
10
|
+
const tsoa_1 = require("tsoa");
|
|
11
|
+
jest.mock('../../config/logger');
|
|
12
|
+
describe('errorHandler', () => {
|
|
13
|
+
let req;
|
|
14
|
+
let res;
|
|
15
|
+
let next;
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
req = {};
|
|
18
|
+
res = {
|
|
19
|
+
status: jest.fn().mockReturnThis(),
|
|
20
|
+
json: jest.fn().mockReturnThis(),
|
|
21
|
+
};
|
|
22
|
+
next = jest.fn();
|
|
23
|
+
});
|
|
24
|
+
it('should handle BeavuckTimeClientError', () => {
|
|
25
|
+
const err = new beavuckTimeClientError_1.BeavuckTimeClientError('Some message');
|
|
26
|
+
(0, errorHandler_1.errorHandler)(err, req, res, next);
|
|
27
|
+
expect(logger_1.logger.warn).toHaveBeenCalledWith(err);
|
|
28
|
+
expect(res.status).toHaveBeenCalledWith(err.code);
|
|
29
|
+
expect(res.json).toHaveBeenCalledWith({ message: `${err.message}` });
|
|
30
|
+
});
|
|
31
|
+
it('should handle CorsError', () => {
|
|
32
|
+
const err = new corsError_1.CorsError('Some message');
|
|
33
|
+
(0, errorHandler_1.errorHandler)(err, req, res, next);
|
|
34
|
+
expect(logger_1.logger.warn).toHaveBeenCalledWith(err);
|
|
35
|
+
expect(res.status).toHaveBeenCalledWith(err.code);
|
|
36
|
+
expect(res.json).toHaveBeenCalledWith({ message: `${err.message}` });
|
|
37
|
+
});
|
|
38
|
+
it('should handle BeavuckTimeServerError', () => {
|
|
39
|
+
const err = new beavuckTimeServerError_1.BeavuckTimeServerError('Some message');
|
|
40
|
+
(0, errorHandler_1.errorHandler)(err, req, res, next);
|
|
41
|
+
expect(logger_1.logger.error).toHaveBeenCalledWith(err);
|
|
42
|
+
expect(res.status).toHaveBeenCalledWith(err.code);
|
|
43
|
+
expect(res.json).toHaveBeenCalledWith({ message: beavuckTimeServerError_1.BeavuckTimeServerError.baseMessage });
|
|
44
|
+
});
|
|
45
|
+
it('should handle non-specific error', () => {
|
|
46
|
+
const err = new Error('Some message');
|
|
47
|
+
(0, errorHandler_1.errorHandler)(err, req, res, next);
|
|
48
|
+
const beavuckError = beavuckTimeServerError_1.BeavuckTimeServerError.fromError(err);
|
|
49
|
+
expect(logger_1.logger.error).toHaveBeenCalledWith(beavuckError);
|
|
50
|
+
expect(res.status).toHaveBeenCalledWith(http_status_codes_1.StatusCodes.INTERNAL_SERVER_ERROR);
|
|
51
|
+
expect(res.json).toHaveBeenCalledWith({ message: beavuckTimeServerError_1.BeavuckTimeServerError.baseMessage });
|
|
52
|
+
});
|
|
53
|
+
it('should call next if no error', () => {
|
|
54
|
+
(0, errorHandler_1.errorHandler)(undefined, req, res, next);
|
|
55
|
+
expect(next).toHaveBeenCalled();
|
|
56
|
+
});
|
|
57
|
+
it('should handle ValidateError', () => {
|
|
58
|
+
const err = new tsoa_1.ValidateError({}, 'Some message');
|
|
59
|
+
(0, errorHandler_1.errorHandler)(err, req, res, next);
|
|
60
|
+
expect(logger_1.logger.error).toHaveBeenCalled();
|
|
61
|
+
expect(res.status).toHaveBeenCalledWith(http_status_codes_1.StatusCodes.UNPROCESSABLE_ENTITY);
|
|
62
|
+
expect(res.json).toHaveBeenCalledWith({
|
|
63
|
+
message: 'Validation Error',
|
|
64
|
+
details: err.fields,
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
it('should handle unknown error type', () => {
|
|
68
|
+
const err = { some: 'unknown error' };
|
|
69
|
+
(0, errorHandler_1.errorHandler)(err, req, res, next);
|
|
70
|
+
expect(logger_1.logger.error).toHaveBeenCalled();
|
|
71
|
+
expect(res.status).toHaveBeenCalledWith(http_status_codes_1.StatusCodes.INTERNAL_SERVER_ERROR);
|
|
72
|
+
expect(res.json).toHaveBeenCalledWith({
|
|
73
|
+
message: 'Internal Server Error',
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
it('should handle null error', () => {
|
|
77
|
+
(0, errorHandler_1.errorHandler)(undefined, req, res, next);
|
|
78
|
+
expect(next).toHaveBeenCalled();
|
|
79
|
+
});
|
|
80
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/__tests__/utils/stringUtil.test.ts
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const stringUtil_1 = require("../../utils/stringUtil");
|
|
5
|
+
describe('sanitizeForLog', () => {
|
|
6
|
+
it('strips C0 control characters', () => {
|
|
7
|
+
expect((0, stringUtil_1.sanitizeForLog)('foo\x00\x01\x1fbar')).toBe('foobar');
|
|
8
|
+
});
|
|
9
|
+
it('strips DEL and C1 control characters', () => {
|
|
10
|
+
expect((0, stringUtil_1.sanitizeForLog)('foo\x7f\x80\x9fbar')).toBe('foobar');
|
|
11
|
+
});
|
|
12
|
+
it('leaves printable characters untouched', () => {
|
|
13
|
+
expect((0, stringUtil_1.sanitizeForLog)('https://evil.com')).toBe('https://evil.com');
|
|
14
|
+
});
|
|
15
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/__tests__/utils/urlUtil.test.ts
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const urlUtil_1 = require("../../utils/urlUtil");
|
|
5
|
+
const node_url_1 = require("node:url");
|
|
6
|
+
const A_VALID_URL_STRING = 'https://example.com:1234';
|
|
7
|
+
const SAME_VALID_URL_DIFFERENT_PORT = 'https://example.com:81';
|
|
8
|
+
const SOME_OTHER_VALID_URL_STRING = 'https://different.com:1234';
|
|
9
|
+
const AN_INVALID_URL_STRING = 'invalid';
|
|
10
|
+
describe('urlUtil', () => {
|
|
11
|
+
describe('isSameOrigin', () => {
|
|
12
|
+
it('returns false when either URL is undefined', () => {
|
|
13
|
+
const url = new node_url_1.URL(A_VALID_URL_STRING);
|
|
14
|
+
expect((0, urlUtil_1.isSameOrigin)(url)).toBe(false);
|
|
15
|
+
expect((0, urlUtil_1.isSameOrigin)(undefined, url)).toBe(false);
|
|
16
|
+
});
|
|
17
|
+
it('returns true when URLs have the same origin', () => {
|
|
18
|
+
const url1 = new node_url_1.URL(A_VALID_URL_STRING);
|
|
19
|
+
const url2 = new node_url_1.URL(A_VALID_URL_STRING);
|
|
20
|
+
expect((0, urlUtil_1.isSameOrigin)(url1, url2)).toBe(true);
|
|
21
|
+
});
|
|
22
|
+
it('returns false when URLs have the same origin but different ports', () => {
|
|
23
|
+
const url1 = new node_url_1.URL(A_VALID_URL_STRING);
|
|
24
|
+
const url2 = new node_url_1.URL(SAME_VALID_URL_DIFFERENT_PORT);
|
|
25
|
+
expect((0, urlUtil_1.isSameOrigin)(url1, url2)).toBe(false);
|
|
26
|
+
});
|
|
27
|
+
it('returns false when URLs have different origins', () => {
|
|
28
|
+
const url1 = new node_url_1.URL(A_VALID_URL_STRING);
|
|
29
|
+
const url2 = new node_url_1.URL(SOME_OTHER_VALID_URL_STRING);
|
|
30
|
+
expect((0, urlUtil_1.isSameOrigin)(url1, url2)).toBe(false);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
describe('tryParseUrl', () => {
|
|
34
|
+
it('returns undefined when URL string is undefined', () => {
|
|
35
|
+
expect((0, urlUtil_1.tryParseUrl)()).toBeUndefined();
|
|
36
|
+
});
|
|
37
|
+
it('returns a URL object when URL string is valid', () => {
|
|
38
|
+
const url = (0, urlUtil_1.tryParseUrl)(A_VALID_URL_STRING);
|
|
39
|
+
expect(url).toBeInstanceOf(node_url_1.URL);
|
|
40
|
+
expect(url?.origin).toBe(A_VALID_URL_STRING);
|
|
41
|
+
});
|
|
42
|
+
it('returns undefined when URL string is invalid', () => {
|
|
43
|
+
expect((0, urlUtil_1.tryParseUrl)(AN_INVALID_URL_STRING)).toBeUndefined();
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
});
|
package/openapi/swagger.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "beavuck-time",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.1",
|
|
4
4
|
"description": "Get time in ISO format, in UTC timezone, from a simple, lightweight node server",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"time",
|
|
@@ -37,24 +37,28 @@
|
|
|
37
37
|
"up-patch": "npm version patch --no-git-tag-version",
|
|
38
38
|
"up-minor": "npm version minor --no-git-tag-version",
|
|
39
39
|
"up-major": "npm version major --no-git-tag-version",
|
|
40
|
-
"upgrade-dependencies": "ncu --format group && ncu -u && npm install
|
|
41
|
-
"
|
|
42
|
-
"
|
|
40
|
+
"upgrade-dependencies": "ncu --cooldown=7d --format group && ncu -u && npm run safe-install && npm prune --verbose && npm run audit",
|
|
41
|
+
"upgrade-dependencies:ci": "ncu --cooldown=7d --format group && ncu -u && npm run safe-install:ci && npm prune --verbose && npm run audit",
|
|
42
|
+
"audit": "npm audit fix --verbose",
|
|
43
|
+
"prepublishOnly": "npm run safe-install && npm run build",
|
|
44
|
+
"safe-install": "npm install --ignore-scripts --allow-git=none --min-release-age=7",
|
|
45
|
+
"safe-install:ci": "npm ci --ignore-scripts --allow-git=none --min-release-age=7",
|
|
46
|
+
"lint": "eslint --fix --cache --cache-location node_modules/.cache/.eslint_cache --report-unused-disable-directives --ignore-pattern .gitignore src/",
|
|
43
47
|
"typecheck": "tsc --noEmit",
|
|
44
|
-
"format": "prettier --write
|
|
48
|
+
"format": "prettier --write --log-level warn --cache --cache-location node_modules/.cache/.prettier_cache src/",
|
|
45
49
|
"clean": "npm run lint && npm run typecheck && npm run format",
|
|
46
50
|
"test": "node --env-file=.env.test node_modules/.bin/jest --coverage",
|
|
47
51
|
"build": "tsoa spec-and-routes && tsc",
|
|
48
52
|
"start": "node build/server.js",
|
|
49
|
-
"go": "rm -rf build && npm install && npm run build && npm run clean && npm run test && npm run start"
|
|
53
|
+
"go": "rm -rf build && npm run safe-install && npm run build && npm run clean && npm run test && npm run start"
|
|
50
54
|
},
|
|
51
55
|
"dependencies": {
|
|
52
56
|
"@tsoa/runtime": "^6.6.0",
|
|
53
57
|
"cors": "^2.8.6",
|
|
54
58
|
"express": "^5.2.1",
|
|
55
|
-
"express-rate-limit": "^8.3.
|
|
59
|
+
"express-rate-limit": "^8.3.2",
|
|
56
60
|
"http-status-codes": "^2.3.0",
|
|
57
|
-
"joi": "^18.1.
|
|
61
|
+
"joi": "^18.1.2",
|
|
58
62
|
"tsoa": "^6.6.0",
|
|
59
63
|
"winston": "^3.19.0",
|
|
60
64
|
"winston-daily-rotate-file": "^5.0.0"
|
|
@@ -64,19 +68,18 @@
|
|
|
64
68
|
"@types/cors": "^2.8.19",
|
|
65
69
|
"@types/express": "^5.0.6",
|
|
66
70
|
"@types/jest": "^30.0.0",
|
|
67
|
-
"@types/node": "^25.5.
|
|
71
|
+
"@types/node": "^25.5.2",
|
|
68
72
|
"@types/supertest": "^7.2.0",
|
|
69
|
-
"eslint": "^10.
|
|
73
|
+
"eslint": "^10.2.0",
|
|
70
74
|
"globals": "^17.4.0",
|
|
71
75
|
"jest": "^30.3.0",
|
|
72
76
|
"jest-util": "^30.3.0",
|
|
73
|
-
"
|
|
74
|
-
"npm-check-updates": "^19.6.6",
|
|
77
|
+
"npm-check-updates": "^20.0.0",
|
|
75
78
|
"prettier": "3.8.1",
|
|
76
79
|
"supertest": "^7.2.2",
|
|
77
|
-
"ts-jest": "^29.4.
|
|
78
|
-
"typescript": "^
|
|
79
|
-
"typescript-eslint": "^8.
|
|
80
|
+
"ts-jest": "^29.4.9",
|
|
81
|
+
"typescript": "^6.0.2",
|
|
82
|
+
"typescript-eslint": "^8.58.0"
|
|
80
83
|
},
|
|
81
84
|
"engines": {
|
|
82
85
|
"node": ">=20.12.0"
|