@darksheep/express 1.0.0 → 2.0.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/CHANGELOG.md +23 -0
- package/express.d.ts +38 -0
- package/package.json +15 -3
- package/src/content-type/middleware.js +45 -0
- package/src/content-type/router.js +27 -0
- package/src/error-handler/http.handler.js +63 -0
- package/src/error-handler/json-schema.handler.js +41 -0
- package/src/error-handler/middleware.js +38 -0
- package/src/error-handler/zod.handler.js +48 -0
- package/src/index.js +11 -1
- package/src/{request-logger.js → logger/middleware.js} +7 -15
- package/src/not-found/middleware.js +11 -0
- package/src/request-id/middleware.js +34 -0
- package/types/content-type/middleware.d.ts +8 -0
- package/types/content-type/router.d.ts +7 -0
- package/types/error-handler/http.handler.d.ts +28 -0
- package/types/error-handler/json-schema.handler.d.ts +5 -0
- package/types/error-handler/middleware.d.ts +1 -0
- package/types/error-handler/zod.handler.d.ts +30 -0
- package/types/index.d.ts +9 -1
- package/types/logger/middleware.d.ts +9 -0
- package/types/not-found/middleware.d.ts +1 -0
- package/types/request-id/middleware.d.ts +13 -0
- package/src/express.d.ts +0 -18
- package/src/get-trail.js +0 -29
- package/types/request-logger.d.ts +0 -10
- /package/src/{should-filter.js → logger/filter.js} +0 -0
- /package/types/{should-filter.d.ts → logger/filter.d.ts} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.0.0](https://github.com/DarkSheepSoftware/node-packages/compare/express-v1.0.1...express-v2.0.0) (2025-07-19)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### ⚠ BREAKING CHANGES
|
|
7
|
+
|
|
8
|
+
* Rework express package into multiple middleware functions ([#698](https://github.com/DarkSheepSoftware/node-packages/issues/698))
|
|
9
|
+
|
|
10
|
+
### 🌟 Features
|
|
11
|
+
|
|
12
|
+
* Add notFoundMiddleware and errorHandlerMiddleware ([#700](https://github.com/DarkSheepSoftware/node-packages/issues/700)) ([9a46f01](https://github.com/DarkSheepSoftware/node-packages/commit/9a46f018f7295611aa3baf09ea823334eebfa232))
|
|
13
|
+
* Rework express package into multiple middleware functions ([#698](https://github.com/DarkSheepSoftware/node-packages/issues/698)) ([5a5a234](https://github.com/DarkSheepSoftware/node-packages/commit/5a5a2343d823b5233b25dfbbf15bb8d9bf82bf4d))
|
|
14
|
+
|
|
15
|
+
## [1.0.1](https://github.com/DarkSheepSoftware/node-packages/compare/express-v1.0.0...express-v1.0.1) (2025-04-19)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Dependencies
|
|
19
|
+
|
|
20
|
+
* The following workspace dependencies were updated
|
|
21
|
+
* devDependencies
|
|
22
|
+
* @darksheep/logger bumped from 1.1.0 to 1.2.0
|
|
23
|
+
* peerDependencies
|
|
24
|
+
* @darksheep/logger bumped from ~1.0.10 to ~1.2.0
|
|
25
|
+
|
|
3
26
|
## 1.0.0 (2025-03-25)
|
|
4
27
|
|
|
5
28
|
|
package/express.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/* eslint @typescript-eslint/consistent-type-definitions: 0 */
|
|
2
|
+
import { type Logger } from '@darksheep/logger';
|
|
3
|
+
|
|
4
|
+
export type ApiError = {
|
|
5
|
+
message?: string;
|
|
6
|
+
name: string;
|
|
7
|
+
path?: (number | string)[] | string;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type ErrorHandler<E extends Error = Error> = {
|
|
11
|
+
format(error: E): ApiError[];
|
|
12
|
+
test(error: unknown): error is E;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
declare global {
|
|
16
|
+
namespace Express {
|
|
17
|
+
interface Application {
|
|
18
|
+
errorHandlers: ErrorHandler[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface Request {
|
|
22
|
+
logger: Logger;
|
|
23
|
+
trail: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface Response {
|
|
27
|
+
trail: string;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export {
|
|
33
|
+
type ErrorRequestHandler,
|
|
34
|
+
type Handler,
|
|
35
|
+
type Request,
|
|
36
|
+
type Response,
|
|
37
|
+
// eslint-disable-next-line n/no-missing-import -- not sure why :sad:
|
|
38
|
+
} from 'express';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@darksheep/express",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "express utilites and other bits",
|
|
5
5
|
"license": "UNLICENCED",
|
|
6
6
|
"type": "module",
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"files": [
|
|
14
14
|
"src",
|
|
15
15
|
"types",
|
|
16
|
+
"express.d.ts",
|
|
16
17
|
"!*.test.*"
|
|
17
18
|
],
|
|
18
19
|
"scripts": {
|
|
@@ -21,19 +22,30 @@
|
|
|
21
22
|
"test:watch": "node --watch --test src/**/*.test.js"
|
|
22
23
|
},
|
|
23
24
|
"dependencies": {
|
|
25
|
+
"@darksheep/errors": "1.1.1",
|
|
24
26
|
"@darksheep/prune": "1.0.4",
|
|
25
27
|
"on-finished": "2.4.1"
|
|
26
28
|
},
|
|
27
29
|
"devDependencies": {
|
|
28
|
-
"@darksheep/
|
|
30
|
+
"@darksheep/json-schema": "",
|
|
31
|
+
"@darksheep/logger": "1.2.0",
|
|
29
32
|
"@types/express": "~5.0.0",
|
|
30
33
|
"@types/on-finished": "^2",
|
|
31
|
-
"express": "~
|
|
34
|
+
"express": "~5.1.0"
|
|
32
35
|
},
|
|
33
36
|
"peerDependencies": {
|
|
37
|
+
"@darksheep/json-schema": "",
|
|
34
38
|
"@darksheep/logger": "~1.0.10",
|
|
35
39
|
"express": "~4.21.2 || ~5"
|
|
36
40
|
},
|
|
41
|
+
"peerDependenciesMeta": {
|
|
42
|
+
"@darksheep/json-schema": {
|
|
43
|
+
"optional": true
|
|
44
|
+
},
|
|
45
|
+
"@darksheep/logger": {
|
|
46
|
+
"optional": true
|
|
47
|
+
}
|
|
48
|
+
},
|
|
37
49
|
"engines": {
|
|
38
50
|
"node": "^20.13.1 || ^22.2.0"
|
|
39
51
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @import { Handler } from '../../express.js'
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { UnsupportedMediaType } from '@darksheep/errors';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @param {string | string[]} targetContentTypes - The permitted content types.
|
|
9
|
+
* @param {boolean} [shouldThrow] - Should the middleware throw an error.
|
|
10
|
+
* @returns {Handler}
|
|
11
|
+
*/
|
|
12
|
+
export function createContentTypeMiddleware(targetContentTypes, shouldThrow = true) {
|
|
13
|
+
const contentTypes = new Set(
|
|
14
|
+
typeof targetContentTypes === 'string'
|
|
15
|
+
? [ targetContentTypes ]
|
|
16
|
+
: targetContentTypes,
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
return (request, response, next) => {
|
|
20
|
+
const provided = request.headers['content-type']?.split(';').at(0);
|
|
21
|
+
if (
|
|
22
|
+
provided != null &&
|
|
23
|
+
contentTypes.has(provided)
|
|
24
|
+
) {
|
|
25
|
+
return next();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (shouldThrow === true) {
|
|
29
|
+
return unsupportedMediaType(request, response, next);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return next('route');
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** @type {Handler} */
|
|
37
|
+
export function unsupportedMediaType(request) {
|
|
38
|
+
const { method } = request;
|
|
39
|
+
const url = request.originalUrl.split('?').at(0) ?? '/';
|
|
40
|
+
const contentType = request.headers['content-type'];
|
|
41
|
+
|
|
42
|
+
throw new UnsupportedMediaType(
|
|
43
|
+
`Unsupported content-type at ${method} ${url}: ${contentType}`,
|
|
44
|
+
);
|
|
45
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @import { Handler } from '../../express.js'
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Router } from 'express';
|
|
6
|
+
|
|
7
|
+
import { createContentTypeMiddleware, unsupportedMediaType } from './middleware.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @param {Record<string, Handler | Handler[]>} contentTypeRoutes - An object containing the contentType and routes.
|
|
11
|
+
* @returns {Router}
|
|
12
|
+
*/
|
|
13
|
+
export function createContentTypeRouter(contentTypeRoutes) {
|
|
14
|
+
const router = Router({ strict: true });
|
|
15
|
+
|
|
16
|
+
for (const [ contentType, route ] of Object.entries(contentTypeRoutes)) {
|
|
17
|
+
router.all(
|
|
18
|
+
/^/u,
|
|
19
|
+
createContentTypeMiddleware(contentType, false),
|
|
20
|
+
...(Array.isArray(route) ? route : [ route ]),
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
router.use(unsupportedMediaType);
|
|
25
|
+
|
|
26
|
+
return router;
|
|
27
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @import { ErrorHandler } from '../../express.js';
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @typedef HttpErrorExtra
|
|
7
|
+
* @property {boolean} expose - Should the error message be sent to the client.
|
|
8
|
+
* @property {Record<string, string> | undefined} headers - Additional headers to send.
|
|
9
|
+
* @property {number} statusCode - The response status code.
|
|
10
|
+
*/
|
|
11
|
+
/** @typedef {Error & HttpErrorExtra} HttpError */
|
|
12
|
+
|
|
13
|
+
/** @type {ErrorHandler<HttpError>} */
|
|
14
|
+
export const HttpErrorHandler = {
|
|
15
|
+
/**
|
|
16
|
+
* @param {unknown} error - The possible http error.
|
|
17
|
+
* @returns {error is HttpError}
|
|
18
|
+
*/
|
|
19
|
+
test(error) {
|
|
20
|
+
return (
|
|
21
|
+
error instanceof Error &&
|
|
22
|
+
'expose' in error &&
|
|
23
|
+
'statusCode' in error &&
|
|
24
|
+
typeof error.expose === 'boolean' &&
|
|
25
|
+
typeof error.statusCode === 'number' &&
|
|
26
|
+
(
|
|
27
|
+
(
|
|
28
|
+
'headers' in error === false
|
|
29
|
+
) ||
|
|
30
|
+
(
|
|
31
|
+
'headers' in error === true &&
|
|
32
|
+
error.headers != null &&
|
|
33
|
+
typeof error.headers === 'object' &&
|
|
34
|
+
Object.values(error.headers).every(
|
|
35
|
+
(header) => typeof header === 'string',
|
|
36
|
+
)
|
|
37
|
+
) || (
|
|
38
|
+
'headers' in error === true &&
|
|
39
|
+
error.headers == null
|
|
40
|
+
)
|
|
41
|
+
)
|
|
42
|
+
);
|
|
43
|
+
},
|
|
44
|
+
format(error) {
|
|
45
|
+
// eslint-disable-next-line n/no-process-env
|
|
46
|
+
if (process.env.NODE_ENV === 'devlopment') {
|
|
47
|
+
return [ {
|
|
48
|
+
message: error.message,
|
|
49
|
+
name: error.name,
|
|
50
|
+
stack: error.stack,
|
|
51
|
+
} ];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (error.expose === false) {
|
|
55
|
+
return [ { name: error.name } ];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return [ {
|
|
59
|
+
message: error.message,
|
|
60
|
+
name: error.name,
|
|
61
|
+
} ];
|
|
62
|
+
},
|
|
63
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @import { ErrorHandler } from '../../express.js';
|
|
3
|
+
* @import { RequestSchemaError, ResponseSchemaError } from '@darksheep/json-schema';
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @type {import('@darksheep/json-schema')}
|
|
8
|
+
*/
|
|
9
|
+
let schema;
|
|
10
|
+
try {
|
|
11
|
+
schema = await import('@darksheep/json-schema');
|
|
12
|
+
} catch {
|
|
13
|
+
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
import { HttpErrorHandler } from './http.handler.js';
|
|
17
|
+
|
|
18
|
+
/** @type {ErrorHandler<RequestSchemaError | ResponseSchemaError>} */
|
|
19
|
+
export const SchemaErrorHandler = {
|
|
20
|
+
/**
|
|
21
|
+
* @param {unknown} error - The possible http error.
|
|
22
|
+
* @returns {error is RequestSchemaError | ResponseSchemaError}
|
|
23
|
+
*/
|
|
24
|
+
test(error) {
|
|
25
|
+
return (
|
|
26
|
+
schema != null &&
|
|
27
|
+
(
|
|
28
|
+
error instanceof schema.RequestSchemaError ||
|
|
29
|
+
error instanceof schema.ResponseSchemaError
|
|
30
|
+
)
|
|
31
|
+
);
|
|
32
|
+
},
|
|
33
|
+
format(error) {
|
|
34
|
+
const base = HttpErrorHandler.format(error);
|
|
35
|
+
|
|
36
|
+
// @ts-ignore
|
|
37
|
+
base.push(...error.errors);
|
|
38
|
+
|
|
39
|
+
return base;
|
|
40
|
+
},
|
|
41
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @import {
|
|
3
|
+
* ErrorRequestHandler,
|
|
4
|
+
* } from '../../express.js'
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/** @type {ErrorRequestHandler} */
|
|
8
|
+
export function errorHandlerMiddleware(error, request, response, _next) {
|
|
9
|
+
response.locals.error = error;
|
|
10
|
+
|
|
11
|
+
if ('headers' in error) {
|
|
12
|
+
response.header(error.headers);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
response.status(
|
|
16
|
+
(
|
|
17
|
+
'statusCode' in error &&
|
|
18
|
+
typeof error.statusCode === 'number'
|
|
19
|
+
)
|
|
20
|
+
? error.statusCode
|
|
21
|
+
: 500,
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
for (const { format, test } of request.app.errorHandlers ?? []) {
|
|
25
|
+
if (test(error)) {
|
|
26
|
+
response.json({
|
|
27
|
+
errors: format(error),
|
|
28
|
+
statusCode: response.statusCode,
|
|
29
|
+
});
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
response.json({
|
|
35
|
+
errors: [ { name: 'InternalServerError' } ],
|
|
36
|
+
statusCode: response.statusCode,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @import { ErrorHandler } from '../../express.js';
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @typedef ZodIssue
|
|
7
|
+
* @property {string} message - The error message for the path.
|
|
8
|
+
* @property {string} code - The zod error code text.
|
|
9
|
+
* @property {string} path - The path to the error in zod.
|
|
10
|
+
*/
|
|
11
|
+
/** @typedef {Error & { errors: ZodIssue[] }} ZodError */
|
|
12
|
+
|
|
13
|
+
/** @type {ErrorHandler<ZodError>} */
|
|
14
|
+
export const ZodErrorHandler = {
|
|
15
|
+
/**
|
|
16
|
+
* @param {unknown} error - The possible http error.
|
|
17
|
+
* @returns {error is ZodError}
|
|
18
|
+
*/
|
|
19
|
+
test(error) {
|
|
20
|
+
return (
|
|
21
|
+
error instanceof Error &&
|
|
22
|
+
'errors' in error &&
|
|
23
|
+
Array.isArray(error.errors) &&
|
|
24
|
+
error.errors.every((issue) => (
|
|
25
|
+
issue != null &&
|
|
26
|
+
'message' in issue &&
|
|
27
|
+
'code' in issue &&
|
|
28
|
+
'path' in issue &&
|
|
29
|
+
typeof issue.message === 'string' &&
|
|
30
|
+
typeof issue.code === 'string' &&
|
|
31
|
+
typeof issue.path === 'string'
|
|
32
|
+
))
|
|
33
|
+
);
|
|
34
|
+
},
|
|
35
|
+
format(error) {
|
|
36
|
+
return [
|
|
37
|
+
{
|
|
38
|
+
message: error.message,
|
|
39
|
+
name: error.name,
|
|
40
|
+
},
|
|
41
|
+
...error.errors.map((issue) => ({
|
|
42
|
+
message: issue.message,
|
|
43
|
+
name: issue.code,
|
|
44
|
+
path: issue.path,
|
|
45
|
+
})),
|
|
46
|
+
];
|
|
47
|
+
},
|
|
48
|
+
};
|
package/src/index.js
CHANGED
|
@@ -1 +1,11 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { createContentTypeMiddleware } from './content-type/middleware.js';
|
|
2
|
+
export { createContentTypeRouter } from './content-type/router.js';
|
|
3
|
+
export { createRequestLoggerMiddleware } from './logger/middleware.js';
|
|
4
|
+
export { createRequestIdMiddleware } from './request-id/middleware.js';
|
|
5
|
+
|
|
6
|
+
export { errorHandlerMiddleware } from './error-handler/middleware.js';
|
|
7
|
+
export { notFoundMiddleware } from './not-found/middleware.js';
|
|
8
|
+
|
|
9
|
+
export { HttpErrorHandler } from './error-handler/http.handler.js';
|
|
10
|
+
export { SchemaErrorHandler } from './error-handler/json-schema.handler.js';
|
|
11
|
+
export { ZodErrorHandler } from './error-handler/zod.handler.js';
|
|
@@ -1,42 +1,33 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @import { Socket } from 'node:net'
|
|
3
|
-
* @import { Handler } from '
|
|
4
|
-
* @import { Filter } from './
|
|
3
|
+
* @import { Handler } from '../../express.js'
|
|
4
|
+
* @import { Filter } from './filter.js'
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { Logger } from '@darksheep/logger';
|
|
8
8
|
import { pruneShallow } from '@darksheep/prune';
|
|
9
9
|
import onFinished from 'on-finished';
|
|
10
10
|
|
|
11
|
-
import { shouldFilterOutRequest } from './
|
|
11
|
+
import { shouldFilterOutRequest } from './filter.js';
|
|
12
12
|
|
|
13
13
|
/** @type {WeakMap<Socket, number>} */
|
|
14
14
|
const sockets = new WeakMap();
|
|
15
15
|
|
|
16
|
-
import { randomUUID } from 'node:crypto';
|
|
17
|
-
|
|
18
16
|
/**
|
|
19
17
|
* @param {Logger} logger - A callback that can be bound to the request.
|
|
20
18
|
* @param {Filter[]} [filters] - A list of requests to not log.
|
|
21
|
-
* @param {string} [headerName] - The name of a header to include in the response with the trail.
|
|
22
19
|
* @returns {Handler}
|
|
23
20
|
*/
|
|
24
|
-
export function
|
|
21
|
+
export function createRequestLoggerMiddleware(
|
|
25
22
|
logger,
|
|
26
23
|
filters,
|
|
27
|
-
headerName = 'X-Request-Id',
|
|
28
24
|
) {
|
|
29
25
|
return (request, response, next) => {
|
|
30
|
-
request.
|
|
31
|
-
request.header(headerName) ??
|
|
32
|
-
request.id ??
|
|
33
|
-
randomUUID()
|
|
34
|
-
);
|
|
35
|
-
request.logger = logger.trail(request.id);
|
|
26
|
+
request.logger = logger;
|
|
36
27
|
|
|
37
28
|
Logger.wrap({
|
|
38
29
|
request: request,
|
|
39
|
-
trail: request.
|
|
30
|
+
trail: request.trail,
|
|
40
31
|
}, () => {
|
|
41
32
|
const [ requestPath ] = request.originalUrl.split('?', 1);
|
|
42
33
|
if (
|
|
@@ -57,6 +48,7 @@ export function createRequestLogger(
|
|
|
57
48
|
const context = pruneShallow({
|
|
58
49
|
duration: Date.now() - requestStartMs,
|
|
59
50
|
error: error,
|
|
51
|
+
cause: response.locals.error,
|
|
60
52
|
|
|
61
53
|
request: request,
|
|
62
54
|
response: response,
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @import { Handler } from '../../express.js'
|
|
3
|
+
*/
|
|
4
|
+
import { NotFound } from '@darksheep/errors';
|
|
5
|
+
|
|
6
|
+
/** @type {Handler} */
|
|
7
|
+
export function notFoundMiddleware(request, _response, next) {
|
|
8
|
+
return next(
|
|
9
|
+
new NotFound(`${request.method} ${request.originalUrl}`),
|
|
10
|
+
);
|
|
11
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @import { Handler, Request } from '../../express.js'
|
|
3
|
+
*/
|
|
4
|
+
import { randomUUID } from 'node:crypto';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @param {Request} request - The express request.
|
|
8
|
+
* @param {string} [headerName] - The name of the header used in correlation.
|
|
9
|
+
* @returns {string}
|
|
10
|
+
*/
|
|
11
|
+
export function getTrail(request, headerName = 'X-Request-Id') {
|
|
12
|
+
if (typeof request.trail === 'string') {
|
|
13
|
+
return request.trail;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const header = request.header(headerName);
|
|
17
|
+
if (typeof header === 'string') {
|
|
18
|
+
return header;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return randomUUID();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @param {string} [headerName] - The name of a header to include in the response with the trail.
|
|
26
|
+
* @returns {Handler}
|
|
27
|
+
*/
|
|
28
|
+
export function createRequestIdMiddleware(headerName) {
|
|
29
|
+
return (request, response, next) => {
|
|
30
|
+
request.trail ??= getTrail(request, headerName);
|
|
31
|
+
response.trail = request.trail;
|
|
32
|
+
return next();
|
|
33
|
+
};
|
|
34
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param {string | string[]} targetContentTypes - The permitted content types.
|
|
3
|
+
* @param {boolean} [shouldThrow] - Should the middleware throw an error.
|
|
4
|
+
* @returns {Handler}
|
|
5
|
+
*/
|
|
6
|
+
export function createContentTypeMiddleware(targetContentTypes: string | string[], shouldThrow?: boolean): Handler;
|
|
7
|
+
export function unsupportedMediaType(req: import(".store/@types-express-serve-static-core-npm-5.0.7-4a816a369e/package").Request<import(".store/@types-express-serve-static-core-npm-5.0.7-4a816a369e/package").ParamsDictionary, any, any, import(".store/@types-qs-npm-6.14.0-e27d9ca044/package").ParsedQs, Record<string, any>>, res: import(".store/@types-express-serve-static-core-npm-5.0.7-4a816a369e/package").Response<any, Record<string, any>, number>, next: import(".store/@types-express-serve-static-core-npm-5.0.7-4a816a369e/package").NextFunction): unknown;
|
|
8
|
+
import type { Handler } from '../../express.js';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param {Record<string, Handler | Handler[]>} contentTypeRoutes - An object containing the contentType and routes.
|
|
3
|
+
* @returns {Router}
|
|
4
|
+
*/
|
|
5
|
+
export function createContentTypeRouter(contentTypeRoutes: Record<string, Handler | Handler[]>): Router;
|
|
6
|
+
import type { Handler } from '../../express.js';
|
|
7
|
+
import { Router } from 'express';
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @import { ErrorHandler } from '../../express.js';
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* @typedef HttpErrorExtra
|
|
6
|
+
* @property {boolean} expose - Should the error message be sent to the client.
|
|
7
|
+
* @property {Record<string, string> | undefined} headers - Additional headers to send.
|
|
8
|
+
* @property {number} statusCode - The response status code.
|
|
9
|
+
*/
|
|
10
|
+
/** @typedef {Error & HttpErrorExtra} HttpError */
|
|
11
|
+
/** @type {ErrorHandler<HttpError>} */
|
|
12
|
+
export const HttpErrorHandler: ErrorHandler<HttpError>;
|
|
13
|
+
export type HttpErrorExtra = {
|
|
14
|
+
/**
|
|
15
|
+
* - Should the error message be sent to the client.
|
|
16
|
+
*/
|
|
17
|
+
expose: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* - Additional headers to send.
|
|
20
|
+
*/
|
|
21
|
+
headers: Record<string, string> | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* - The response status code.
|
|
24
|
+
*/
|
|
25
|
+
statusCode: number;
|
|
26
|
+
};
|
|
27
|
+
export type HttpError = Error & HttpErrorExtra;
|
|
28
|
+
import type { ErrorHandler } from '../../express.js';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** @type {ErrorHandler<RequestSchemaError | ResponseSchemaError>} */
|
|
2
|
+
export const SchemaErrorHandler: ErrorHandler<RequestSchemaError | ResponseSchemaError>;
|
|
3
|
+
import type { RequestSchemaError } from '@darksheep/json-schema';
|
|
4
|
+
import type { ResponseSchemaError } from '@darksheep/json-schema';
|
|
5
|
+
import type { ErrorHandler } from '../../express.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function errorHandlerMiddleware(err: any, req: import(".store/@types-express-serve-static-core-npm-5.0.7-4a816a369e/package").Request<import(".store/@types-express-serve-static-core-npm-5.0.7-4a816a369e/package").ParamsDictionary, any, any, import(".store/@types-qs-npm-6.14.0-e27d9ca044/package").ParsedQs, Record<string, any>>, res: import(".store/@types-express-serve-static-core-npm-5.0.7-4a816a369e/package").Response<any, Record<string, any>, number>, next: import(".store/@types-express-serve-static-core-npm-5.0.7-4a816a369e/package").NextFunction): unknown;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @import { ErrorHandler } from '../../express.js';
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* @typedef ZodIssue
|
|
6
|
+
* @property {string} message - The error message for the path.
|
|
7
|
+
* @property {string} code - The zod error code text.
|
|
8
|
+
* @property {string} path - The path to the error in zod.
|
|
9
|
+
*/
|
|
10
|
+
/** @typedef {Error & { errors: ZodIssue[] }} ZodError */
|
|
11
|
+
/** @type {ErrorHandler<ZodError>} */
|
|
12
|
+
export const ZodErrorHandler: ErrorHandler<ZodError>;
|
|
13
|
+
export type ZodIssue = {
|
|
14
|
+
/**
|
|
15
|
+
* - The error message for the path.
|
|
16
|
+
*/
|
|
17
|
+
message: string;
|
|
18
|
+
/**
|
|
19
|
+
* - The zod error code text.
|
|
20
|
+
*/
|
|
21
|
+
code: string;
|
|
22
|
+
/**
|
|
23
|
+
* - The path to the error in zod.
|
|
24
|
+
*/
|
|
25
|
+
path: string;
|
|
26
|
+
};
|
|
27
|
+
export type ZodError = Error & {
|
|
28
|
+
errors: ZodIssue[];
|
|
29
|
+
};
|
|
30
|
+
import type { ErrorHandler } from '../../express.js';
|
package/types/index.d.ts
CHANGED
|
@@ -1 +1,9 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { createContentTypeMiddleware } from "./content-type/middleware.js";
|
|
2
|
+
export { createContentTypeRouter } from "./content-type/router.js";
|
|
3
|
+
export { createRequestLoggerMiddleware } from "./logger/middleware.js";
|
|
4
|
+
export { createRequestIdMiddleware } from "./request-id/middleware.js";
|
|
5
|
+
export { errorHandlerMiddleware } from "./error-handler/middleware.js";
|
|
6
|
+
export { notFoundMiddleware } from "./not-found/middleware.js";
|
|
7
|
+
export { HttpErrorHandler } from "./error-handler/http.handler.js";
|
|
8
|
+
export { SchemaErrorHandler } from "./error-handler/json-schema.handler.js";
|
|
9
|
+
export { ZodErrorHandler } from "./error-handler/zod.handler.js";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param {Logger} logger - A callback that can be bound to the request.
|
|
3
|
+
* @param {Filter[]} [filters] - A list of requests to not log.
|
|
4
|
+
* @returns {Handler}
|
|
5
|
+
*/
|
|
6
|
+
export function createRequestLoggerMiddleware(logger: Logger, filters?: Filter[]): Handler;
|
|
7
|
+
import { Logger } from '@darksheep/logger';
|
|
8
|
+
import type { Filter } from './filter.js';
|
|
9
|
+
import type { Handler } from '../../express.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function notFoundMiddleware(req: import(".store/@types-express-serve-static-core-npm-5.0.7-4a816a369e/package").Request<import(".store/@types-express-serve-static-core-npm-5.0.7-4a816a369e/package").ParamsDictionary, any, any, import(".store/@types-qs-npm-6.14.0-e27d9ca044/package").ParsedQs, Record<string, any>>, res: import(".store/@types-express-serve-static-core-npm-5.0.7-4a816a369e/package").Response<any, Record<string, any>, number>, next: import(".store/@types-express-serve-static-core-npm-5.0.7-4a816a369e/package").NextFunction): unknown;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param {Request} request - The express request.
|
|
3
|
+
* @param {string} [headerName] - The name of the header used in correlation.
|
|
4
|
+
* @returns {string}
|
|
5
|
+
*/
|
|
6
|
+
export function getTrail(request: Request, headerName?: string): string;
|
|
7
|
+
/**
|
|
8
|
+
* @param {string} [headerName] - The name of a header to include in the response with the trail.
|
|
9
|
+
* @returns {Handler}
|
|
10
|
+
*/
|
|
11
|
+
export function createRequestIdMiddleware(headerName?: string): Handler;
|
|
12
|
+
import type { Request } from '../../express.js';
|
|
13
|
+
import type { Handler } from '../../express.js';
|
package/src/express.d.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
/* eslint @typescript-eslint/consistent-type-definitions: 0, n/no-missing-import: 0 */
|
|
2
|
-
|
|
3
|
-
import { type Logger } from '@darksheep/logger';
|
|
4
|
-
|
|
5
|
-
declare global {
|
|
6
|
-
namespace Express {
|
|
7
|
-
interface Request {
|
|
8
|
-
id: string;
|
|
9
|
-
logger: Logger;
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export {
|
|
15
|
-
type Handler,
|
|
16
|
-
type Request,
|
|
17
|
-
type Response,
|
|
18
|
-
} from 'express';
|
package/src/get-trail.js
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @import { Request } from './express.js'
|
|
3
|
-
* @import { Logger } from '@darksheep/logger'
|
|
4
|
-
*/
|
|
5
|
-
import { randomUUID } from 'node:crypto';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* @param {Request} request - The express request.
|
|
9
|
-
* @param {Logger} logger - The root logger.
|
|
10
|
-
* @param {string} headerName - The name of the header used in correlation.
|
|
11
|
-
* @returns {string}
|
|
12
|
-
*/
|
|
13
|
-
export function getTrail(request, logger, headerName) {
|
|
14
|
-
const loggerTrail = logger.get('trail');
|
|
15
|
-
if (typeof loggerTrail === 'string') {
|
|
16
|
-
return loggerTrail;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const header = request.header(headerName);
|
|
20
|
-
if (typeof header === 'string') {
|
|
21
|
-
return header;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
if (typeof request.id === 'string') {
|
|
25
|
-
return request.id;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
return randomUUID();
|
|
29
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @param {Logger} logger - A callback that can be bound to the request.
|
|
3
|
-
* @param {Filter[]} [filters] - A list of requests to not log.
|
|
4
|
-
* @param {string} [headerName] - The name of a header to include in the response with the trail.
|
|
5
|
-
* @returns {Handler}
|
|
6
|
-
*/
|
|
7
|
-
export function createRequestLogger(logger: Logger, filters?: Filter[], headerName?: string): Handler;
|
|
8
|
-
import { Logger } from '@darksheep/logger';
|
|
9
|
-
import type { Filter } from './should-filter.js';
|
|
10
|
-
import type { Handler } from './express.js';
|
|
File without changes
|
|
File without changes
|