@emoyly/problem 7.0.6 → 8.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 (102) hide show
  1. package/README.md +1 -1
  2. package/cjs/defaults/4xx.d.ts +144 -144
  3. package/cjs/defaults/5xx.d.ts +54 -54
  4. package/cjs/defaults/aws.d.ts +8 -8
  5. package/cjs/defaults/cloudflare.d.ts +44 -44
  6. package/cjs/defaults/iis.d.ts +12 -12
  7. package/cjs/defaults/nginx.d.ts +24 -24
  8. package/cjs/defaults/others.d.ts +28 -28
  9. package/cjs/middleware/axios.js +1 -1
  10. package/cjs/middleware/base.d.ts +1 -1
  11. package/cjs/middleware/base.js +4 -4
  12. package/cjs/middleware/express.js +10 -28
  13. package/cjs/parsers/axios.js +9 -8
  14. package/cjs/parsers/jsonwebtoken.js +3 -0
  15. package/cjs/parsers/mikroorm.js +1 -0
  16. package/cjs/parsers/tsoa.js +3 -2
  17. package/cjs/parsers/zod.js +2 -1
  18. package/cjs/problem.d.ts +7 -7
  19. package/cjs/problem.js +41 -32
  20. package/cjs/tsconfig.tsbuildinfo +1 -1
  21. package/cjs/typings/codes.d.ts +2 -2
  22. package/cjs/typings/problem.d.ts +13 -2
  23. package/cjs/util/getProblems.d.ts +4 -4
  24. package/cjs/util/getProblems.js +18 -15
  25. package/cjs/util/index.d.ts +1 -1
  26. package/cjs/util/index.js +1 -1
  27. package/cjs/util/misc.d.ts +2 -2
  28. package/cjs/util/misc.js +3 -4
  29. package/cjs/util/validation.d.ts +5 -0
  30. package/cjs/util/validation.js +38 -0
  31. package/cjs/util/version.d.ts +1 -2
  32. package/cjs/util/version.js +1 -16
  33. package/esm/defaults/4xx.d.ts +144 -144
  34. package/esm/defaults/5xx.d.ts +54 -54
  35. package/esm/defaults/aws.d.ts +8 -8
  36. package/esm/defaults/cloudflare.d.ts +44 -44
  37. package/esm/defaults/iis.d.ts +12 -12
  38. package/esm/defaults/nginx.d.ts +24 -24
  39. package/esm/defaults/others.d.ts +28 -28
  40. package/esm/middleware/axios.js +1 -1
  41. package/esm/middleware/base.d.ts +1 -1
  42. package/esm/middleware/base.js +4 -4
  43. package/esm/middleware/express.js +11 -29
  44. package/esm/parsers/axios.js +9 -8
  45. package/esm/parsers/jsonwebtoken.js +3 -0
  46. package/esm/parsers/mikroorm.js +1 -0
  47. package/esm/parsers/tsoa.js +3 -2
  48. package/esm/parsers/zod.js +2 -1
  49. package/esm/problem.d.ts +7 -7
  50. package/esm/problem.js +41 -32
  51. package/esm/tsconfig.tsbuildinfo +1 -1
  52. package/esm/typings/codes.d.ts +2 -2
  53. package/esm/typings/problem.d.ts +13 -2
  54. package/esm/util/getProblems.d.ts +4 -4
  55. package/esm/util/getProblems.js +18 -15
  56. package/esm/util/index.d.ts +1 -1
  57. package/esm/util/index.js +1 -1
  58. package/esm/util/misc.d.ts +2 -2
  59. package/esm/util/misc.js +2 -3
  60. package/esm/util/validation.d.ts +5 -0
  61. package/esm/util/validation.js +33 -0
  62. package/esm/util/version.d.ts +1 -2
  63. package/esm/util/version.js +1 -15
  64. package/package.json +18 -13
  65. package/tsconfig.json +5 -1
  66. package/.editorconfig +0 -11
  67. package/.vscode/extensions.json +0 -7
  68. package/.vscode/settings.json +0 -2
  69. package/cjs/util/defaults.d.ts +0 -4
  70. package/cjs/util/defaults.js +0 -7
  71. package/esm/util/defaults.d.ts +0 -4
  72. package/esm/util/defaults.js +0 -4
  73. package/scripts/ensureCorrectVersion.js +0 -20
  74. package/src/defaults/4xx.ts +0 -149
  75. package/src/defaults/5xx.ts +0 -59
  76. package/src/defaults/aws.ts +0 -14
  77. package/src/defaults/cloudflare.ts +0 -50
  78. package/src/defaults/iis.ts +0 -19
  79. package/src/defaults/index.ts +0 -7
  80. package/src/defaults/nginx.ts +0 -34
  81. package/src/defaults/others.ts +0 -37
  82. package/src/index.ts +0 -4
  83. package/src/middleware/axios.ts +0 -28
  84. package/src/middleware/base.ts +0 -78
  85. package/src/middleware/express.ts +0 -71
  86. package/src/parsers/axios.ts +0 -60
  87. package/src/parsers/jsonwebtoken.ts +0 -107
  88. package/src/parsers/mikroorm.ts +0 -21
  89. package/src/parsers/tsoa.ts +0 -26
  90. package/src/parsers/zod.ts +0 -23
  91. package/src/problem.ts +0 -56
  92. package/src/typings/codes.ts +0 -6
  93. package/src/typings/index.ts +0 -4
  94. package/src/typings/middleware.ts +0 -14
  95. package/src/typings/parser.ts +0 -3
  96. package/src/typings/problem.ts +0 -27
  97. package/src/util/defaults.ts +0 -4
  98. package/src/util/getProblems.ts +0 -56
  99. package/src/util/index.ts +0 -4
  100. package/src/util/misc.ts +0 -21
  101. package/src/util/problemArray.ts +0 -21
  102. package/src/util/version.ts +0 -16
