@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
package/src/index.ts DELETED
@@ -1,4 +0,0 @@
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'
@@ -1,28 +0,0 @@
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
- name = 'axios'
9
-
10
- constructor(options?: PartialMiddlewareOptions) {
11
- super({
12
- 'parsers': [axiosParser]
13
- }, options)
14
- }
15
-
16
- interceptor = async (error: unknown) => {
17
- const problems = await this.parse(error)
18
-
19
- this.emit(problems)
20
-
21
- return Promise.reject(problems)
22
- }
23
-
24
- /**
25
- * @example instance.interceptors.response.use(...middleware.use())
26
- */
27
- use = (): InterceptorArray => [undefined, this.interceptor]
28
- }
@@ -1,78 +0,0 @@
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
- options: MiddlewareOptions
12
- abstract 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
- enableFallback = true
17
-
18
- constructor(defaultOptions: MiddlewareOptions, options?: PartialMiddlewareOptions) {
19
- this.options = { ...defaultOptions, ...options }
20
- }
21
-
22
- /**
23
- * Call this function when you want to use the middleware function
24
- */
25
- abstract use(): unknown
26
-
27
- /**
28
- * Parse input using parsers
29
- */
30
- parse = async (input: unknown): Promise<Problem[]> => {
31
- const problems = getProblems(input, this.options.parsers)
32
-
33
- if (!problems || !Array.isArray(problems) || problems.length < 1) {
34
- if (this.enableFallback) return [new Problem({
35
- ...this.fallback,
36
- 'errorObject': input,
37
- 'stack': (typeof input === 'object' && input !== null && 'stack' in input && typeof input.stack === 'string') ? input.stack : undefined,
38
- 'middleware': this.name
39
- })]
40
-
41
- return []
42
- }
43
-
44
- for (const p of problems) {
45
- if (!p.middleware) p.middleware = this.name
46
- }
47
-
48
- return problems
49
- }
50
-
51
- fallback: ProblemOpts = otherErrors.unknown
52
-
53
- problemListeners: ProblemListener[] = []
54
-
55
- protected emit = (list: Problem[]) => {
56
- for (const listener of this.problemListeners) {
57
- listener(list)
58
- }
59
- }
60
-
61
- /**
62
- * Listen to problems
63
- */
64
- onProblem = (listener: ProblemListener) => {
65
- this.problemListeners.push(listener)
66
- }
67
-
68
- /**
69
- * Un-listen to problems
70
- */
71
- offProblem = (listener: ProblemListener) => {
72
- const idx = this.problemListeners.findIndex(val => val === listener)
73
-
74
- if (idx < 0 || isNaN(idx)) return
75
-
76
- this.problemListeners.splice(idx, 1)
77
- }
78
- }
@@ -1,71 +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
- import { Problem } from '../problem.js'
5
- import { codes4xx, otherErrors } from '../defaults/index.js'
6
- import { defaultInstance } from '../util/defaults.js'
7
-
8
- export class ExpressMiddleware extends MiddlewareBase {
9
- name = 'express'
10
-
11
- middleware = (error: unknown, req: Request, res: Response, next: NextFunction) => {
12
- this.parse(error)
13
- .then(problems => {
14
- for (const problem of problems) {
15
- if (problem.instance === defaultInstance) {
16
- problem.instance = req.originalUrl
17
- }
18
- }
19
-
20
- this.emit(problems)
21
-
22
- res
23
- .contentType('application/problem+json')
24
- .status(problems[0]?.status ?? 500)
25
- .json(problems.map(val => val.toObject()))
26
- })
27
- .catch(err => {
28
- res
29
- .contentType('application/problem+json')
30
- .status(500)
31
- .json([
32
- new Problem({
33
- ...otherErrors.unknown,
34
- 'instance': req.originalUrl,
35
- 'status': 500,
36
- 'stack': err?.stack,
37
- 'errorObject': err,
38
- }).toObject()
39
- ])
40
-
41
- throw err
42
- })
43
- }
44
-
45
- /**
46
- * This function applies the middleware to your Express application. Put this at the very end of your middleware/routes.
47
- * If you want to also handle 404 errors, check out the notFound middleware function.
48
- * @example app.use(middleware.use())
49
- */
50
- use = () => this.middleware
51
-
52
- constructor(options?: PartialMiddlewareOptions) {
53
- super({
54
- 'parsers': []
55
- }, options)
56
- }
57
-
58
- /**
59
- * Put this right before the middleware.use() to catch all 404 errors and respond with a Problem instead of the default Express response
60
- * @example app.use(middleware.notFound)
61
- */
62
- notFound = (req: Request, res: Response, next: NextFunction) => {
63
- const error = new Problem({
64
- ...codes4xx[404],
65
- 'instance': req.originalUrl,
66
- 'detail': `No API endpoints exist on ${req.url} for request method ${req.method}`
67
- })
68
-
69
- return next(error)
70
- }
71
- }
@@ -1,60 +0,0 @@
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 { getHttpError } from '../util/misc.js'
6
- import type { AxiosError } from 'axios'
7
-
8
- function isAxiosError<T = unknown, D = unknown>(payload: unknown): payload is AxiosError<T, D> {
9
- return typeof payload === 'object' && payload !== null && 'isAxiosError' in payload && payload.isAxiosError === true
10
- }
11
-
12
- const parse: Parser = (input) => {
13
- if (typeof input !== 'object' || input == null) return []
14
- if (!isAxiosError(input)) return []
15
- const request = input.request
16
-
17
- if (input?.response?.data) {
18
- const problems = getProblems(input?.response?.data)
19
- if (problems?.length) return problems
20
- }
21
-
22
- if (input?.response) {
23
- const opts = getHttpError(input.response.status)
24
- if (opts) return [new Problem({
25
- ...opts,
26
- 'instance': input?.config?.url,
27
- 'stack': input?.stack,
28
- 'errorObject': input
29
- })]
30
- }
31
-
32
- // TODO: Fix this, this seems bad
33
- if (request) {
34
- const isNode = typeof process !== 'undefined' && process.versions && process.versions.node
35
- const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined'
36
-
37
- if (isBrowser && request instanceof XMLHttpRequest) {
38
- return [new Problem({
39
- ...otherErrors.networkError,
40
- 'instance': input?.config?.url,
41
- 'stack': input?.stack,
42
- 'errorObject': input
43
- })]
44
- }
45
-
46
- // Probably a ClientRequest
47
- if (isNode && 'pipe' in request && 'destroy' in request) {
48
- return [new Problem({
49
- ...otherErrors.networkError,
50
- 'instance': input?.config?.url,
51
- 'stack': input?.stack,
52
- 'errorObject': input
53
- })]
54
- }
55
- }
56
-
57
- return []
58
- }
59
-
60
- export default parse
@@ -1,107 +0,0 @@
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[] = [
12
- {
13
- 'search': 'invalid token',
14
- 'result': {
15
- 'type': '/errors/jsonwebtoken/invalidtoken',
16
- 'status': 400,
17
- 'title': 'Invalid JSON Web Token',
18
- }
19
- },
20
- {
21
- 'search': 'jwt malformed',
22
- 'result': {
23
- 'type': '/errors/jsonwebtoken/malformed',
24
- 'status': 400,
25
- 'title': 'Malformed JSON Web Token',
26
- }
27
- },
28
- {
29
- 'search': 'jwt signature is required',
30
- 'result': {
31
- 'type': '/errors/jsonwebtoken/signaturerequired',
32
- 'status': 400,
33
- 'title': 'JSON Web Token is missing, but required',
34
- }
35
- },
36
- {
37
- 'search': 'invalid signature',
38
- 'result': {
39
- 'type': '/errors/jsonwebtoken/invalidsignature',
40
- 'status': 400,
41
- 'title': 'The JSON Web Token signature is invalid',
42
- }
43
- },
44
- {
45
- 'search': /^(jwt audience invalid\. expected:)/,
46
- 'result': {
47
- 'type': '/errors/jsonwebtoken/invalidaud',
48
- 'status': 400,
49
- 'title': 'The JSON Web Token audience is invalid',
50
- }
51
- },
52
- {
53
- 'search': /^(jwt issuer invalid\. expected:)/,
54
- 'result': {
55
- 'type': '/errors/jsonwebtoken/invalidiss',
56
- 'status': 400,
57
- 'title': 'The JSON Web Token issuer is invalid',
58
- }
59
- },
60
- {
61
- 'search': /^(jwt id invalid\. expected:)/,
62
- 'result': {
63
- 'type': '/errors/jsonwebtoken/invalidid',
64
- 'status': 400,
65
- 'title': 'The JSON Web Token id is invalid',
66
- }
67
- },
68
- {
69
- 'search': /^(jwt subject invalid\. expected:)/,
70
- 'result': {
71
- 'type': '/errors/jsonwebtoken/invalidsubject',
72
- 'status': 400,
73
- 'title': 'The JSON Web Token subject is invalid',
74
- }
75
- }
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
-
82
- if (found) {
83
- return [new Problem({
84
- ...found.result,
85
- 'errorObject': input,
86
- })]
87
- }
88
- } else if (input instanceof TokenExpiredError && input.name === 'TokenExpiredError') {
89
- return [new Problem({
90
- 'title': 'The JSON Web Token has expired',
91
- 'type': '/errors/jsonwebtoken/tokenexpired',
92
- 'detail': `The JSON Web Token expired at ${input.expiredAt}`,
93
- 'status': 400,
94
- 'errorObject': input,
95
- })]
96
- } else if (input instanceof NotBeforeError && input.name === 'NotBeforeError') {
97
- return [new Problem({
98
- 'title': 'The JSON Web Token is not valid yet',
99
- 'type': '/errors/jsonwebtoken/notbefore',
100
- 'detail': `The JSON Web Token is not valid before ${input.date}`,
101
- 'status': 400,
102
- 'errorObject': input,
103
- })]
104
- }
105
- }
106
-
107
- export default parse
@@ -1,21 +0,0 @@
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
- })
18
- ]
19
- }
20
-
21
- export default parse
@@ -1,26 +0,0 @@
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 shit
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
- })
23
- ]
24
- }
25
-
26
- export default parse
@@ -1,23 +0,0 @@
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
- 'status': 400,
15
- 'stack': input.stack,
16
- 'errorObject': input,
17
- 'data': {
18
- 'issues': input.issues
19
- }
20
- })]
21
- }
22
-
23
- export default parse
package/src/problem.ts DELETED
@@ -1,56 +0,0 @@
1
- import type { IProblem, ProblemOpts, JsonProblem } from './typings/problem.js'
2
- import { defaultDetail, defaultInstance, defaultTitle, defaultType } from './util/defaults.js'
3
- import { version } from './util/version.js'
4
-
5
- export class Problem extends Error implements IProblem {
6
- public type = defaultType
7
- public title = defaultTitle
8
- public status = 500
9
- public detail = defaultDetail
10
- public instance = defaultInstance
11
- public errorObject?: unknown
12
- public __problemVersion = version
13
- public data: unknown = undefined
14
- public middleware?: string
15
-
16
- constructor({ type, title, status, detail, instance, stack, errorObject, data, middleware }: ProblemOpts) {
17
- super(detail ?? title ?? 'HTTP Problem Details')
18
- // TODO: Figure out why i wrote this?
19
- Object.setPrototypeOf(this, new.target.prototype)
20
-
21
- this.type = type
22
- this.title = title
23
- this.status = status
24
-
25
- if (detail) this.detail = detail
26
- if (instance) this.instance = instance
27
- if (errorObject) this.errorObject = errorObject
28
- if (middleware) this.middleware = middleware
29
-
30
- if (stack) {
31
- this.stack = stack
32
- } else if (errorObject && typeof errorObject === 'object' && 'stack' in errorObject && typeof errorObject.stack === 'string') {
33
- this.stack = errorObject.stack
34
- } else {
35
- if (typeof Error.captureStackTrace === 'function') {
36
- Error.captureStackTrace(this, this.constructor)
37
- } else {
38
- this.stack = new Error(detail ?? title ?? 'HTTP Problem Details').stack
39
- }
40
- }
41
-
42
- if (data) this.data = data
43
- }
44
-
45
- public toObject = (): JsonProblem => {
46
- return {
47
- 'type': this.type,
48
- 'title': this.title,
49
- 'status': this.status,
50
- 'detail': this.detail,
51
- 'instance': this.instance,
52
- '__problemVersion': this.__problemVersion,
53
- 'data': this.data
54
- }
55
- }
56
- }
@@ -1,6 +0,0 @@
1
- import type { ProblemOpts } from './problem.js'
2
-
3
- export interface Codes {
4
- [key: number]: ProblemOpts | undefined
5
- [key: string]: ProblemOpts | undefined
6
- }
@@ -1,4 +0,0 @@
1
- export * from './middleware.js'
2
- export * from './parser.js'
3
- export * from './problem.js'
4
- export * from './codes.js'
@@ -1,14 +0,0 @@
1
- import type { Problem } from '../problem.js'
2
- import type { Parser } from './parser.js'
3
-
4
- export interface MiddlewareOptions {
5
- /**
6
- * A list of parsers to be used by the middleware.
7
- * Parsers are run in the order they sit in the array
8
- */
9
- parsers: Parser[]
10
- }
11
-
12
- export type PartialMiddlewareOptions = Partial<MiddlewareOptions>
13
-
14
- export type ProblemListener = (problems: Problem[]) => void
@@ -1,3 +0,0 @@
1
- import type { Problem } from '../problem.js'
2
-
3
- export type Parser = (input: unknown) => Problem[] | undefined
@@ -1,27 +0,0 @@
1
- /**
2
- * @param {string} type - A URI refrence that identifies the problem type.
3
- * @param {string} title - A short, human-readable summary of the problem
4
- * type. It SHOULD NOT change from occurrence to occurrence of the problem
5
- * @param {number} status - The HTTP status code
6
- * @param {string} detail - A human-readable explanation specific to this
7
- occurrence of the problem.
8
- * @property {string} instance - A URI reference that identifies the specific
9
- occurrence of the problem
10
- **/
11
-
12
- export interface IProblem {
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
- middleware?: string
23
- }
24
-
25
- export type JsonProblem = Omit<IProblem, 'stack' | 'errorObject' | 'middleware'>
26
-
27
- export type ProblemOpts = Omit<IProblem, '__problemVersion'>
@@ -1,4 +0,0 @@
1
- export const defaultType = '/errors/default/unknown'
2
- export const defaultTitle = 'Unknown error'
3
- export const defaultDetail = 'No extended details are available'
4
- export const defaultInstance = '/unknown'
@@ -1,56 +0,0 @@
1
- import { Problem } from '../problem.js'
2
- import { Parser } from '../typings/parser.js'
3
- import type { JsonProblem } from '../typings/problem.js'
4
- import { isProblemArray } from './problemArray.js'
5
- import { isCompatibleVersion } from './version.js'
6
-
7
- const strings = ['title', 'type', 'instance', 'detail', '__problemVersion'] as const
8
- export function isJsonProblem(input: unknown): input is JsonProblem {
9
- if (input === null) return false
10
- if (typeof input !== 'object') return false
11
-
12
- if (!strings.every(val => val in input && typeof input[val as keyof typeof input] === 'string')) return false
13
- if (!isCompatibleVersion(('__problemVersion' in input && typeof input['__problemVersion'] === 'string') ? input['__problemVersion'] : '')) return false
14
-
15
- if (!('status' in input) || typeof input.status !== 'number') return false
16
-
17
- return true
18
- }
19
-
20
- export function createFromJson({ title, type, instance, detail, status, data }: JsonProblem): Problem {
21
- return new Problem({ title, type, instance, detail, status, data })
22
- }
23
-
24
- /**
25
- * Get an array of Problems from a given input.
26
- * Handles existing Problems, arrays of Problems, JSON Problems, JSON Problem arrays and optionally attempts to parse any input with the given parsers.
27
- * Only returns an array of Problems if it can successfully parse the input.
28
- */
29
- export function getProblems(input: unknown, parsers?: Parser[]): Problem[] | undefined {
30
- if (typeof input === 'string') {
31
- try {
32
- const p = JSON.parse(input)
33
- input = p
34
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
35
- } catch (err) { /**/ }
36
- }
37
-
38
- if (input instanceof Problem) return [input]
39
- if (isProblemArray(input, false)) return input
40
-
41
- if (Array.isArray(input) && input.length > 0 && input.every(val => isJsonProblem(val))) {
42
- return input.map(val => createFromJson(val))
43
- }
44
-
45
- if (isJsonProblem(input)) {
46
- return [createFromJson(input)]
47
- }
48
-
49
- if (parsers) {
50
- for (const parser of parsers) {
51
- const problems = parser(input)
52
- if (!problems || !problems.length) continue
53
- return problems
54
- }
55
- }
56
- }
package/src/util/index.ts DELETED
@@ -1,4 +0,0 @@
1
- export * from './getProblems.js'
2
- export * from './defaults.js'
3
- export * from './problemArray.js'
4
- export * from './misc.js'
package/src/util/misc.ts DELETED
@@ -1,21 +0,0 @@
1
- import * as allCodes from '../defaults/index.js'
2
- import { ProblemOpts, Codes } from '../typings/index.js'
3
-
4
- export function getHttpError(statusCode: number | string): ProblemOpts | undefined {
5
- for (const key in allCodes) {
6
- // This ensures all codes are written correctly as well, so that's a bonus
7
- const codeObject: Codes = allCodes[key as keyof typeof allCodes]
8
-
9
- if (typeof statusCode === 'string') {
10
- const conv = Number(statusCode)
11
- if (!isNaN(conv)) statusCode = conv
12
- }
13
-
14
- const code = codeObject[statusCode]
15
-
16
- if (!code) return
17
-
18
- return code
19
- }
20
- }
21
-
@@ -1,21 +0,0 @@
1
- import { Problem } from '../problem.js'
2
-
3
- /**
4
- * Checks whether a given object is an array of Problem objects
5
- * @param input Object to test
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.
7
- */
8
- export function isProblemArray(input: unknown, allowEmpty?: boolean): input is Problem[] {
9
- if (!Array.isArray(input)) return false
10
-
11
- if (input.length < 1) {
12
- if (typeof allowEmpty !== 'boolean') {
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
15
- }
16
-
17
- return allowEmpty
18
- }
19
-
20
- return input.every(val => val instanceof Problem)
21
- }
@@ -1,16 +0,0 @@
1
- export const version = '7.0.6'
2
- export const major = Number(version.split('.')[0])
3
-
4
- export function isCompatibleVersion(inputVersion: string) {
5
- // TODO: Remove this
6
- if (inputVersion === 'lua-dev') return true
7
-
8
- const parts = inputVersion.split('.')
9
- const inputMajor = Number(parts[0])
10
-
11
- if (parts.length !== 3) return false
12
- if (isNaN(inputMajor)) return false
13
- if (inputMajor !== major) return false
14
-
15
- return true
16
- }