UNPKG

4.24 kBJavaScriptView Raw
1// @ts-nocheck
2
3import { inspect } from "util";
4import { isNil } from "./lodash.js";
5
6/**
7 * Standard error to use. This contains a key, status code and info object.
8 * Mostly provided to make it easier to return errors from your API's.
9 *
10 * @since 0.1.0
11 * @class
12 */
13export class AppError extends Error {
14 /**
15 * @param {string} key
16 * @param {number} status
17 * @param {Record<string, any>} [info={}]
18 * @param {Error} [originalError]
19 */
20 constructor(key, status, info, originalError) {
21 super();
22
23 this.key = key;
24 this.status = status;
25 this.info = info || {};
26 this.originalError = originalError;
27
28 Object.setPrototypeOf(this, AppError.prototype);
29
30 if (typeof status !== "number" || typeof key !== "string") {
31 return AppError.serverError(
32 {
33 appErrorConstructParams: {
34 key,
35 status,
36 },
37 },
38 this,
39 );
40 }
41 }
42
43 /**
44 * @param {*} value
45 * @returns {value is AppError}
46 */
47 static instanceOf(value) {
48 return (
49 value &&
50 typeof value.key === "string" &&
51 typeof value.status === "number" &&
52 !!value.info
53 );
54 }
55
56 /**
57 * @param {Record<string, any>} [info={}]
58 * @param {Error} [error]
59 * @returns {AppError}
60 */
61 static notFound(info = {}, error = undefined) {
62 return new AppError("error.server.notFound", 404, info, error);
63 }
64
65 /**
66 * @param {Record<string, any>} [info={}]
67 * @param {Error} [error]
68 * @returns {AppError}
69 */
70 static notImplemented(info = {}, error = undefined) {
71 return new AppError("error.server.notImplemented", 405, info, error);
72 }
73
74 /**
75 * @param {Record<string, any>} [info={}]
76 * @param {Error} [error]
77 * @returns {AppError}
78 */
79 static serverError(info = {}, error = undefined) {
80 return new AppError("error.server.internal", 500, info, error);
81 }
82
83 /**
84 * @param {string} key
85 * @param {Record<string, any>} [info={}]
86 * @param {Error} [error]
87 * @returns {AppError}
88 */
89 static validationError(key, info = {}, error = undefined) {
90 return new AppError(key, 400, info, error);
91 }
92
93 /**
94 * Format any error skipping the stack automatically for nested errors
95 *
96 * @param {AppError|Error|undefined|null|{}} [e]
97 * @returns {Record<string, any>}
98 */
99 static format(e) {
100 if (!e) {
101 return {
102 warning: "Missing error",
103 };
104 }
105
106 const stack = (e?.stack ?? "").split("\n").map((it) => it.trim());
107 // Remove first element as this is the Error name
108 stack.shift();
109
110 if (isNil(e)) {
111 return e;
112 } else if (AppError.instanceOf(e)) {
113 return {
114 key: e.key,
115 status: e.status,
116 info: e.info,
117 stack,
118 originalError: e.originalError
119 ? AppError.format(e.originalError)
120 : undefined,
121 };
122 } else if (e.name === "PostgresError") {
123 return {
124 name: e.name,
125 message: e.message,
126 postgres: {
127 severity: e?.severity,
128 code: e?.code,
129 position: e?.position,
130 routine: e?.routine,
131 },
132 stack,
133 };
134 } else if (e.isAxiosError) {
135 return {
136 name: e.name,
137 message: e.message,
138 axios: {
139 requestPath: e.request?.path,
140 requestMethod: e.request?.method,
141 responseStatus: e.response?.status,
142 responseHeaders: e.response?.headers,
143 responseBody: e.response?.data,
144 },
145 stack,
146 };
147 } else if (typeof e.toJSON === "function") {
148 const result = e.toJSON();
149 result.stack = stack;
150 return result;
151 }
152
153 // Any unhandled case
154 return {
155 name: e.name,
156 message: e.message,
157 stack,
158 };
159 }
160
161 /**
162 * Use AppError#format when AppError is passed to console.log / console.error.
163 * This works because it uses `util.inspect` under the hood.
164 * Util#inspect checks if the Symbol `util.inspect.custom` is available.
165 */
166 [inspect.custom]() {
167 return AppError.format(this);
168 }
169
170 /**
171 * Use AppError#format when AppError is passed to JSON.stringify().
172 * This is used in the compas insight logger in production mode.
173 */
174 toJSON() {
175 return AppError.format(this);
176 }
177}