@emoyly/problem 8.0.5 → 9.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.
Files changed (146) hide show
  1. package/LICENSE +1 -1
  2. package/{cjs → dist}/middleware/axios.d.ts +3 -3
  3. package/{cjs → dist}/middleware/base.d.ts +7 -7
  4. package/{esm → dist}/middleware/express.d.ts +4 -4
  5. package/{esm → dist}/problem.d.ts +1 -1
  6. package/{esm → dist}/util/index.d.ts +1 -0
  7. package/{esm → dist}/util/index.js +1 -0
  8. package/dist/util/version.d.ts +2 -0
  9. package/{esm → dist}/util/version.js +1 -1
  10. package/package.json +14 -45
  11. package/{cjs/defaults/4xx.js → src/defaults/4xx.ts} +5 -5
  12. package/{cjs/defaults/5xx.js → src/defaults/5xx.ts} +5 -5
  13. package/{cjs/defaults/aws.js → src/defaults/aws.ts} +5 -5
  14. package/{cjs/defaults/cloudflare.js → src/defaults/cloudflare.ts} +5 -5
  15. package/{cjs/defaults/iis.js → src/defaults/iis.ts} +5 -5
  16. package/src/defaults/index.ts +7 -0
  17. package/{cjs/defaults/nginx.js → src/defaults/nginx.ts} +5 -5
  18. package/{cjs/defaults/others.js → src/defaults/others.ts} +4 -5
  19. package/src/index.ts +4 -0
  20. package/src/middleware/axios.ts +26 -0
  21. package/src/middleware/base.ts +72 -0
  22. package/src/middleware/express.ts +52 -0
  23. package/src/parsers/axios.ts +59 -0
  24. package/{cjs/parsers/jsonwebtoken.js → src/parsers/jsonwebtoken.ts} +42 -35
  25. package/src/parsers/mikroorm.ts +22 -0
  26. package/src/parsers/tsoa.ts +27 -0
  27. package/src/parsers/zod.ts +23 -0
  28. package/src/problem.ts +70 -0
  29. package/src/typings/codes.ts +7 -0
  30. package/src/typings/index.ts +4 -0
  31. package/{esm/typings/middleware.d.ts → src/typings/middleware.ts} +8 -5
  32. package/src/typings/parser.ts +3 -0
  33. package/{esm/typings/problem.d.ts → src/typings/problem.ts} +18 -17
  34. package/src/util/getProblems.ts +62 -0
  35. package/src/util/index.ts +5 -0
  36. package/src/util/misc.ts +20 -0
  37. package/{cjs/util/problemArray.js → src/util/problemArray.ts} +11 -11
  38. package/src/util/validation.ts +39 -0
  39. package/src/util/version.ts +2 -0
  40. package/.yarn/versions/524dd037.yml +0 -0
  41. package/.yarn/versions/b57d536b.yml +0 -0
  42. package/.yarn/versions/d984e272.yml +0 -0
  43. package/cjs/defaults/index.js +0 -17
  44. package/cjs/index.js +0 -22
  45. package/cjs/middleware/axios.js +0 -23
  46. package/cjs/middleware/base.js +0 -62
  47. package/cjs/middleware/express.d.ts +0 -19
  48. package/cjs/middleware/express.js +0 -46
  49. package/cjs/package.json +0 -3
  50. package/cjs/parsers/axios.js +0 -56
  51. package/cjs/parsers/mikroorm.js +0 -20
  52. package/cjs/parsers/tsoa.js +0 -25
  53. package/cjs/parsers/zod.js +0 -21
  54. package/cjs/problem.d.ts +0 -14
  55. package/cjs/problem.js +0 -76
  56. package/cjs/tsconfig.tsbuildinfo +0 -1
  57. package/cjs/typings/codes.js +0 -2
  58. package/cjs/typings/index.js +0 -20
  59. package/cjs/typings/middleware.js +0 -2
  60. package/cjs/typings/parser.js +0 -2
  61. package/cjs/typings/problem.js +0 -12
  62. package/cjs/util/getProblems.js +0 -62
  63. package/cjs/util/index.d.ts +0 -4
  64. package/cjs/util/index.js +0 -20
  65. package/cjs/util/misc.js +0 -18
  66. package/cjs/util/validation.js +0 -38
  67. package/cjs/util/version.d.ts +0 -2
  68. package/cjs/util/version.js +0 -5
  69. package/eslint.config.mjs +0 -5
  70. package/esm/defaults/4xx.d.ts +0 -147
  71. package/esm/defaults/5xx.d.ts +0 -57
  72. package/esm/defaults/aws.d.ts +0 -12
  73. package/esm/defaults/cloudflare.d.ts +0 -47
  74. package/esm/defaults/iis.d.ts +0 -17
  75. package/esm/defaults/index.d.ts +0 -7
  76. package/esm/defaults/nginx.d.ts +0 -32
  77. package/esm/defaults/others.d.ts +0 -37
  78. package/esm/index.d.ts +0 -4
  79. package/esm/middleware/axios.d.ts +0 -13
  80. package/esm/middleware/base.d.ts +0 -30
  81. package/esm/package.json +0 -3
  82. package/esm/parsers/axios.d.ts +0 -3
  83. package/esm/parsers/jsonwebtoken.d.ts +0 -3
  84. package/esm/parsers/mikroorm.d.ts +0 -3
  85. package/esm/parsers/tsoa.d.ts +0 -3
  86. package/esm/parsers/zod.d.ts +0 -3
  87. package/esm/tsconfig.tsbuildinfo +0 -1
  88. package/esm/typings/codes.d.ts +0 -5
  89. package/esm/typings/index.d.ts +0 -4
  90. package/esm/typings/parser.d.ts +0 -2
  91. package/esm/util/getProblems.d.ts +0 -11
  92. package/esm/util/misc.d.ts +0 -2
  93. package/esm/util/problemArray.d.ts +0 -7
  94. package/esm/util/validation.d.ts +0 -5
  95. package/esm/util/version.d.ts +0 -2
  96. package/tsconfig.json +0 -19
  97. /package/{cjs → dist}/defaults/4xx.d.ts +0 -0
  98. /package/{esm → dist}/defaults/4xx.js +0 -0
  99. /package/{cjs → dist}/defaults/5xx.d.ts +0 -0
  100. /package/{esm → dist}/defaults/5xx.js +0 -0
  101. /package/{cjs → dist}/defaults/aws.d.ts +0 -0
  102. /package/{esm → dist}/defaults/aws.js +0 -0
  103. /package/{cjs → dist}/defaults/cloudflare.d.ts +0 -0
  104. /package/{esm → dist}/defaults/cloudflare.js +0 -0
  105. /package/{cjs → dist}/defaults/iis.d.ts +0 -0
  106. /package/{esm → dist}/defaults/iis.js +0 -0
  107. /package/{cjs → dist}/defaults/index.d.ts +0 -0
  108. /package/{esm → dist}/defaults/index.js +0 -0
  109. /package/{cjs → dist}/defaults/nginx.d.ts +0 -0
  110. /package/{esm → dist}/defaults/nginx.js +0 -0
  111. /package/{cjs → dist}/defaults/others.d.ts +0 -0
  112. /package/{esm → dist}/defaults/others.js +0 -0
  113. /package/{cjs → dist}/index.d.ts +0 -0
  114. /package/{esm → dist}/index.js +0 -0
  115. /package/{esm → dist}/middleware/axios.js +0 -0
  116. /package/{esm → dist}/middleware/base.js +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
