@devmoods/express-extras 0.18.1 → 0.20.0
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/README.md +5 -5
- package/dist/auth.js +1 -0
- package/dist/context.d.ts +1 -9
- package/dist/context.js +15 -17
- package/dist/errors.js +1 -2
- package/dist/listen.js +1 -1
- package/dist/logger.js +9 -10
- package/dist/middleware.js +32 -1
- package/dist/slack.js +1 -1
- package/package.json +15 -17
package/README.md
CHANGED
|
@@ -77,7 +77,7 @@ app.get(
|
|
|
77
77
|
route(async () => {
|
|
78
78
|
const posts = await postgres.query('SELECT * FROM posts LIMIT 10');
|
|
79
79
|
return posts;
|
|
80
|
-
})
|
|
80
|
+
}),
|
|
81
81
|
);
|
|
82
82
|
|
|
83
83
|
app.post(
|
|
@@ -100,13 +100,13 @@ app.post(
|
|
|
100
100
|
const post = { title, content };
|
|
101
101
|
|
|
102
102
|
await postgres.insert(
|
|
103
|
-
sql`INSERT INTO posts (title, content) VALUES (${title}, ${content})
|
|
103
|
+
sql`INSERT INTO posts (title, content) VALUES (${title}, ${content})`,
|
|
104
104
|
);
|
|
105
105
|
|
|
106
106
|
slack.postMessage(`New post created with title ${title}`);
|
|
107
107
|
|
|
108
108
|
return [201, post];
|
|
109
|
-
})
|
|
109
|
+
}),
|
|
110
110
|
);
|
|
111
111
|
|
|
112
112
|
app.use((req, res, next) => {
|
|
@@ -118,7 +118,7 @@ app.use(
|
|
|
118
118
|
shouldHandleError(error) {
|
|
119
119
|
return true;
|
|
120
120
|
},
|
|
121
|
-
})
|
|
121
|
+
}),
|
|
122
122
|
);
|
|
123
123
|
|
|
124
124
|
app.use(
|
|
@@ -130,7 +130,7 @@ app.use(
|
|
|
130
130
|
logger.error(error);
|
|
131
131
|
}
|
|
132
132
|
},
|
|
133
|
-
})
|
|
133
|
+
}),
|
|
134
134
|
);
|
|
135
135
|
|
|
136
136
|
listen(server, {
|
package/dist/auth.js
CHANGED
|
@@ -118,6 +118,7 @@ export function createAuth({ getUserByToken, methods: { emailAndPassword }, filt
|
|
|
118
118
|
}));
|
|
119
119
|
router.get('/token/refresh', middleware(false), route(async (req, res) => {
|
|
120
120
|
const user = useCurrentUser();
|
|
121
|
+
logger.info('refreshed token', { user });
|
|
121
122
|
if (!user) {
|
|
122
123
|
throw Unauthorized();
|
|
123
124
|
}
|
package/dist/context.d.ts
CHANGED
|
@@ -10,16 +10,8 @@ export declare function withRequestContext({ getRequestId, }?: RequestContextOpt
|
|
|
10
10
|
* Return a value from the request context
|
|
11
11
|
*/
|
|
12
12
|
export declare function useContext<T>(key: string): T | undefined;
|
|
13
|
-
/**
|
|
14
|
-
* @deprecated
|
|
15
|
-
*/
|
|
16
|
-
export declare const getContext: typeof useContext;
|
|
17
13
|
/**
|
|
18
14
|
* Set a value on the request context
|
|
19
15
|
*/
|
|
20
|
-
export declare function setContext<T>(key: string, value: T): any;
|
|
16
|
+
export declare function setContext<T>(key: string, value: T): Map<string, any> | undefined;
|
|
21
17
|
export declare function useRequestId(): string | undefined;
|
|
22
|
-
/**
|
|
23
|
-
* @deprecated
|
|
24
|
-
*/
|
|
25
|
-
export declare const getRequestId: typeof useRequestId;
|
package/dist/context.js
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
2
|
import { v4 as uuid } from 'uuid';
|
|
3
|
-
const requestContext =
|
|
3
|
+
const requestContext = new AsyncLocalStorage();
|
|
4
|
+
const REQUEST_ID = 'requestId';
|
|
4
5
|
/**
|
|
5
6
|
* Middleware for setting a `requestId` on the request context.
|
|
6
7
|
*/
|
|
7
8
|
export function withRequestContext({ getRequestId = (req) => req.headers['X-Request-ID'], } = {}) {
|
|
8
9
|
return (req, res, next) => {
|
|
9
10
|
const requestId = getRequestId(req) || uuid();
|
|
10
|
-
requestContext.run(() => {
|
|
11
|
-
requestContext.
|
|
11
|
+
requestContext.run(new Map(), () => {
|
|
12
|
+
const store = requestContext.getStore();
|
|
13
|
+
if (store) {
|
|
14
|
+
store.set(REQUEST_ID, requestId);
|
|
15
|
+
}
|
|
12
16
|
next();
|
|
13
17
|
});
|
|
14
18
|
};
|
|
@@ -17,26 +21,20 @@ export function withRequestContext({ getRequestId = (req) => req.headers['X-Requ
|
|
|
17
21
|
* Return a value from the request context
|
|
18
22
|
*/
|
|
19
23
|
export function useContext(key) {
|
|
20
|
-
|
|
21
|
-
|
|
24
|
+
const store = requestContext.getStore();
|
|
25
|
+
if (store) {
|
|
26
|
+
return store.get(key);
|
|
22
27
|
}
|
|
23
28
|
}
|
|
24
|
-
/**
|
|
25
|
-
* @deprecated
|
|
26
|
-
*/
|
|
27
|
-
export const getContext = useContext;
|
|
28
29
|
/**
|
|
29
30
|
* Set a value on the request context
|
|
30
31
|
*/
|
|
31
32
|
export function setContext(key, value) {
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
const store = requestContext.getStore();
|
|
34
|
+
if (store) {
|
|
35
|
+
return store.set(key, value);
|
|
34
36
|
}
|
|
35
37
|
}
|
|
36
38
|
export function useRequestId() {
|
|
37
|
-
return useContext(
|
|
39
|
+
return useContext(REQUEST_ID);
|
|
38
40
|
}
|
|
39
|
-
/**
|
|
40
|
-
* @deprecated
|
|
41
|
-
*/
|
|
42
|
-
export const getRequestId = useRequestId;
|
package/dist/errors.js
CHANGED
package/dist/listen.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createTerminus } from '@godaddy/terminus';
|
|
2
2
|
import { getLogger } from './logger.js';
|
|
3
|
-
const logger = getLogger();
|
|
3
|
+
const logger = getLogger('core');
|
|
4
4
|
const READINESS_PERIOD_MS = parseInt(process.env.DM_READINESS_PERIOD_MS || '', 10) || 0;
|
|
5
5
|
const FORCE_EXIT_TIMEOUT = parseInt(process.env.DM_FORCE_EXIT_TIMEOUT || '', 10) || 1000;
|
|
6
6
|
/**
|
package/dist/logger.js
CHANGED
|
@@ -27,41 +27,40 @@ export function createLogger({ name, getContext, silent = () => process.env.NODE
|
|
|
27
27
|
if (silent()) {
|
|
28
28
|
logger.level(bunyan.FATAL + 1);
|
|
29
29
|
}
|
|
30
|
-
function getFinalContext() {
|
|
30
|
+
function getFinalContext(context) {
|
|
31
31
|
return {
|
|
32
32
|
...defaultGetContext(),
|
|
33
33
|
...getContext?.(),
|
|
34
|
+
...context,
|
|
34
35
|
};
|
|
35
36
|
}
|
|
36
37
|
return {
|
|
37
38
|
debug(message, context = {}) {
|
|
38
|
-
return logger.
|
|
39
|
+
return logger.debug(getFinalContext(context), message);
|
|
39
40
|
},
|
|
40
41
|
info(message, context = {}) {
|
|
41
|
-
return logger.
|
|
42
|
+
return logger.info(getFinalContext(context), message);
|
|
42
43
|
},
|
|
43
44
|
warn(message, context = {}) {
|
|
44
|
-
return logger.
|
|
45
|
+
return logger.warn(getFinalContext(context), message);
|
|
45
46
|
},
|
|
46
47
|
error(error, message) {
|
|
47
|
-
const context = getFinalContext();
|
|
48
|
+
const context = getFinalContext({ err: error });
|
|
48
49
|
if (_sentryClient != null) {
|
|
49
50
|
_sentryClient.captureException(error, {
|
|
50
51
|
tags: context,
|
|
51
52
|
});
|
|
52
53
|
}
|
|
53
|
-
return logger
|
|
54
|
-
.child(context, true)
|
|
55
|
-
.error(error, message || error?.message);
|
|
54
|
+
return logger.error(context, message || error?.message);
|
|
56
55
|
},
|
|
57
56
|
fatal(error, message) {
|
|
58
|
-
const context = getFinalContext();
|
|
57
|
+
const context = getFinalContext({ err: error });
|
|
59
58
|
if (_sentryClient != null) {
|
|
60
59
|
_sentryClient.captureException(error, {
|
|
61
60
|
tags: context,
|
|
62
61
|
});
|
|
63
62
|
}
|
|
64
|
-
return logger.
|
|
63
|
+
return logger.fatal(context, message);
|
|
65
64
|
},
|
|
66
65
|
};
|
|
67
66
|
}
|
package/dist/middleware.js
CHANGED
|
@@ -5,14 +5,18 @@ import cookieParser from 'cookie-parser';
|
|
|
5
5
|
import helmet from 'helmet';
|
|
6
6
|
import { withRequestContext } from './context.js';
|
|
7
7
|
import { NotFound } from './errors.js';
|
|
8
|
+
import { getLogger } from './logger.js';
|
|
8
9
|
export const urlencodedParser = bodyParser.urlencoded;
|
|
9
10
|
export const jsonParser = bodyParser.json;
|
|
11
|
+
const logger = getLogger('core');
|
|
10
12
|
export { asyncMiddleware as route };
|
|
11
13
|
/**
|
|
12
14
|
* Register middleware that must come before the app routes.
|
|
13
15
|
*/
|
|
14
16
|
export function before({ cookies = false, helmet: useHelmet = true, json: useJson = true, allowUnsafeInlineScripts = false, } = {}) {
|
|
15
17
|
return compose([
|
|
18
|
+
withRequestContext(),
|
|
19
|
+
logRequestDuration(),
|
|
16
20
|
Sentry.Handlers.requestHandler(),
|
|
17
21
|
Sentry.Handlers.tracingHandler(),
|
|
18
22
|
useHelmet &&
|
|
@@ -39,9 +43,36 @@ export function before({ cookies = false, helmet: useHelmet = true, json: useJso
|
|
|
39
43
|
}),
|
|
40
44
|
useJson && jsonParser(),
|
|
41
45
|
cookies && cookieParser(),
|
|
42
|
-
withRequestContext(),
|
|
43
46
|
].filter(Boolean));
|
|
44
47
|
}
|
|
48
|
+
function logRequestDuration() {
|
|
49
|
+
const getDurationInMilliseconds = (start) => {
|
|
50
|
+
const NS_PER_SEC = 1e9;
|
|
51
|
+
const NS_TO_S = 1e9;
|
|
52
|
+
const diff = process.hrtime(start);
|
|
53
|
+
return (diff[0] * NS_PER_SEC + diff[1]) / NS_TO_S;
|
|
54
|
+
};
|
|
55
|
+
return (req, res, next) => {
|
|
56
|
+
const start = process.hrtime();
|
|
57
|
+
logger.info('http request', {
|
|
58
|
+
method: req.method,
|
|
59
|
+
url: req.originalUrl,
|
|
60
|
+
path: req.path,
|
|
61
|
+
ip: req.ip,
|
|
62
|
+
});
|
|
63
|
+
res.on('finish', () => {
|
|
64
|
+
const duration = getDurationInMilliseconds(start);
|
|
65
|
+
logger.info('http response', {
|
|
66
|
+
method: req.method,
|
|
67
|
+
url: req.originalUrl,
|
|
68
|
+
path: req.path,
|
|
69
|
+
duration,
|
|
70
|
+
status: res.statusCode,
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
next();
|
|
74
|
+
};
|
|
75
|
+
}
|
|
45
76
|
/**
|
|
46
77
|
* Middleware that sends a 404
|
|
47
78
|
*
|
package/dist/slack.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devmoods/express-extras",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"description": "Kubernetes-ready utilities for rapid Node.js application development",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -23,55 +23,53 @@
|
|
|
23
23
|
],
|
|
24
24
|
"license": "ISC",
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@devmoods/eslint-config": "^1.
|
|
27
|
-
"@sentry/types": "^7.
|
|
26
|
+
"@devmoods/eslint-config": "^1.2.0",
|
|
27
|
+
"@sentry/types": "^7.60.1",
|
|
28
28
|
"@types/express-serve-static-core": "^4.17.35",
|
|
29
|
-
"@types/node": "20.
|
|
29
|
+
"@types/node": "20.4.5",
|
|
30
30
|
"@types/supertest": "2.0.12",
|
|
31
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
32
|
-
"@typescript-eslint/parser": "^
|
|
31
|
+
"@typescript-eslint/eslint-plugin": "^6.2.0",
|
|
32
|
+
"@typescript-eslint/parser": "^6.2.0",
|
|
33
33
|
"chalk": "^5.3.0",
|
|
34
|
-
"eslint": "^8.
|
|
34
|
+
"eslint": "^8.46.0",
|
|
35
35
|
"eslint-plugin-jsx-a11y": "^6.7.1",
|
|
36
|
-
"eslint-plugin-prettier": "^
|
|
37
|
-
"eslint-plugin-react": "^7.
|
|
36
|
+
"eslint-plugin-prettier": "^5.0.0",
|
|
37
|
+
"eslint-plugin-react": "^7.33.1",
|
|
38
38
|
"eslint-plugin-react-hooks": "^4.6.0",
|
|
39
39
|
"import-sort-cli": "6.0.0",
|
|
40
40
|
"import-sort-style-module": "6.0.0",
|
|
41
41
|
"isomorphic-fetch": "3.0.0",
|
|
42
|
-
"prettier": "
|
|
42
|
+
"prettier": "3.0.0",
|
|
43
43
|
"supertest": "6.3.3",
|
|
44
44
|
"ts-node": "10.9.1",
|
|
45
45
|
"typescript": "5.1.6",
|
|
46
|
-
"vitest": "^0.
|
|
46
|
+
"vitest": "^0.33.0"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@devmoods/fetch": "^3.3.0",
|
|
50
50
|
"@godaddy/terminus": "^4.12.1",
|
|
51
|
-
"@sentry/node": "^7.
|
|
51
|
+
"@sentry/node": "^7.60.1",
|
|
52
52
|
"@types/bcrypt": "^5.0.0",
|
|
53
53
|
"@types/body-parser": "^1.19.2",
|
|
54
54
|
"@types/bunyan": "^1.8.8",
|
|
55
|
-
"@types/cls-hooked": "^4.3.4",
|
|
56
55
|
"@types/cookie-parser": "^1.4.3",
|
|
57
56
|
"@types/express": "^4.17.17",
|
|
58
57
|
"@types/jsonwebtoken": "^9.0.2",
|
|
59
|
-
"@types/nodemailer": "^6.4.
|
|
58
|
+
"@types/nodemailer": "^6.4.9",
|
|
60
59
|
"@types/pg": "^8.10.2",
|
|
61
60
|
"@types/uuid": "^9.0.2",
|
|
62
61
|
"ajv": "^8.12.0",
|
|
63
62
|
"bcrypt": "^5.1.0",
|
|
64
63
|
"body-parser": "^1.20.2",
|
|
65
64
|
"bunyan": "^1.8.15",
|
|
66
|
-
"cls-hooked": "^4.2.2",
|
|
67
65
|
"compose-middleware": "^5.0.1",
|
|
68
66
|
"cookie-parser": "^1.4.6",
|
|
69
67
|
"dotenv": "^16.3.1",
|
|
70
68
|
"express": "^4.18.2",
|
|
71
69
|
"helmet": "^7.0.0",
|
|
72
|
-
"jsonwebtoken": "^9.0.
|
|
70
|
+
"jsonwebtoken": "^9.0.1",
|
|
73
71
|
"meow": "^12.0.1",
|
|
74
|
-
"nodemailer": "^6.9.
|
|
72
|
+
"nodemailer": "^6.9.4",
|
|
75
73
|
"pg": "^8.11.1",
|
|
76
74
|
"redis": "^4.6.7",
|
|
77
75
|
"sql-template-strings": "^2.2.2",
|