Index: frontend/node_modules/compression/index.js
===================================================================
--- frontend/node_modules/compression/index.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/compression/index.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,325 @@
+/*!
+ * compression
+ * Copyright(c) 2010 Sencha Inc.
+ * Copyright(c) 2011 TJ Holowaychuk
+ * Copyright(c) 2014 Jonathan Ong
+ * Copyright(c) 2014-2015 Douglas Christopher Wilson
+ * MIT Licensed
+ */
+
+'use strict'
+
+/**
+ * Module dependencies.
+ * @private
+ */
+
+var Negotiator = require('negotiator')
+var Buffer = require('safe-buffer').Buffer
+var bytes = require('bytes')
+var compressible = require('compressible')
+var debug = require('debug')('compression')
+var onHeaders = require('on-headers')
+var vary = require('vary')
+var zlib = require('zlib')
+
+/**
+ * Module exports.
+ */
+
+module.exports = compression
+module.exports.filter = shouldCompress
+
+/**
+ * @const
+ * whether current node version has brotli support
+ */
+var hasBrotliSupport = 'createBrotliCompress' in zlib
+
+/**
+ * Module variables.
+ * @private
+ */
+var cacheControlNoTransformRegExp = /(?:^|,)\s*?no-transform\s*?(?:,|$)/
+var SUPPORTED_ENCODING = hasBrotliSupport ? ['br', 'gzip', 'deflate', 'identity'] : ['gzip', 'deflate', 'identity']
+var PREFERRED_ENCODING = hasBrotliSupport ? ['br', 'gzip'] : ['gzip']
+
+var encodingSupported = ['gzip', 'deflate', 'identity', 'br']
+
+/**
+ * Compress response data with gzip / deflate.
+ *
+ * @param {Object} [options]
+ * @return {Function} middleware
+ * @public
+ */
+
+function compression (options) {
+  var opts = options || {}
+  var optsBrotli = {}
+
+  if (hasBrotliSupport) {
+    Object.assign(optsBrotli, opts.brotli)
+
+    var brotliParams = {}
+    brotliParams[zlib.constants.BROTLI_PARAM_QUALITY] = 4
+
+    // set the default level to a reasonable value with balanced speed/ratio
+    optsBrotli.params = Object.assign(brotliParams, optsBrotli.params)
+  }
+
+  // options
+  var filter = opts.filter || shouldCompress
+  var threshold = bytes.parse(opts.threshold)
+  var enforceEncoding = opts.enforceEncoding || 'identity'
+
+  if (threshold == null) {
+    threshold = 1024
+  }
+
+  return function compression (req, res, next) {
+    var ended = false
+    var length
+    var listeners = []
+    var stream
+
+    var _end = res.end
+    var _on = res.on
+    var _write = res.write
+
+    // flush
+    res.flush = function flush () {
+      if (stream) {
+        stream.flush()
+      }
+    }
+
+    // proxy
+
+    res.write = function write (chunk, encoding) {
+      if (ended) {
+        return false
+      }
+
+      if (!headersSent(res)) {
+        this.writeHead(this.statusCode)
+      }
+
+      return stream
+        ? stream.write(toBuffer(chunk, encoding))
+        : _write.call(this, chunk, encoding)
+    }
+
+    res.end = function end (chunk, encoding) {
+      if (ended) {
+        return false
+      }
+
+      if (!headersSent(res)) {
+        // estimate the length
+        if (!this.getHeader('Content-Length')) {
+          length = chunkLength(chunk, encoding)
+        }
+
+        this.writeHead(this.statusCode)
+      }
+
+      if (!stream) {
+        return _end.call(this, chunk, encoding)
+      }
+
+      // mark ended
+      ended = true
+
+      // write Buffer for Node.js 0.8
+      return chunk
+        ? stream.end(toBuffer(chunk, encoding))
+        : stream.end()
+    }
+
+    res.on = function on (type, listener) {
+      if (!listeners || type !== 'drain') {
+        return _on.call(this, type, listener)
+      }
+
+      if (stream) {
+        return stream.on(type, listener)
+      }
+
+      // buffer listeners for future stream
+      listeners.push([type, listener])
+
+      return this
+    }
+
+    function nocompress (msg) {
+      debug('no compression: %s', msg)
+      addListeners(res, _on, listeners)
+      listeners = null
+    }
+
+    onHeaders(res, function onResponseHeaders () {
+      // determine if request is filtered
+      if (!filter(req, res)) {
+        nocompress('filtered')
+        return
+      }
+
+      // determine if the entity should be transformed
+      if (!shouldTransform(req, res)) {
+        nocompress('no transform')
+        return
+      }
+
+      // vary
+      vary(res, 'Accept-Encoding')
+
+      // content-length below threshold
+      if (Number(res.getHeader('Content-Length')) < threshold || length < threshold) {
+        nocompress('size below threshold')
+        return
+      }
+
+      var encoding = res.getHeader('Content-Encoding') || 'identity'
+
+      // already encoded
+      if (encoding !== 'identity') {
+        nocompress('already encoded')
+        return
+      }
+
+      // head
+      if (req.method === 'HEAD') {
+        nocompress('HEAD request')
+        return
+      }
+
+      // compression method
+      var negotiator = new Negotiator(req)
+      var method = negotiator.encoding(SUPPORTED_ENCODING, PREFERRED_ENCODING)
+
+      // if no method is found, use the default encoding
+      if (!req.headers['accept-encoding'] && encodingSupported.indexOf(enforceEncoding) !== -1) {
+        method = enforceEncoding
+      }
+
+      // negotiation failed
+      if (!method || method === 'identity') {
+        nocompress('not acceptable')
+        return
+      }
+
+      // compression stream
+      debug('%s compression', method)
+      stream = method === 'gzip'
+        ? zlib.createGzip(opts)
+        : method === 'br'
+          ? zlib.createBrotliCompress(optsBrotli)
+          : zlib.createDeflate(opts)
+
+      // add buffered listeners to stream
+      addListeners(stream, stream.on, listeners)
+
+      // header fields
+      res.setHeader('Content-Encoding', method)
+      res.removeHeader('Content-Length')
+
+      // compression
+      stream.on('data', function onStreamData (chunk) {
+        if (_write.call(res, chunk) === false) {
+          stream.pause()
+        }
+      })
+
+      stream.on('end', function onStreamEnd () {
+        _end.call(res)
+      })
+
+      _on.call(res, 'drain', function onResponseDrain () {
+        stream.resume()
+      })
+    })
+
+    next()
+  }
+}
+
+/**
+ * Add bufferred listeners to stream
+ * @private
+ */
+
+function addListeners (stream, on, listeners) {
+  for (var i = 0; i < listeners.length; i++) {
+    on.apply(stream, listeners[i])
+  }
+}
+
+/**
+ * Get the length of a given chunk
+ */
+
+function chunkLength (chunk, encoding) {
+  if (!chunk) {
+    return 0
+  }
+
+  return Buffer.isBuffer(chunk)
+    ? chunk.length
+    : Buffer.byteLength(chunk, encoding)
+}
+
+/**
+ * Default filter function.
+ * @private
+ */
+
+function shouldCompress (req, res) {
+  var type = res.getHeader('Content-Type')
+
+  if (type === undefined || !compressible(type)) {
+    debug('%s not compressible', type)
+    return false
+  }
+
+  return true
+}
+
+/**
+ * Determine if the entity should be transformed.
+ * @private
+ */
+
+function shouldTransform (req, res) {
+  var cacheControl = res.getHeader('Cache-Control')
+
+  // Don't compress for Cache-Control: no-transform
+  // https://tools.ietf.org/html/rfc7234#section-5.2.2.4
+  return !cacheControl ||
+    !cacheControlNoTransformRegExp.test(cacheControl)
+}
+
+/**
+ * Coerce arguments to Buffer
+ * @private
+ */
+
+function toBuffer (chunk, encoding) {
+  return Buffer.isBuffer(chunk)
+    ? chunk
+    : Buffer.from(chunk, encoding)
+}
+
+/**
+ * Determine if the response headers have been sent.
+ *
+ * @param {object} res
+ * @returns {boolean}
+ * @private
+ */
+
+function headersSent (res) {
+  return typeof res.headersSent !== 'boolean'
+    ? Boolean(res._header)
+    : res.headersSent
+}
