@tryghost/mw-error-handler 3.0.3 → 3.2.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2013-2026 Ghost Foundation
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -8,7 +8,7 @@ Express middleware helpers for Ghost-style API error handling.
8
8
 
9
9
  or
10
10
 
11
- `yarn add @tryghost/mw-error-handler`
11
+ `pnpm add @tryghost/mw-error-handler`
12
12
 
13
13
  ## Purpose
14
14
 
@@ -3,8 +3,8 @@ const path = require('path');
3
3
  const semver = require('semver');
4
4
  const debug = require('@tryghost/debug')('error-handler');
5
5
  const errors = require('@tryghost/errors');
6
- const {prepareStackForUser} = require('@tryghost/errors').utils;
7
- const {isReqResUserSpecific, cacheControlValues} = require('@tryghost/http-cache-utils');
6
+ const { prepareStackForUser } = require('@tryghost/errors').utils;
7
+ const { isReqResUserSpecific, cacheControlValues } = require('@tryghost/http-cache-utils');
8
8
  const tpl = require('@tryghost/tpl');
9
9
 
10
10
  const messages = {
@@ -14,19 +14,21 @@ const messages = {
14
14
  resourceNotFound: 'Resource not found',
15
15
  methodNotAcceptableVersionAhead: {
16
16
  message: 'Request could not be served, the endpoint was not found.',
17
- context: 'Provided client accept-version {acceptVersion} is ahead of current Ghost version {ghostVersion}.',
18
- help: 'Try upgrading your Ghost install.'
17
+ context:
18
+ 'Provided client accept-version {acceptVersion} is ahead of current Ghost version {ghostVersion}.',
19
+ help: 'Try upgrading your Ghost install.',
19
20
  },
20
21
  methodNotAcceptableVersionBehind: {
21
22
  message: 'Request could not be served, the endpoint was not found.',
22
- context: 'Provided client accept-version {acceptVersion} is behind current Ghost version {ghostVersion}.',
23
- help: 'Try upgrading your Ghost API client.'
23
+ context:
24
+ 'Provided client accept-version {acceptVersion} is behind current Ghost version {ghostVersion}.',
25
+ help: 'Try upgrading your Ghost API client.',
24
26
  },
25
27
  badVersion: 'Requested version is not supported.',
26
28
  actions: {
27
29
  images: {
28
- upload: 'upload image'
29
- }
30
+ upload: 'upload image',
31
+ },
30
32
  },
31
33
  userMessages: {
32
34
  BookshelfRelationsError: 'Database error, cannot {action}.',
@@ -50,10 +52,11 @@ const messages = {
50
52
  EmailError: 'Error sending email!',
51
53
  ThemeValidationError: 'Theme validation error, cannot {action}.',
52
54
  HostLimitError: 'Host Limit error, cannot {action}.',
53
- DisabledFeatureError: 'Theme validation error, the {{{helperName}}} helper is not available. Cannot {action}.',
54
- UpdateCollisionError: 'Saving failed! Someone else is editing this post.'
55
+ DisabledFeatureError:
56
+ 'Theme validation error, the {{{helperName}}} helper is not available. Cannot {action}.',
57
+ UpdateCollisionError: 'Saving failed! Someone else is editing this post.',
55
58
  },
56
- UnknownError: 'Unknown error - {name}, cannot {action}.'
59
+ UnknownError: 'Unknown error - {name}, cannot {action}.',
57
60
  };
58
61
 
59
62
  function isDependencyInStack(dependency, err) {
@@ -78,18 +81,21 @@ module.exports.prepareError = function prepareError(err, req, res, next) {
78
81
  // Catch bookshelf empty errors and other 404s, and turn into a Ghost 404
79
82
  if ((err.statusCode && err.statusCode === 404) || err.message === 'EmptyResponse') {
80
83
  err = new errors.NotFoundError({
81
- err: err
84
+ err: err,
82
85
  });
83
- // Catch handlebars / express-hbs errors, and render them as 400, rather than 500 errors as the server isn't broken
84
- } else if (isDependencyInStack('handlebars', err) || isDependencyInStack('express-hbs', err)) {
86
+ // Catch handlebars / express-hbs errors, and render them as 400, rather than 500 errors as the server isn't broken
87
+ } else if (
88
+ isDependencyInStack('handlebars', err) ||
89
+ isDependencyInStack('express-hbs', err)
90
+ ) {
85
91
  // Temporary handling of theme errors from handlebars
86
92
  // @TODO remove this when #10496 is solved properly
87
93
  err = new errors.IncorrectUsageError({
88
94
  err: err,
89
95
  message: err.message,
90
- statusCode: err.statusCode
96
+ statusCode: err.statusCode,
91
97
  });
92
- // Catch database errors and turn them into 500 errors, but log some useful data to sentry
98
+ // Catch database errors and turn them into 500 errors, but log some useful data to sentry
93
99
  } else if (isDependencyInStack('mysql2', err)) {
94
100
  // we don't want to return raw database errors to our users
95
101
  err.sqlErrorCode = err.code;
@@ -98,24 +104,24 @@ module.exports.prepareError = function prepareError(err, req, res, next) {
98
104
  err = new errors.ValidationError({
99
105
  message: tpl(messages.invalidValue),
100
106
  context: err.message,
101
- err
107
+ err,
102
108
  });
103
109
  } else {
104
110
  err = new errors.InternalServerError({
105
111
  err: err,
106
112
  message: tpl(messages.genericError),
107
113
  statusCode: err.statusCode,
108
- code: 'UNEXPECTED_ERROR'
114
+ code: 'UNEXPECTED_ERROR',
109
115
  });
110
116
  }
111
- // For everything else, create a generic 500 error, with context set to the original error message
117
+ // For everything else, create a generic 500 error, with context set to the original error message
112
118
  } else {
113
119
  err = new errors.InternalServerError({
114
120
  err: err,
115
121
  message: tpl(messages.genericError),
116
122
  context: err.message,
117
123
  statusCode: err.statusCode,
118
- code: 'UNEXPECTED_ERROR'
124
+ code: 'UNEXPECTED_ERROR',
119
125
  });
120
126
  }
121
127
  }
@@ -129,7 +135,7 @@ module.exports.prepareError = function prepareError(err, req, res, next) {
129
135
  next(err);
130
136
  };
131
137
 
132
- module.exports.prepareStack = function prepareStack(err, req, res, next) { // eslint-disable-line no-unused-vars
138
+ module.exports.prepareStack = function prepareStack(err, _req, _res, next) {
133
139
  const clonedError = prepareStackForUser(err);
134
140
 
135
141
  next(clonedError);
@@ -142,21 +148,23 @@ module.exports.prepareStack = function prepareStack(err, req, res, next) { // es
142
148
  * @param {import('express').Response} res
143
149
  * @param {import('express').NextFunction} next
144
150
  */
145
- module.exports.jsonErrorRenderer = function jsonErrorRenderer(err, req, res, next) { // eslint-disable-line no-unused-vars
151
+ module.exports.jsonErrorRenderer = function jsonErrorRenderer(err, req, res, _next) {
146
152
  const userError = prepareUserMessage(err, req);
147
153
 
148
154
  res.json({
149
- errors: [{
150
- message: userError.message,
151
- context: userError.context || null,
152
- type: err.errorType || null,
153
- details: err.errorDetails || null,
154
- property: err.property || null,
155
- help: err.help || null,
156
- code: err.code || null,
157
- id: err.id || null,
158
- ghostErrorCode: err.ghostErrorCode || null
159
- }]
155
+ errors: [
156
+ {
157
+ message: userError.message,
158
+ context: userError.context || null,
159
+ type: err.errorType || null,
160
+ details: err.errorDetails || null,
161
+ property: err.property || null,
162
+ help: err.help || null,
163
+ code: err.code || null,
164
+ id: err.id || null,
165
+ ghostErrorCode: err.ghostErrorCode || null,
166
+ },
167
+ ],
160
168
  });
161
169
  };
162
170
 
@@ -164,7 +172,9 @@ module.exports.jsonErrorRenderer = function jsonErrorRenderer(err, req, res, nex
164
172
  *
165
173
  * @param {String} [cacheControlHeaderValue] cache-control header value
166
174
  */
167
- module.exports.prepareErrorCacheControl = function prepareErrorCacheControl(cacheControlHeaderValue) {
175
+ module.exports.prepareErrorCacheControl = function prepareErrorCacheControl(
176
+ cacheControlHeaderValue,
177
+ ) {
168
178
  return function prepareErrorCacheControlInner(err, req, res, next) {
169
179
  let cacheControl = cacheControlHeaderValue;
170
180
  if (!cacheControlHeaderValue) {
@@ -178,7 +188,7 @@ module.exports.prepareErrorCacheControl = function prepareErrorCacheControl(cach
178
188
  }
179
189
 
180
190
  res.set({
181
- 'Cache-Control': cacheControl
191
+ 'Cache-Control': cacheControl,
182
192
  });
183
193
 
184
194
  next(err);
@@ -188,7 +198,7 @@ module.exports.prepareErrorCacheControl = function prepareErrorCacheControl(cach
188
198
  const prepareUserMessage = function prepareUserMessage(err, req) {
189
199
  const userError = {
190
200
  message: err.message,
191
- context: err.context
201
+ context: err.context,
192
202
  };
193
203
 
194
204
  const docName = _.get(req, 'frameOptions.docName');
@@ -202,7 +212,7 @@ const prepareUserMessage = function prepareUserMessage(err, req) {
202
212
  read: 'read',
203
213
  add: 'save',
204
214
  edit: 'edit',
205
- destroy: 'delete'
215
+ destroy: 'delete',
206
216
  };
207
217
 
208
218
  if (_.get(messages.actions, [docName, method])) {
@@ -225,9 +235,9 @@ const prepareUserMessage = function prepareUserMessage(err, req) {
225
235
  }
226
236
 
227
237
  if (_.get(messages.userMessages, err.name)) {
228
- userError.message = tpl(messages.userMessages[err.name], {action: action});
238
+ userError.message = tpl(messages.userMessages[err.name], { action: action });
229
239
  } else {
230
- userError.message = tpl(messages.UnknownError, {action, name: err.name});
240
+ userError.message = tpl(messages.UnknownError, { action, name: err.name });
231
241
  }
232
242
  }
233
243
  }
@@ -240,41 +250,39 @@ module.exports.resourceNotFound = function resourceNotFound(req, res, next) {
240
250
  // Protect against invalid `Accept-Version` headers
241
251
  const acceptVersionSemver = semver.coerce(req.headers['accept-version']);
242
252
  if (!acceptVersionSemver) {
243
- return next(new errors.BadRequestError({
244
- message: tpl(messages.badVersion)
245
- }));
253
+ return next(
254
+ new errors.BadRequestError({
255
+ message: tpl(messages.badVersion),
256
+ }),
257
+ );
246
258
  }
247
259
 
248
260
  if (semver.compare(acceptVersionSemver, semver.coerce(res.locals.safeVersion)) !== 0) {
249
261
  const versionComparison = semver.compare(
250
262
  acceptVersionSemver,
251
- semver.coerce(res.locals.safeVersion)
263
+ semver.coerce(res.locals.safeVersion),
252
264
  );
253
265
 
254
266
  let notAcceptableError;
255
267
  if (versionComparison === 1) {
256
268
  notAcceptableError = new errors.RequestNotAcceptableError({
257
- message: tpl(
258
- messages.methodNotAcceptableVersionAhead.message
259
- ),
269
+ message: tpl(messages.methodNotAcceptableVersionAhead.message),
260
270
  context: tpl(messages.methodNotAcceptableVersionAhead.context, {
261
271
  acceptVersion: req.headers['accept-version'],
262
- ghostVersion: `v${res.locals.safeVersion}`
272
+ ghostVersion: `v${res.locals.safeVersion}`,
263
273
  }),
264
274
  help: tpl(messages.methodNotAcceptableVersionAhead.help),
265
- code: 'UPDATE_GHOST'
275
+ code: 'UPDATE_GHOST',
266
276
  });
267
277
  } else {
268
278
  notAcceptableError = new errors.RequestNotAcceptableError({
269
- message: tpl(
270
- messages.methodNotAcceptableVersionBehind.message
271
- ),
279
+ message: tpl(messages.methodNotAcceptableVersionBehind.message),
272
280
  context: tpl(messages.methodNotAcceptableVersionBehind.context, {
273
281
  acceptVersion: req.headers['accept-version'],
274
- ghostVersion: `v${res.locals.safeVersion}`
282
+ ghostVersion: `v${res.locals.safeVersion}`,
275
283
  }),
276
284
  help: tpl(messages.methodNotAcceptableVersionBehind.help),
277
- code: 'UPDATE_CLIENT'
285
+ code: 'UPDATE_CLIENT',
278
286
  });
279
287
  }
280
288
 
@@ -282,14 +290,14 @@ module.exports.resourceNotFound = function resourceNotFound(req, res, next) {
282
290
  }
283
291
  }
284
292
 
285
- next(new errors.NotFoundError({message: tpl(messages.resourceNotFound)}));
293
+ next(new errors.NotFoundError({ message: tpl(messages.resourceNotFound) }));
286
294
  };
287
295
 
288
296
  module.exports.pageNotFound = function pageNotFound(req, res, next) {
289
- next(new errors.NotFoundError({message: tpl(messages.pageNotFound)}));
297
+ next(new errors.NotFoundError({ message: tpl(messages.pageNotFound) }));
290
298
  };
291
299
 
292
- module.exports.handleJSONResponse = sentry => [
300
+ module.exports.handleJSONResponse = (sentry) => [
293
301
  // Make sure the error can be served
294
302
  module.exports.prepareError,
295
303
  // Add cache-control header
@@ -299,10 +307,10 @@ module.exports.handleJSONResponse = sentry => [
299
307
  // Format the stack for the user
300
308
  module.exports.prepareStack,
301
309
  // Render the error using JSON format
302
- module.exports.jsonErrorRenderer
310
+ module.exports.jsonErrorRenderer,
303
311
  ];
304
312
 
305
- module.exports.handleHTMLResponse = sentry => [
313
+ module.exports.handleHTMLResponse = (sentry) => [
306
314
  // Make sure the error can be served
307
315
  module.exports.prepareError,
308
316
  // Add cache-control header
@@ -310,5 +318,5 @@ module.exports.handleHTMLResponse = sentry => [
310
318
  // Handle the error in Sentry
311
319
  sentry.errorHandler,
312
320
  // Format the stack for the user
313
- module.exports.prepareStack
321
+ module.exports.prepareStack,
314
322
  ];
package/package.json CHANGED
@@ -1,39 +1,36 @@
1
1
  {
2
2
  "name": "@tryghost/mw-error-handler",
3
- "version": "3.0.3",
3
+ "version": "3.2.0",
4
4
  "description": "Express middleware utilities for normalizing and rendering Ghost API errors",
5
- "author": "Ghost Foundation",
6
5
  "license": "MIT",
7
- "main": "index.js",
8
- "publishConfig": {
9
- "access": "public"
10
- },
11
- "scripts": {
12
- "dev": "echo \"Implement me!\"",
13
- "test:unit": "NODE_ENV=testing vitest run --coverage --config ../../vitest.config.ts",
14
- "test": "yarn test:unit",
15
- "lint:code": "eslint *.js lib/ --ext .js --cache",
16
- "lint": "yarn lint:code && yarn lint:test",
17
- "lint:test": "eslint -c test/.eslintrc.js test/ --ext .js --cache"
6
+ "author": "Ghost Foundation",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/TryGhost/framework.git",
10
+ "directory": "packages/mw-error-handler"
18
11
  },
19
12
  "files": [
20
13
  "index.js",
21
14
  "lib"
22
15
  ],
23
- "devDependencies": {
24
- "sinon": "21.0.2"
16
+ "main": "index.js",
17
+ "publishConfig": {
18
+ "access": "public"
25
19
  },
26
20
  "dependencies": {
27
- "@tryghost/debug": "^2.0.3",
28
- "@tryghost/errors": "^3.0.3",
29
- "@tryghost/http-cache-utils": "^2.0.3",
30
- "@tryghost/tpl": "^2.0.3",
31
- "lodash": "4.17.23",
32
- "semver": "7.7.4"
21
+ "lodash": "4.18.1",
22
+ "semver": "7.7.4",
23
+ "@tryghost/debug": "2.2.0",
24
+ "@tryghost/errors": "3.2.0",
25
+ "@tryghost/http-cache-utils": "2.2.0",
26
+ "@tryghost/tpl": "2.2.0"
33
27
  },
34
- "repository": {
35
- "type": "git",
36
- "url": "git+https://github.com/TryGhost/framework.git",
37
- "directory": "packages/mw-error-handler"
28
+ "devDependencies": {
29
+ "sinon": "21.1.2"
30
+ },
31
+ "scripts": {
32
+ "dev": "echo \"Implement me!\"",
33
+ "test": "NODE_ENV=testing vitest run --coverage",
34
+ "lint": "oxlint -c ../../.oxlintrc.json ."
38
35
  }
39
- }
36
+ }