Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
@skava/graphql / src / deps / intercept.ts
Size: Mime:
import { isFunction } from 'exotic'
import { Request, Response, NextFunction } from 'express'

export interface InterceptFunction {
  (req: Request, res: Response): string[] | any
}
export default function interceptFor(fn: InterceptFunction) {
  const debug = require('@skava/modules/___dist/_forks/debug')(
    'express-interceptor'
  )

  // eslint-disable-next-line
  return function interceptionMiddleware(
    req: Request,
    res: Response,
    next: NextFunction
  ) {
    const methods = fn(req, res)
    _validateParams(methods)

    const originalEnd = res.end
    const originalWrite = res.write
    const chunks = []
    let isFirstWrite = true
    let isIntercepting

    // eslint-disable-next-line
    function intercept(rawChunk, encoding?: string) {
      if (isFirstWrite) {
        isFirstWrite = false
        isIntercepting = methods.isInterceptable()
      }

      debug('isIntercepting? %s', isIntercepting)
      if (isIntercepting) {
        // collect all the parts of a response
        if (rawChunk) {
          let chunk = rawChunk
          if (
            rawChunk !== null &&
            !Buffer.isBuffer(chunk) &&
            encoding !== 'buffer'
          ) {
            if (!encoding) {
              chunk = new Buffer(rawChunk)
            } else {
              chunk = new Buffer(rawChunk, encoding)
            }
          }
          chunks.push(chunk)
        }

        // @todo @hack - very bad scope leak
        if (typeof cb === 'object') {
          cb()
        }
      }

      return isIntercepting
    }

    (res as any).write = function onWrite(chunk, encoding, cb) {
      debug('write called')
      if (!intercept(chunk, encoding)) {
        originalWrite.apply(res, arguments)
      }
    }

    function afterSend(oldBody: string | Buffer, newBody: string | Buffer) {
      if (isFunction(methods.afterSend)) {
        const onTick = () => {
          debug(
            'methods.afterSend running now, body size: %s %s',
            oldBody.length,
            newBody.length
          )
          methods.afterSend(oldBody, newBody)
        }
        process.nextTick(onTick)
      }
    }

    // eslint-disable-next-line
    (res as any).end = function onResEnd(chunk, encoding?: string, cb?: Function) {
      debug('end called')
      const args = Array.prototype.slice.call(arguments)

      if (intercept(chunk, encoding)) {
        isIntercepting = false
        const oldBody = Buffer.concat(chunks).toString('utf-8')

        if (methods.intercept) {
          if (typeof methods.intercept !== 'function') {
            throw new Error(
              '`send` must be a function with the body to be sent as the only param'
            )
          }

          res.removeHeader('Content-Length')
          // allow the user to re-write response
          const onIntercept = newBody => {
            args[0] = newBody
            originalEnd.apply(res, args)
            afterSend(oldBody, newBody)
          }
          methods.intercept(oldBody, onIntercept)
        } else {
          debug(' methods.send isnt defined')
          afterSend(oldBody, oldBody)
          originalEnd.apply(res, args)
        }
      } else {
        originalEnd.apply(res, args)
      }
    }

    next()
  }
}

const VALID_PARAMS = Object.freeze([
  'isInterceptable',
  'intercept',
  'afterSend',
])

function _validateParams(methods: Array<string>): void {
  for (const k in methods) {
    if (!VALID_PARAMS.includes(k)) {
      throw new Error(`${k} isn't a valid param (${VALID_PARAMS.join(', ')})`)
    }
  }

  if (!('isInterceptable' in methods)) {
    throw new Error('isInterceptable is a required param (function)')
  }
}