@@ -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
@@ -0,0 +1,27 @@
1
+ import { ValidateError } from 'tsoa'
2
+ import { Problem } from '../problem.js'
3
+ import { otherErrors } from '../defaults/others.js'
4
+ import type { Parser } from '../typings/parser.js'
5
+
6
+ const parse: Parser = (input) => {
7
+ if (!(input instanceof ValidateError)) {
8
+ return
9
+ }
10
+
11
+ // TODO: Give actual useful responses instead of this 💩
12
+ return [
13
+ new Problem({
14
+ ...otherErrors.inputValidationError,
15
+ 'detail': input.message,
16
+ 'status': input.status,
17
+ 'data': {
18
+ 'fields': input.fields,
19
+ },
20
+ 'stack': input.stack,
21
+ 'errorObject': input,
22
+ 'source': 'parser:tsoa'
23
+ })
24
+ ]
25
+ }
26
+
27
+ export default parse
@@ -0,0 +1,23 @@
1
+ import { ZodError } from 'zod'
2
+ import type { Parser } from '../typings/parser.js'
3
+ import { Problem } from '../problem.js'
4
+ import { otherErrors } from '../defaults/others.js'
5
+
6
+ const parse: Parser = (input) => {
7
+ if (!(input instanceof ZodError)) {
8
+ return
9
+ }
10
+
11
+ return [new Problem({
12
+ ...otherErrors.inputValidationError,
13
+ 'detail': `Got ${input.issues.length} validation errors`,
14
+ 'stack': input.stack,
15
+ 'errorObject': input,
16
+ 'data': {
17
+ 'issues': input.issues
18
+ },
19
+ 'source': 'parser:zod'
20
+ })]
21
+ }
22
+
23
+ export default parse
package/src/problem.ts ADDED
@@ -0,0 +1,70 @@
1
+ import type { IProblem, ProblemOpts, JsonProblemOutput } from './typings/problem.js'
2
+ import { isJsonProblem } from './util/getProblems.js'
3
+ import { validateBasicInput } from './util/validation.js'
4
+ import { version } from './util/version.js'
5
+
6
+ export class Problem extends Error implements IProblem {
7
+ public type
8
+ public title
9
+ public status
10
+ public detail
11
+ public instance
12
+ public errorObject?: unknown
13
+ public readonly __problemVersion = version
14
+ public data: unknown = undefined
15
+ public source?: string
16
+
17
+ constructor(opts: ProblemOpts) {
18
+ const valid = validateBasicInput(opts)
19
+ if (valid !== true) throw valid
20
+
21
+ super(opts.detail ?? opts.title ?? 'HTTP Problem Details')
22
+ // TODO: Figure out if this is still necessary
23
+ Object.setPrototypeOf(this, new.target.prototype)
24
+
25
+ this.type = opts.type
26
+ this.title = opts.title
27
+ this.status = opts.status
28
+
29
+ if (opts.detail) this.detail = opts.detail
30
+ if (opts.instance) this.instance = opts.instance
31
+ if (opts.errorObject) this.errorObject = opts.errorObject
32
+ if (opts.source) this.source = opts.source
33
+
34
+ if (opts.stack) {
35
+ this.stack = opts.stack
36
+ } else if (opts.errorObject && typeof opts.errorObject === 'object' && 'stack' in opts.errorObject && typeof opts.errorObject.stack === 'string') {
37
+ this.stack = opts.errorObject.stack
38
+ } else {
39
+ if (typeof Error.captureStackTrace === 'function') {
40
+ Error.captureStackTrace(this, this.constructor)
41
+ } else {
42
+ this.stack = new Error(opts.detail ?? opts.title ?? 'HTTP Problem Details').stack
43
+ }
44
+ }
45
+
46
+ if (opts.data) this.data = opts.data
47
+
48
+ if (opts.data && opts.data instanceof Problem) {
49
+ console.warn(new Error('@emoyly/problem -> Problem: Problem instance passed in "data" to new Problem. This probably means something went wrong.'))
50
+ } else if (opts.data && isJsonProblem(opts.data)){
51
+ console.warn(new Error('@emoyly/problem -> Problem: Something which looks like a "JSON Problem" passed in "data" to new Problem. This probably means something went wrong.'))
52
+ }
53
+ }
54
+
55
+ public toObject = (): JsonProblemOutput => {
56
+ const out: JsonProblemOutput = {
57
+ 'type': this.type,
58
+ 'title': this.title,
59
+ 'status': this.status,
60
+ '__problemVersion': this.__problemVersion,
61
+ }
62
+
63
+ if (this.detail) out.detail = this.detail
64
+ if (this.instance) out.instance = this.instance
65
+ if (this.source) out.source = this.source
66
+ if (this.data) out.data = this.data
67
+
68
+ return out
69
+ }
70
+ }
@@ -0,0 +1,7 @@
1
+ import type { ProblemOpts } from './problem.js'
2
+
3
+ export type CodeOpts = Omit<ProblemOpts, 'instance' | 'stack' | '__problemVersion' | 'source' | 'data'>
4
+
5
+ export interface Codes {
6
+ [key: number | string]: CodeOpts | undefined
7
+ }
@@ -0,0 +1,4 @@
1
+ export * from './middleware.js'
2
+ export * from './parser.js'
3
+ export * from './problem.js'
4
+ export * from './codes.js'
@@ -1,11 +1,14 @@
1
- import type { Problem } from '../problem.js';
2
- import type { Parser } from './parser.js';
1
+ import type { Problem } from '../problem.js'
2
+ import type { Parser } from './parser.js'
3
+
3
4
  export interface MiddlewareOptions {
4
5
  /**
5
6
  * A list of parsers to be used by the middleware.
6
7
  * Parsers are run in the order they sit in the array
7
8
  */
8
- parsers: Parser[];
9
+ parsers: Parser[]
9
10
  }
