feat: convert to TypeScript
BREAKING CHANGE: Conversion to TypeScript may cause API breakage Signed-off-by: Rifa Achrinza <25147899+achrinza@users.noreply.github.com>
This commit is contained in:
parent
0d2220bbfa
commit
df0b5ac9ab
|
@ -0,0 +1,4 @@
|
||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
|
coverage/
|
||||||
|
.eslintrc.js
|
|
@ -0,0 +1,28 @@
|
||||||
|
module.exports = {
|
||||||
|
extends: [
|
||||||
|
'@loopback/eslint-config/eslintrc.js',
|
||||||
|
// 'plugin:prettier/recommended',
|
||||||
|
],
|
||||||
|
plugins: ['prettier'],
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: ['**/*.ts'],
|
||||||
|
rules: {
|
||||||
|
/*
|
||||||
|
* The mocha plugin reports the following signature as a violation of
|
||||||
|
* `mocha/handle-done-callback` (misinterpreting `this` as `done`).
|
||||||
|
*
|
||||||
|
* See https://github.com/lo1tuma/eslint-plugin-mocha/issues/270
|
||||||
|
*
|
||||||
|
* ```ts
|
||||||
|
* before(async function setupApplication(this: Mocha.Context) {
|
||||||
|
* this.timeout(6000);
|
||||||
|
* // ...
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
'mocha/handle-done-callback': 'off',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"exit": true,
|
||||||
|
"recursive": true,
|
||||||
|
"require": "source-map-support/register"
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
dist
|
||||||
|
*.json
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"bracketSpacing": false,
|
||||||
|
"singleQuote": true,
|
||||||
|
"printWidth": 80,
|
||||||
|
"trailingComma": "all",
|
||||||
|
"arrowParens": "avoid"
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
|
||||||
|
{
|
||||||
|
"editor.rulers": [80],
|
||||||
|
"editor.tabCompletion": "on",
|
||||||
|
"editor.tabSize": 2,
|
||||||
|
"editor.trimAutoWhitespace": true,
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.organizeImports": true,
|
||||||
|
"source.fixAll.eslint": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"files.exclude": {
|
||||||
|
"**/.DS_Store": true,
|
||||||
|
"**/.git": true,
|
||||||
|
"**/.hg": true,
|
||||||
|
"**/.svn": true,
|
||||||
|
"**/CVS": true,
|
||||||
|
"dist": true,
|
||||||
|
},
|
||||||
|
"files.insertFinalNewline": true,
|
||||||
|
"files.trimTrailingWhitespace": true,
|
||||||
|
|
||||||
|
"typescript.tsdk": "./node_modules/typescript/lib",
|
||||||
|
"typescript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": false,
|
||||||
|
"typescript.preferences.quoteStyle": "single",
|
||||||
|
"eslint.run": "onSave",
|
||||||
|
"eslint.nodePath": "./node_modules",
|
||||||
|
"eslint.validate": [
|
||||||
|
"javascript",
|
||||||
|
"typescript"
|
||||||
|
]
|
||||||
|
}
|
|
@ -1,65 +0,0 @@
|
||||||
// Copyright IBM Corp. 2018. All Rights Reserved.
|
|
||||||
// Node module: strong-error-handler
|
|
||||||
// This file is licensed under the MIT License.
|
|
||||||
// License text available at https://opensource.org/licenses/MIT
|
|
||||||
|
|
||||||
// Type definitions for strong-error-handler 3.x
|
|
||||||
// Project: https://github.com/strongloop/strong-error-handler
|
|
||||||
// Definitions by: Kyusung Shim <https://github.com/shimks>
|
|
||||||
// TypeScript Version: 3.0
|
|
||||||
|
|
||||||
import * as Express from 'express';
|
|
||||||
|
|
||||||
export = errorHandlerFactory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a middleware function for error-handling
|
|
||||||
* @param options Options for error handler settings
|
|
||||||
*/
|
|
||||||
declare function errorHandlerFactory(
|
|
||||||
options?: errorHandlerFactory.ErrorHandlerOptions
|
|
||||||
): errorHandlerFactory.StrongErrorHandler;
|
|
||||||
|
|
||||||
declare namespace errorHandlerFactory {
|
|
||||||
/**
|
|
||||||
* Writes thrown error to response
|
|
||||||
* @param err Error to handle
|
|
||||||
* @param req Incoming request
|
|
||||||
* @param res Response
|
|
||||||
* @param options Options for error handler settings
|
|
||||||
*/
|
|
||||||
function writeErrorToResponse(
|
|
||||||
err: Error,
|
|
||||||
req: Express.Request,
|
|
||||||
res: Express.Response,
|
|
||||||
options?: ErrorWriterOptions
|
|
||||||
): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Error-handling middleware function. Includes server-side logging
|
|
||||||
*/
|
|
||||||
type StrongErrorHandler = (
|
|
||||||
err: Error,
|
|
||||||
req: Express.Request,
|
|
||||||
res: Express.Response,
|
|
||||||
next: (err?: any) => void
|
|
||||||
) => void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Options for writing errors to the response
|
|
||||||
*/
|
|
||||||
interface ErrorWriterOptions {
|
|
||||||
debug?: boolean;
|
|
||||||
safeFields?: string[];
|
|
||||||
defaultType?: string;
|
|
||||||
negotiateContentType?: boolean;
|
|
||||||
rootProperty?: string | false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Options for error-handling
|
|
||||||
*/
|
|
||||||
interface ErrorHandlerOptions extends ErrorWriterOptions {
|
|
||||||
log?: boolean;
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
18
package.json
18
package.json
|
@ -4,13 +4,13 @@
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": "10 || 12 || 14 || 16"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/loopbackio/strong-error-handler.git"
|
"url": "https://github.com/loopbackio/strong-error-handler.git"
|
||||||
},
|
},
|
||||||
"main": "lib/handler.js",
|
"main": "dist/handler.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"test": "mocha",
|
"test": "mocha",
|
||||||
|
@ -21,19 +21,29 @@
|
||||||
"debug": "^4.1.1",
|
"debug": "^4.1.1",
|
||||||
"ejs": "^3.1.3",
|
"ejs": "^3.1.3",
|
||||||
"fast-safe-stringify": "^2.0.6",
|
"fast-safe-stringify": "^2.0.6",
|
||||||
|
"http-errors": "^1.7.2",
|
||||||
"http-status": "^1.1.2",
|
"http-status": "^1.1.2",
|
||||||
"js2xmlparser": "^4.0.0",
|
"js2xmlparser": "^4.0.0",
|
||||||
"strong-globalize": "^6.0.1"
|
"strong-globalize": "^6.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@commitlint/config-conventional": "^12.1.4",
|
"@commitlint/config-conventional": "^12.1.4",
|
||||||
|
"@loopback/build": "^7.0.0",
|
||||||
|
"@loopback/eslint-config": "^11.0.0",
|
||||||
|
"@types/chai": "^4.2.21",
|
||||||
|
"@types/debug": "^4.1.6",
|
||||||
|
"@types/ejs": "^3.0.7",
|
||||||
"@types/express": "^4.17.13",
|
"@types/express": "^4.17.13",
|
||||||
|
"@types/http-errors": "^1.8.1",
|
||||||
|
"@types/mocha": "^8.2.3",
|
||||||
"chai": "^4.1.2",
|
"chai": "^4.1.2",
|
||||||
"eslint": "^7.0.0",
|
"eslint": "^7.0.0",
|
||||||
"eslint-config-loopback": "^13.1.0",
|
"eslint-plugin-prettier": "^3.4.0",
|
||||||
"express": "^4.16.3",
|
"express": "^4.16.3",
|
||||||
"mocha": "^9.0.2",
|
"mocha": "^9.0.2",
|
||||||
"supertest": "^6.1.4"
|
"supertest": "^6.1.4",
|
||||||
|
"tslib": "^1.14.1",
|
||||||
|
"typescript": "^4.3.5"
|
||||||
},
|
},
|
||||||
"browser": {
|
"browser": {
|
||||||
"strong-error-handler": false
|
"strong-error-handler": false
|
||||||
|
|
|
@ -3,22 +3,19 @@
|
||||||
// This file is licensed under the MIT License.
|
// This file is licensed under the MIT License.
|
||||||
// License text available at https://opensource.org/licenses/MIT
|
// License text available at https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
'use strict';
|
|
||||||
module.exports = cloneAllProperties;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* clone the error properties to the data objects
|
* clone the error properties to the data objects
|
||||||
* [err.name, err.message, err.stack] are not enumerable properties
|
* [err.name, err.message, err.stack] are not enumerable properties
|
||||||
* @param data Object to be altered
|
* @param data Object to be altered
|
||||||
* @param err Error Object
|
* @param err Error Object
|
||||||
*/
|
*/
|
||||||
function cloneAllProperties(data, err) {
|
export function cloneAllProperties(data: object, err: Error) {
|
||||||
data.name = err.name;
|
data['name'] = err.name;
|
||||||
data.message = err.message;
|
data['message'] = err.message;
|
||||||
for (const p in err) {
|
for (const p in err) {
|
||||||
if ((p in data)) continue;
|
if (p in data) continue;
|
||||||
data[p] = err[p];
|
data[p] = err[p];
|
||||||
}
|
}
|
||||||
// stack is appended last to ensure order is the same for response
|
// stack is appended last to ensure order is the same for response
|
||||||
data.stack = err.stack;
|
data['stack'] = err.stack;
|
||||||
}
|
}
|
|
@ -3,15 +3,15 @@
|
||||||
// This file is licensed under the MIT License.
|
// This file is licensed under the MIT License.
|
||||||
// License text available at https://opensource.org/licenses/MIT
|
// License text available at https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
'use strict';
|
import accepts from 'accepts';
|
||||||
const accepts = require('accepts');
|
import debugFactory from 'debug';
|
||||||
const debug = require('debug')('strong-error-handler:http-response');
|
import util from 'util';
|
||||||
const sendJson = require('./send-json');
|
import {sendHtml} from './send-html';
|
||||||
const sendHtml = require('./send-html');
|
import {sendJson} from './send-json';
|
||||||
const sendXml = require('./send-xml');
|
import {sendXml} from './send-xml';
|
||||||
const util = require('util');
|
import {ErrorWriterOptions} from './types';
|
||||||
|
|
||||||
module.exports = negotiateContentProducer;
|
const debug = debugFactory('strong-error-handler:http-response');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles req.accepts and req.query._format and options.defaultType
|
* Handles req.accepts and req.query._format and options.defaultType
|
||||||
|
@ -22,11 +22,18 @@ module.exports = negotiateContentProducer;
|
||||||
* @param {Object} options options of strong-error-handler
|
* @param {Object} options options of strong-error-handler
|
||||||
* @returns {Function} Operation function with signature `fn(res, data)`
|
* @returns {Function} Operation function with signature `fn(res, data)`
|
||||||
*/
|
*/
|
||||||
function negotiateContentProducer(req, logWarning, options) {
|
export function negotiateContentProducer(
|
||||||
|
req,
|
||||||
|
logWarning,
|
||||||
|
options: ErrorWriterOptions,
|
||||||
|
) {
|
||||||
const SUPPORTED_TYPES = [
|
const SUPPORTED_TYPES = [
|
||||||
'application/json', 'json',
|
'application/json',
|
||||||
'text/html', 'html',
|
'json',
|
||||||
'text/xml', 'xml',
|
'text/html',
|
||||||
|
'html',
|
||||||
|
'text/xml',
|
||||||
|
'xml',
|
||||||
];
|
];
|
||||||
|
|
||||||
options = options || {};
|
options = options || {};
|
||||||
|
@ -38,8 +45,12 @@ function negotiateContentProducer(req, logWarning, options) {
|
||||||
debug('Accepting options.defaultType `%s`', options.defaultType);
|
debug('Accepting options.defaultType `%s`', options.defaultType);
|
||||||
defaultType = options.defaultType;
|
defaultType = options.defaultType;
|
||||||
} else {
|
} else {
|
||||||
debug('defaultType: `%s` is not supported, ' +
|
debug(
|
||||||
'falling back to defaultType: `%s`', options.defaultType, defaultType);
|
'defaultType: `%s` is not supported, ' +
|
||||||
|
'falling back to defaultType: `%s`',
|
||||||
|
options.defaultType,
|
||||||
|
defaultType,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,13 +69,15 @@ function negotiateContentProducer(req, logWarning, options) {
|
||||||
|
|
||||||
if (options.negotiateContentType === false) {
|
if (options.negotiateContentType === false) {
|
||||||
if (SUPPORTED_TYPES.indexOf(options.defaultType) > -1) {
|
if (SUPPORTED_TYPES.indexOf(options.defaultType) > -1) {
|
||||||
debug('Forcing options.defaultType `%s`',
|
debug('Forcing options.defaultType `%s`', options.defaultType);
|
||||||
options.defaultType);
|
|
||||||
contentType = options.defaultType;
|
contentType = options.defaultType;
|
||||||
} else {
|
} else {
|
||||||
debug('contentType: `%s` is not supported, ' +
|
debug(
|
||||||
|
'contentType: `%s` is not supported, ' +
|
||||||
'falling back to contentType: `%s`',
|
'falling back to contentType: `%s`',
|
||||||
options.defaultType, contentType);
|
options.defaultType,
|
||||||
|
contentType,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,14 +90,20 @@ function negotiateContentProducer(req, logWarning, options) {
|
||||||
contentType = query._format;
|
contentType = query._format;
|
||||||
} else {
|
} else {
|
||||||
// format passed through query but not supported
|
// format passed through query but not supported
|
||||||
const msg = util.format('Response _format "%s" is not supported' +
|
const msg = util.format(
|
||||||
'used "%s" instead"', query._format, defaultType);
|
'Response _format "%s" is not supported' + 'used "%s" instead"',
|
||||||
|
query._format,
|
||||||
|
defaultType,
|
||||||
|
);
|
||||||
logWarning(msg);
|
logWarning(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
debug('Content-negotiation: req.headers.accept: `%s` Resolved as: `%s`',
|
debug(
|
||||||
req.headers.accept, contentType);
|
'Content-negotiation: req.headers.accept: `%s` Resolved as: `%s`',
|
||||||
|
req.headers.accept,
|
||||||
|
contentType,
|
||||||
|
);
|
||||||
return resolveOperation(contentType);
|
return resolveOperation(contentType);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,14 +3,11 @@
|
||||||
// This file is licensed under the MIT License.
|
// This file is licensed under the MIT License.
|
||||||
// License text available at https://opensource.org/licenses/MIT
|
// License text available at https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
'use strict';
|
import httpStatus from 'http-status';
|
||||||
|
import {cloneAllProperties} from './clone';
|
||||||
|
import {ErrorWriterOptions} from './types';
|
||||||
|
|
||||||
const cloneAllProperties = require('../lib/clone.js');
|
export function buildResponseData(err: Error, options: ErrorWriterOptions) {
|
||||||
const httpStatus = require('http-status');
|
|
||||||
|
|
||||||
module.exports = buildResponseData;
|
|
||||||
|
|
||||||
function buildResponseData(err, options) {
|
|
||||||
// Debugging mode is disabled by default. When turned on (in dev),
|
// Debugging mode is disabled by default. When turned on (in dev),
|
||||||
// all error properties (including) stack traces are sent in the response
|
// all error properties (including) stack traces are sent in the response
|
||||||
const isDebugMode = options.debug;
|
const isDebugMode = options.debug;
|
||||||
|
@ -50,16 +47,15 @@ function serializeArrayOfErrors(errors, options) {
|
||||||
const details = errors.map(e => buildResponseData(e, options));
|
const details = errors.map(e => buildResponseData(e, options));
|
||||||
return {
|
return {
|
||||||
statusCode: 500,
|
statusCode: 500,
|
||||||
message: 'Failed with multiple errors, ' +
|
message:
|
||||||
'see `details` for more information.',
|
'Failed with multiple errors, ' + 'see `details` for more information.',
|
||||||
details: details,
|
details: details,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function fillStatusCode(data, err) {
|
function fillStatusCode(data, err) {
|
||||||
data.statusCode = err.statusCode || err.status;
|
data.statusCode = err.statusCode || err.status;
|
||||||
if (!data.statusCode || data.statusCode < 400)
|
if (!data.statusCode || data.statusCode < 400) data.statusCode = 500;
|
||||||
data.statusCode = 500;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function fillDebugData(data, err) {
|
function fillDebugData(data, err) {
|
|
@ -3,18 +3,24 @@
|
||||||
// This file is licensed under the MIT License.
|
// This file is licensed under the MIT License.
|
||||||
// License text available at https://opensource.org/licenses/MIT
|
// License text available at https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
'use strict';
|
import debugFactory from 'debug';
|
||||||
|
import type Express from 'express';
|
||||||
|
import {HttpError, isHttpError} from 'http-errors';
|
||||||
|
import path from 'path';
|
||||||
|
import SG from 'strong-globalize';
|
||||||
|
import {negotiateContentProducer} from './content-negotiation';
|
||||||
|
import {buildResponseData} from './data-builder';
|
||||||
|
import {logToConsole} from './logger';
|
||||||
|
import {
|
||||||
|
ErrorHandlerOptions,
|
||||||
|
ErrorWriterOptions,
|
||||||
|
StrongErrorHandler,
|
||||||
|
} from './types';
|
||||||
|
|
||||||
const path = require('path');
|
|
||||||
const SG = require('strong-globalize');
|
|
||||||
SG.SetRootDir(path.resolve(__dirname, '..'));
|
SG.SetRootDir(path.resolve(__dirname, '..'));
|
||||||
const buildResponseData = require('./data-builder');
|
const debug = debugFactory('strong-error-handler');
|
||||||
const debug = require('debug')('strong-error-handler');
|
|
||||||
const logToConsole = require('./logger');
|
|
||||||
const negotiateContentProducer = require('./content-negotiation');
|
|
||||||
|
|
||||||
function noop() {
|
function noop() {}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a middleware error handler function.
|
* Create a middleware error handler function.
|
||||||
|
@ -22,15 +28,20 @@ function noop() {
|
||||||
* @param {Object} options
|
* @param {Object} options
|
||||||
* @returns {Function}
|
* @returns {Function}
|
||||||
*/
|
*/
|
||||||
function createStrongErrorHandler(options) {
|
export function createStrongErrorHandler(
|
||||||
options = options || {};
|
options: ErrorHandlerOptions = {},
|
||||||
|
): StrongErrorHandler {
|
||||||
debug('Initializing with options %j', options);
|
debug('Initializing with options %j', options);
|
||||||
|
|
||||||
// Log all errors via console.error (enabled by default)
|
// Log all errors via console.error (enabled by default)
|
||||||
const logError = options.log !== false ? logToConsole : noop;
|
const logError = options.log !== false ? logToConsole : noop;
|
||||||
|
|
||||||
return function strongErrorHandler(err, req, res, next) {
|
return function strongErrorHandler(
|
||||||
|
err: Error,
|
||||||
|
req: Express.Request,
|
||||||
|
res: Express.Response,
|
||||||
|
next: (err: unknown) => void,
|
||||||
|
) {
|
||||||
logError(req, err);
|
logError(req, err);
|
||||||
writeErrorToResponse(err, req, res, options);
|
writeErrorToResponse(err, req, res, options);
|
||||||
};
|
};
|
||||||
|
@ -39,24 +50,27 @@ function createStrongErrorHandler(options) {
|
||||||
/**
|
/**
|
||||||
* Writes thrown error to response
|
* Writes thrown error to response
|
||||||
*
|
*
|
||||||
* @param {Error} err
|
* @param err
|
||||||
* @param {Express.Request} req
|
* @param req
|
||||||
* @param {Express.Response} res
|
* @param res
|
||||||
* @param {Object} options
|
* @param options
|
||||||
*/
|
*/
|
||||||
function writeErrorToResponse(err, req, res, options) {
|
function writeErrorToResponse(
|
||||||
|
err: Error | HttpError,
|
||||||
|
req: Express.Request,
|
||||||
|
res: Express.Response,
|
||||||
|
options: ErrorWriterOptions = {},
|
||||||
|
) {
|
||||||
debug('Handling %s', err.stack || err);
|
debug('Handling %s', err.stack || err);
|
||||||
|
|
||||||
options = options || {};
|
|
||||||
|
|
||||||
if (res.headersSent) {
|
if (res.headersSent) {
|
||||||
debug('Response was already sent, closing the underlying connection');
|
debug('Response was already sent, closing the underlying connection');
|
||||||
return req.socket.destroy();
|
return req.socket.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
// this will alter the err object, to handle when res.statusCode is an error
|
// this will alter the err object, to handle when res.statusCode is an error
|
||||||
if (!err.status && !err.statusCode && res.statusCode >= 400)
|
if (!isHttpError(err) && res.statusCode >= 400)
|
||||||
err.statusCode = res.statusCode;
|
(err as Partial<HttpError> & Error).statusCode = res.statusCode;
|
||||||
|
|
||||||
const data = buildResponseData(err, options);
|
const data = buildResponseData(err, options);
|
||||||
debug('Response status %s data %j', data.statusCode, data);
|
debug('Response status %s data %j', data.statusCode, data);
|
||||||
|
@ -67,11 +81,11 @@ function writeErrorToResponse(err, req, res, options) {
|
||||||
const sendResponse = negotiateContentProducer(req, warn, options);
|
const sendResponse = negotiateContentProducer(req, warn, options);
|
||||||
sendResponse(res, data, options);
|
sendResponse(res, data, options);
|
||||||
|
|
||||||
function warn(msg) {
|
function warn(msg: string) {
|
||||||
res.header('X-Warning', msg);
|
res.header('X-Warning', msg);
|
||||||
debug(msg);
|
debug(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports = module.exports = createStrongErrorHandler;
|
module.exports = createStrongErrorHandler;
|
||||||
exports.writeErrorToResponse = writeErrorToResponse;
|
module.exports.writeErrorToResponse = writeErrorToResponse;
|
|
@ -3,23 +3,25 @@
|
||||||
// This file is licensed under the MIT License.
|
// This file is licensed under the MIT License.
|
||||||
// License text available at https://opensource.org/licenses/MIT
|
// License text available at https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
'use strict';
|
import type Express from 'express';
|
||||||
|
import SG from 'strong-globalize';
|
||||||
|
import {format} from 'util';
|
||||||
|
const g = new SG();
|
||||||
|
|
||||||
const format = require('util').format;
|
export function logToConsole(req: Express.Request, err: Error) {
|
||||||
const g = require('strong-globalize')();
|
|
||||||
|
|
||||||
module.exports = function logToConsole(req, err) {
|
|
||||||
if (!Array.isArray(err)) {
|
if (!Array.isArray(err)) {
|
||||||
g.error('Request %s %s failed: %s',
|
g.error('Request %s %s failed: %s', req.method, req.url, err.stack || err);
|
||||||
req.method, req.url, err.stack || err);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const errMsg = g.f('Request %s %s failed with multiple errors:\n',
|
const errMsg = g.f(
|
||||||
req.method, req.url);
|
'Request %s %s failed with multiple errors:\n',
|
||||||
|
req.method,
|
||||||
|
req.url,
|
||||||
|
);
|
||||||
const errors = err.map(formatError).join('\n');
|
const errors = err.map(formatError).join('\n');
|
||||||
console.error(errMsg, errors);
|
console.error(errMsg, errors);
|
||||||
};
|
}
|
||||||
|
|
||||||
function formatError(err) {
|
function formatError(err) {
|
||||||
return format('%s', err.stack || err);
|
return format('%s', err.stack || err);
|
|
@ -3,10 +3,10 @@
|
||||||
// This file is licensed under the MIT License.
|
// This file is licensed under the MIT License.
|
||||||
// License text available at https://opensource.org/licenses/MIT
|
// License text available at https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
'use strict';
|
import ejs from 'ejs';
|
||||||
const ejs = require('ejs');
|
import type Express from 'express';
|
||||||
const fs = require('fs');
|
import fs from 'fs';
|
||||||
const path = require('path');
|
import path from 'path';
|
||||||
|
|
||||||
const assetDir = path.resolve(__dirname, '../views');
|
const assetDir = path.resolve(__dirname, '../views');
|
||||||
const compiledTemplates = {
|
const compiledTemplates = {
|
||||||
|
@ -14,9 +14,11 @@ const compiledTemplates = {
|
||||||
default: loadDefaultTemplates(),
|
default: loadDefaultTemplates(),
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = sendHtml;
|
export function sendHtml(
|
||||||
|
res: Express.Response,
|
||||||
function sendHtml(res, data, options) {
|
data: ejs.Data['data'],
|
||||||
|
options: ejs.Data['options'],
|
||||||
|
) {
|
||||||
const toRender = {options, data};
|
const toRender = {options, data};
|
||||||
// TODO: ability to call non-default template functions from options
|
// TODO: ability to call non-default template functions from options
|
||||||
const body = compiledTemplates.default(toRender);
|
const body = compiledTemplates.default(toRender);
|
||||||
|
@ -27,9 +29,9 @@ function sendHtml(res, data, options) {
|
||||||
* Compile and cache the file with the `filename` key in options
|
* Compile and cache the file with the `filename` key in options
|
||||||
*
|
*
|
||||||
* @param filepath (description)
|
* @param filepath (description)
|
||||||
* @returns {Function} render function with signature fn(data);
|
* @returns Render function with signature fn(data);
|
||||||
*/
|
*/
|
||||||
function compileTemplate(filepath) {
|
function compileTemplate(filepath: string): ejs.TemplateFunction {
|
||||||
const options = {cache: true, filename: filepath};
|
const options = {cache: true, filename: filepath};
|
||||||
const fileContent = fs.readFileSync(filepath, 'utf8');
|
const fileContent = fs.readFileSync(filepath, 'utf8');
|
||||||
return ejs.compile(fileContent, options);
|
return ejs.compile(fileContent, options);
|
||||||
|
@ -41,7 +43,7 @@ function loadDefaultTemplates() {
|
||||||
return compileTemplate(defaultTemplate);
|
return compileTemplate(defaultTemplate);
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendResponse(res, body) {
|
function sendResponse(res: Express.Response, body: string) {
|
||||||
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
||||||
res.end(body);
|
res.end(body);
|
||||||
}
|
}
|
|
@ -3,18 +3,20 @@
|
||||||
// This file is licensed under the MIT License.
|
// This file is licensed under the MIT License.
|
||||||
// License text available at https://opensource.org/licenses/MIT
|
// License text available at https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
'use strict';
|
import type Express from 'express';
|
||||||
|
import safeStringify from 'fast-safe-stringify';
|
||||||
|
|
||||||
const safeStringify = require('fast-safe-stringify');
|
export function sendJson(res: Express.Response, data, options) {
|
||||||
|
|
||||||
module.exports = function sendJson(res, data, options) {
|
|
||||||
options = options || {};
|
options = options || {};
|
||||||
// Set `options.rootProperty` to not wrap the data into an `error` object
|
// Set `options.rootProperty` to not wrap the data into an `error` object
|
||||||
const err = options.rootProperty === false ? data : {
|
const err =
|
||||||
|
options.rootProperty === false
|
||||||
|
? data
|
||||||
|
: {
|
||||||
// Use `options.rootProperty`, if not set, default to `error`
|
// Use `options.rootProperty`, if not set, default to `error`
|
||||||
[options.rootProperty || 'error']: data,
|
[options.rootProperty || 'error']: data,
|
||||||
};
|
};
|
||||||
const content = safeStringify(err);
|
const content = safeStringify(err);
|
||||||
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
||||||
res.end(content, 'utf-8');
|
res.end(content, 'utf-8');
|
||||||
};
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
import type Express from 'express';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options for writing errors to the response
|
||||||
|
*/
|
||||||
|
export interface ErrorWriterOptions {
|
||||||
|
debug?: boolean;
|
||||||
|
safeFields?: string[];
|
||||||
|
defaultType?: string;
|
||||||
|
negotiateContentType?: boolean;
|
||||||
|
rootProperty?: string | false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options for error-handling
|
||||||
|
*/
|
||||||
|
export interface ErrorHandlerOptions extends ErrorWriterOptions {
|
||||||
|
log?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error-handling middleware function. Includes server-side logging
|
||||||
|
*/
|
||||||
|
export type StrongErrorHandler = (
|
||||||
|
err: Error,
|
||||||
|
req: Express.Request,
|
||||||
|
res: Express.Response,
|
||||||
|
next: (err?: unknown) => void,
|
||||||
|
) => void;
|
|
@ -3,8 +3,6 @@
|
||||||
// This file is licensed under the MIT License.
|
// This file is licensed under the MIT License.
|
||||||
// License text available at https://opensource.org/licenses/MIT
|
// License text available at https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const cloneAllProperties = require('../lib/clone.js');
|
const cloneAllProperties = require('../lib/clone.js');
|
||||||
const debug = require('debug')('test');
|
const debug = require('debug')('test');
|
||||||
const expect = require('chai').expect;
|
const expect = require('chai').expect;
|
||||||
|
@ -177,7 +175,7 @@ describe('strong-error-handler', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const _consoleError = console.error;
|
const consoleError = console.error;
|
||||||
function redirectConsoleError() {
|
function redirectConsoleError() {
|
||||||
logs = [];
|
logs = [];
|
||||||
console.error = function() {
|
console.error = function() {
|
||||||
|
@ -187,7 +185,7 @@ describe('strong-error-handler', function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function restoreConsoleError() {
|
function restoreConsoleError() {
|
||||||
console.error = _consoleError;
|
console.error = consoleError;
|
||||||
logs = [];
|
logs = [];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"__comments__": [
|
||||||
|
"tsconfig.build.json is used by eslint to constrain files to be checked",
|
||||||
|
"tsconfig.json is used by tsc"
|
||||||
|
],
|
||||||
|
"compilerOptions": {
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"noEmit": true
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"**/.eslintrc.js",
|
||||||
|
"**/*.js",
|
||||||
|
"**/*.ts"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"**/node_modules/**",
|
||||||
|
"**/dist/**",
|
||||||
|
"**/*.d.ts",
|
||||||
|
"coverage"
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"$schema": "http://json.schemastore.org/tsconfig",
|
||||||
|
"extends": "@loopback/build/config/tsconfig.common.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "dist",
|
||||||
|
"rootDir": "src"
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src"
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in New Issue