@@ -1,47 +1,47 @@
1
1
  export declare const statusCodes: {
2
- 520: {
3
- status: number;
4
- title: string;
5
- type: string;
6
- };
7
- 521: {
8
- status: number;
9
- title: string;
10
- type: string;
11
- };
12
- 522: {
13
- status: number;
14
- title: string;
15
- type: string;
16
- };
17
- 523: {
18
- status: number;
19
- title: string;
20
- type: string;
21
- };
22
- 524: {
23
- status: number;
24
- title: string;
25
- type: string;
26
- };
27
- 525: {
28
- status: number;
29
- title: string;
30
- type: string;
31
- };
32
- 526: {
33
- status: number;
34
- title: string;
35
- type: string;
36
- };
37
- 527: {
38
- status: number;
39
- title: string;
40
- type: string;
41
- };
42
- 530: {
43
- status: number;
44
- title: string;
45
- type: string;
2
+ readonly 520: {
3
+ readonly status: 520;
4
+ readonly title: "Web Server returned an unknown error";
5
+ readonly type: "/errors/defaults/cloudflare/unknownerror";
6
+ };
7
+ readonly 521: {
8
+ readonly status: 521;
9
+ readonly title: "Web server is down";
10
+ readonly type: "/errors/defaults/cloudflare/webserverdown";
11
+ };
12
+ readonly 522: {
13
+ readonly status: 522;
14
+ readonly title: "Connection timed out";
15
+ readonly type: "/errors/defaults/cloudflare/connectiontimeout";
16
+ };
17
+ readonly 523: {
18
+ readonly status: 523;
19
+ readonly title: "Origin is unreachable";
20
+ readonly type: "/errors/defaults/cloudflare/originunreachable";
21
+ };
22
+ readonly 524: {
23
+ readonly status: 524;
24
+ readonly title: "A timeout occurred";
25
+ readonly type: "/errors/defaults/cloudflare/timeout";
26
+ };
27
+ readonly 525: {
28
+ readonly status: 525;
29
+ readonly title: "SSL handshake failed";
30
+ readonly type: "/errors/defaults/cloudflare/sslhandshakefail";
31
+ };
32
+ readonly 526: {
33
+ readonly status: 526;
34
+ readonly title: "Invalid SSL certificate";
35
+ readonly type: "/errors/defaults/cloudflare/invalidssl";
36
+ };
37
+ readonly 527: {
38
+ readonly status: 527;
39
+ readonly title: "Railgun Listener to origin error";
40
+ readonly type: "/errors/defaults/cloudflare/railgunoriginerr";
41
+ };
42
+ readonly 530: {
43
+ readonly status: 530;
44
+ readonly title: "Cloudflare returned 1xxx error";
45
+ readonly type: "/errors/defaults/cloudflare/1xxx";
46
46
  };
47
47
  };
@@ -1,17 +1,17 @@
1
1
  export declare const statusCodes: {
2
- 440: {
3
- status: number;
4
- title: string;
5
- type: string;
2
+ readonly 440: {
3
+ readonly status: 440;
4
+ readonly title: "Login Time-out";
5
+ readonly type: "/errors/defaults/iis/logintimeout";
6
6
  };
7
- 449: {
8
- status: number;
9
- title: string;
10
- type: string;
7
+ readonly 449: {
8
+ readonly status: 449;
9
+ readonly title: "Retry with required information";
10
+ readonly type: "/errors/defaults/iis/retrywith";
11
11
  };
12
- 451: {
13
- status: number;
14
- title: string;
15
- type: string;
12
+ readonly 451: {
13
+ readonly status: 451;
14
+ readonly title: "Redirect";
15
+ readonly type: "/errors/defaults/iis/redirect";
16
16
  };
17
17
  };
@@ -1,32 +1,32 @@
1
1
  export declare const statusCodes: {
2
- 444: {
3
- status: number;
4
- title: string;
5
- type: string;
2
+ readonly 444: {
3
+ readonly status: 494;
4
+ readonly title: "No Response";
5
+ readonly type: "/errors/defaults/nginx/noresponse";
6
6
  };
7
- 494: {
8
- status: number;
9
- title: string;
10
- type: string;
7
+ readonly 494: {
8
+ readonly status: 494;
9
+ readonly title: "Request header too large";
10
+ readonly type: "/errors/defaults/nginx/headertoolarge";
11
11
  };
12
- 495: {
13
- status: number;
14
- title: string;
15
- type: string;
12
+ readonly 495: {
13
+ readonly status: 495;
14
+ readonly title: "SSL Certificate Error";
15
+ readonly type: "/errors/defaults/nginx/sslcerterr";
16
16
  };
17
- 496: {
18
- status: number;
19
- title: string;
20
- type: string;
17
+ readonly 496: {
18
+ readonly status: 496;
19
+ readonly title: "SSL Certificate Required";
20
+ readonly type: "/errors/defaults/nginx/sslcertrequired";
21
21
  };
22
- 497: {
23
- status: number;
24
- title: string;
25
- type: string;
22
+ readonly 497: {
23
+ readonly status: 497;
24
+ readonly title: "HTTP Request Sent to HTTPS Port";
25
+ readonly type: "/errors/defaults/nginx/httpreqtohttpsport";
26
26
  };
27
- 499: {
28
- status: number;
29
- title: string;
30
- type: string;
27
+ readonly 499: {
28
+ readonly status: 499;
29
+ readonly title: "Client Closed Request";
30
+ readonly type: "/errors/defaults/nginx/clientclosedreq";
31
31
  };
32
32
  };
@@ -1,37 +1,37 @@
1
1
  export declare const otherErrors: {
2
- unknown: {
3
- status: number;
4
- title: string;
5
- type: string;
2
+ readonly unknown: {
3
+ readonly status: 500;
4
+ readonly title: "Unknown error";
5
+ readonly type: "/errors/defaults/others/unknown";
6
6
  };
7
- corsError: {
8
- status: number;
9
- title: string;
10
- type: string;
7
+ readonly corsError: {
8
+ readonly status: 401;
9
+ readonly title: "Request not allowed by CORS policy";
10
+ readonly type: "/errors/defaults/others/corsdenied";
11
11
  };
12
- problemParseError: {
13
- status: number;
14
- title: string;
15
- type: string;
12
+ readonly problemParseError: {
13
+ readonly status: 1000;
14
+ readonly title: "Could not parse problem details";
15
+ readonly type: "/errors/defaults/others/problemparseerror";
16
16
  };
17
- problemJsonError: {
18
- status: number;
19
- title: string;
20
- type: string;
17
+ readonly problemJsonError: {
18
+ readonly status: 1000;
19
+ readonly title: "Could not parse problem JSON";
20
+ readonly type: "/errors/defaults/others/problemjsonerror";
21
21
  };
22
- notAnError: {
23
- status: number;
24
- title: string;
25
- type: string;
22
+ readonly notAnError: {
23
+ readonly status: 1000;
24
+ readonly title: "Not an error";
25
+ readonly type: "/errors/defaults/others/notanerror";
26
26
  };
27
- networkError: {
28
- status: number;
29
- title: string;
30
- type: string;
27
+ readonly networkError: {
28
+ readonly status: 1000;
29
+ readonly title: "Network error";
30
+ readonly type: "/errors/defaults/others/networkerror";
31
31
  };
32
- inputValidationError: {
33
- status: number;
34
- title: string;
35
- type: string;
32
+ readonly inputValidationError: {
33
+ readonly status: 400;
34
+ readonly title: "Input failed validation";
35
+ readonly type: "/errors/defaults/others/validationerror";
36
36
  };
37
37
  };
@@ -8,7 +8,7 @@ export class AxiosMiddleware extends MiddlewareBase {
8
8
  }, options);
9
9
  }
10
10
  interceptor = async (error) => {
11
- const problems = await this.parse(error);
11
+ const problems = this.parse(error);
12
12
  this.emit(problems);
13
13
  return Promise.reject(problems);
14
14
  };
@@ -19,7 +19,7 @@ export declare abstract class MiddlewareBase {
19
19
  /**
20
20
  * Parse input using parsers
21
21
  */
22
- parse: (input: unknown) => Promise<Problem[]>;
22
+ parse: (input: unknown) => Problem[];
23
23
  fallback: ProblemOpts;
24
24
  problemListeners: ProblemListener[];
25
25
  protected emit: (list: Problem[]) => void;
@@ -16,7 +16,7 @@ export class MiddlewareBase {
16
16
  /**
17
17
  * Parse input using parsers
18
18
  */
19
- parse = async (input) => {
19
+ parse = (input) => {
20
20
  const problems = getProblems(input, this.options.parsers);
21
21
  if (!problems || !Array.isArray(problems) || problems.length < 1) {
22
22
  if (this.enableFallback)
@@ -24,13 +24,13 @@ export class MiddlewareBase {
24
24
  ...this.fallback,
25
25
  'errorObject': input,
26
26
  'stack': (typeof input === 'object' && input !== null && 'stack' in input && typeof input.stack === 'string') ? input.stack : undefined,
27
- 'middleware': this.name
27
+ 'source': `middleware:${this.name}`
28
28
  })];
29
29
  return [];
30
30
  }
31
31
  for (const p of problems) {
32
- if (!p.middleware)
33
- p.middleware = this.name;
32
+ if (!p.source)
33
+ p.source = `middleware:${this.name}`;
34
34
  }
35
35
  return problems;
36
36
  };
@@ -1,38 +1,20 @@
1
1
  import { MiddlewareBase } from './base.js';
2
2
  import { Problem } from '../problem.js';
3
- import { codes4xx, otherErrors } from '../defaults/index.js';
4
- import { defaultInstance } from '../util/defaults.js';
3
+ import { codes4xx } from '../defaults/index.js';
5
4
  export class ExpressMiddleware extends MiddlewareBase {
6
5
  name = 'express';
7
6
  middleware = (error, req, res, next) => {
8
- this.parse(error)
9
- .then(problems => {
10
- for (const problem of problems) {
11
- if (problem.instance === defaultInstance) {
12
- problem.instance = req.originalUrl;
13
- }
7
+ const problems = this.parse(error);
8
+ for (const problem of problems) {
9
+ if (!problem.instance) {
10
+ problem.instance = req.originalUrl;
14
11
  }
15
- this.emit(problems);
16
- res
17
- .contentType('application/problem+json')
18
- .status(problems[0]?.status ?? 500)
19
- .json(problems.map(val => val.toObject()));
20
- })
21
- .catch(err => {
22
- res
23
- .contentType('application/problem+json')
24
- .status(500)
25
- .json([
26
- new Problem({
27
- ...otherErrors.unknown,
28
- 'instance': req.originalUrl,
29
- 'status': 500,
30
- 'stack': err?.stack,
31
- 'errorObject': err,
32
- }).toObject()
33
- ]);
34
- throw err;
35
- });
12
+ }
13
+ this.emit(problems);
14
+ res
15
+ .contentType('application/problem+json')
16
+ .status(problems[0]?.status ?? 500)
17
+ .json(problems.map(val => val.toObject()));
36
18
  };
37
19
  /**
38
20
  * This function applies the middleware to your Express application. Put this at the very end of your middleware/routes.
@@ -1,10 +1,8 @@
1
1
  import { otherErrors } from '../defaults/others.js';
2
2
  import { Problem } from '../problem.js';
3
3
  import { getProblems } from '../util/getProblems.js';
4
- import { getHttpError } from '../util/misc.js';
5
- function isAxiosError(payload) {
6
- return typeof payload === 'object' && payload !== null && 'isAxiosError' in payload && payload.isAxiosError === true;
7
- }
4
+ import { getCodeDefaults } from '../util/misc.js';
5
+ import { isAxiosError } from 'axios';
8
6
  const parse = (input) => {
9
7
  if (typeof input !== 'object' || input == null)
10
8
  return [];
@@ -17,13 +15,14 @@ const parse = (input) => {
17
15
  return problems;
18
16
  }
19
17
  if (input?.response) {
20
- const opts = getHttpError(input.response.status);
18
+ const opts = getCodeDefaults(input.response.status);
21
19
  if (opts)
22
20
  return [new Problem({
23
21
  ...opts,
24
22
  'instance': input?.config?.url,
25
23
  'stack': input?.stack,
26
- 'errorObject': input
24
+ 'errorObject': input,
25
+ 'source': 'parser:axios'
27
26
  })];
28
27
  }
29
28
  // TODO: Fix this, this seems bad
@@ -35,7 +34,8 @@ const parse = (input) => {
35
34
  ...otherErrors.networkError,
36
35
  'instance': input?.config?.url,
37
36
  'stack': input?.stack,
38
- 'errorObject': input
37
+ 'errorObject': input,
38
+ 'source': 'parser:axios'
39
39
  })];
40
40
  }
41
41
  // Probably a ClientRequest
@@ -44,7 +44,8 @@ const parse = (input) => {
44
44
  ...otherErrors.networkError,
45
45
  'instance': input?.config?.url,
46
46
  'stack': input?.stack,
47
- 'errorObject': input
47
+ 'errorObject': input,
48
+ 'source': 'parser:axios'
48
49
  })];
49
50
  }
50
51
  }
@@ -73,6 +73,7 @@ const parse = (input) => {
73
73
  return [new Problem({
74
74
  ...found.result,
75
75
  'errorObject': input,
76
+ 'source': 'parser:jsonwebtoken'
76
77
  })];
77
78
  }
78
79
  }
@@ -83,6 +84,7 @@ const parse = (input) => {
83
84
  'detail': `The JSON Web Token expired at ${input.expiredAt}`,
84
85
  'status': 400,
85
86
  'errorObject': input,
87
+ 'source': 'parser:jsonwebtoken'
86
88
  })];
87
89
  }
88
90
  else if (input instanceof NotBeforeError && input.name === 'NotBeforeError') {
@@ -92,6 +94,7 @@ const parse = (input) => {
92
94
  'detail': `The JSON Web Token is not valid before ${input.date}`,
93
95
  'status': 400,
94
96
  'errorObject': input,
97
+ 'source': 'parser:jsonwebtoken'
95
98
  })];
96
99
  }
97
100
  };
@@ -11,6 +11,7 @@ const parse = (input) => {
11
11
  ...statusCodes['404'],
12
12
  'stack': input.stack,
13
13
  'errorObject': input,
14
+ 'source': 'parser:mikroorm'
14
15
  })
15
16
  ];
16
17
  };
@@ -5,7 +5,7 @@ const parse = (input) => {
5
5
  if (!(input instanceof ValidateError)) {
6
6
  return;
7
7
  }
8
- // TODO: Give actual useful responses instead of this shit
8
+ // TODO: Give actual useful responses instead of this 💩
9
9
  return [
10
10
  new Problem({
11
11
  ...otherErrors.inputValidationError,
@@ -15,7 +15,8 @@ const parse = (input) => {
15
15
  'fields': input.fields,
16
16
  },
17
17
  'stack': input.stack,
18
- 'errorObject': input
18
+ 'errorObject': input,
19
+ 'source': 'parser:tsoa'
19
20
  })
20
21
  ];
21
22
  };
@@ -13,7 +13,8 @@ const parse = (input) => {
13
13
  'errorObject': input,
14
14
  'data': {
15
15
  'issues': input.issues
16
- }
16
+ },
17
+ 'source': 'parser:zod'
17
18
  })];
18
19
  };
19
20
  export default parse;
package/esm/problem.d.ts CHANGED
@@ -1,14 +1,14 @@
1
- import type { IProblem, ProblemOpts, JsonProblem } from './typings/problem.js';
1
+ import type { IProblem, ProblemOpts, JsonProblemOutput } from './typings/problem.js';
2
2
  export declare class Problem extends Error implements IProblem {
3
3
  type: string;
4
4
  title: string;
5
5
  status: number;
6
- detail: string;
7
- instance: string;
6
+ detail: string | undefined;
7
+ instance: string | undefined;
8
8
  errorObject?: unknown;
9
- __problemVersion: string;
9
+ readonly __problemVersion = "8.0.0";
10
10
  data: unknown;
11
- middleware?: string;
12
- constructor({ type, title, status, detail, instance, stack, errorObject, data, middleware }: ProblemOpts);
13
- toObject: () => JsonProblem;
11
+ source?: string;
12
+ constructor(opts: ProblemOpts);
13
+ toObject: () => JsonProblemOutput;
14
14
  }
package/esm/problem.js CHANGED
@@ -1,56 +1,65 @@
1
- import { defaultDetail, defaultInstance, defaultTitle, defaultType } from './util/defaults.js';
1
+ import { validateBasicInput } from './util/validation.js';
2
2
  import { version } from './util/version.js';
3
3
  export class Problem extends Error {
4
- type = defaultType;
5
- title = defaultTitle;
6
- status = 500;
7
- detail = defaultDetail;
8
- instance = defaultInstance;
4
+ type;
5
+ title;
6
+ status;
7
+ detail;
8
+ instance;
9
9
  errorObject;
10
10
  __problemVersion = version;
11
11
  data = undefined;
12
- middleware;
13
- constructor({ type, title, status, detail, instance, stack, errorObject, data, middleware }) {
14
- super(detail ?? title ?? 'HTTP Problem Details');
15
- // TODO: Figure out why i wrote this?
12
+ source;
13
+ constructor(opts) {
14
+ const valid = validateBasicInput(opts);
15
+ if (valid !== true)
16
+ throw valid;
17
+ super(opts.detail ?? opts.title ?? 'HTTP Problem Details');
18
+ // TODO: Figure out if this is still necessary
16
19
  Object.setPrototypeOf(this, new.target.prototype);
17
- this.type = type;
18
- this.title = title;
19
- this.status = status;
20
- if (detail)
21
- this.detail = detail;
22
- if (instance)
23
- this.instance = instance;
24
- if (errorObject)
25
- this.errorObject = errorObject;
26
- if (middleware)
27
- this.middleware = middleware;
28
- if (stack) {
29
- this.stack = stack;
20
+ this.type = opts.type;
21
+ this.title = opts.title;
22
+ this.status = opts.status;
23
+ if (opts.detail)
24
+ this.detail = opts.detail;
25
+ if (opts.instance)
26
+ this.instance = opts.instance;
27
+ if (opts.errorObject)
28
+ this.errorObject = opts.errorObject;
29
+ if (opts.source)
30
+ this.source = opts.source;
31
+ if (opts.stack) {
32
+ this.stack = opts.stack;
30
33
  }
31
- else if (errorObject && typeof errorObject === 'object' && 'stack' in errorObject && typeof errorObject.stack === 'string') {
32
- this.stack = errorObject.stack;
34
+ else if (opts.errorObject && typeof opts.errorObject === 'object' && 'stack' in opts.errorObject && typeof opts.errorObject.stack === 'string') {
35
+ this.stack = opts.errorObject.stack;
33
36
  }
34
37
  else {
35
38
  if (typeof Error.captureStackTrace === 'function') {
36
39
  Error.captureStackTrace(this, this.constructor);
37
40
  }
38
41
  else {
39
- this.stack = new Error(detail ?? title ?? 'HTTP Problem Details').stack;
42
+ this.stack = new Error(opts.detail ?? opts.title ?? 'HTTP Problem Details').stack;
40
43
  }
41
44
  }
42
- if (data)
43
- this.data = data;
45
+ if (opts.data)
46
+ this.data = opts.data;
44
47
  }
45
48
  toObject = () => {
46
- return {
49
+ const out = {
47
50
  'type': this.type,
48
51
  'title': this.title,
49
52
  'status': this.status,
50
- 'detail': this.detail,
51
- 'instance': this.instance,
52
53
  '__problemVersion': this.__problemVersion,
53
- 'data': this.data
54
54
  };
55
+ if (this.detail)
56
+ out.detail = this.detail;
57
+ if (this.instance)
58
+ out.instance = this.instance;
59
+ if (this.source)
60
+ out.source = this.source;
61
+ if (this.data)
62
+ out.data = this;
63
+ return out;
55
64
  };
56
65
  }