10
- export type PartialMiddlewareOptions = Partial<MiddlewareOptions>;
11
- export type ProblemListener = (problems: Problem[]) => void;
11
+
12
+ export type PartialMiddlewareOptions = Partial<MiddlewareOptions>
13
+
14
+ export type ProblemListener = (problems: Problem[]) => void
@@ -0,0 +1,3 @@
1
+ import type { Problem } from '../problem.js'
2
+
3
+ export type Parser = (input: unknown) => Problem[] | undefined
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @param {string} type - A URI refrence that identifies the problem type.
2
+ * @param {string} type - A URI refrence that identifies the problem type.
3
3
  * @param {string} title - A short, human-readable summary of the problem
4
4
  * type. It SHOULD NOT change from occurrence to occurrence of the problem
5
5
  * @param {number} status - The HTTP status code
@@ -8,28 +8,29 @@
8
8
  * @property {string} instance - A URI reference that identifies the specific
9
9
  occurrence of the problem
10
10
  **/
11
+
11
12
  export interface IProblem {
12
- type: string;
13
- title: string;
14
- status: number;
15
- detail?: string;
16
- instance?: string;
17
- stack?: string;
18
- errorObject?: unknown;
19
- __problemVersion: string;
20
- data?: unknown;
21
- source?: string;
13
+ type: string
14
+ title: string
15
+ status: number
16
+ detail?: string
17
+ instance?: string
18
+ stack?: string
19
+ errorObject?: unknown
20
+ __problemVersion: string
21
+ data?: unknown
22
+ source?: string
22
23
  }
