Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More β†’
Socket
Sign inDemoInstall
Socket

hapi-dev-errors

Package Overview
Dependencies
Maintainers
2
Versions
24
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

hapi-dev-errors - npm Package Compare versions

Comparing version 3.2.0 to 3.2.1

lib/error-handler.js

2

.eslintrc.json

@@ -8,4 +8,4 @@ {

"parserOptions": {
"ecmaVersion": 8
"ecmaVersion": 2018
}
}
# Changelog
## Version [3.2.1](https://github.com/fs-opensource/hapi-dev-errors/compare/v3.2.0...v3.2.1) (2019-01-22)
### Updated
- use `request.path` over `request.url.path` to support hapi 18
- refactor plugin: error handler class and plugin entry point
- split plugin handling and extension point registration from request handling
- move icons (Google, Stack Overflow) to SVG files
- refine texts in Readme
- refactor code in examples
- bump dependencies
- update tests to use `it` over `test`
## Version [3.2.0](https://github.com/fs-opensource/hapi-dev-errors/compare/v3.1.0...v3.2.0) (2018-10-10)
- `add` new option [`links`](https://github.com/fs-opensource/hapi-dev-errors#plugin-registration-options) which represents an array of callback functions to render helpful links. By default, `hapi-dev-errors` renders linked SVG icons for Google and Stack Overflow to search for help based on the error message
## Version [3.1.0](https://github.com/fs-opensource/hapi-dev-errors/compare/v3.0.1...v3.1.0) (2018-09-28)

@@ -7,0 +21,0 @@ - `add` pass `request` to custom view: this allows you to access every request property

@@ -5,8 +5,3 @@ 'use strict'

// create new server instance
// add server’s connection information
const server = new Hapi.Server({
host: 'localhost',
port: 3000
})
const server = new Hapi.Server({ host: 'localhost', port: 3000 })

@@ -22,5 +17,5 @@ async function launchIt () {

server.route({
method: 'GET',
method: '*',
path: '/{path*}',
handler: (request, h) => {
handler: (_, h) => {
return h.notAvailable()

@@ -30,10 +25,6 @@ }

try {
await server.start()
console.log('Server running at: ' + server.info.uri)
} catch (err) {
throw err
}
await server.start()
console.log('Server running at: ' + server.info.uri)
}
launchIt()

@@ -6,8 +6,3 @@ 'use strict'

// create new server instance
// add server’s connection information
const server = new Hapi.Server({
host: 'localhost',
port: 3000
})
const server = new Hapi.Server({ host: 'localhost', port: 3000 })

@@ -38,5 +33,5 @@ async function launchIt () {

server.route({
method: 'GET',
method: '*',
path: '/{path*}',
handler: (request, reply) => {
handler: (_, reply) => {
reply.notAvailable()

@@ -46,10 +41,6 @@ }

try {
await server.start()
console.log('Server running at: ' + server.info.uri)
} catch (err) {
throw err
}
await server.start()
console.log('Server running at: ' + server.info.uri)
}
launchIt()

@@ -5,8 +5,3 @@ 'use strict'

// create new server instance
// add server’s connection information
const server = new Hapi.Server({
host: 'localhost',
port: 3000
})
const server = new Hapi.Server({ host: 'localhost', port: 3000 })

@@ -22,3 +17,3 @@ async function launchIt () {

return `<a rel="noopener noreferrer" target="_blank" href="https://github.com/fs-opensource/hapi-dev-errors/search?q=${error.message}">
Search Youch on GitHub
Search hapi-dev-errors on GitHub
</a>`

@@ -33,3 +28,3 @@ }

path: '/{path*}',
handler: (request, h) => {
handler: (_, h) => {
h.notAvailable()

@@ -39,10 +34,6 @@ }

try {
await server.start()
console.log('Server running at: ' + server.info.uri)
} catch (err) {
throw err
}
await server.start()
console.log('Server running at: ' + server.info.uri)
}
launchIt()
'use strict'
const Youch = require('youch')
const ForTerminal = require('youch-terminal')
const ErrorHandler = require('./error-handler')
/**
* Create a Youch instance for pretty error printing.
* This instance is used to format output for the
* console and for a web view.
*
* @param {Object} request - the request object
* @param {Object} error - object with error details
*
* @returns {Object}
*/
function createYouch ({ request, error, links = [] }) {
/**
* hapi’s request and error objects don’t match the
* expected structure in Youch. We need to adjust
* properties to display them correctly.
*/
request.url = request.path
request.httpVersion = request.raw.req.httpVersion
error.status = error.output.statusCode
try {
const youch = new Youch(error, request)
links.forEach(link => youch.addLink(link))
return youch
} catch (error) {
console.error(error)
throw error
}
}
/**
* Check whether the incoming request requires a JSON response.
* This is true for requests where the "accept" header
* contains "json" or the agent is a CLI/GUI app.
*
* @param {Object}
*
* @returns {Boolean}
*/
function wantsJson ({ agent, accept }) {
return matches(agent, /curl|wget|postman|insomnia/i) || matches(accept, /json/)
}
/**
* Helper function to test whether a given
* string matches a RegEx.
*
* @param {String} str
* @param {String} regex
*
* @returns {Boolean}
*/
function matches (str, regex) {
return str && str.match(regex)
}
/**
* Returns a link to Google that includes
* the error message as the search
* term. The link is an SVG icon.
*
* @param {Object} error
*
* @returns {String}
*/
function googleIcon (error) {
return `<a rel="noopener noreferrer" target="_blank" href="https://google.com/search?q=${encodeURIComponent(error.message)}" title="Search Google for &quot;${error.message}&quot;">
<!-- Google icon by Picons.me, found at https://www.iconfinder.com/Picons -->
<!-- Free for commercial use -->
<svg width="24" height="24" viewBox="0 0 56.6934 56.6934" xmlns="http://www.w3.org/2000/svg">
<path d="M51.981,24.4812c-7.7173-0.0038-15.4346-0.0019-23.1518-0.001c0.001,3.2009-0.0038,6.4018,0.0019,9.6017 c4.4693-0.001,8.9386-0.0019,13.407,0c-0.5179,3.0673-2.3408,5.8723-4.9258,7.5991c-1.625,1.0926-3.492,1.8018-5.4168,2.139 c-1.9372,0.3306-3.9389,0.3729-5.8713-0.0183c-1.9651-0.3921-3.8409-1.2108-5.4773-2.3649 c-2.6166-1.8383-4.6135-4.5279-5.6388-7.5549c-1.0484-3.0788-1.0561-6.5046,0.0048-9.5805 c0.7361-2.1679,1.9613-4.1705,3.5708-5.8002c1.9853-2.0324,4.5664-3.4853,7.3473-4.0811c2.3812-0.5083,4.8921-0.4113,7.2234,0.294 c1.9815,0.6016,3.8082,1.6874,5.3044,3.1163c1.5125-1.5039,3.0173-3.0164,4.527-4.5231c0.7918-0.811,1.624-1.5865,2.3908-2.4196 c-2.2928-2.1218-4.9805-3.8274-7.9172-4.9056C32.0723,4.0363,26.1097,3.995,20.7871,5.8372 C14.7889,7.8907,9.6815,12.3763,6.8497,18.0459c-0.9859,1.9536-1.7057,4.0388-2.1381,6.1836 C3.6238,29.5732,4.382,35.2707,6.8468,40.1378c1.6019,3.1768,3.8985,6.001,6.6843,8.215c2.6282,2.0958,5.6916,3.6439,8.9396,4.5078 c4.0984,1.0993,8.461,1.0743,12.5864,0.1355c3.7284-0.8581,7.256-2.6397,10.0725-5.24c2.977-2.7358,5.1006-6.3403,6.2249-10.2138 C52.5807,33.3171,52.7498,28.8064,51.981,24.4812z"/>
</svg>
</a>`
}
/**
* Returns a link to Stack Overflow that
* includes the error message as the
* search term. The link is an SVG icon.
*
* @param {Object} error
*
* @returns {String}
*/
function stackOverflowIcon (error) {
return `<a rel="noopener noreferrer" target="_blank" href="https://stackoverflow.com/search?q=${encodeURIComponent(error.message)}" title="Search Stack Overflow for &quot;${error.message}&quot;">
<!-- Stack Overflow icon by Picons.me, found at https://www.iconfinder.com/Picons -->
<!-- Free for commercial use -->
<svg width="24" height="24" viewBox="-1163 1657.697 56.693 56.693" xmlns="http://www.w3.org/2000/svg">
<rect height="4.1104" transform="matrix(-0.8613 -0.508 0.508 -0.8613 -2964.1831 2556.6357)" width="19.2465" x="-1142.8167" y="1680.7778"/><rect height="4.1105" transform="matrix(-0.9657 -0.2596 0.2596 -0.9657 -2672.0498 3027.386)" width="19.2462" x="-1145.7363" y="1688.085"/><rect height="4.1098" transform="matrix(-0.9958 -0.0918 0.0918 -0.9958 -2425.5647 3282.8535)" width="19.246" x="-1146.9451" y="1695.1263"/><rect height="4.111" width="19.2473" x="-1147.2625" y="1701.293"/><path d="M-1121.4579,1710.9474c0,0,0,0.9601-0.0323,0.9601v0.0156h-30.7953c0,0-0.9598,0-0.9598-0.0156h-0.0326v-20.03h3.2877 v16.8049h25.2446v-16.8049h3.2877V1710.9474z"/><rect height="4.111" transform="matrix(0.5634 0.8262 -0.8262 0.5634 892.9033 1662.7915)" width="19.247" x="-1136.5389" y="1674.2235"/><rect height="4.1108" transform="matrix(0.171 0.9853 -0.9853 0.171 720.9987 2489.031)" width="19.2461" x="-1128.3032" y="1670.9347"/>
</svg>
</a>`
}
/**
* Render better error views during development.
*
* @param {Object} server - hapi server instance where the plugin is registered
* @param {Object} options - plugin options
*/
async function register (server, options) {
const defaults = {
showErrors: false,
toTerminal: true,
links: [
(error) => googleIcon(error),
(error) => stackOverflowIcon(error)
]
}
const { showErrors = false, template, ...config } = options
const config = Object.assign({}, defaults, options)
/**
* Cut early if `showErrors` is false. No need to
* hook the extension point in production.
* Cut early and don’t register extension point,
* e.g. when in production.
*/
if (!config.showErrors) {
if (!showErrors) {
return
}
// require `vision` plugin in case the user provides an error template
if (config.template) {
/**
* Ensure the user server is able to render
* templates when going for a custom one.
*/
if (template) {
server.dependency(['vision'])
}
// Make sure the `links` are an array
if (!Array.isArray(config.links)) {
config.links = [config.links]
}
/**
* Alright, go for gold!
*/
const errorHandler = new ErrorHandler({ template, ...config })
// extend the request lifecycle at `onPreResponse`
// to change the default error handling behavior (if enabled)
server.ext('onPreResponse', async (request, h) => {
const error = request.response
// only show `bad implementation` developer errors (status code 500)
if (error.isBoom && error.output.statusCode === 500) {
const accept = request.raw.req.headers.accept
const agent = request.raw.req.headers['user-agent']
const statusCode = error.output.statusCode
const errorResponse = {
title: error.output.payload.error,
statusCode,
message: error.message,
method: request.raw.req.method,
url: request.url.path,
headers: request.raw.req.headers,
payload: request.raw.req.method !== 'GET' ? request.payload : '',
stacktrace: error.stack
}
const youch = createYouch({ request, error, links: config.links })
// print a pretty error to terminal as well
if (config.toTerminal) {
const json = await youch.toJSON()
console.log(ForTerminal(json))
}
// take priority:
// - check "agent" header for REST request (cURL, Postman & Co.)
// - check "accept" header for JSON request
if (wantsJson({ accept, agent })) {
const details = Object.assign({}, errorResponse, {
stacktrace: errorResponse.stacktrace.split('\n').map(line => line.trim())
})
return h
.response(JSON.stringify(details, null, 2))
.type('application/json')
.code(statusCode)
}
// did the user explicitly specify an error template
// favor a user’s custom template over the default template
if (config.template) {
return h
.view(config.template, { request, error, ...errorResponse })
.code(statusCode)
}
// render Youch HTML template
const html = await youch.toHTML()
return h
.response(html)
.type('text/html')
.code(statusCode)
}
// no developer error, go ahead with the response
return h.continue
return errorHandler.handle(request, h)
})

@@ -202,0 +32,0 @@ }

{
"name": "hapi-dev-errors",
"description": "Return better error details and skip the look at command line to catch the issue.",
"version": "3.2.0",
"version": "3.2.1",
"author": "Future Studio <info@futurestud.io>",

@@ -14,19 +14,19 @@ "bugs": {

"devDependencies": {
"boom": "~7.2.0",
"code": "~5.2.0",
"eslint": "~4.19.1",
"eslint-config-standard": "~11.0.0",
"eslint-plugin-import": "~2.13.0",
"eslint-plugin-node": "~6.0.1",
"eslint-plugin-promise": "~3.8.0",
"eslint-plugin-standard": "~3.1.0",
"hapi": "~17.6.0",
"husky": "~1.1.1",
"joi": "~13.7.0",
"lab": "~15.5.0",
"sinon": "~6.3.5",
"vision": "~5.4.0"
"boom": "~7.3.0",
"code": "~5.2.4",
"eslint": "~5.12.1",
"eslint-config-standard": "~12.0.0",
"eslint-plugin-import": "~2.14.0",
"eslint-plugin-node": "~8.0.1",
"eslint-plugin-promise": "~4.0.1",
"eslint-plugin-standard": "~4.0.0",
"hapi": "~18.0.0",
"husky": "~1.3.1",
"joi": "~14.3.1",
"lab": "~18.0.1",
"sinon": "~7.2.2",
"vision": "~5.4.4"
},
"engines": {
"node": ">=8.0.0"
"node": ">=8"
},

@@ -33,0 +33,0 @@ "homepage": "https://github.com/fs-opensource/hapi-dev-errors#readme",

@@ -20,2 +20,3 @@ <div align="center">

<a href="https://www.npmjs.com/package/hapi-dev-errors"><img src="https://img.shields.io/npm/v/hapi-dev-errors.svg" alt="hapi-dev-errors Version" data-canonical-src="https://img.shields.io/npm/v/hapi-dev-errors.svg" style="max-width:100%;"></a>
<a href="https://greenkeeper.io/" rel="nofollow"><img src="https://camo.githubusercontent.com/dfb11cc7fc0b1600f0ba93236eff58bb592b8500/68747470733a2f2f6261646765732e677265656e6b65657065722e696f2f66732d6f70656e736f757263652f686170692d6465762d6572726f72732e737667" alt="Greenkeeper badge" data-canonical-src="https://badges.greenkeeper.io/fs-opensource/hapi-dev-errors.svg" style="max-width:100%;"></a>
</p>

@@ -117,5 +118,6 @@ <p>

links: [ (error) => {
return `<a href="https://github.com/fs-opensource/hapi-dev-errors/search?q=${error.message}">
Search Youch on GitHub
</a>`
return `
<a href="https://github.com/fs-opensource/hapi-dev-errors/search?q=${error.message}">
Search hapi-dev-errors on GitHub
</a>`
}

@@ -122,0 +124,0 @@ ]

@@ -9,3 +9,3 @@ 'use strict'

const { experiment, test, before } = (exports.lab = Lab.script())
const { experiment, it, before } = (exports.lab = Lab.script())

@@ -36,3 +36,3 @@ experiment('hapi-dev-error falls back to json', () => {

test('test if the plugin responds json with json accept header', async () => {
it('responds json with json accept header', async () => {
const response = await server.inject({

@@ -51,3 +51,3 @@ url: '/error',

test('test if the plugin responds json with curl user-agent', async () => {
it('responds json with curl user-agent', async () => {
const response = await server.inject({

@@ -54,0 +54,0 @@ url: '/error',

@@ -6,2 +6,3 @@ 'use strict'

const Hapi = require('hapi')
const Boom = require('boom')

@@ -24,5 +25,3 @@ const server = new Hapi.Server()

method: 'GET',
handler: () => {
return new Error('failure')
}
handler: () => new Error('failure')
}

@@ -44,2 +43,25 @@

})
test('does not render the gorgeous error view for 503 errors', async () => {
const routeOptions = {
path: '/503-unhandled',
method: 'GET',
handler: () => Boom.serverUnavailable('not ready')
}
server.route(routeOptions)
const request = {
url: routeOptions.path,
method: routeOptions.method
}
const response = await server.inject(request)
const payload = JSON.parse(response.payload || '{}')
Code.expect(response.statusCode).to.equal(503)
Code.expect(payload.message).to.equal('not ready')
Code.expect(payload.error).to.equal('Service Unavailable')
})
})

@@ -11,3 +11,3 @@ 'use strict'

const { experiment, test, before, beforeEach } = (exports.lab = Lab.script())
const { experiment, it, before, beforeEach } = (exports.lab = Lab.script())
const expect = Code.expect

@@ -30,10 +30,7 @@

test('test if the plugin is enabled in development for web requests', async () => {
it('is enabled in development for web requests', async () => {
const routeOptions = {
path: '/showErrorsForWeb',
method: 'GET',
handler: () => {
return Boom.badImplementation('a fancy server error')
}
}
handler: () => Boom.badImplementation('a fancy server error') }

@@ -54,3 +51,3 @@ server.route(routeOptions)

test('test if the plugin is enabled in development for JSON/REST requests', async () => {
it('is enabled in development for JSON/REST requests', async () => {
const routeOptions = {

@@ -83,9 +80,7 @@ path: '/showErrorsForREST',

test('test when the error is from a rejected Promise', async () => {
it('test when the error is from a rejected Promise', async () => {
const routeOptions = {
path: '/showPromiseError',
method: 'GET',
handler: () => {
return Promise.reject(new Error('server error'))
}
handler: () => new Error('server error')
}

@@ -112,9 +107,7 @@

test('test if the request payload is added to the error response', async () => {
it('test if the request payload is added to the error response', async () => {
const routeOptions = {
path: '/with-request-payload',
method: 'POST',
handler: () => {
return Promise.reject(new Error('server error with payload'))
}
handler: () => new Error('server error with payload')
}

@@ -175,3 +168,3 @@

test('render a template', async () => {
it('render a template', async () => {
const routeOptions = {

@@ -178,0 +171,0 @@ path: '/custom-view',

@@ -6,6 +6,7 @@ 'use strict'

const Hapi = require('hapi')
const Boom = require('boom')
let server
const { experiment, test, before } = (exports.lab = Lab.script())
const { experiment, it, before } = (exports.lab = Lab.script())
const expect = Code.expect

@@ -26,9 +27,7 @@

test('test if the plugin skips handling for non-error response', async () => {
it('skips handling for non-error response', async () => {
const routeOptions = {
path: '/ok',
method: 'GET',
handler: () => {
return 'ok'
}
handler: () => 'ok'
}

@@ -49,2 +48,20 @@

})
it('skips handling for 404 errors', async () => {
const routeOptions = {
path: '/404',
method: 'GET',
handler: () => Boom.notFound()
}
server.route(routeOptions)
const request = {
url: routeOptions.path,
method: routeOptions.method
}
const response = await server.inject(request)
expect(response.statusCode).to.equal(404)
})
})

@@ -8,3 +8,3 @@ 'use strict'

const { experiment, test, beforeEach, afterEach } = (exports.lab = Lab.script())
const { experiment, it, beforeEach, afterEach } = (exports.lab = Lab.script())

@@ -43,3 +43,3 @@ experiment('hapi-dev-error handles custom user links', () => {

test('that the plugin works fine with empty links', async () => {
it('works fine with empty links', async () => {
const server = await createServer({ links: [] })

@@ -58,3 +58,3 @@

test('that the plugin throws if the links are strings', async () => {
it('throws if the links are strings', async () => {
const server = await createServer({ links: [ 'error' ] })

@@ -74,3 +74,3 @@

test('that the plugin throws if the links is not an array of functions', async () => {
it('throws if the links is not an array of functions', async () => {
const server = await createServer({ links: 'error' })

@@ -90,3 +90,3 @@

test('that the plugin works fine with a link function', async () => {
it('works fine with a link function', async () => {
const server = await createServer({ links: () => `link` })

@@ -93,0 +93,0 @@

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚑️ by Socket Inc