Error handling

pkgman = require 'pkgman'

TransmittableError

Extend this class if you'd like to implement an error.

exports.TransmittableError = class TransmittableError extends Error

TransmittableError#constructor

See: https://github.com/jashkenas/coffee-script/issues/2359

  constructor: (@message) ->

TransmittableError#errorType

A unique key for this error.

  errorType: 'unknown'

TransmittableError#template

The template used to format the error output.

  template: 'Unknown error: :message'

TransmittableError#toJSON

Implement this if you need to transmit more than just the error type and the message. Truss uses the result from this function to serialize the error over the wire.

  toJSON: -> [@errorType, @message]

errors.instantiate

  • (string) errorType - The error type.

  • (any) args... - Additional arguments to pass to the error type's

constructor. Instantiate an error based on error type, passing along args to the error's constructor.

exports.instantiate = (errorType, args...) ->

Look up the error type and use it. If it's not registered, fall back to the TransmittableError superclass.

  Types = exports.transmittableErrors()
  Type = if Types[errorType]? then Types[errorType] else TransmittableError

Trickery to be able to essentially call new with Function::apply.

  IType = do (Type) ->
    F = (args) -> Type.apply this, args
    F.prototype = Type.prototype
    (args) -> new F args

  error = IType args

Throw so we have a (possibly) meaningful stack.

  try
    throw new Error()
  catch stackError
    stackError.message = exports.message error
    error.stack = stackError.stack

  error

errors.message

  • (Error) error - The error object.

Extract an error message from an error.

exports.message = (error) ->

One of us! One of us!

  output = if error instanceof TransmittableError
    error.template

Abstract Error.

  else if error instanceof Error
    TransmittableError::template.replace ':message', error.message

Not an instance of Error. This probably shouldn't happen, but we deal with it anyway.

  else
    TransmittableError::template.replace ':message', error.toString()

Replace placeholders in the template.

  output = output.replace ":#{key}", value for key, value of error
  output

errors.serialize

  • (Error) error - The error object.

Serialize an error to send over the wire.

exports.serialize = (error) ->

One of us! One of us!

  if error instanceof TransmittableError
    error.toJSON()

Abstract Error.

  else if error instanceof Error
    [undefined, error.message]

Not an instance of Error. This probably shouldn't happen, but we deal with it anyway.

  else
    [undefined, error]

errors.stack

  • (Error) error - The error object.

Extract the stack trace from an error.

exports.stack = (error) ->

Does the stack trace exist?

  formatStack = if (formatStack = error.stack)?

If so, shift off the first line (the message).

    formatStack = formatStack.split '\n'
    formatStack.shift()
    '\n' + formatStack.join '\n'

Otherwise, we don't have much to work with...

  else
    ''

Prepend our pretty formatted message before the stack trace.

  "#{@message error}#{formatStack}"

errors.transmittableErrors

Collect the error types implemented by packages.

exports.transmittableErrors = ->

  _ = require 'lodash'

Invoke hook trussTransmittableErrors

Allows packages to specify transmittable errors. Implementations should return a subclass of TransmittableError.

  Types = {}

  Types[Type::errorType] = Type for Type in [TransmittableError].concat(
    _.flatten pkgman.invokeFlat 'trussTransmittableErrors'
  )

  Types

errors.unserialize

Unserialize an error from over the wire.

exports.unserialize = (data) -> exports.instantiate.apply null, data