@emoyly/problem 8.0.6 → 9.1.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.
Files changed (146) hide show
  1. package/LICENSE +1 -1
  2. package/{esm → dist}/problem.d.ts +1 -1
  3. package/{esm → dist}/util/index.d.ts +1 -0
  4. package/{esm → dist}/util/index.js +1 -0
  5. package/dist/util/version.d.ts +2 -0
  6. package/{esm → dist}/util/version.js +1 -1
  7. package/package.json +36 -60
  8. package/{cjs/defaults/4xx.js → src/defaults/4xx.ts} +5 -5
  9. package/{cjs/defaults/5xx.js → src/defaults/5xx.ts} +5 -5
  10. package/{cjs/defaults/aws.js → src/defaults/aws.ts} +5 -5
  11. package/{cjs/defaults/cloudflare.js → src/defaults/cloudflare.ts} +5 -5
  12. package/{cjs/defaults/iis.js → src/defaults/iis.ts} +5 -5
  13. package/src/defaults/index.ts +7 -0
  14. package/{cjs/defaults/nginx.js → src/defaults/nginx.ts} +5 -5
  15. package/{cjs/defaults/others.js → src/defaults/others.ts} +4 -5
  16. package/src/index.ts +4 -0
  17. package/src/middleware/axios.ts +26 -0
  18. package/src/middleware/base.ts +72 -0
  19. package/src/middleware/express.ts +52 -0
  20. package/src/parsers/axios.ts +59 -0
  21. package/{cjs/parsers/jsonwebtoken.js → src/parsers/jsonwebtoken.ts} +42 -35
  22. package/src/parsers/mikroorm.ts +22 -0
  23. package/src/parsers/tsoa.ts +27 -0
  24. package/src/parsers/zod.ts +23 -0
  25. package/src/problem.ts +70 -0
  26. package/src/typings/codes.ts +7 -0
  27. package/src/typings/index.ts +4 -0
  28. package/{esm/typings/middleware.d.ts → src/typings/middleware.ts} +8 -5
  29. package/src/typings/parser.ts +3 -0
  30. package/{esm/typings/problem.d.ts → src/typings/problem.ts} +18 -17
  31. package/src/util/getProblems.ts +62 -0
  32. package/src/util/index.ts +5 -0
  33. package/src/util/misc.ts +20 -0
  34. package/{cjs/util/problemArray.js → src/util/problemArray.ts} +11 -11
  35. package/src/util/validation.ts +39 -0
  36. package/src/util/version.ts +2 -0
  37. package/.yarn/versions/524dd037.yml +0 -0
  38. package/.yarn/versions/b57d536b.yml +0 -0
  39. package/.yarn/versions/d984e272.yml +0 -0
  40. package/cjs/defaults/index.js +0 -17
  41. package/cjs/index.js +0 -22
  42. package/cjs/middleware/axios.js +0 -23
  43. package/cjs/middleware/base.js +0 -62
  44. package/cjs/middleware/express.js +0 -46
  45. package/cjs/package.json +0 -3
  46. package/cjs/parsers/axios.js +0 -56
  47. package/cjs/parsers/mikroorm.js +0 -20
  48. package/cjs/parsers/tsoa.js +0 -25
  49. package/cjs/parsers/zod.js +0 -21
  50. package/cjs/problem.d.ts +0 -14
  51. package/cjs/problem.js +0 -76
  52. package/cjs/tsconfig.tsbuildinfo +0 -1
  53. package/cjs/typings/codes.js +0 -2
  54. package/cjs/typings/index.js +0 -20
  55. package/cjs/typings/middleware.js +0 -2
  56. package/cjs/typings/parser.js +0 -2
  57. package/cjs/typings/problem.js +0 -12
  58. package/cjs/util/getProblems.js +0 -62
  59. package/cjs/util/index.d.ts +0 -4
  60. package/cjs/util/index.js +0 -20
  61. package/cjs/util/misc.js +0 -18
  62. package/cjs/util/validation.js +0 -38
  63. package/cjs/util/version.d.ts +0 -2
  64. package/cjs/util/version.js +0 -5
  65. package/eslint.config.mjs +0 -5
  66. package/esm/defaults/4xx.d.ts +0 -147
  67. package/esm/defaults/5xx.d.ts +0 -57
  68. package/esm/defaults/aws.d.ts +0 -12
  69. package/esm/defaults/cloudflare.d.ts +0 -47
  70. package/esm/defaults/iis.d.ts +0 -17
  71. package/esm/defaults/index.d.ts +0 -7
  72. package/esm/defaults/nginx.d.ts +0 -32
  73. package/esm/defaults/others.d.ts +0 -37
  74. package/esm/index.d.ts +0 -4
  75. package/esm/middleware/axios.d.ts +0 -13
  76. package/esm/middleware/base.d.ts +0 -30
  77. package/esm/middleware/express.d.ts +0 -19
  78. package/esm/package.json +0 -3
  79. package/esm/parsers/axios.d.ts +0 -3
  80. package/esm/parsers/jsonwebtoken.d.ts +0 -3
  81. package/esm/parsers/mikroorm.d.ts +0 -3
  82. package/esm/parsers/tsoa.d.ts +0 -3
  83. package/esm/parsers/zod.d.ts +0 -3
  84. package/esm/tsconfig.tsbuildinfo +0 -1
  85. package/esm/typings/codes.d.ts +0 -5
  86. package/esm/typings/index.d.ts +0 -4
  87. package/esm/typings/parser.d.ts +0 -2
  88. package/esm/util/getProblems.d.ts +0 -11
  89. package/esm/util/misc.d.ts +0 -2
  90. package/esm/util/problemArray.d.ts +0 -7
  91. package/esm/util/validation.d.ts +0 -5
  92. package/esm/util/version.d.ts +0 -2
  93. package/tsconfig.json +0 -19
  94. /package/{cjs → dist}/defaults/4xx.d.ts +0 -0
  95. /package/{esm → dist}/defaults/4xx.js +0 -0
  96. /package/{cjs → dist}/defaults/5xx.d.ts +0 -0
  97. /package/{esm → dist}/defaults/5xx.js +0 -0
  98. /package/{cjs → dist}/defaults/aws.d.ts +0 -0
  99. /package/{esm → dist}/defaults/aws.js +0 -0
  100. /package/{cjs → dist}/defaults/cloudflare.d.ts +0 -0
  101. /package/{esm → dist}/defaults/cloudflare.js +0 -0
  102. /package/{cjs → dist}/defaults/iis.d.ts +0 -0
  103. /package/{esm → dist}/defaults/iis.js +0 -0
  104. /package/{cjs → dist}/defaults/index.d.ts +0 -0
  105. /package/{esm → dist}/defaults/index.js +0 -0
  106. /package/{cjs → dist}/defaults/nginx.d.ts +0 -0
  107. /package/{esm → dist}/defaults/nginx.js +0 -0
  108. /package/{cjs → dist}/defaults/others.d.ts +0 -0
  109. /package/{esm → dist}/defaults/others.js +0 -0
  110. /package/{cjs → dist}/index.d.ts +0 -0
  111. /package/{esm → dist}/index.js +0 -0
  112. /package/{cjs → dist}/middleware/axios.d.ts +0 -0
  113. /package/{esm → dist}/middleware/axios.js +0 -0
  114. /package/{cjs → dist}/middleware/base.d.ts +0 -0
  115. /package/{esm → dist}/middleware/base.js +0 -0
  116. /package/{cjs → dist}/middleware/express.d.ts +0 -0
  117. /package/{esm → dist}/middleware/express.js +0 -0
  118. /package/{cjs → dist}/parsers/axios.d.ts +0 -0
  119. /package/{esm → dist}/parsers/axios.js +0 -0
  120. /package/{cjs → dist}/parsers/jsonwebtoken.d.ts +0 -0
  121. /package/{esm → dist}/parsers/jsonwebtoken.js +0 -0
  122. /package/{cjs → dist}/parsers/mikroorm.d.ts +0 -0
  123. /package/{esm → dist}/parsers/mikroorm.js +0 -0
  124. /package/{cjs → dist}/parsers/tsoa.d.ts +0 -0
  125. /package/{esm → dist}/parsers/tsoa.js +0 -0
  126. /package/{cjs → dist}/parsers/zod.d.ts +0 -0
  127. /package/{esm → dist}/parsers/zod.js +0 -0
  128. /package/{esm → dist}/problem.js +0 -0
  129. /package/{cjs → dist}/typings/codes.d.ts +0 -0
  130. /package/{esm → dist}/typings/codes.js +0 -0
  131. /package/{cjs → dist}/typings/index.d.ts +0 -0
  132. /package/{esm → dist}/typings/index.js +0 -0
  133. /package/{cjs → dist}/typings/middleware.d.ts +0 -0
  134. /package/{esm → dist}/typings/middleware.js +0 -0
  135. /package/{cjs → dist}/typings/parser.d.ts +0 -0
  136. /package/{esm → dist}/typings/parser.js +0 -0
  137. /package/{cjs → dist}/typings/problem.d.ts +0 -0
  138. /package/{esm → dist}/typings/problem.js +0 -0
  139. /package/{cjs → dist}/util/getProblems.d.ts +0 -0
  140. /package/{esm → dist}/util/getProblems.js +0 -0
  141. /package/{cjs → dist}/util/misc.d.ts +0 -0
  142. /package/{esm → dist}/util/misc.js +0 -0
  143. /package/{cjs → dist}/util/problemArray.d.ts +0 -0
  144. /package/{esm → dist}/util/problemArray.js +0 -0
  145. /package/{cjs → dist}/util/validation.d.ts +0 -0
  146. /package/{esm → dist}/util/validation.js +0 -0
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2020 Emil Petersen
3
+ Copyright (c) 2025 Emil Petersen
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -6,7 +6,7 @@ export declare class Problem extends Error implements IProblem {
6
6
  detail: string | undefined;
7
7
  instance: string | undefined;
8
8
  errorObject?: unknown;
9
- readonly __problemVersion = "8.0.6";
9
+ readonly __problemVersion = "9.1.0";
10
10
  data: unknown;
11
11
  source?: string;
12
12
  constructor(opts: ProblemOpts);
@@ -2,3 +2,4 @@ export * from './getProblems.js';
2
2
  export * from './problemArray.js';
3
3
  export * from './misc.js';
4
4
  export * from './validation.js';
5
+ export { major as majorVersion, version } from './version.js';
@@ -2,3 +2,4 @@ export * from './getProblems.js';
2
2
  export * from './problemArray.js';
3
3
  export * from './misc.js';
4
4
  export * from './validation.js';
5
+ export { major as majorVersion, version } from './version.js';
@@ -0,0 +1,2 @@
1
+ export declare const version = "9.1.0";
2
+ export declare const major: number;
@@ -1,2 +1,2 @@
1
- export const version = '8.0.6';
1
+ export const version = '9.1.0';
2
2
  export const major = Number(version.split('.')[0]);
package/package.json CHANGED
@@ -1,39 +1,41 @@
1
1
  {
2
2
  "name": "@emoyly/problem",
3
- "version": "8.0.6",
3
+ "version": "9.1.0",
4
4
  "description": "A simple error library based around the RFC-7807 standard with optional support for different parsers and middleware.",
5
- "main": "cjs/index.js",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
6
7
  "repository": "https://github.com/emoyly/problem",
7
8
  "author": "Emil Petersen <emoyly@gmail.com>",
8
9
  "license": "MIT",
9
10
  "type": "module",
10
11
  "scripts": {
11
- "build": "yarn dualBuild && tsc -p test/tsconfig.json",
12
- "lint": "eslint src && eslint test",
12
+ "build": "concurrently \"tsc\" \"tsc -p test/tsconfig.json\"",
13
+ "lint": "concurrently \"eslint src\" \"eslint test\"",
13
14
  "publish": "yarn npm publish --access public --tolerate-republish",
14
- "test": "node --import @swc-node/register/esm-register --test ./test/**/*.test.ts"
15
+ "test": "concurrently \"node --import @swc-node/register/esm-register --test ./test/**/*.test.ts\" \"node --import @swc-node/register/esm-register ./test/consumptionTest.ts\""
15
16
  },
16
17
  "devDependencies": {
17
- "@emoyly/devutils": "^1.0.5",
18
- "@emoyly/eslint-config": "^1.1.2",
19
- "@emoyly/utils": "^1.0.4",
20
- "@mikro-orm/core": "^6.4.2",
21
- "@stylistic/eslint-plugin-js": "^2.12.1",
22
- "@stylistic/eslint-plugin-ts": "^2.12.1",
23
- "@swc-node/register": "^1.10.9",
24
- "@swc/core": "^1.10.3",
25
- "@types/express": "^5.0.0",
26
- "@types/jsonwebtoken": "^9.0.7",
27
- "@types/node": "^22.10.2",
28
- "@types/semver": "^7.5.8",
29
- "axios": "^1.7.9",
30
- "eslint": "^9.17.0",
18
+ "@emoyly/devutils": "^1.1.0",
19
+ "@emoyly/eslint-config": "^1.2.2",
20
+ "@emoyly/utils": "^2.0.3",
21
+ "@mikro-orm/core": "^6.5.3",
22
+ "@stylistic/eslint-plugin-js": "^4.4.1",
23
+ "@stylistic/eslint-plugin-ts": "^4.4.1",
24
+ "@swc-node/register": "^1.11.1",
25
+ "@swc/core": "^1.13.5",
26
+ "@types/express": "^5.0.3",
27
+ "@types/jsonwebtoken": "^9.0.10",
28
+ "@types/node": "^24.5.0",
29
+ "@types/semver": "^7.7.1",
30
+ "axios": "^1.12.2",
31
+ "concurrently": "^9.2.1",
32
+ "eslint": "^9.35.0",
31
33
  "jsonwebtoken": "^9.0.2",
32
- "semver": "^7.6.3",
34
+ "semver": "^7.7.2",
33
35
  "tsoa": "^6.6.0",
34
- "typescript": "^5.7.2",
35
- "typescript-eslint": "^8.18.2",
36
- "zod": "^3.24.1"
36
+ "typescript": "^5.9.2",
37
+ "typescript-eslint": "^8.44.0",
38
+ "zod": "^4.1.8"
37
39
  },
38
40
  "peerDependencies": {
39
41
  "@mikro-orm/core": "*",
@@ -59,47 +61,21 @@
59
61
  "optional": true
60
62
  }
61
63
  },
62
- "packageManager": "yarn@4.5.3",
64
+ "packageManager": "yarn@4.9.4",
63
65
  "publishConfig": {
64
66
  "access": "public",
65
67
  "registry": "https://registry.npmjs.org"
66
68
  },
67
69
  "exports": {
68
- ".": {
69
- "import": "./esm/index.js",
70
- "require": "./cjs/index.js"
71
- },
72
- "./middleware/axios": {
73
- "import": "./esm/middleware/axios.js",
74
- "require": "./cjs/middleware/axios.js"
75
- },
76
- "./middleware/base": {
77
- "import": "./esm/middleware/base.js",
78
- "require": "./cjs/middleware/base.js"
79
- },
80
- "./middleware/express": {
81
- "import": "./esm/middleware/express.js",
82
- "require": "./cjs/middleware/express.js"
83
- },
84
- "./parsers/axios": {
85
- "import": "./esm/parsers/axios.js",
86
- "require": "./cjs/parsers/axios.js"
87
- },
88
- "./parsers/jsonwebtoken": {
89
- "import": "./esm/parsers/jsonwebtoken.js",
90
- "require": "./cjs/parsers/jsonwebtoken.js"
91
- },
92
- "./parsers/mikroorm": {
93
- "import": "./esm/parsers/mikroorm.js",
94
- "require": "./cjs/parsers/mikroorm.js"
95
- },
96
- "./parsers/tsoa": {
97
- "import": "./esm/parsers/tsoa.js",
98
- "require": "./cjs/parsers/tsoa.js"
99
- },
100
- "./parsers/zod": {
101
- "import": "./esm/parsers/zod.js",
102
- "require": "./cjs/parsers/zod.js"
103
- }
70
+ ".": "./dist/index.js",
71
+ "./defaults": "./dist/defaults/index.js",
72
+ "./middleware/axios": "./dist/middleware/axios.js",
73
+ "./middleware/base": "./dist/middleware/base.js",
74
+ "./middleware/express": "./dist/middleware/express.js",
75
+ "./parsers/axios": "./dist/parsers/axios.js",
76
+ "./parsers/jsonwebtoken": "./dist/parsers/jsonwebtoken.js",
77
+ "./parsers/mikroorm": "./dist/parsers/mikroorm.js",
78
+ "./parsers/tsoa": "./dist/parsers/tsoa.js",
79
+ "./parsers/zod": "./dist/parsers/zod.js"
104
80
  }
105
81
  }