24
+
23
25
  /**
24
26
  * Returned by `toObject()` function on `Problem` class.
25
27
  */
26
- export type JsonProblemOutput = Omit<IProblem, 'stack' | 'errorObject'>;
28
+ export type JsonProblemOutput = Omit<IProblem, 'stack' | 'errorObject'>
29
+
27
30
  /**
28
31
  * Input for creating a Problem instance from JSON. Please use `createFromJson` function for this.
29
32
  * Extra data is allowed, and will be stored in the `data` property.
30
33
  */
31
- export type JsonProblemInput = Omit<IProblem, '__problemVersion'> & {
32
- __problemVersion?: string;
33
- [key: string]: unknown;
34
- };
35
- export type ProblemOpts = Omit<IProblem, '__problemVersion'>;
34
+ export type JsonProblemInput = Omit<IProblem, '__problemVersion'> & { __problemVersion?: string, [key: string]: unknown }
35
+
36
+ export type ProblemOpts = Omit<IProblem, '__problemVersion'>
@@ -0,0 +1,62 @@
1
+ import { Problem } from '../problem.js'
2
+ import { Parser } from '../typings/parser.js'
3
+ import { JsonProblemInput } from '../typings/problem.js'
4
+ import { isProblemArray } from './problemArray.js'
5
+ import { validateBasicInput } from './validation.js'
6
+
7
+ export function isJsonProblem(input: unknown): input is JsonProblemInput {
8
+ return validateBasicInput(input) === true
9
+ }
10
+
11
+ export function createFromJson({ title, type, instance, detail, status, data: _data, __problemVersion, source, errorObject, stack, ...extras }: JsonProblemInput): Problem {
12
+ let data: unknown
13
+
14
+ const extraKeys = Object.keys(extras)
15
+
16
+ if /* No extras, just use data */ (extraKeys.length < 1) {
17
+ data = _data
18
+ } else if /* Extras but no data, just use extras */ ((_data === undefined || _data === null)) {
19
+ data = extras
20
+ } else if /* Data is not object we can merge with, and we do have extras */ (typeof _data !== 'object' || Array.isArray(_data) || Object.keys(_data).some(val => extraKeys.includes(val))) {
21
+ data = { original: _data, extras }
22
+ } else {
23
+ data = { ...extras, ..._data }
24
+ }
25
+
26
+ // TODO: Validate that source, errorObject and stack should actually be passed through
27
+ return new Problem({ title, type, instance, detail, status, data, source, errorObject, stack })
28
+ }
29
+
30
+ /**
31
+ * Get an array of Problems from any given input.
32
+ * Handles existing Problems, arrays of Problems, JSON Problems, JSON Problem arrays and optionally attempts to parse any input with the given parsers.
33
+ * Only returns an array of Problems if it can successfully parse the input.
34
+ */
35
+ export function getProblems(input: unknown, parsers?: Parser[]): Problem[] | undefined {
36
+ if (typeof input === 'string') {
37
+ try {
38
+ const p = JSON.parse(input)
39
+ input = p
40
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
41
+ } catch (err) { /**/ }
42
+ }
43
+
44
+ if (input instanceof Problem) return [input]
45
+ if (isProblemArray(input, false)) return input
46
+
47
+ if (Array.isArray(input) && input.length > 0 && input.every(val => isJsonProblem(val))) {
48
+ return input.map(val => createFromJson(val))
49
+ }
50
+
51
+ if (isJsonProblem(input)) {
52
+ return [createFromJson(input)]
53
+ }
54
+
55
+ if (parsers) {
56
+ for (const parser of parsers) {
57
+ const problems = parser(input)
58
+ if (!problems || !problems.length) continue
59
+ return problems
60
+ }
61
+ }
62
+ }
@@ -0,0 +1,5 @@
1
+ export * from './getProblems.js'
2
+ export * from './problemArray.js'
3
+ export * from './misc.js'
4
+ export * from './validation.js'
5
+ export { major as majorVersion, version } from './version.js'
@@ -0,0 +1,20 @@
1
+ import * as allCodes from '../defaults/index.js'
2
+ import { Codes, CodeOpts } from '../typings/index.js'
3
+
4
+ export function getCodeDefaults(statusCode: number | string): CodeOpts | undefined {
5
+ for (const key in allCodes) {
6
+ const codeObject: Codes = allCodes[key as keyof typeof allCodes]
7
+
8
+ if (typeof statusCode === 'string') {
9
+ const conv = Number(statusCode)
10
+ if (!isNaN(conv)) statusCode = conv
11
+ }
12
+
13
+ const code = codeObject[statusCode]
14
+
15
+ if (!code) continue
16
+
17
+ return code
18
+ }
19
+ }
20
+
@@ -1,21 +1,21 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isProblemArray = isProblemArray;
4
- const problem_js_1 = require("../problem.js");
1
+ import { Problem } from '../problem.js'
2
+
5
3
  /**
6
4
  * Checks whether a given object is an array of Problem objects
7
5
  * @param input Object to test
8
6
  * @param allowEmpty If true or undefined, an empty array is considered a Problem array. If false, an empty array is not considered a Problem array.
9
7
  */