@@ -1,8 +1,8 @@
1
- "use strict";
2
1
  // Source: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
3
- Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.statusCodes = void 0;
5
- exports.statusCodes = {
2
+
3
+ import { Codes } from '../typings/codes.js'
4
+
5
+ export const statusCodes = {
6
6
  400: {
7
7
  status: 400,
8
8
  title: 'Bad Request',
@@ -148,4 +148,4 @@ exports.statusCodes = {
148
148
  title: 'Unavailable For Legal Reasons (RFC 7725)',
149
149
  type: 'errors/defaults/4xx/unavailableforlegalreasons'
150
150
  }
151
- };
151
+ } as const satisfies Codes
@@ -1,8 +1,8 @@
1
- "use strict";
2
1
  // Source: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
3
- Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.statusCodes = void 0;
5
- exports.statusCodes = {
2
+
3
+ import { Codes } from '../typings/codes.js'
4
+
5
+ export const statusCodes = {
6
6
  500: {
7
7
  status: 500,
8
8
  title: 'Internal Server Error',
@@ -58,4 +58,4 @@ exports.statusCodes = {
58
58
  title: 'Not Extended (RFC 2774)',
59
59
  type: 'errors/defaults/5xx/notextended'
60
60
  }
61
- };
61
+ } as const satisfies Codes
@@ -1,8 +1,8 @@
1
- "use strict";
2
1
  // Source: https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-troubleshooting.html
3
- Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.statusCodes = void 0;
5
- exports.statusCodes = {
2
+
3
+ import { Codes } from '../typings/codes.js'
4
+
5
+ export const statusCodes = {
6
6
  460: {
7
7
  status: 460,
8
8
  title: 'Client closed connection before idle timeout period elapsed',
@@ -13,4 +13,4 @@ exports.statusCodes = {
13
13
  title: 'X-Forwarded-For contains more than 30 ips',
14
14
  type: '/errors/defaults/aws/toomanyips'
15
15
  }
16
- };
16
+ } as const satisfies Codes
@@ -1,8 +1,8 @@
1
- "use strict";
2
1
  // Source: https://support.cloudflare.com/hc/en-us/articles/115003011431
3
- Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.statusCodes = void 0;
5
- exports.statusCodes = {
2
+
3
+ import { Codes } from '../typings/codes.js'
4
+
5
+ export const statusCodes = {
6
6
  520: {
7
7
  status: 520,
8
8
  title: 'Web Server returned an unknown error',
@@ -49,4 +49,4 @@ exports.statusCodes = {
49
49
  title: 'Cloudflare returned 1xxx error',
50
50
  type: '/errors/defaults/cloudflare/1xxx'
51
51
  }
52
- };
52
+ } as const satisfies Codes
@@ -1,8 +1,8 @@
1
- "use strict";
2
1
  // Source: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
3
- Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.statusCodes = void 0;
5
- exports.statusCodes = {
2
+
3
+ import { Codes } from '../typings/codes.js'
4
+
5
+ export const statusCodes = {
6
6
  440: {
7
7
  status: 440,
8
8
  title: 'Login Time-out',
@@ -18,4 +18,4 @@ exports.statusCodes = {
18
18
  title: 'Redirect',
19
19
  type: '/errors/defaults/iis/redirect'
20
20
  }
21
- };
21
+ } as const satisfies Codes
@@ -0,0 +1,7 @@
1
+ export { statusCodes as codes4xx } from '../defaults/4xx.js'
2
+ export { statusCodes as codes5xx } from '../defaults/5xx.js'
3
+ export { statusCodes as codesAws } from '../defaults/aws.js'
4
+ export { statusCodes as codesCloudflare } from '../defaults/cloudflare.js'
5
+ export { statusCodes as codesIis } from '../defaults/iis.js'
6
+ export { statusCodes as codesNginx } from '../defaults/nginx.js'
7
+ export { otherErrors } from '../defaults/others.js'
@@ -1,8 +1,8 @@
1
- "use strict";
2
1
  // Source: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
3
- Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.statusCodes = void 0;
5
- exports.statusCodes = {
2
+
3
+ import { Codes } from '../typings/codes.js'
4
+
5
+ export const statusCodes = {
6
6
  444: {
7
7
  status: 494,
8
8
  title: 'No Response',
@@ -33,4 +33,4 @@ exports.statusCodes = {
33
33
  title: 'Client Closed Request',
34
34
  type: '/errors/defaults/nginx/clientclosedreq'
35
35
  }
36
- };
36
+ } as const satisfies Codes
@@ -1,7 +1,6 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.otherErrors = void 0;
4
- exports.otherErrors = {
1
+ import { Codes } from '../typings/codes.js'
2
+
3
+ export const otherErrors = {
5
4
  'unknown': {
6
5
  status: 500,
7
6
  title: 'Unknown error',
@@ -37,4 +36,4 @@ exports.otherErrors = {
37
36
  title: 'Input failed validation',
38
37
  type: '/errors/defaults/others/validationerror'
39
38
  }
40
- };
39
+ } as const satisfies Codes
package/src/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ export { Problem } from './problem.js'
2
+ export * as defaults from './defaults/index.js'
3
+ export * from './typings/index.js'
4
+ export * from './util/index.js'
@@ -0,0 +1,26 @@
1
+ import type { PartialMiddlewareOptions } from '../typings/middleware.js'
2
+ import { MiddlewareBase } from './base.js'
3
+ import axiosParser from '../parsers/axios.js'
4
+
5
+ type InterceptorArray = [undefined, (error: unknown) => Promise<never>]
6
+
7
+ export class AxiosMiddleware extends MiddlewareBase {
8
+ readonly name = 'axios'
9
+
10
+ constructor(options?: PartialMiddlewareOptions) {
11
+ super({
12
+ 'parsers': [axiosParser]
13
+ }, options)
14
+ }
15
+
16
+ private readonly interceptor = async (error: unknown) => {
17
+ const problems = this.parse(error)
18
+ this.emit(problems)
19
+ return Promise.reject(problems)
20
+ }
21
+
22
+ /**
23
+ * @example instance.interceptors.response.use(...middleware.use())
24
+ */
25
+ readonly use = (): InterceptorArray => [undefined, this.interceptor]
26
+ }
@@ -0,0 +1,72 @@
1
+ import { otherErrors } from '../defaults/index.js'
2
+ import { Problem } from '../problem.js'
3
+ import type { ProblemOpts } from '../typings/index.js'
4
+ import type { MiddlewareOptions, PartialMiddlewareOptions, ProblemListener } from '../typings/middleware.js'
5
+ import { getProblems } from '../util/getProblems.js'
6
+
7
+ /**
8
+ * Middleware collects errors from somewhere, transforms them into something recognizable by a parser, and then passes them to the parsers, and then returns an array of Problems to whatever is using the middleware
9
+ */
10
+ export abstract class MiddlewareBase {
11
+ readonly options: MiddlewareOptions
12
+ abstract readonly name: string
13
+ /**
14
+ * When nothing is returned from the parsers, just throw in a default "fallback" Problem object, so that a Problem is still actually returned/emitted
15
+ */
16
+ readonly enableFallback: boolean = true
17
+
18
+ constructor(defaultOptions: MiddlewareOptions, options?: PartialMiddlewareOptions) {
19
+ this.options = { ...defaultOptions, ...options }
20
+ }
21
+
22
+ /**
23
+ * Parse input using parsers
24
+ */
25
+ protected readonly parse = (input: unknown): Problem[] => {
26
+ const problems = getProblems(input, this.options.parsers)
27
+
28
+ if (!problems || !Array.isArray(problems) || problems.length < 1) {
29
+ if (this.enableFallback) return [new Problem({
30
+ ...this.fallback,
31
+ 'errorObject': input,
32
+ 'source': `middleware:${this.name}`
33
+ })]
34
+
35
+ return []
36
+ }
37
+
38
+ for (const p of problems) {
39
+ if (!p.source) p.source = `middleware:${this.name}`
40
+ }
41
+
42
+ return problems
43
+ }
44
+
45
+ protected readonly fallback: ProblemOpts = otherErrors.unknown
46
+
47
+ protected readonly problemListeners: ProblemListener[] = []
48
+
49
+ protected emit = (list: Problem[]) => {
50
+ for (const listener of this.problemListeners) {
51
+ listener(list)
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Listen to problems
57
+ */
58
+ readonly onProblem = (listener: ProblemListener) => {
59
+ this.problemListeners.push(listener)
60
+ }
61
+
62
+ /**
63
+ * Un-listen to problems
64
+ */
65
+ readonly offProblem = (listener: ProblemListener) => {
66
+ const idx = this.problemListeners.findIndex(val => val === listener)
67
+
68
+ if (idx < 0 || isNaN(idx)) return
69
+
70
+ this.problemListeners.splice(idx, 1)
71
+ }
72
+ }
@@ -0,0 +1,52 @@
1
+ import type { NextFunction, Request, Response } from 'express'
2
+ import type { PartialMiddlewareOptions } from '../typings/middleware.js'
3
+ import { MiddlewareBase } from './base.js'
4
+ import { Problem } from '../problem.js'
5
+ import { codes4xx } from '../defaults/index.js'
6
+
7
+ export class ExpressMiddleware extends MiddlewareBase {
8
+ readonly name = 'express'
9
+
10
+ private middleware = (error: unknown, req: Request, res: Response, next: NextFunction) => {
11
+ const problems = this.parse(error)
12
+ for (const problem of problems) {
13
+ if (!problem.instance) {
14
+ problem.instance = req.originalUrl
15
+ }
16
+ }
17
+
18
+ this.emit(problems)
19
+
20
+ res
21
+ .contentType('application/problem+json')
22
+ .status(problems[0]?.status ?? 500)
23
+ .json(problems.map(val => val.toObject()))
24
+ }
25
+
26
+ /**
27
+ * This function applies the middleware to your Express application. Put this at the very end of your middleware/routes.
28
+ * If you want to also handle 404 errors, check out the notFound middleware function.
29
+ * @example app.use(middleware.use())
30
+ */
31
+ readonly use = () => this.middleware
32
+
33
+ constructor(options?: PartialMiddlewareOptions) {
34
+ super({
35
+ 'parsers': []
36
+ }, options)
37
+ }
38
+
39
+ /**
40
+ * Put this right before the middleware.use() to catch all 404 errors and respond with a Problem instead of the default Express response
41
+ * @example app.use(middleware.notFound)
42
+ */
43
+ readonly notFound = (req: Request, res: Response, next: NextFunction) => {
44
+ const error = new Problem({
45
+ ...codes4xx[404],
46
+ 'instance': req.originalUrl,
47
+ 'detail': `No API endpoints exist on ${req.url} for request method ${req.method}`
48
+ })
49
+
50
+ return next(error)
51
+ }
52
+ }
@@ -0,0 +1,59 @@
1
+ import { otherErrors } from '../defaults/others.js'
2
+ import { Problem } from '../problem.js'
3
+ import type { Parser } from '../typings/parser.js'
4
+ import { getProblems } from '../util/getProblems.js'
5
+ import { getCodeDefaults } from '../util/misc.js'
6
+ import { isAxiosError } from 'axios'
7
+
8
+ const parse: Parser = (input) => {
9
+ if (typeof input !== 'object' || input == null) return []
10
+ if (!isAxiosError(input)) return []
11
+ const request = input.request
12
+
13
+ if (input?.response?.data) {
14
+ const problems = getProblems(input?.response?.data)
15
+ if (problems?.length) return problems
16
+ }
17
+
18
+ if (input?.response) {
19
+ const opts = getCodeDefaults(input.response.status)
20
+ if (opts) return [new Problem({
21
+ ...opts,
22
+ 'instance': input?.config?.url,
23
+ 'stack': input?.stack,
24
+ 'errorObject': input,
25
+ 'source': 'parser:axios'
26
+ })]
27
+ }
28
+
29
+ // TODO: Fix this, this seems bad
30
+ if (request) {
31
+ const isNode = typeof process !== 'undefined' && process.versions && process.versions.node
32
+ const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined'
33
+
34
+ if (isBrowser && request instanceof XMLHttpRequest) {
35
+ return [new Problem({
36
+ ...otherErrors.networkError,
37
+ 'instance': input?.config?.url,
38
+ 'stack': input?.stack,
39
+ 'errorObject': input,
40
+ 'source': 'parser:axios'
41
+ })]
42
+ }
43
+
44
+ // Probably a ClientRequest
45
+ if (isNode && 'pipe' in request && 'destroy' in request) {
46
+ return [new Problem({
47
+ ...otherErrors.networkError,
48
+ 'instance': input?.config?.url,
49
+ 'stack': input?.stack,
50
+ 'errorObject': input,
51
+ 'source': 'parser:axios'
52
+ })]
53
+ }
54
+ }
55
+
56
+ return []
57
+ }
58
+
59
+ export default parse
@@ -1,8 +1,14 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const problem_js_1 = require("../problem.js");
4
- const jsonwebtoken_1 = require("jsonwebtoken");
5
- const errorMap = [
1
+ import { Problem } from '../problem.js'
2
+ import type { Parser } from '../typings/parser.js'
3
+ import { JsonWebTokenError, NotBeforeError, TokenExpiredError } from 'jsonwebtoken'
4
+ import type { ProblemOpts } from '../typings/problem.js'
5
+
6
+ interface ErrorMap {
7
+ 'search': string | RegExp,
8
+ 'result': ProblemOpts,
9
+ }
10
+
11
+ const errorMap: ErrorMap[] = [
6
12
  {
7
13
  'search': 'invalid token',
8
14
  'result': {
@@ -67,37 +73,38 @@ const errorMap = [
67
73
  'title': 'The JSON Web Token subject is invalid',
68
74
  }
69
75
  }
70
- ];
71
- const parse = (input) => {
72
- if (input instanceof jsonwebtoken_1.JsonWebTokenError && input.name === 'JsonWebTokenError') {
73
- const found = errorMap.find(val => typeof val.search === 'string' ? val.search === input.message : val.search.test(input.message));
76
+ ]
77
+
78
+ const parse: Parser = (input) => {
79
+ if (input instanceof JsonWebTokenError && input.name === 'JsonWebTokenError') {
80
+ const found = errorMap.find(val => typeof val.search === 'string' ? val.search === input.message : val.search.test(input.message))
81
+
74
82
  if (found) {
75
- return [new problem_js_1.Problem({
76
- ...found.result,
77
- 'errorObject': input,
78
- 'source': 'parser:jsonwebtoken'
79
- })];
80
- }
81
- }
82
- else if (input instanceof jsonwebtoken_1.TokenExpiredError && input.name === 'TokenExpiredError') {
83
- return [new problem_js_1.Problem({
84
- 'title': 'The JSON Web Token has expired',
85
- 'type': '/errors/jsonwebtoken/tokenexpired',
86
- 'detail': `The JSON Web Token expired at ${input.expiredAt}`,
87
- 'status': 400,
88
- 'errorObject': input,
89
- 'source': 'parser:jsonwebtoken'
90
- })];
91
- }
92
- else if (input instanceof jsonwebtoken_1.NotBeforeError && input.name === 'NotBeforeError') {
93
- return [new problem_js_1.Problem({
94
- 'title': 'The JSON Web Token is not valid yet',
95
- 'type': '/errors/jsonwebtoken/notbefore',
96
- 'detail': `The JSON Web Token is not valid before ${input.date}`,
97
- 'status': 400,
83
+ return [new Problem({
84
+ ...found.result,
98
85
  'errorObject': input,
99
86
  'source': 'parser:jsonwebtoken'
100
- })];
87
+ })]
88
+ }
89
+ } else if (input instanceof TokenExpiredError && input.name === 'TokenExpiredError') {
90
+ return [new Problem({
91
+ 'title': 'The JSON Web Token has expired',
92
+ 'type': '/errors/jsonwebtoken/tokenexpired',
93
+ 'detail': `The JSON Web Token expired at ${input.expiredAt}`,
94
+ 'status': 400,
95
+ 'errorObject': input,
96
+ 'source': 'parser:jsonwebtoken'
97
+ })]
98
+ } else if (input instanceof NotBeforeError && input.name === 'NotBeforeError') {
99
+ return [new Problem({
100
+ 'title': 'The JSON Web Token is not valid yet',
101
+ 'type': '/errors/jsonwebtoken/notbefore',
102
+ 'detail': `The JSON Web Token is not valid before ${input.date}`,
103
+ 'status': 400,
104
+ 'errorObject': input,
105
+ 'source': 'parser:jsonwebtoken'
106
+ })]
101
107
  }
102
- };
103
- exports.default = parse;
108
+ }
109
+
110
+ export default parse
@@ -0,0 +1,22 @@
1
+ import { Problem } from '../problem.js'
2
+ import { statusCodes } from '../defaults/4xx.js'
3
+ import type { Parser } from '../typings/parser.js'
4
+ import { NotFoundError } from '@mikro-orm/core'
5
+
6
+ // TODO: Make this less bad
7
+ const parse: Parser = (input) => {
8
+ if (!(input instanceof NotFoundError)) {
9
+ return
10
+ }
11
+
12
+ return [
13
+ new Problem({
14
+ ...statusCodes['404'],
15
+ 'stack': input.stack,
16
+ 'errorObject': input,
17
+ 'source': 'parser:mikroorm'
18
+ })
19
+ ]
20
+ }
21
+
22
+ export default parse