10
- function isProblemArray(input, allowEmpty) {
11
- if (!Array.isArray(input))
12
- return false;
8
+ export function isProblemArray(input: unknown, allowEmpty?: boolean): input is Problem[] {
9
+ if (!Array.isArray(input)) return false
10
+
13
11
  if (input.length < 1) {
14
12
  if (typeof allowEmpty !== 'boolean') {
15
- console.warn(new Error('@emoyly/problem -> isProblemArray: input is an empty array, and therefore is considered a Problem array. If this is not the intended behavior, consider setting the "onlyWhenContent" argument to true.'));
16
- return true;
13
+ console.warn(new Error('@emoyly/problem -> isProblemArray: input is an empty array, and therefore is considered a Problem array. If this is not the intended behavior, consider setting the "onlyWhenContent" argument to true.'))
14
+ return true
17
15
  }
18
- return allowEmpty;
16
+
17
+ return allowEmpty
19
18
  }
20
- return input.every(val => val instanceof problem_js_1.Problem);
19
+
20
+ return input.every(val => val instanceof Problem)
21
21
  }
@@ -0,0 +1,39 @@
1
+ export class ProblemValidationError extends Error {
2
+ errors: string[]
3
+
4
+ constructor(errors: string[]) {
5
+ super(`Problem input is invalid:\n${errors.join('\n')}`)
6
+ this.errors = errors
7
+ }
8
+ }
9
+
10
+ const requiredStrings = ['title', 'type'] as const
11
+ const optionalStrings = ['detail', '__problemVersion', 'instance', 'stack', 'source'] as const
12
+
13
+ export function validateBasicInput(input: unknown): true | ProblemValidationError {
14
+ const errors: string[] = []
15
+
16
+ if (input === null) {
17
+ errors.push('Input is null')
18
+ return new ProblemValidationError(errors)
19
+ }
20
+ if (typeof input !== 'object') {
21
+ errors.push('Input is not an object')
22
+ return new ProblemValidationError(errors)
23
+ }
24
+
25
+ const missingRequired = requiredStrings.filter(val => typeof input[val as keyof typeof input] !== 'string')
26
+ for (const miss of missingRequired) {
27
+ errors.push(`"${miss}" is missing or not a string`)
28
+ }
29
+
30
+ const wrongOptional = optionalStrings.filter((val) => val in input && input[val as keyof typeof input] !== undefined && input[val as keyof typeof input] !== null && typeof input[val as keyof typeof input] !== 'string')
31
+ for (const wrong of wrongOptional) {
32
+ errors.push(`"${wrong}" is set, and is not a string`)
33
+ }
34
+
35
+ if (!('status' in input) || typeof input.status !== 'number') errors.push('Status is not a number')
36
+
37
+ if (errors.length > 0) return new ProblemValidationError(errors)
38
+ return true
39
+ }
@@ -0,0 +1,2 @@
1
+ export const version = '9.0.0'
2
+ export const major = Number(version.split('.')[0])
File without changes
File without changes
File without changes
@@ -1,17 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.otherErrors = exports.codesNginx = exports.codesIis = exports.codesCloudflare = exports.codesAws = exports.codes5xx = exports.codes4xx = void 0;
4
- var _4xx_js_1 = require("../defaults/4xx.js");
5
- Object.defineProperty(exports, "codes4xx", { enumerable: true, get: function () { return _4xx_js_1.statusCodes; } });
6
- var _5xx_js_1 = require("../defaults/5xx.js");
7
- Object.defineProperty(exports, "codes5xx", { enumerable: true, get: function () { return _5xx_js_1.statusCodes; } });
8
- var aws_js_1 = require("../defaults/aws.js");
9
- Object.defineProperty(exports, "codesAws", { enumerable: true, get: function () { return aws_js_1.statusCodes; } });
10
- var cloudflare_js_1 = require("../defaults/cloudflare.js");
11
- Object.defineProperty(exports, "codesCloudflare", { enumerable: true, get: function () { return cloudflare_js_1.statusCodes; } });
12
- var iis_js_1 = require("../defaults/iis.js");
13
- Object.defineProperty(exports, "codesIis", { enumerable: true, get: function () { return iis_js_1.statusCodes; } });
14
- var nginx_js_1 = require("../defaults/nginx.js");
15
- Object.defineProperty(exports, "codesNginx", { enumerable: true, get: function () { return nginx_js_1.statusCodes; } });
16
- var others_js_1 = require("../defaults/others.js");
17
- Object.defineProperty(exports, "otherErrors", { enumerable: true, get: function () { return others_js_1.otherErrors; } });
package/cjs/index.js DELETED
@@ -1,22 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
- };
16
- Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.defaults = exports.Problem = void 0;
18
- var problem_js_1 = require("./problem.js");
19
- Object.defineProperty(exports, "Problem", { enumerable: true, get: function () { return problem_js_1.Problem; } });
20
- exports.defaults = require("./defaults/index.js");
21
- __exportStar(require("./typings/index.js"), exports);
22
- __exportStar(require("./util/index.js"), exports);
@@ -1,23 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AxiosMiddleware = void 0;
4
- const base_js_1 = require("./base.js");
5
- const axios_js_1 = require("../parsers/axios.js");
6
- class AxiosMiddleware extends base_js_1.MiddlewareBase {
7
- name = 'axios';
8
- constructor(options) {
9
- super({
10
- 'parsers': [axios_js_1.default]
11
- }, options);
12
- }
13
- interceptor = async (error) => {
14
- const problems = this.parse(error);
15
- this.emit(problems);
16
- return Promise.reject(problems);
17
- };
18
- /**
19
- * @example instance.interceptors.response.use(...middleware.use())
20
- */
21
- use = () => [undefined, this.interceptor];
22
- }
23
- exports.AxiosMiddleware = AxiosMiddleware;
@@ -1,62 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MiddlewareBase = void 0;
4
- const index_js_1 = require("../defaults/index.js");
5
- const problem_js_1 = require("../problem.js");
6
- const getProblems_js_1 = require("../util/getProblems.js");
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
- class MiddlewareBase {
11
- options;
12
- /**
13
- * When nothing is returned from the parsers, just throw in a default "fallback" Problem object, so that a Problem is still actually returned/emitted
14
- */
15
- enableFallback = true;
16
- constructor(defaultOptions, options) {
17
- this.options = { ...defaultOptions, ...options };
18
- }
19
- /**
20
- * Parse input using parsers
21
- */
22
- parse = (input) => {
23
- const problems = (0, getProblems_js_1.getProblems)(input, this.options.parsers);
24
- if (!problems || !Array.isArray(problems) || problems.length < 1) {
25
- if (this.enableFallback)
26
- return [new problem_js_1.Problem({
27
- ...this.fallback,
28
- 'errorObject': input,
29
- 'source': `middleware:${this.name}`
30
- })];
31
- return [];
32
- }
33
- for (const p of problems) {
34
- if (!p.source)
35
- p.source = `middleware:${this.name}`;
36
- }
37
- return problems;
38
- };
39
- fallback = index_js_1.otherErrors.unknown;
40
- problemListeners = [];
41
- emit = (list) => {
42
- for (const listener of this.problemListeners) {
43
- listener(list);
44
- }
45
- };
46
- /**
47
- * Listen to problems
48
- */
49
- onProblem = (listener) => {
50
- this.problemListeners.push(listener);
51
- };
52
- /**
53
- * Un-listen to problems
54
- */
55
- offProblem = (listener) => {
56
- const idx = this.problemListeners.findIndex(val => val === listener);
57
- if (idx < 0 || isNaN(idx))
58
- return;
59
- this.problemListeners.splice(idx, 1);
60
- };
61
- }
62
- exports.MiddlewareBase = MiddlewareBase;
@@ -1,19 +0,0 @@
1
- import type { NextFunction, Request, Response } from 'express';
2
- import type { PartialMiddlewareOptions } from '../typings/middleware.js';
3
- import { MiddlewareBase } from './base.js';
4
- export declare class ExpressMiddleware extends MiddlewareBase {
5
- name: string;
6
- middleware: (error: unknown, req: Request, res: Response, next: NextFunction) => void;
7
- /**
8
- * This function applies the middleware to your Express application. Put this at the very end of your middleware/routes.
9
- * If you want to also handle 404 errors, check out the notFound middleware function.
10
- * @example app.use(middleware.use())
11
- */
12
- use: () => (error: unknown, req: Request, res: Response, next: NextFunction) => void;
13
- constructor(options?: PartialMiddlewareOptions);
14
- /**
15
- * Put this right before the middleware.use() to catch all 404 errors and respond with a Problem instead of the default Express response
16
- * @example app.use(middleware.notFound)
17
- */
18
- notFound: (req: Request, res: Response, next: NextFunction) => void;
19
- }