Browse Source

add redux for token

serg1557733 1 year ago
commit
e52e1e6328
100 changed files with 12588 additions and 0 deletions
  1. 251 0
      backend/app.js
  2. 13 0
      backend/controlers/token.js
  3. 9 0
      backend/db/models/Message.js
  4. 12 0
      backend/db/models/User.js
  5. 2644 0
      backend/node_modules/.package-lock.json
  6. 51 0
      backend/node_modules/@mapbox/node-pre-gyp/lib/build.js
  7. 31 0
      backend/node_modules/@mapbox/node-pre-gyp/lib/clean.js
  8. 52 0
      backend/node_modules/@mapbox/node-pre-gyp/lib/configure.js
  9. 38 0
      backend/node_modules/@mapbox/node-pre-gyp/lib/info.js
  10. 235 0
      backend/node_modules/@mapbox/node-pre-gyp/lib/install.js
  11. 125 0
      backend/node_modules/@mapbox/node-pre-gyp/lib/main.js
  12. 309 0
      backend/node_modules/@mapbox/node-pre-gyp/lib/node-pre-gyp.js
  13. 73 0
      backend/node_modules/@mapbox/node-pre-gyp/lib/package.js
  14. 34 0
      backend/node_modules/@mapbox/node-pre-gyp/lib/pre-binding.js
  15. 81 0
      backend/node_modules/@mapbox/node-pre-gyp/lib/publish.js
  16. 20 0
      backend/node_modules/@mapbox/node-pre-gyp/lib/rebuild.js
  17. 19 0
      backend/node_modules/@mapbox/node-pre-gyp/lib/reinstall.js
  18. 32 0
      backend/node_modules/@mapbox/node-pre-gyp/lib/reveal.js
  19. 79 0
      backend/node_modules/@mapbox/node-pre-gyp/lib/testbinary.js
  20. 53 0
      backend/node_modules/@mapbox/node-pre-gyp/lib/testpackage.js
  21. 41 0
      backend/node_modules/@mapbox/node-pre-gyp/lib/unpublish.js
  22. 2602 0
      backend/node_modules/@mapbox/node-pre-gyp/lib/util/abi_crosswalk.json
  23. 93 0
      backend/node_modules/@mapbox/node-pre-gyp/lib/util/compile.js
  24. 102 0
      backend/node_modules/@mapbox/node-pre-gyp/lib/util/handle_gyp_opts.js
  25. 205 0
      backend/node_modules/@mapbox/node-pre-gyp/lib/util/napi.js
  26. 9 0
      backend/node_modules/@mapbox/node-pre-gyp/lib/util/nw-pre-gyp/package.json
  27. 163 0
      backend/node_modules/@mapbox/node-pre-gyp/lib/util/s3_setup.js
  28. 335 0
      backend/node_modules/@mapbox/node-pre-gyp/lib/util/versioning.js
  29. 54 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/nopt/bin/nopt.js
  30. 441 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/nopt/lib/nopt.js
  31. 34 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/nopt/package.json
  32. 183 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/bin/semver.js
  33. 136 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/classes/comparator.js
  34. 5 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/classes/index.js
  35. 519 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/classes/range.js
  36. 287 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/classes/semver.js
  37. 6 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/clean.js
  38. 52 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/cmp.js
  39. 52 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/coerce.js
  40. 7 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/compare-build.js
  41. 3 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/compare-loose.js
  42. 5 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/compare.js
  43. 23 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/diff.js
  44. 3 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/eq.js
  45. 3 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/gt.js
  46. 3 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/gte.js
  47. 18 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/inc.js
  48. 3 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/lt.js
  49. 3 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/lte.js
  50. 3 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/major.js
  51. 3 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/minor.js
  52. 3 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/neq.js
  53. 33 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/parse.js
  54. 3 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/patch.js
  55. 6 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/prerelease.js
  56. 3 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/rcompare.js
  57. 3 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/rsort.js
  58. 10 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/satisfies.js
  59. 3 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/sort.js
  60. 6 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/valid.js
  61. 48 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/index.js
  62. 17 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/internal/constants.js
  63. 9 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/internal/debug.js
  64. 23 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/internal/identifiers.js
  65. 11 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/internal/parse-options.js
  66. 182 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/internal/re.js
  67. 75 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/package.json
  68. 2 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/preload.js
  69. 4 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/ranges/gtr.js
  70. 7 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/ranges/intersects.js
  71. 4 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/ranges/ltr.js
  72. 25 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/ranges/max-satisfying.js
  73. 24 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/ranges/min-satisfying.js
  74. 61 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/ranges/min-version.js
  75. 80 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/ranges/outside.js
  76. 47 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/ranges/simplify.js
  77. 244 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/ranges/subset.js
  78. 8 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/ranges/to-comparators.js
  79. 11 0
      backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/ranges/valid.js
  80. 62 0
      backend/node_modules/@mapbox/node-pre-gyp/package.json
  81. 245 0
      backend/node_modules/@sindresorhus/is/dist/index.js
  82. 63 0
      backend/node_modules/@sindresorhus/is/package.json
  83. 47 0
      backend/node_modules/@szmarczak/http-timer/package.json
  84. 99 0
      backend/node_modules/@szmarczak/http-timer/source/index.js
  85. 25 0
      backend/node_modules/@types/component-emitter/package.json
  86. 30 0
      backend/node_modules/@types/cookie/package.json
  87. 30 0
      backend/node_modules/@types/cors/package.json
  88. 230 0
      backend/node_modules/@types/node/package.json
  89. 25 0
      backend/node_modules/@types/webidl-conversions/package.json
  90. 33 0
      backend/node_modules/@types/whatwg-url/package.json
  91. 61 0
      backend/node_modules/abbrev/abbrev.js
  92. 21 0
      backend/node_modules/abbrev/package.json
  93. 238 0
      backend/node_modules/accepts/index.js
  94. 47 0
      backend/node_modules/accepts/package.json
  95. 203 0
      backend/node_modules/agent-base/dist/src/index.js
  96. 18 0
      backend/node_modules/agent-base/dist/src/promisify.js
  97. 59 0
      backend/node_modules/agent-base/node_modules/debug/package.json
  98. 269 0
      backend/node_modules/agent-base/node_modules/debug/src/browser.js
  99. 274 0
      backend/node_modules/agent-base/node_modules/debug/src/common.js
  100. 0 0
      backend/node_modules/agent-base/node_modules/debug/src/index.js

+ 251 - 0
backend/app.js

@@ -0,0 +1,251 @@
+const express = require('express');
+const app = express()
+const cors = require("cors");
+const http = require('http'); //create new http
+const mongoose = require('mongoose');
+const socket = require("socket.io");
+const User = require('./db/models/User');
+const Message = require('./db/models/Message');
+const jwt = require('jsonwebtoken');
+const bcrypt = require('bcrypt');
+require('dotenv').config(); // add dotnv for config
+
+console.log(Message)
+
+
+const server = http.createServer(app);
+app.use(cors());
+app.use(express.json());
+
+const io = require("socket.io")(server, {
+    cors: {
+        origin: "http://localhost:3000" //client endpoint and port
+    }
+});
+const randomColor = require('randomcolor'); 
+
+const PORT = process.env.PORT || 5000;
+const TOKEN_KEY = process.env.TOKEN_KEY || 'rGH4r@3DKOg06hgj'; 
+const HASH_KEY = 7;
+
+//main test page
+app.get('/', (req, res) => {
+    res.send('here will be login page')
+})
+
+const generateToken = (id, userName, isAdmin) => {
+    const payload = {
+        id,
+        userName,
+        isAdmin
+    }
+    return jwt.sign(payload, TOKEN_KEY);
+}
+
+const isValidUserName = (userName) => {
+    const nameRegex = /[^A-Za-z0-9]/ ;
+    return (!nameRegex.test(userName) && userName.trim().length > 2);
+}
+
+const getAllDbUsers = async (socket) => {
+    const usersDb = await User.find({})
+    socket.emit('allDbUsers', usersDb) 
+}
+
+const getOneUser = async (userName) => {
+    const userInDb = await User.findOne({userName});
+    return userInDb;
+}
+
+app.post('/login', async (req, res) => {
+    try {
+        const {userName,password} = req.body;
+        if (!isValidUserName(userName)){
+            return res.status(400).json({message: 'Invalid username'})
+        }
+        const dbUser = await getOneUser(userName)
+
+        if (dbUser?.isBanned){
+            return res.status(401).json({message: 'Your account has been banned!!!'})
+        }
+        const hashPassword = await bcrypt.hash(password, HASH_KEY);
+        if (!dbUser) {
+            const user = new User({
+                userName,
+                hashPassword,
+                isAdmin: !await User.count().exec(),
+                isBanned: false,
+                isMutted: false
+            });
+
+            await user.save()
+
+            return res.json({
+                token: generateToken(user.id, user.userName, user.isAdmin)
+
+            });
+        }
+
+        if (dbUser && !bcrypt.compareSync(password, dbUser.hashPassword)){
+            return res.status(400).json({message: 'Invalid credantials'})
+        }
+        res.json({
+            token:  generateToken(dbUser.id, dbUser.userName, dbUser.isAdmin),
+            message: 'Login success!!!'
+
+        })
+
+    } catch (e) {
+        console.log(e);
+        res.status(500).json({message: `Error ${e}`});
+    }
+})
+
+
+io.use( async (socket, next) => {
+    const token = socket.handshake.auth.token;
+    const sockets = await io.fetchSockets();
+    
+    if(!token) {
+        socket.disconnect();
+        return;
+    }
+
+    const usersOnline = [];
+    sockets.map((sock) => {
+        usersOnline.push(sock.user);
+    }) 
+
+   
+    try {
+        const user = jwt.verify(token, TOKEN_KEY);
+        const userName = user.userName;
+        const dbUser = await getOneUser(userName);
+
+        if(dbUser.isBanned){
+            socket.disconnect();
+            return;
+        }
+        socket.user = user;
+        socket.user.color = randomColor();
+        const exist = sockets.find((current) => current.user.userName == socket.user.userName)
+
+        if(exist) {  //&& !user.isAdmin  - add for two or more admins 
+            console.log(exist.userName, 'exist twice entering...')   
+            exist.disconnect(); 
+        } 
+    } catch(e) {
+        console.log(e);
+        socket.disconnect();
+    }
+    next();
+});
+
+io.on("connection", async (socket) => {
+    const userName = socket.user.userName;
+    const sockets = await io.fetchSockets();
+    const dbUser = await getOneUser(userName);
+
+    io.emit('usersOnline', sockets.map((sock) => sock.user)); // send array online users  
+    socket.emit('connected', dbUser); //socket.user
+   
+    if(socket.user.isAdmin){
+         getAllDbUsers(socket); 
+    }//sent all users from db to admin
+
+    const messagesToShow = await Message.find({}).sort({ 'createDate': -1 }).limit(20);
+
+    socket.emit('allmessages', messagesToShow.reverse());
+    socket.on("message", async (data) => {
+        const dateNow = Date.now(); // for correct working latest post 
+        const post = await Message.findOne({userName}).sort({ 'createDate': -1 })
+        const oneUser = await getOneUser(userName);
+        if(oneUser.isMutted){  //(oneUser.isMutted || !post)
+            return;
+        }
+
+        if(((Date.now() - Date.parse(post?.createDate)) < 1500)){
+            console.log((Date.now() - Date.parse(post?.createDate)))// can use to show timer near by button
+            return;
+        }
+
+        // if(!oneUser.isMutted && post){
+        // if(((Date.now() - Date.parse(post.createDate)) > 150)){//change later 15000  
+        const message = new Message({
+                text: data.message,
+                userName: userName,
+                createDate: Date.now()
+            });
+            try {
+                await message.save(); 
+            } catch (error) {
+                console.log('Message save to db error', error);   
+            }
+            io.emit('message', message);
+        // }
+        // } 
+    });
+    
+    try {
+        socket.on("disconnect", async () => {
+            const sockets = await io.fetchSockets();
+            io.emit('usersOnline', sockets.map((sock) => sock.user));
+            console.log(`user :${socket.user.userName} , disconnected to socket`); 
+        });
+            console.log(`user :${socket.user.userName} , connected to socket`); 
+        
+        socket.on("muteUser",async (data) => {
+            if(!socket.user.isAdmin){
+                return;
+            }
+                // if(socket.user.isAdmin){
+                    const {user, prevStatus} = data;
+                    const sockets = await io.fetchSockets();
+                    const mute = await User.updateOne({userName : user}, {$set: {isMutted :!prevStatus}});
+                    getAllDbUsers(socket);
+                    const exist = sockets.find( current => current.user.userName == user)
+                    const dbUser = await getOneUser(user);
+                    
+                    if(exist){
+                        exist.emit('connected', dbUser);   
+                    } 
+                // }
+           });
+    
+        socket.on("banUser",async (data) => {
+            if(!socket.user.isAdmin){
+                return;
+            }
+
+            // if(socket.user.isAdmin) { 
+                const {user, prevStatus} = data;
+                const sockets = await io.fetchSockets();
+                const ban = await User.updateOne({userName : user}, {$set: {isBanned:!prevStatus}});
+                getAllDbUsers(socket)
+                const exist = sockets.find( current => current.user.userName == user)
+                
+                if(exist){
+                    exist.disconnect();  
+                }
+            // }
+           });
+    } catch (e) {
+        console.log(e);
+    }
+});
+
+
+//server and database start
+const start = async () => {
+    try {
+        await mongoose.connect('mongodb://localhost:27017/chat')
+            .then(() => console.log(`DB started`))
+        server.listen(PORT, () => {
+            console.log(`Server started. Port: ${PORT}`);
+        })
+    } catch (e) {
+        console.log(e);
+    }
+}
+
+start();

+ 13 - 0
backend/controlers/token.js

@@ -0,0 +1,13 @@
+const jwt = require('jsonwebtoken');
+const KEY = '777'
+
+export const generateToken = (id, userName, isAdmin) => {
+    const payload = {
+        id,
+        userName,
+        isAdmin
+    }
+    return jwt.sign(payload, KEY, {expiresIn: '12h'});
+
+}
+

+ 9 - 0
backend/db/models/Message.js

@@ -0,0 +1,9 @@
+const {model, Schema} = require('mongoose');
+
+const Message = new Schema({
+    text: {type: String, required: true},
+    userName : {type: String, required: true},
+    createDate: {type: Date, required: true}
+})
+
+module.exports = model('Message', Message)

+ 12 - 0
backend/db/models/User.js

@@ -0,0 +1,12 @@
+const {model, Schema} = require('mongoose');
+
+const User = new Schema({
+    userName: {type: String, unique: true, required: true},
+    hashPassword: {type: String, required: true},
+    isAdmin: {type: Boolean, default: false},
+    isBanned: {type: Boolean, default: false},
+    isMutted: {type: Boolean, default: false}
+
+})
+
+module.exports = model('User', User)

File diff suppressed because it is too large
+ 2644 - 0
backend/node_modules/.package-lock.json


+ 51 - 0
backend/node_modules/@mapbox/node-pre-gyp/lib/build.js

@@ -0,0 +1,51 @@
+'use strict';
+
+module.exports = exports = build;
+
+exports.usage = 'Attempts to compile the module by dispatching to node-gyp or nw-gyp';
+
+const napi = require('./util/napi.js');
+const compile = require('./util/compile.js');
+const handle_gyp_opts = require('./util/handle_gyp_opts.js');
+const configure = require('./configure.js');
+
+function do_build(gyp, argv, callback) {
+  handle_gyp_opts(gyp, argv, (err, result) => {
+    let final_args = ['build'].concat(result.gyp).concat(result.pre);
+    if (result.unparsed.length > 0) {
+      final_args = final_args.
+        concat(['--']).
+        concat(result.unparsed);
+    }
+    if (!err && result.opts.napi_build_version) {
+      napi.swap_build_dir_in(result.opts.napi_build_version);
+    }
+    compile.run_gyp(final_args, result.opts, (err2) => {
+      if (result.opts.napi_build_version) {
+        napi.swap_build_dir_out(result.opts.napi_build_version);
+      }
+      return callback(err2);
+    });
+  });
+}
+
+function build(gyp, argv, callback) {
+
+  // Form up commands to pass to node-gyp:
+  // We map `node-pre-gyp build` to `node-gyp configure build` so that we do not
+  // trigger a clean and therefore do not pay the penalty of a full recompile
+  if (argv.length && (argv.indexOf('rebuild') > -1)) {
+    argv.shift(); // remove `rebuild`
+    // here we map `node-pre-gyp rebuild` to `node-gyp rebuild` which internally means
+    // "clean + configure + build" and triggers a full recompile
+    compile.run_gyp(['clean'], {}, (err3) => {
+      if (err3) return callback(err3);
+      configure(gyp, argv, (err4) => {
+        if (err4) return callback(err4);
+        return do_build(gyp, argv, callback);
+      });
+    });
+  } else {
+    return do_build(gyp, argv, callback);
+  }
+}

+ 31 - 0
backend/node_modules/@mapbox/node-pre-gyp/lib/clean.js

@@ -0,0 +1,31 @@
+'use strict';
+
+module.exports = exports = clean;
+
+exports.usage = 'Removes the entire folder containing the compiled .node module';
+
+const rm = require('rimraf');
+const exists = require('fs').exists || require('path').exists;
+const versioning = require('./util/versioning.js');
+const napi = require('./util/napi.js');
+const path = require('path');
+
+function clean(gyp, argv, callback) {
+  const package_json = gyp.package_json;
+  const napi_build_version = napi.get_napi_build_version_from_command_args(argv);
+  const opts = versioning.evaluate(package_json, gyp.opts, napi_build_version);
+  const to_delete = opts.module_path;
+  if (!to_delete) {
+    return callback(new Error('module_path is empty, refusing to delete'));
+  } else if (path.normalize(to_delete) === path.normalize(process.cwd())) {
+    return callback(new Error('module_path is not set, refusing to delete'));
+  } else {
+    exists(to_delete, (found) => {
+      if (found) {
+        if (!gyp.opts.silent_clean) console.log('[' + package_json.name + '] Removing "%s"', to_delete);
+        return rm(to_delete, callback);
+      }
+      return callback();
+    });
+  }
+}

+ 52 - 0
backend/node_modules/@mapbox/node-pre-gyp/lib/configure.js

@@ -0,0 +1,52 @@
+'use strict';
+
+module.exports = exports = configure;
+
+exports.usage = 'Attempts to configure node-gyp or nw-gyp build';
+
+const napi = require('./util/napi.js');
+const compile = require('./util/compile.js');
+const handle_gyp_opts = require('./util/handle_gyp_opts.js');
+
+function configure(gyp, argv, callback) {
+  handle_gyp_opts(gyp, argv, (err, result) => {
+    let final_args = result.gyp.concat(result.pre);
+    // pull select node-gyp configure options out of the npm environ
+    const known_gyp_args = ['dist-url', 'python', 'nodedir', 'msvs_version'];
+    known_gyp_args.forEach((key) => {
+      const val = gyp.opts[key] || gyp.opts[key.replace('-', '_')];
+      if (val) {
+        final_args.push('--' + key + '=' + val);
+      }
+    });
+    // --ensure=false tell node-gyp to re-install node development headers
+    // but it is only respected by node-gyp install, so we have to call install
+    // as a separate step if the user passes it
+    if (gyp.opts.ensure === false) {
+      const install_args = final_args.concat(['install', '--ensure=false']);
+      compile.run_gyp(install_args, result.opts, (err2) => {
+        if (err2) return callback(err2);
+        if (result.unparsed.length > 0) {
+          final_args = final_args.
+            concat(['--']).
+            concat(result.unparsed);
+        }
+        compile.run_gyp(['configure'].concat(final_args), result.opts, (err3) => {
+          return callback(err3);
+        });
+      });
+    } else {
+      if (result.unparsed.length > 0) {
+        final_args = final_args.
+          concat(['--']).
+          concat(result.unparsed);
+      }
+      compile.run_gyp(['configure'].concat(final_args), result.opts, (err4) => {
+        if (!err4 && result.opts.napi_build_version) {
+          napi.swap_build_dir_out(result.opts.napi_build_version);
+        }
+        return callback(err4);
+      });
+    }
+  });
+}

+ 38 - 0
backend/node_modules/@mapbox/node-pre-gyp/lib/info.js

@@ -0,0 +1,38 @@
+'use strict';
+
+module.exports = exports = info;
+
+exports.usage = 'Lists all published binaries (requires aws-sdk)';
+
+const log = require('npmlog');
+const versioning = require('./util/versioning.js');
+const s3_setup = require('./util/s3_setup.js');
+
+function info(gyp, argv, callback) {
+  const package_json = gyp.package_json;
+  const opts = versioning.evaluate(package_json, gyp.opts);
+  const config = {};
+  s3_setup.detect(opts, config);
+  const s3 = s3_setup.get_s3(config);
+  const s3_opts = {
+    Bucket: config.bucket,
+    Prefix: config.prefix
+  };
+  s3.listObjects(s3_opts, (err, meta) => {
+    if (err && err.code === 'NotFound') {
+      return callback(new Error('[' + package_json.name + '] Not found: https://' + s3_opts.Bucket + '.s3.amazonaws.com/' + config.prefix));
+    } else if (err) {
+      return callback(err);
+    } else {
+      log.verbose(JSON.stringify(meta, null, 1));
+      if (meta && meta.Contents) {
+        meta.Contents.forEach((obj) => {
+          console.log(obj.Key);
+        });
+      } else {
+        console.error('[' + package_json.name + '] No objects found at https://' + s3_opts.Bucket + '.s3.amazonaws.com/' + config.prefix);
+      }
+      return callback();
+    }
+  });
+}

+ 235 - 0
backend/node_modules/@mapbox/node-pre-gyp/lib/install.js

@@ -0,0 +1,235 @@
+'use strict';
+
+module.exports = exports = install;
+
+exports.usage = 'Attempts to install pre-built binary for module';
+
+const fs = require('fs');
+const path = require('path');
+const log = require('npmlog');
+const existsAsync = fs.exists || path.exists;
+const versioning = require('./util/versioning.js');
+const napi = require('./util/napi.js');
+const makeDir = require('make-dir');
+// for fetching binaries
+const fetch = require('node-fetch');
+const tar = require('tar');
+
+let npgVersion = 'unknown';
+try {
+  // Read own package.json to get the current node-pre-pyp version.
+  const ownPackageJSON = fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8');
+  npgVersion = JSON.parse(ownPackageJSON).version;
+} catch (e) {
+  // do nothing
+}
+
+function place_binary(uri, targetDir, opts, callback) {
+  log.http('GET', uri);
+
+  // Try getting version info from the currently running npm.
+  const envVersionInfo = process.env.npm_config_user_agent ||
+        'node ' + process.version;
+
+  const sanitized = uri.replace('+', '%2B');
+  const requestOpts = {
+    uri: sanitized,
+    headers: {
+      'User-Agent': 'node-pre-gyp (v' + npgVersion + ', ' + envVersionInfo + ')'
+    },
+    follow_max: 10
+  };
+
+  if (opts.cafile) {
+    try {
+      requestOpts.ca = fs.readFileSync(opts.cafile);
+    } catch (e) {
+      return callback(e);
+    }
+  } else if (opts.ca) {
+    requestOpts.ca = opts.ca;
+  }
+
+  const proxyUrl = opts.proxy ||
+                    process.env.http_proxy ||
+                    process.env.HTTP_PROXY ||
+                    process.env.npm_config_proxy;
+  let agent;
+  if (proxyUrl) {
+    const ProxyAgent = require('https-proxy-agent');
+    agent = new ProxyAgent(proxyUrl);
+    log.http('download', 'proxy agent configured using: "%s"', proxyUrl);
+  }
+
+  fetch(sanitized, { agent })
+    .then((res) => {
+      if (!res.ok) {
+        throw new Error(`response status ${res.status} ${res.statusText} on ${sanitized}`);
+      }
+      const dataStream = res.body;
+
+      return new Promise((resolve, reject) => {
+        let extractions = 0;
+        const countExtractions = (entry) => {
+          extractions += 1;
+          log.info('install', 'unpacking %s', entry.path);
+        };
+
+        dataStream.pipe(extract(targetDir, countExtractions))
+          .on('error', (e) => {
+            reject(e);
+          });
+        dataStream.on('end', () => {
+          resolve(`extracted file count: ${extractions}`);
+        });
+        dataStream.on('error', (e) => {
+          reject(e);
+        });
+      });
+    })
+    .then((text) => {
+      log.info(text);
+      callback();
+    })
+    .catch((e) => {
+      log.error(`install ${e.message}`);
+      callback(e);
+    });
+}
+
+function extract(to, onentry) {
+  return tar.extract({
+    cwd: to,
+    strip: 1,
+    onentry
+  });
+}
+
+function extract_from_local(from, targetDir, callback) {
+  if (!fs.existsSync(from)) {
+    return callback(new Error('Cannot find file ' + from));
+  }
+  log.info('Found local file to extract from ' + from);
+
+  // extract helpers
+  let extractCount = 0;
+  function countExtractions(entry) {
+    extractCount += 1;
+    log.info('install', 'unpacking ' + entry.path);
+  }
+  function afterExtract(err) {
+    if (err) return callback(err);
+    if (extractCount === 0) {
+      return callback(new Error('There was a fatal problem while extracting the tarball'));
+    }
+    log.info('tarball', 'done parsing tarball');
+    callback();
+  }
+
+  fs.createReadStream(from).pipe(extract(targetDir, countExtractions))
+    .on('close', afterExtract)
+    .on('error', afterExtract);
+}
+
+function do_build(gyp, argv, callback) {
+  const args = ['rebuild'].concat(argv);
+  gyp.todo.push({ name: 'build', args: args });
+  process.nextTick(callback);
+}
+
+function print_fallback_error(err, opts, package_json) {
+  const fallback_message = ' (falling back to source compile with node-gyp)';
+  let full_message = '';
+  if (err.statusCode !== undefined) {
+    // If we got a network response it but failed to download
+    // it means remote binaries are not available, so let's try to help
+    // the user/developer with the info to debug why
+    full_message = 'Pre-built binaries not found for ' + package_json.name + '@' + package_json.version;
+    full_message += ' and ' + opts.runtime + '@' + (opts.target || process.versions.node) + ' (' + opts.node_abi + ' ABI, ' + opts.libc + ')';
+    full_message += fallback_message;
+    log.warn('Tried to download(' + err.statusCode + '): ' + opts.hosted_tarball);
+    log.warn(full_message);
+    log.http(err.message);
+  } else {
+    // If we do not have a statusCode that means an unexpected error
+    // happened and prevented an http response, so we output the exact error
+    full_message = 'Pre-built binaries not installable for ' + package_json.name + '@' + package_json.version;
+    full_message += ' and ' + opts.runtime + '@' + (opts.target || process.versions.node) + ' (' + opts.node_abi + ' ABI, ' + opts.libc + ')';
+    full_message += fallback_message;
+    log.warn(full_message);
+    log.warn('Hit error ' + err.message);
+  }
+}
+
+//
+// install
+//
+function install(gyp, argv, callback) {
+  const package_json = gyp.package_json;
+  const napi_build_version = napi.get_napi_build_version_from_command_args(argv);
+  const source_build = gyp.opts['build-from-source'] || gyp.opts.build_from_source;
+  const update_binary = gyp.opts['update-binary'] || gyp.opts.update_binary;
+  const should_do_source_build = source_build === package_json.name || (source_build === true || source_build === 'true');
+  if (should_do_source_build) {
+    log.info('build', 'requesting source compile');
+    return do_build(gyp, argv, callback);
+  } else {
+    const fallback_to_build = gyp.opts['fallback-to-build'] || gyp.opts.fallback_to_build;
+    let should_do_fallback_build = fallback_to_build === package_json.name || (fallback_to_build === true || fallback_to_build === 'true');
+    // but allow override from npm
+    if (process.env.npm_config_argv) {
+      const cooked = JSON.parse(process.env.npm_config_argv).cooked;
+      const match = cooked.indexOf('--fallback-to-build');
+      if (match > -1 && cooked.length > match && cooked[match + 1] === 'false') {
+        should_do_fallback_build = false;
+        log.info('install', 'Build fallback disabled via npm flag: --fallback-to-build=false');
+      }
+    }
+    let opts;
+    try {
+      opts = versioning.evaluate(package_json, gyp.opts, napi_build_version);
+    } catch (err) {
+      return callback(err);
+    }
+
+    opts.ca = gyp.opts.ca;
+    opts.cafile = gyp.opts.cafile;
+
+    const from = opts.hosted_tarball;
+    const to = opts.module_path;
+    const binary_module = path.join(to, opts.module_name + '.node');
+    existsAsync(binary_module, (found) => {
+      if (!update_binary) {
+        if (found) {
+          console.log('[' + package_json.name + '] Success: "' + binary_module + '" already installed');
+          console.log('Pass --update-binary to reinstall or --build-from-source to recompile');
+          return callback();
+        }
+        log.info('check', 'checked for "' + binary_module + '" (not found)');
+      }
+
+      makeDir(to).then(() => {
+        const fileName = from.startsWith('file://') && from.slice('file://'.length);
+        if (fileName) {
+          extract_from_local(fileName, to, after_place);
+        } else {
+          place_binary(from, to, opts, after_place);
+        }
+      }).catch((err) => {
+        after_place(err);
+      });
+
+      function after_place(err) {
+        if (err && should_do_fallback_build) {
+          print_fallback_error(err, opts, package_json);
+          return do_build(gyp, argv, callback);
+        } else if (err) {
+          return callback(err);
+        } else {
+          console.log('[' + package_json.name + '] Success: "' + binary_module + '" is installed via remote');
+          return callback();
+        }
+      }
+    });
+  }
+}

+ 125 - 0
backend/node_modules/@mapbox/node-pre-gyp/lib/main.js

@@ -0,0 +1,125 @@
+'use strict';
+
+/**
+ * Set the title.
+ */
+
+process.title = 'node-pre-gyp';
+
+const node_pre_gyp = require('../');
+const log = require('npmlog');
+
+/**
+ * Process and execute the selected commands.
+ */
+
+const prog = new node_pre_gyp.Run({ argv: process.argv });
+let completed = false;
+
+if (prog.todo.length === 0) {
+  if (~process.argv.indexOf('-v') || ~process.argv.indexOf('--version')) {
+    console.log('v%s', prog.version);
+    process.exit(0);
+  } else if (~process.argv.indexOf('-h') || ~process.argv.indexOf('--help')) {
+    console.log('%s', prog.usage());
+    process.exit(0);
+  }
+  console.log('%s', prog.usage());
+  process.exit(1);
+}
+
+// if --no-color is passed
+if (prog.opts && Object.hasOwnProperty.call(prog, 'color') && !prog.opts.color) {
+  log.disableColor();
+}
+
+log.info('it worked if it ends with', 'ok');
+log.verbose('cli', process.argv);
+log.info('using', process.title + '@%s', prog.version);
+log.info('using', 'node@%s | %s | %s', process.versions.node, process.platform, process.arch);
+
+
+/**
+ * Change dir if -C/--directory was passed.
+ */
+
+const dir = prog.opts.directory;
+if (dir) {
+  const fs = require('fs');
+  try {
+    const stat = fs.statSync(dir);
+    if (stat.isDirectory()) {
+      log.info('chdir', dir);
+      process.chdir(dir);
+    } else {
+      log.warn('chdir', dir + ' is not a directory');
+    }
+  } catch (e) {
+    if (e.code === 'ENOENT') {
+      log.warn('chdir', dir + ' is not a directory');
+    } else {
+      log.warn('chdir', 'error during chdir() "%s"', e.message);
+    }
+  }
+}
+
+function run() {
+  const command = prog.todo.shift();
+  if (!command) {
+    // done!
+    completed = true;
+    log.info('ok');
+    return;
+  }
+
+  // set binary.host when appropriate. host determines the s3 target bucket.
+  const target = prog.setBinaryHostProperty(command.name);
+  if (target && ['install', 'publish', 'unpublish', 'info'].indexOf(command.name) >= 0) {
+    log.info('using binary.host: ' + prog.package_json.binary.host);
+  }
+
+  prog.commands[command.name](command.args, function(err) {
+    if (err) {
+      log.error(command.name + ' error');
+      log.error('stack', err.stack);
+      errorMessage();
+      log.error('not ok');
+      console.log(err.message);
+      return process.exit(1);
+    }
+    const args_array = [].slice.call(arguments, 1);
+    if (args_array.length) {
+      console.log.apply(console, args_array);
+    }
+    // now run the next command in the queue
+    process.nextTick(run);
+  });
+}
+
+process.on('exit', (code) => {
+  if (!completed && !code) {
+    log.error('Completion callback never invoked!');
+    errorMessage();
+    process.exit(6);
+  }
+});
+
+process.on('uncaughtException', (err) => {
+  log.error('UNCAUGHT EXCEPTION');
+  log.error('stack', err.stack);
+  errorMessage();
+  process.exit(7);
+});
+
+function errorMessage() {
+  // copied from npm's lib/util/error-handler.js
+  const os = require('os');
+  log.error('System', os.type() + ' ' + os.release());
+  log.error('command', process.argv.map(JSON.stringify).join(' '));
+  log.error('cwd', process.cwd());
+  log.error('node -v', process.version);
+  log.error(process.title + ' -v', 'v' + prog.package.version);
+}
+
+// start running the given commands!
+run();

+ 309 - 0
backend/node_modules/@mapbox/node-pre-gyp/lib/node-pre-gyp.js

@@ -0,0 +1,309 @@
+'use strict';
+
+/**
+ * Module exports.
+ */
+
+module.exports = exports;
+
+/**
+ * Module dependencies.
+ */
+
+// load mocking control function for accessing s3 via https. the function is a noop always returning
+// false if not mocking.
+exports.mockS3Http = require('./util/s3_setup').get_mockS3Http();
+exports.mockS3Http('on');
+const mocking = exports.mockS3Http('get');
+
+
+const fs = require('fs');
+const path = require('path');
+const nopt = require('nopt');
+const log = require('npmlog');
+log.disableProgress();
+const napi = require('./util/napi.js');
+
+const EE = require('events').EventEmitter;
+const inherits = require('util').inherits;
+const cli_commands = [
+  'clean',
+  'install',
+  'reinstall',
+  'build',
+  'rebuild',
+  'package',
+  'testpackage',
+  'publish',
+  'unpublish',
+  'info',
+  'testbinary',
+  'reveal',
+  'configure'
+];
+const aliases = {};
+
+// differentiate node-pre-gyp's logs from npm's
+log.heading = 'node-pre-gyp';
+
+if (mocking) {
+  log.warn(`mocking s3 to ${process.env.node_pre_gyp_mock_s3}`);
+}
+
+// this is a getter to avoid circular reference warnings with node v14.
+Object.defineProperty(exports, 'find', {
+  get: function() {
+    return require('./pre-binding').find;
+  },
+  enumerable: true
+});
+
+// in the following, "my_module" is using node-pre-gyp to
+// prebuild and install pre-built binaries. "main_module"
+// is using "my_module".
+//
+// "bin/node-pre-gyp" invokes Run() without a path. the
+// expectation is that the working directory is the package
+// root "my_module". this is true because in all cases npm is
+// executing a script in the context of "my_module".
+//
+// "pre-binding.find()" is executed by "my_module" but in the
+// context of "main_module". this is because "main_module" is
+// executing and requires "my_module" which is then executing
+// "pre-binding.find()" via "node-pre-gyp.find()", so the working
+// directory is that of "main_module".
+//
+// that's why "find()" must pass the path to package.json.
+//
+function Run({ package_json_path = './package.json', argv }) {
+  this.package_json_path = package_json_path;
+  this.commands = {};
+
+  const self = this;
+  cli_commands.forEach((command) => {
+    self.commands[command] = function(argvx, callback) {
+      log.verbose('command', command, argvx);
+      return require('./' + command)(self, argvx, callback);
+    };
+  });
+
+  this.parseArgv(argv);
+
+  // this is set to true after the binary.host property was set to
+  // either staging_host or production_host.
+  this.binaryHostSet = false;
+}
+inherits(Run, EE);
+exports.Run = Run;
+const proto = Run.prototype;
+
+/**
+ * Export the contents of the package.json.
+ */
+
+proto.package = require('../package.json');
+
+/**
+ * nopt configuration definitions
+ */
+
+proto.configDefs = {
+  help: Boolean,     // everywhere
+  arch: String,      // 'configure'
+  debug: Boolean,    // 'build'
+  directory: String, // bin
+  proxy: String,     // 'install'
+  loglevel: String  // everywhere
+};
+
+/**
+ * nopt shorthands
+ */
+
+proto.shorthands = {
+  release: '--no-debug',
+  C: '--directory',
+  debug: '--debug',
+  j: '--jobs',
+  silent: '--loglevel=silent',
+  silly: '--loglevel=silly',
+  verbose: '--loglevel=verbose'
+};
+
+/**
+ * expose the command aliases for the bin file to use.
+ */
+
+proto.aliases = aliases;
+
+/**
+ * Parses the given argv array and sets the 'opts', 'argv',
+ * 'command', and 'package_json' properties.
+ */
+
+proto.parseArgv = function parseOpts(argv) {
+  this.opts = nopt(this.configDefs, this.shorthands, argv);
+  this.argv = this.opts.argv.remain.slice();
+  const commands = this.todo = [];
+
+  // create a copy of the argv array with aliases mapped
+  argv = this.argv.map((arg) => {
+    // is this an alias?
+    if (arg in this.aliases) {
+      arg = this.aliases[arg];
+    }
+    return arg;
+  });
+
+  // process the mapped args into "command" objects ("name" and "args" props)
+  argv.slice().forEach((arg) => {
+    if (arg in this.commands) {
+      const args = argv.splice(0, argv.indexOf(arg));
+      argv.shift();
+      if (commands.length > 0) {
+        commands[commands.length - 1].args = args;
+      }
+      commands.push({ name: arg, args: [] });
+    }
+  });
+  if (commands.length > 0) {
+    commands[commands.length - 1].args = argv.splice(0);
+  }
+
+
+  // if a directory was specified package.json is assumed to be relative
+  // to it.
+  let package_json_path = this.package_json_path;
+  if (this.opts.directory) {
+    package_json_path = path.join(this.opts.directory, package_json_path);
+  }
+
+  this.package_json = JSON.parse(fs.readFileSync(package_json_path));
+
+  // expand commands entries for multiple napi builds
+  this.todo = napi.expand_commands(this.package_json, this.opts, commands);
+
+  // support for inheriting config env variables from npm
+  const npm_config_prefix = 'npm_config_';
+  Object.keys(process.env).forEach((name) => {
+    if (name.indexOf(npm_config_prefix) !== 0) return;
+    const val = process.env[name];
+    if (name === npm_config_prefix + 'loglevel') {
+      log.level = val;
+    } else {
+      // add the user-defined options to the config
+      name = name.substring(npm_config_prefix.length);
+      // avoid npm argv clobber already present args
+      // which avoids problem of 'npm test' calling
+      // script that runs unique npm install commands
+      if (name === 'argv') {
+        if (this.opts.argv &&
+             this.opts.argv.remain &&
+             this.opts.argv.remain.length) {
+          // do nothing
+        } else {
+          this.opts[name] = val;
+        }
+      } else {
+        this.opts[name] = val;
+      }
+    }
+  });
+
+  if (this.opts.loglevel) {
+    log.level = this.opts.loglevel;
+  }
+  log.resume();
+};
+
+/**
+ * allow the binary.host property to be set at execution time.
+ *
+ * for this to take effect requires all the following to be true.
+ * - binary is a property in package.json
+ * - binary.host is falsey
+ * - binary.staging_host is not empty
+ * - binary.production_host is not empty
+ *
+ * if any of the previous checks fail then the function returns an empty string
+ * and makes no changes to package.json's binary property.
+ *
+ *
+ * if command is "publish" then the default is set to "binary.staging_host"
+ * if command is not "publish" the the default is set to "binary.production_host"
+ *
+ * if the command-line option '--s3_host' is set to "staging" or "production" then
+ * "binary.host" is set to the specified "staging_host" or "production_host". if
+ * '--s3_host' is any other value an exception is thrown.
+ *
+ * if '--s3_host' is not present then "binary.host" is set to the default as above.
+ *
+ * this strategy was chosen so that any command other than "publish" or "unpublish" uses "production"
+ * as the default without requiring any command-line options but that "publish" and "unpublish" require
+ * '--s3_host production_host' to be specified in order to *really* publish (or unpublish). publishing
+ * to staging can be done freely without worrying about disturbing any production releases.
+ */
+proto.setBinaryHostProperty = function(command) {
+  if (this.binaryHostSet) {
+    return this.package_json.binary.host;
+  }
+  const p = this.package_json;
+  // don't set anything if host is present. it must be left blank to trigger this.
+  if (!p || !p.binary || p.binary.host) {
+    return '';
+  }
+  // and both staging and production must be present. errors will be reported later.
+  if (!p.binary.staging_host || !p.binary.production_host) {
+    return '';
+  }
+  let target = 'production_host';
+  if (command === 'publish' || command === 'unpublish') {
+    target = 'staging_host';
+  }
+  // the environment variable has priority over the default or the command line. if
+  // either the env var or the command line option are invalid throw an error.
+  const npg_s3_host = process.env.node_pre_gyp_s3_host;
+  if (npg_s3_host === 'staging' || npg_s3_host === 'production') {
+    target = `${npg_s3_host}_host`;
+  } else if (this.opts['s3_host'] === 'staging' || this.opts['s3_host'] === 'production') {
+    target = `${this.opts['s3_host']}_host`;
+  } else if (this.opts['s3_host'] || npg_s3_host) {
+    throw new Error(`invalid s3_host ${this.opts['s3_host'] || npg_s3_host}`);
+  }
+
+  p.binary.host = p.binary[target];
+  this.binaryHostSet = true;
+
+  return p.binary.host;
+};
+
+/**
+ * Returns the usage instructions for node-pre-gyp.
+ */
+
+proto.usage = function usage() {
+  const str = [
+    '',
+    '  Usage: node-pre-gyp <command> [options]',
+    '',
+    '  where <command> is one of:',
+    cli_commands.map((c) => {
+      return '    - ' + c + ' - ' + require('./' + c).usage;
+    }).join('\n'),
+    '',
+    'node-pre-gyp@' + this.version + '  ' + path.resolve(__dirname, '..'),
+    'node@' + process.versions.node
+  ].join('\n');
+  return str;
+};
+
+/**
+ * Version number getter.
+ */
+
+Object.defineProperty(proto, 'version', {
+  get: function() {
+    return this.package.version;
+  },
+  enumerable: true
+});

+ 73 - 0
backend/node_modules/@mapbox/node-pre-gyp/lib/package.js

@@ -0,0 +1,73 @@
+'use strict';
+
+module.exports = exports = _package;
+
+exports.usage = 'Packs binary (and enclosing directory) into locally staged tarball';
+
+const fs = require('fs');
+const path = require('path');
+const log = require('npmlog');
+const versioning = require('./util/versioning.js');
+const napi = require('./util/napi.js');
+const existsAsync = fs.exists || path.exists;
+const makeDir = require('make-dir');
+const tar = require('tar');
+
+function readdirSync(dir) {
+  let list = [];
+  const files = fs.readdirSync(dir);
+
+  files.forEach((file) => {
+    const stats = fs.lstatSync(path.join(dir, file));
+    if (stats.isDirectory()) {
+      list = list.concat(readdirSync(path.join(dir, file)));
+    } else {
+      list.push(path.join(dir, file));
+    }
+  });
+  return list;
+}
+
+function _package(gyp, argv, callback) {
+  const package_json = gyp.package_json;
+  const napi_build_version = napi.get_napi_build_version_from_command_args(argv);
+  const opts = versioning.evaluate(package_json, gyp.opts, napi_build_version);
+  const from = opts.module_path;
+  const binary_module = path.join(from, opts.module_name + '.node');
+  existsAsync(binary_module, (found) => {
+    if (!found) {
+      return callback(new Error('Cannot package because ' + binary_module + ' missing: run `node-pre-gyp rebuild` first'));
+    }
+    const tarball = opts.staged_tarball;
+    const filter_func = function(entry) {
+      const basename = path.basename(entry);
+      if (basename.length && basename[0] !== '.') {
+        console.log('packing ' + entry);
+        return true;
+      } else {
+        console.log('skipping ' + entry);
+      }
+      return false;
+    };
+    makeDir(path.dirname(tarball)).then(() => {
+      let files = readdirSync(from);
+      const base = path.basename(from);
+      files = files.map((file) => {
+        return path.join(base, path.relative(from, file));
+      });
+      tar.create({
+        portable: false,
+        gzip: true,
+        filter: filter_func,
+        file: tarball,
+        cwd: path.dirname(from)
+      }, files, (err2) => {
+        if (err2)  console.error('[' + package_json.name + '] ' + err2.message);
+        else log.info('package', 'Binary staged at "' + tarball + '"');
+        return callback(err2);
+      });
+    }).catch((err) => {
+      return callback(err);
+    });
+  });
+}

+ 34 - 0
backend/node_modules/@mapbox/node-pre-gyp/lib/pre-binding.js

@@ -0,0 +1,34 @@
+'use strict';
+
+const npg = require('..');
+const versioning = require('../lib/util/versioning.js');
+const napi = require('../lib/util/napi.js');
+const existsSync = require('fs').existsSync || require('path').existsSync;
+const path = require('path');
+
+module.exports = exports;
+
+exports.usage = 'Finds the require path for the node-pre-gyp installed module';
+
+exports.validate = function(package_json, opts) {
+  versioning.validate_config(package_json, opts);
+};
+
+exports.find = function(package_json_path, opts) {
+  if (!existsSync(package_json_path)) {
+    throw new Error(package_json_path + 'does not exist');
+  }
+  const prog = new npg.Run({ package_json_path, argv: process.argv });
+  prog.setBinaryHostProperty();
+  const package_json = prog.package_json;
+
+  versioning.validate_config(package_json, opts);
+  let napi_build_version;
+  if (napi.get_napi_build_versions(package_json, opts)) {
+    napi_build_version = napi.get_best_napi_build_version(package_json, opts);
+  }
+  opts = opts || {};
+  if (!opts.module_root) opts.module_root = path.dirname(package_json_path);
+  const meta = versioning.evaluate(package_json, opts, napi_build_version);
+  return meta.module;
+};

+ 81 - 0
backend/node_modules/@mapbox/node-pre-gyp/lib/publish.js

@@ -0,0 +1,81 @@
+'use strict';
+
+module.exports = exports = publish;
+
+exports.usage = 'Publishes pre-built binary (requires aws-sdk)';
+
+const fs = require('fs');
+const path = require('path');
+const log = require('npmlog');
+const versioning = require('./util/versioning.js');
+const napi = require('./util/napi.js');
+const s3_setup = require('./util/s3_setup.js');
+const existsAsync = fs.exists || path.exists;
+const url = require('url');
+
+function publish(gyp, argv, callback) {
+  const package_json = gyp.package_json;
+  const napi_build_version = napi.get_napi_build_version_from_command_args(argv);
+  const opts = versioning.evaluate(package_json, gyp.opts, napi_build_version);
+  const tarball = opts.staged_tarball;
+  existsAsync(tarball, (found) => {
+    if (!found) {
+      return callback(new Error('Cannot publish because ' + tarball + ' missing: run `node-pre-gyp package` first'));
+    }
+
+    log.info('publish', 'Detecting s3 credentials');
+    const config = {};
+    s3_setup.detect(opts, config);
+    const s3 = s3_setup.get_s3(config);
+
+    const key_name = url.resolve(config.prefix, opts.package_name);
+    const s3_opts = {
+      Bucket: config.bucket,
+      Key: key_name
+    };
+    log.info('publish', 'Authenticating with s3');
+    log.info('publish', config);
+
+    log.info('publish', 'Checking for existing binary at ' + opts.hosted_path);
+    s3.headObject(s3_opts, (err, meta) => {
+      if (meta) log.info('publish', JSON.stringify(meta));
+      if (err && err.code === 'NotFound') {
+        // we are safe to publish because
+        // the object does not already exist
+        log.info('publish', 'Preparing to put object');
+        const s3_put_opts = {
+          ACL: 'public-read',
+          Body: fs.createReadStream(tarball),
+          Key: key_name,
+          Bucket: config.bucket
+        };
+        log.info('publish', 'Putting object', s3_put_opts.ACL, s3_put_opts.Bucket, s3_put_opts.Key);
+        try {
+          s3.putObject(s3_put_opts, (err2, resp) => {
+            log.info('publish', 'returned from putting object');
+            if (err2) {
+              log.info('publish', 's3 putObject error: "' + err2 + '"');
+              return callback(err2);
+            }
+            if (resp) log.info('publish', 's3 putObject response: "' + JSON.stringify(resp) + '"');
+            log.info('publish', 'successfully put object');
+            console.log('[' + package_json.name + '] published to ' + opts.hosted_path);
+            return callback();
+          });
+        } catch (err3) {
+          log.info('publish', 's3 putObject error: "' + err3 + '"');
+          return callback(err3);
+        }
+      } else if (err) {
+        log.info('publish', 's3 headObject error: "' + err + '"');
+        return callback(err);
+      } else {
+        log.error('publish', 'Cannot publish over existing version');
+        log.error('publish', "Update the 'version' field in package.json and try again");
+        log.error('publish', 'If the previous version was published in error see:');
+        log.error('publish', '\t node-pre-gyp unpublish');
+        return callback(new Error('Failed publishing to ' + opts.hosted_path));
+      }
+    });
+  });
+}

+ 20 - 0
backend/node_modules/@mapbox/node-pre-gyp/lib/rebuild.js

@@ -0,0 +1,20 @@
+'use strict';
+
+module.exports = exports = rebuild;
+
+exports.usage = 'Runs "clean" and "build" at once';
+
+const napi = require('./util/napi.js');
+
+function rebuild(gyp, argv, callback) {
+  const package_json = gyp.package_json;
+  let commands = [
+    { name: 'clean', args: [] },
+    { name: 'build', args: ['rebuild'] }
+  ];
+  commands = napi.expand_commands(package_json, gyp.opts, commands);
+  for (let i = commands.length; i !== 0; i--) {
+    gyp.todo.unshift(commands[i - 1]);
+  }
+  process.nextTick(callback);
+}

+ 19 - 0
backend/node_modules/@mapbox/node-pre-gyp/lib/reinstall.js

@@ -0,0 +1,19 @@
+'use strict';
+
+module.exports = exports = rebuild;
+
+exports.usage = 'Runs "clean" and "install" at once';
+
+const napi = require('./util/napi.js');
+
+function rebuild(gyp, argv, callback) {
+  const package_json = gyp.package_json;
+  let installArgs = [];
+  const napi_build_version = napi.get_best_napi_build_version(package_json, gyp.opts);
+  if (napi_build_version != null) installArgs = [napi.get_command_arg(napi_build_version)];
+  gyp.todo.unshift(
+    { name: 'clean', args: [] },
+    { name: 'install', args: installArgs }
+  );
+  process.nextTick(callback);
+}

+ 32 - 0
backend/node_modules/@mapbox/node-pre-gyp/lib/reveal.js

@@ -0,0 +1,32 @@
+'use strict';
+
+module.exports = exports = reveal;
+
+exports.usage = 'Reveals data on the versioned binary';
+
+const versioning = require('./util/versioning.js');
+const napi = require('./util/napi.js');
+
+function unix_paths(key, val) {
+  return val && val.replace ? val.replace(/\\/g, '/') : val;
+}
+
+function reveal(gyp, argv, callback) {
+  const package_json = gyp.package_json;
+  const napi_build_version = napi.get_napi_build_version_from_command_args(argv);
+  const opts = versioning.evaluate(package_json, gyp.opts, napi_build_version);
+  let hit = false;
+  // if a second arg is passed look to see
+  // if it is a known option
+  // console.log(JSON.stringify(gyp.opts,null,1))
+  const remain = gyp.opts.argv.remain[gyp.opts.argv.remain.length - 1];
+  if (remain && Object.hasOwnProperty.call(opts, remain)) {
+    console.log(opts[remain].replace(/\\/g, '/'));
+    hit = true;
+  }
+  // otherwise return all options as json
+  if (!hit) {
+    console.log(JSON.stringify(opts, unix_paths, 2));
+  }
+  return callback();
+}

+ 79 - 0
backend/node_modules/@mapbox/node-pre-gyp/lib/testbinary.js

@@ -0,0 +1,79 @@
+'use strict';
+
+module.exports = exports = testbinary;
+
+exports.usage = 'Tests that the binary.node can be required';
+
+const path = require('path');
+const log = require('npmlog');
+const cp = require('child_process');
+const versioning = require('./util/versioning.js');
+const napi = require('./util/napi.js');
+
+function testbinary(gyp, argv, callback) {
+  const args = [];
+  const options = {};
+  let shell_cmd = process.execPath;
+  const package_json = gyp.package_json;
+  const napi_build_version = napi.get_napi_build_version_from_command_args(argv);
+  const opts = versioning.evaluate(package_json, gyp.opts, napi_build_version);
+  // skip validation for runtimes we don't explicitly support (like electron)
+  if (opts.runtime &&
+        opts.runtime !== 'node-webkit' &&
+        opts.runtime !== 'node') {
+    return callback();
+  }
+  const nw = (opts.runtime && opts.runtime === 'node-webkit');
+  // ensure on windows that / are used for require path
+  const binary_module = opts.module.replace(/\\/g, '/');
+  if ((process.arch !== opts.target_arch) ||
+        (process.platform !== opts.target_platform)) {
+    let msg = 'skipping validation since host platform/arch (';
+    msg += process.platform + '/' + process.arch + ')';
+    msg += ' does not match target (';
+    msg += opts.target_platform + '/' + opts.target_arch + ')';
+    log.info('validate', msg);
+    return callback();
+  }
+  if (nw) {
+    options.timeout = 5000;
+    if (process.platform === 'darwin') {
+      shell_cmd = 'node-webkit';
+    } else if (process.platform === 'win32') {
+      shell_cmd = 'nw.exe';
+    } else {
+      shell_cmd = 'nw';
+    }
+    const modulePath = path.resolve(binary_module);
+    const appDir = path.join(__dirname, 'util', 'nw-pre-gyp');
+    args.push(appDir);
+    args.push(modulePath);
+    log.info('validate', "Running test command: '" + shell_cmd + ' ' + args.join(' ') + "'");
+    cp.execFile(shell_cmd, args, options, (err, stdout, stderr) => {
+      // check for normal timeout for node-webkit
+      if (err) {
+        if (err.killed === true && err.signal && err.signal.indexOf('SIG') > -1) {
+          return callback();
+        }
+        const stderrLog = stderr.toString();
+        log.info('stderr', stderrLog);
+        if (/^\s*Xlib:\s*extension\s*"RANDR"\s*missing\s*on\s*display\s*":\d+\.\d+"\.\s*$/.test(stderrLog)) {
+          log.info('RANDR', 'stderr contains only RANDR error, ignored');
+          return callback();
+        }
+        return callback(err);
+      }
+      return callback();
+    });
+    return;
+  }
+  args.push('--eval');
+  args.push("require('" + binary_module.replace(/'/g, '\'') + "')");
+  log.info('validate', "Running test command: '" + shell_cmd + ' ' + args.join(' ') + "'");
+  cp.execFile(shell_cmd, args, options, (err, stdout, stderr) => {
+    if (err) {
+      return callback(err, { stdout: stdout, stderr: stderr });
+    }
+    return callback();
+  });
+}

+ 53 - 0
backend/node_modules/@mapbox/node-pre-gyp/lib/testpackage.js

@@ -0,0 +1,53 @@
+'use strict';
+
+module.exports = exports = testpackage;
+
+exports.usage = 'Tests that the staged package is valid';
+
+const fs = require('fs');
+const path = require('path');
+const log = require('npmlog');
+const existsAsync = fs.exists || path.exists;
+const versioning = require('./util/versioning.js');
+const napi = require('./util/napi.js');
+const testbinary = require('./testbinary.js');
+const tar = require('tar');
+const makeDir = require('make-dir');
+
+function testpackage(gyp, argv, callback) {
+  const package_json = gyp.package_json;
+  const napi_build_version = napi.get_napi_build_version_from_command_args(argv);
+  const opts = versioning.evaluate(package_json, gyp.opts, napi_build_version);
+  const tarball = opts.staged_tarball;
+  existsAsync(tarball, (found) => {
+    if (!found) {
+      return callback(new Error('Cannot test package because ' + tarball + ' missing: run `node-pre-gyp package` first'));
+    }
+    const to = opts.module_path;
+    function filter_func(entry) {
+      log.info('install', 'unpacking [' + entry.path + ']');
+    }
+
+    makeDir(to).then(() => {
+      tar.extract({
+        file: tarball,
+        cwd: to,
+        strip: 1,
+        onentry: filter_func
+      }).then(after_extract, callback);
+    }).catch((err) => {
+      return callback(err);
+    });
+
+    function after_extract() {
+      testbinary(gyp, argv, (err) => {
+        if (err) {
+          return callback(err);
+        } else {
+          console.log('[' + package_json.name + '] Package appears valid');
+          return callback();
+        }
+      });
+    }
+  });
+}

+ 41 - 0
backend/node_modules/@mapbox/node-pre-gyp/lib/unpublish.js

@@ -0,0 +1,41 @@
+'use strict';
+
+module.exports = exports = unpublish;
+
+exports.usage = 'Unpublishes pre-built binary (requires aws-sdk)';
+
+const log = require('npmlog');
+const versioning = require('./util/versioning.js');
+const napi = require('./util/napi.js');
+const s3_setup = require('./util/s3_setup.js');
+const url = require('url');
+
+function unpublish(gyp, argv, callback) {
+  const package_json = gyp.package_json;
+  const napi_build_version = napi.get_napi_build_version_from_command_args(argv);
+  const opts = versioning.evaluate(package_json, gyp.opts, napi_build_version);
+  const config = {};
+  s3_setup.detect(opts, config);
+  const s3 = s3_setup.get_s3(config);
+  const key_name = url.resolve(config.prefix, opts.package_name);
+  const s3_opts = {
+    Bucket: config.bucket,
+    Key: key_name
+  };
+  s3.headObject(s3_opts, (err, meta) => {
+    if (err && err.code === 'NotFound') {
+      console.log('[' + package_json.name + '] Not found: https://' + s3_opts.Bucket + '.s3.amazonaws.com/' + s3_opts.Key);
+      return callback();
+    } else if (err) {
+      return callback(err);
+    } else {
+      log.info('unpublish', JSON.stringify(meta));
+      s3.deleteObject(s3_opts, (err2, resp) => {
+        if (err2) return callback(err2);
+        log.info(JSON.stringify(resp));
+        console.log('[' + package_json.name + '] Success: removed https://' + s3_opts.Bucket + '.s3.amazonaws.com/' + s3_opts.Key);
+        return callback();
+      });
+    }
+  });
+}

File diff suppressed because it is too large
+ 2602 - 0
backend/node_modules/@mapbox/node-pre-gyp/lib/util/abi_crosswalk.json


+ 93 - 0
backend/node_modules/@mapbox/node-pre-gyp/lib/util/compile.js

@@ -0,0 +1,93 @@
+'use strict';
+
+module.exports = exports;
+
+const fs = require('fs');
+const path = require('path');
+const win = process.platform === 'win32';
+const existsSync = fs.existsSync || path.existsSync;
+const cp = require('child_process');
+
+// try to build up the complete path to node-gyp
+/* priority:
+  - node-gyp on ENV:npm_config_node_gyp (https://github.com/npm/npm/pull/4887)
+  - node-gyp on NODE_PATH
+  - node-gyp inside npm on NODE_PATH (ignore on iojs)
+  - node-gyp inside npm beside node exe
+*/
+function which_node_gyp() {
+  let node_gyp_bin;
+  if (process.env.npm_config_node_gyp) {
+    try {
+      node_gyp_bin = process.env.npm_config_node_gyp;
+      if (existsSync(node_gyp_bin)) {
+        return node_gyp_bin;
+      }
+    } catch (err) {
+      // do nothing
+    }
+  }
+  try {
+    const node_gyp_main = require.resolve('node-gyp'); // eslint-disable-line node/no-missing-require
+    node_gyp_bin = path.join(path.dirname(
+      path.dirname(node_gyp_main)),
+    'bin/node-gyp.js');
+    if (existsSync(node_gyp_bin)) {
+      return node_gyp_bin;
+    }
+  } catch (err) {
+    // do nothing
+  }
+  if (process.execPath.indexOf('iojs') === -1) {
+    try {
+      const npm_main = require.resolve('npm'); // eslint-disable-line node/no-missing-require
+      node_gyp_bin = path.join(path.dirname(
+        path.dirname(npm_main)),
+      'node_modules/node-gyp/bin/node-gyp.js');
+      if (existsSync(node_gyp_bin)) {
+        return node_gyp_bin;
+      }
+    } catch (err) {
+      // do nothing
+    }
+  }
+  const npm_base = path.join(path.dirname(
+    path.dirname(process.execPath)),
+  'lib/node_modules/npm/');
+  node_gyp_bin = path.join(npm_base, 'node_modules/node-gyp/bin/node-gyp.js');
+  if (existsSync(node_gyp_bin)) {
+    return node_gyp_bin;
+  }
+}
+
+module.exports.run_gyp = function(args, opts, callback) {
+  let shell_cmd = '';
+  const cmd_args = [];
+  if (opts.runtime && opts.runtime === 'node-webkit') {
+    shell_cmd = 'nw-gyp';
+    if (win) shell_cmd += '.cmd';
+  } else {
+    const node_gyp_path = which_node_gyp();
+    if (node_gyp_path) {
+      shell_cmd = process.execPath;
+      cmd_args.push(node_gyp_path);
+    } else {
+      shell_cmd = 'node-gyp';
+      if (win) shell_cmd += '.cmd';
+    }
+  }
+  const final_args = cmd_args.concat(args);
+  const cmd = cp.spawn(shell_cmd, final_args, { cwd: undefined, env: process.env, stdio: [0, 1, 2] });
+  cmd.on('error', (err) => {
+    if (err) {
+      return callback(new Error("Failed to execute '" + shell_cmd + ' ' + final_args.join(' ') + "' (" + err + ')'));
+    }
+    callback(null, opts);
+  });
+  cmd.on('close', (code) => {
+    if (code && code !== 0) {
+      return callback(new Error("Failed to execute '" + shell_cmd + ' ' + final_args.join(' ') + "' (" + code + ')'));
+    }
+    callback(null, opts);
+  });
+};

+ 102 - 0
backend/node_modules/@mapbox/node-pre-gyp/lib/util/handle_gyp_opts.js

@@ -0,0 +1,102 @@
+'use strict';
+
+module.exports = exports = handle_gyp_opts;
+
+const versioning = require('./versioning.js');
+const napi = require('./napi.js');
+
+/*
+
+Here we gather node-pre-gyp generated options (from versioning) and pass them along to node-gyp.
+
+We massage the args and options slightly to account for differences in what commands mean between
+node-pre-gyp and node-gyp (e.g. see the difference between "build" and "rebuild" below)
+
+Keep in mind: the values inside `argv` and `gyp.opts` below are different depending on whether
+node-pre-gyp is called directory, or if it is called in a `run-script` phase of npm.
+
+We also try to preserve any command line options that might have been passed to npm or node-pre-gyp.
+But this is fairly difficult without passing way to much through. For example `gyp.opts` contains all
+the process.env and npm pushes a lot of variables into process.env which node-pre-gyp inherits. So we have
+to be very selective about what we pass through.
+
+For example:
+
+`npm install --build-from-source` will give:
+
+argv == [ 'rebuild' ]
+gyp.opts.argv == { remain: [ 'install' ],
+  cooked: [ 'install', '--fallback-to-build' ],
+  original: [ 'install', '--fallback-to-build' ] }
+
+`./bin/node-pre-gyp build` will give:
+
+argv == []
+gyp.opts.argv == { remain: [ 'build' ],
+  cooked: [ 'build' ],
+  original: [ '-C', 'test/app1', 'build' ] }
+
+*/
+
+// select set of node-pre-gyp versioning info
+// to share with node-gyp
+const share_with_node_gyp = [
+  'module',
+  'module_name',
+  'module_path',
+  'napi_version',
+  'node_abi_napi',
+  'napi_build_version',
+  'node_napi_label'
+];
+
+function handle_gyp_opts(gyp, argv, callback) {
+
+  // Collect node-pre-gyp specific variables to pass to node-gyp
+  const node_pre_gyp_options = [];
+  // generate custom node-pre-gyp versioning info
+  const napi_build_version = napi.get_napi_build_version_from_command_args(argv);
+  const opts = versioning.evaluate(gyp.package_json, gyp.opts, napi_build_version);
+  share_with_node_gyp.forEach((key) => {
+    const val = opts[key];
+    if (val) {
+      node_pre_gyp_options.push('--' + key + '=' + val);
+    } else if (key === 'napi_build_version') {
+      node_pre_gyp_options.push('--' + key + '=0');
+    } else {
+      if (key !== 'napi_version' && key !== 'node_abi_napi')
+        return callback(new Error('Option ' + key + ' required but not found by node-pre-gyp'));
+    }
+  });
+
+  // Collect options that follow the special -- which disables nopt parsing
+  const unparsed_options = [];
+  let double_hyphen_found = false;
+  gyp.opts.argv.original.forEach((opt) => {
+    if (double_hyphen_found) {
+      unparsed_options.push(opt);
+    }
+    if (opt === '--') {
+      double_hyphen_found = true;
+    }
+  });
+
+  // We try respect and pass through remaining command
+  // line options (like --foo=bar) to node-gyp
+  const cooked = gyp.opts.argv.cooked;
+  const node_gyp_options = [];
+  cooked.forEach((value) => {
+    if (value.length > 2 && value.slice(0, 2) === '--') {
+      const key = value.slice(2);
+      const val = cooked[cooked.indexOf(value) + 1];
+      if (val && val.indexOf('--') === -1) { // handle '--foo=bar' or ['--foo','bar']
+        node_gyp_options.push('--' + key + '=' + val);
+      } else { // pass through --foo
+        node_gyp_options.push(value);
+      }
+    }
+  });
+
+  const result = { 'opts': opts, 'gyp': node_gyp_options, 'pre': node_pre_gyp_options, 'unparsed': unparsed_options };
+  return callback(null, result);
+}

+ 205 - 0
backend/node_modules/@mapbox/node-pre-gyp/lib/util/napi.js

@@ -0,0 +1,205 @@
+'use strict';
+
+const fs = require('fs');
+
+module.exports = exports;
+
+const versionArray = process.version
+  .substr(1)
+  .replace(/-.*$/, '')
+  .split('.')
+  .map((item) => {
+    return +item;
+  });
+
+const napi_multiple_commands = [
+  'build',
+  'clean',
+  'configure',
+  'package',
+  'publish',
+  'reveal',
+  'testbinary',
+  'testpackage',
+  'unpublish'
+];
+
+const napi_build_version_tag = 'napi_build_version=';
+
+module.exports.get_napi_version = function() {
+  // returns the non-zero numeric napi version or undefined if napi is not supported.
+  // correctly supporting target requires an updated cross-walk
+  let version = process.versions.napi; // can be undefined
+  if (!version) { // this code should never need to be updated
+    if (versionArray[0] === 9 && versionArray[1] >= 3) version = 2; // 9.3.0+
+    else if (versionArray[0] === 8) version = 1; // 8.0.0+
+  }
+  return version;
+};
+
+module.exports.get_napi_version_as_string = function(target) {
+  // returns the napi version as a string or an empty string if napi is not supported.
+  const version = module.exports.get_napi_version(target);
+  return version ? '' + version : '';
+};
+
+module.exports.validate_package_json = function(package_json, opts) { // throws Error
+
+  const binary = package_json.binary;
+  const module_path_ok = pathOK(binary.module_path);
+  const remote_path_ok = pathOK(binary.remote_path);
+  const package_name_ok = pathOK(binary.package_name);
+  const napi_build_versions = module.exports.get_napi_build_versions(package_json, opts, true);
+  const napi_build_versions_raw = module.exports.get_napi_build_versions_raw(package_json);
+
+  if (napi_build_versions) {
+    napi_build_versions.forEach((napi_build_version)=> {
+      if (!(parseInt(napi_build_version, 10) === napi_build_version && napi_build_version > 0)) {
+        throw new Error('All values specified in napi_versions must be positive integers.');
+      }
+    });
+  }
+
+  if (napi_build_versions && (!module_path_ok || (!remote_path_ok && !package_name_ok))) {
+    throw new Error('When napi_versions is specified; module_path and either remote_path or ' +
+			"package_name must contain the substitution string '{napi_build_version}`.");
+  }
+
+  if ((module_path_ok || remote_path_ok || package_name_ok) && !napi_build_versions_raw) {
+    throw new Error("When the substitution string '{napi_build_version}` is specified in " +
+			'module_path, remote_path, or package_name; napi_versions must also be specified.');
+  }
+
+  if (napi_build_versions && !module.exports.get_best_napi_build_version(package_json, opts) &&
+	module.exports.build_napi_only(package_json)) {
+    throw new Error(
+      'The Node-API version of this Node instance is ' + module.exports.get_napi_version(opts ? opts.target : undefined) + '. ' +
+			'This module supports Node-API version(s) ' + module.exports.get_napi_build_versions_raw(package_json) + '. ' +
+			'This Node instance cannot run this module.');
+  }
+
+  if (napi_build_versions_raw && !napi_build_versions && module.exports.build_napi_only(package_json)) {
+    throw new Error(
+      'The Node-API version of this Node instance is ' + module.exports.get_napi_version(opts ? opts.target : undefined) + '. ' +
+			'This module supports Node-API version(s) ' + module.exports.get_napi_build_versions_raw(package_json) + '. ' +
+			'This Node instance cannot run this module.');
+  }
+
+};
+
+function pathOK(path) {
+  return path && (path.indexOf('{napi_build_version}') !== -1 || path.indexOf('{node_napi_label}') !== -1);
+}
+
+module.exports.expand_commands = function(package_json, opts, commands) {
+  const expanded_commands = [];
+  const napi_build_versions = module.exports.get_napi_build_versions(package_json, opts);
+  commands.forEach((command)=> {
+    if (napi_build_versions && command.name === 'install') {
+      const napi_build_version = module.exports.get_best_napi_build_version(package_json, opts);
+      const args = napi_build_version ? [napi_build_version_tag + napi_build_version] : [];
+      expanded_commands.push({ name: command.name, args: args });
+    } else if (napi_build_versions && napi_multiple_commands.indexOf(command.name) !== -1) {
+      napi_build_versions.forEach((napi_build_version)=> {
+        const args = command.args.slice();
+        args.push(napi_build_version_tag + napi_build_version);
+        expanded_commands.push({ name: command.name, args: args });
+      });
+    } else {
+      expanded_commands.push(command);
+    }
+  });
+  return expanded_commands;
+};
+
+module.exports.get_napi_build_versions = function(package_json, opts, warnings) { // opts may be undefined
+  const log = require('npmlog');
+  let napi_build_versions = [];
+  const supported_napi_version = module.exports.get_napi_version(opts ? opts.target : undefined);
+  // remove duplicates, verify each napi version can actaully be built
+  if (package_json.binary && package_json.binary.napi_versions) {
+    package_json.binary.napi_versions.forEach((napi_version) => {
+      const duplicated = napi_build_versions.indexOf(napi_version) !== -1;
+      if (!duplicated && supported_napi_version && napi_version <= supported_napi_version) {
+        napi_build_versions.push(napi_version);
+      } else if (warnings && !duplicated && supported_napi_version) {
+        log.info('This Node instance does not support builds for Node-API version', napi_version);
+      }
+    });
+  }
+  if (opts && opts['build-latest-napi-version-only']) {
+    let latest_version = 0;
+    napi_build_versions.forEach((napi_version) => {
+      if (napi_version > latest_version) latest_version = napi_version;
+    });
+    napi_build_versions = latest_version ? [latest_version] : [];
+  }
+  return napi_build_versions.length ? napi_build_versions : undefined;
+};
+
+module.exports.get_napi_build_versions_raw = function(package_json) {
+  const napi_build_versions = [];
+  // remove duplicates
+  if (package_json.binary && package_json.binary.napi_versions) {
+    package_json.binary.napi_versions.forEach((napi_version) => {
+      if (napi_build_versions.indexOf(napi_version) === -1) {
+        napi_build_versions.push(napi_version);
+      }
+    });
+  }
+  return napi_build_versions.length ? napi_build_versions : undefined;
+};
+
+module.exports.get_command_arg = function(napi_build_version) {
+  return napi_build_version_tag + napi_build_version;
+};
+
+module.exports.get_napi_build_version_from_command_args = function(command_args) {
+  for (let i = 0; i < command_args.length; i++) {
+    const arg = command_args[i];
+    if (arg.indexOf(napi_build_version_tag) === 0) {
+      return parseInt(arg.substr(napi_build_version_tag.length), 10);
+    }
+  }
+  return undefined;
+};
+
+module.exports.swap_build_dir_out = function(napi_build_version) {
+  if (napi_build_version) {
+    const rm = require('rimraf');
+    rm.sync(module.exports.get_build_dir(napi_build_version));
+    fs.renameSync('build', module.exports.get_build_dir(napi_build_version));
+  }
+};
+
+module.exports.swap_build_dir_in = function(napi_build_version) {
+  if (napi_build_version) {
+    const rm = require('rimraf');
+    rm.sync('build');
+    fs.renameSync(module.exports.get_build_dir(napi_build_version), 'build');
+  }
+};
+
+module.exports.get_build_dir = function(napi_build_version) {
+  return 'build-tmp-napi-v' + napi_build_version;
+};
+
+module.exports.get_best_napi_build_version = function(package_json, opts) {
+  let best_napi_build_version = 0;
+  const napi_build_versions = module.exports.get_napi_build_versions(package_json, opts);
+  if (napi_build_versions) {
+    const our_napi_version = module.exports.get_napi_version(opts ? opts.target : undefined);
+    napi_build_versions.forEach((napi_build_version)=> {
+      if (napi_build_version > best_napi_build_version &&
+				napi_build_version <= our_napi_version) {
+        best_napi_build_version = napi_build_version;
+      }
+    });
+  }
+  return best_napi_build_version === 0 ? undefined : best_napi_build_version;
+};
+
+module.exports.build_napi_only = function(package_json) {
+  return package_json.binary && package_json.binary.package_name &&
+	package_json.binary.package_name.indexOf('{node_napi_label}') === -1;
+};

+ 9 - 0
backend/node_modules/@mapbox/node-pre-gyp/lib/util/nw-pre-gyp/package.json

@@ -0,0 +1,9 @@
+{
+"main": "index.html",
+"name": "nw-pre-gyp-module-test",
+"description": "Node-webkit-based module test.",
+"version": "0.0.1",
+"window": {
+   "show": false
+}
+}

+ 163 - 0
backend/node_modules/@mapbox/node-pre-gyp/lib/util/s3_setup.js

@@ -0,0 +1,163 @@
+'use strict';
+
+module.exports = exports;
+
+const url = require('url');
+const fs = require('fs');
+const path = require('path');
+
+module.exports.detect = function(opts, config) {
+  const to = opts.hosted_path;
+  const uri = url.parse(to);
+  config.prefix = (!uri.pathname || uri.pathname === '/') ? '' : uri.pathname.replace('/', '');
+  if (opts.bucket && opts.region) {
+    config.bucket = opts.bucket;
+    config.region = opts.region;
+    config.endpoint = opts.host;
+    config.s3ForcePathStyle = opts.s3ForcePathStyle;
+  } else {
+    const parts = uri.hostname.split('.s3');
+    const bucket = parts[0];
+    if (!bucket) {
+      return;
+    }
+    if (!config.bucket) {
+      config.bucket = bucket;
+    }
+    if (!config.region) {
+      const region = parts[1].slice(1).split('.')[0];
+      if (region === 'amazonaws') {
+        config.region = 'us-east-1';
+      } else {
+        config.region = region;
+      }
+    }
+  }
+};
+
+module.exports.get_s3 = function(config) {
+
+  if (process.env.node_pre_gyp_mock_s3) {
+    // here we're mocking. node_pre_gyp_mock_s3 is the scratch directory
+    // for the mock code.
+    const AWSMock = require('mock-aws-s3');
+    const os = require('os');
+
+    AWSMock.config.basePath = `${os.tmpdir()}/mock`;
+
+    const s3 = AWSMock.S3();
+
+    // wrapped callback maker. fs calls return code of ENOENT but AWS.S3 returns
+    // NotFound.
+    const wcb = (fn) => (err, ...args) => {
+      if (err && err.code === 'ENOENT') {
+        err.code = 'NotFound';
+      }
+      return fn(err, ...args);
+    };
+
+    return {
+      listObjects(params, callback) {
+        return s3.listObjects(params, wcb(callback));
+      },
+      headObject(params, callback) {
+        return s3.headObject(params, wcb(callback));
+      },
+      deleteObject(params, callback) {
+        return s3.deleteObject(params, wcb(callback));
+      },
+      putObject(params, callback) {
+        return s3.putObject(params, wcb(callback));
+      }
+    };
+  }
+
+  // if not mocking then setup real s3.
+  const AWS = require('aws-sdk');
+
+  AWS.config.update(config);
+  const s3 = new AWS.S3();
+
+  // need to change if additional options need to be specified.
+  return {
+    listObjects(params, callback) {
+      return s3.listObjects(params, callback);
+    },
+    headObject(params, callback) {
+      return s3.headObject(params, callback);
+    },
+    deleteObject(params, callback) {
+      return s3.deleteObject(params, callback);
+    },
+    putObject(params, callback) {
+      return s3.putObject(params, callback);
+    }
+  };
+
+
+
+};
+
+//
+// function to get the mocking control function. if not mocking it returns a no-op.
+//
+// if mocking it sets up the mock http interceptors that use the mocked s3 file system
+// to fulfill reponses.
+module.exports.get_mockS3Http = function() {
+  let mock_s3 = false;
+  if (!process.env.node_pre_gyp_mock_s3) {
+    return () => mock_s3;
+  }
+
+  const nock = require('nock');
+  // the bucket used for testing, as addressed by https.
+  const host = 'https://mapbox-node-pre-gyp-public-testing-bucket.s3.us-east-1.amazonaws.com';
+  const mockDir = process.env.node_pre_gyp_mock_s3 + '/mapbox-node-pre-gyp-public-testing-bucket';
+
+  // function to setup interceptors. they are "turned off" by setting mock_s3 to false.
+  const mock_http = () => {
+    // eslint-disable-next-line no-unused-vars
+    function get(uri, requestBody) {
+      const filepath = path.join(mockDir, uri.replace('%2B', '+'));
+
+      try {
+        fs.accessSync(filepath, fs.constants.R_OK);
+      } catch (e) {
+        return [404, 'not found\n'];
+      }
+
+      // the mock s3 functions just write to disk, so just read from it.
+      return [200, fs.createReadStream(filepath)];
+    }
+
+    // eslint-disable-next-line no-unused-vars
+    return nock(host)
+      .persist()
+      .get(() => mock_s3) // mock any uri for s3 when true
+      .reply(get);
+  };
+
+  // setup interceptors. they check the mock_s3 flag to determine whether to intercept.
+  mock_http(nock, host, mockDir);
+  // function to turn matching all requests to s3 on/off.
+  const mockS3Http = (action) => {
+    const previous = mock_s3;
+    if (action === 'off') {
+      mock_s3 = false;
+    } else if (action === 'on') {
+      mock_s3 = true;
+    } else if (action !== 'get') {
+      throw new Error(`illegal action for setMockHttp ${action}`);
+    }
+    return previous;
+  };
+
+  // call mockS3Http with the argument
+  // - 'on' - turn it on
+  // - 'off' - turn it off (used by fetch.test.js so it doesn't interfere with redirects)
+  // - 'get' - return true or false for 'on' or 'off'
+  return mockS3Http;
+};
+
+
+

+ 335 - 0
backend/node_modules/@mapbox/node-pre-gyp/lib/util/versioning.js

@@ -0,0 +1,335 @@
+'use strict';
+
+module.exports = exports;
+
+const path = require('path');
+const semver = require('semver');
+const url = require('url');
+const detect_libc = require('detect-libc');
+const napi = require('./napi.js');
+
+let abi_crosswalk;
+
+// This is used for unit testing to provide a fake
+// ABI crosswalk that emulates one that is not updated
+// for the current version
+if (process.env.NODE_PRE_GYP_ABI_CROSSWALK) {
+  abi_crosswalk = require(process.env.NODE_PRE_GYP_ABI_CROSSWALK);
+} else {
+  abi_crosswalk = require('./abi_crosswalk.json');
+}
+
+const major_versions = {};
+Object.keys(abi_crosswalk).forEach((v) => {
+  const major = v.split('.')[0];
+  if (!major_versions[major]) {
+    major_versions[major] = v;
+  }
+});
+
+function get_electron_abi(runtime, target_version) {
+  if (!runtime) {
+    throw new Error('get_electron_abi requires valid runtime arg');
+  }
+  if (typeof target_version === 'undefined') {
+    // erroneous CLI call
+    throw new Error('Empty target version is not supported if electron is the target.');
+  }
+  // Electron guarantees that patch version update won't break native modules.
+  const sem_ver = semver.parse(target_version);
+  return runtime + '-v' + sem_ver.major + '.' + sem_ver.minor;
+}
+module.exports.get_electron_abi = get_electron_abi;
+
+function get_node_webkit_abi(runtime, target_version) {
+  if (!runtime) {
+    throw new Error('get_node_webkit_abi requires valid runtime arg');
+  }
+  if (typeof target_version === 'undefined') {
+    // erroneous CLI call
+    throw new Error('Empty target version is not supported if node-webkit is the target.');
+  }
+  return runtime + '-v' + target_version;
+}
+module.exports.get_node_webkit_abi = get_node_webkit_abi;
+
+function get_node_abi(runtime, versions) {
+  if (!runtime) {
+    throw new Error('get_node_abi requires valid runtime arg');
+  }
+  if (!versions) {
+    throw new Error('get_node_abi requires valid process.versions object');
+  }
+  const sem_ver = semver.parse(versions.node);
+  if (sem_ver.major === 0 && sem_ver.minor % 2) { // odd series
+    // https://github.com/mapbox/node-pre-gyp/issues/124
+    return runtime + '-v' + versions.node;
+  } else {
+    // process.versions.modules added in >= v0.10.4 and v0.11.7
+    // https://github.com/joyent/node/commit/ccabd4a6fa8a6eb79d29bc3bbe9fe2b6531c2d8e
+    return versions.modules ? runtime + '-v' + (+versions.modules) :
+      'v8-' + versions.v8.split('.').slice(0, 2).join('.');
+  }
+}
+module.exports.get_node_abi = get_node_abi;
+
+function get_runtime_abi(runtime, target_version) {
+  if (!runtime) {
+    throw new Error('get_runtime_abi requires valid runtime arg');
+  }
+  if (runtime === 'node-webkit') {
+    return get_node_webkit_abi(runtime, target_version || process.versions['node-webkit']);
+  } else if (runtime === 'electron') {
+    return get_electron_abi(runtime, target_version || process.versions.electron);
+  } else {
+    if (runtime !== 'node') {
+      throw new Error("Unknown Runtime: '" + runtime + "'");
+    }
+    if (!target_version) {
+      return get_node_abi(runtime, process.versions);
+    } else {
+      let cross_obj;
+      // abi_crosswalk generated with ./scripts/abi_crosswalk.js
+      if (abi_crosswalk[target_version]) {
+        cross_obj = abi_crosswalk[target_version];
+      } else {
+        const target_parts = target_version.split('.').map((i) => { return +i; });
+        if (target_parts.length !== 3) { // parse failed
+          throw new Error('Unknown target version: ' + target_version);
+        }
+        /*
+                    The below code tries to infer the last known ABI compatible version
+                    that we have recorded in the abi_crosswalk.json when an exact match
+                    is not possible. The reasons for this to exist are complicated:
+
+                       - We support passing --target to be able to allow developers to package binaries for versions of node
+                         that are not the same one as they are running. This might also be used in combination with the
+                         --target_arch or --target_platform flags to also package binaries for alternative platforms
+                       - When --target is passed we can't therefore determine the ABI (process.versions.modules) from the node
+                         version that is running in memory
+                       - So, therefore node-pre-gyp keeps an "ABI crosswalk" (lib/util/abi_crosswalk.json) to be able to look
+                         this info up for all versions
+                       - But we cannot easily predict what the future ABI will be for released versions
+                       - And node-pre-gyp needs to be a `bundledDependency` in apps that depend on it in order to work correctly
+                         by being fully available at install time.
+                       - So, the speed of node releases and the bundled nature of node-pre-gyp mean that a new node-pre-gyp release
+                         need to happen for every node.js/io.js/node-webkit/nw.js/atom-shell/etc release that might come online if
+                         you want the `--target` flag to keep working for the latest version
+                       - Which is impractical ^^
+                       - Hence the below code guesses about future ABI to make the need to update node-pre-gyp less demanding.
+
+                    In practice then you can have a dependency of your app like `node-sqlite3` that bundles a `node-pre-gyp` that
+                    only knows about node v0.10.33 in the `abi_crosswalk.json` but target node v0.10.34 (which is assumed to be
+                    ABI compatible with v0.10.33).
+
+                    TODO: use semver module instead of custom version parsing
+                */
+        const major = target_parts[0];
+        let minor = target_parts[1];
+        let patch = target_parts[2];
+        // io.js: yeah if node.js ever releases 1.x this will break
+        // but that is unlikely to happen: https://github.com/iojs/io.js/pull/253#issuecomment-69432616
+        if (major === 1) {
+          // look for last release that is the same major version
+          // e.g. we assume io.js 1.x is ABI compatible with >= 1.0.0
+          while (true) {
+            if (minor > 0) --minor;
+            if (patch > 0) --patch;
+            const new_iojs_target = '' + major + '.' + minor + '.' + patch;
+            if (abi_crosswalk[new_iojs_target]) {
+              cross_obj = abi_crosswalk[new_iojs_target];
+              console.log('Warning: node-pre-gyp could not find exact match for ' + target_version);
+              console.log('Warning: but node-pre-gyp successfully choose ' + new_iojs_target + ' as ABI compatible target');
+              break;
+            }
+            if (minor === 0 && patch === 0) {
+              break;
+            }
+          }
+        } else if (major >= 2) {
+          // look for last release that is the same major version
+          if (major_versions[major]) {
+            cross_obj = abi_crosswalk[major_versions[major]];
+            console.log('Warning: node-pre-gyp could not find exact match for ' + target_version);
+            console.log('Warning: but node-pre-gyp successfully choose ' + major_versions[major] + ' as ABI compatible target');
+          }
+        } else if (major === 0) { // node.js
+          if (target_parts[1] % 2 === 0) { // for stable/even node.js series
+            // look for the last release that is the same minor release
+            // e.g. we assume node 0.10.x is ABI compatible with >= 0.10.0
+            while (--patch > 0) {
+              const new_node_target = '' + major + '.' + minor + '.' + patch;
+              if (abi_crosswalk[new_node_target]) {
+                cross_obj = abi_crosswalk[new_node_target];
+                console.log('Warning: node-pre-gyp could not find exact match for ' + target_version);
+                console.log('Warning: but node-pre-gyp successfully choose ' + new_node_target + ' as ABI compatible target');
+                break;
+              }
+            }
+          }
+        }
+      }
+      if (!cross_obj) {
+        throw new Error('Unsupported target version: ' + target_version);
+      }
+      // emulate process.versions
+      const versions_obj = {
+        node: target_version,
+        v8: cross_obj.v8 + '.0',
+        // abi_crosswalk uses 1 for node versions lacking process.versions.modules
+        // process.versions.modules added in >= v0.10.4 and v0.11.7
+        modules: cross_obj.node_abi > 1 ? cross_obj.node_abi : undefined
+      };
+      return get_node_abi(runtime, versions_obj);
+    }
+  }
+}
+module.exports.get_runtime_abi = get_runtime_abi;
+
+const required_parameters = [
+  'module_name',
+  'module_path',
+  'host'
+];
+
+function validate_config(package_json, opts) {
+  const msg = package_json.name + ' package.json is not node-pre-gyp ready:\n';
+  const missing = [];
+  if (!package_json.main) {
+    missing.push('main');
+  }
+  if (!package_json.version) {
+    missing.push('version');
+  }
+  if (!package_json.name) {
+    missing.push('name');
+  }
+  if (!package_json.binary) {
+    missing.push('binary');
+  }
+  const o = package_json.binary;
+  if (o) {
+    required_parameters.forEach((p) => {
+      if (!o[p] || typeof o[p] !== 'string') {
+        missing.push('binary.' + p);
+      }
+    });
+  }
+
+  if (missing.length >= 1) {
+    throw new Error(msg + 'package.json must declare these properties: \n' + missing.join('\n'));
+  }
+  if (o) {
+    // enforce https over http
+    const protocol = url.parse(o.host).protocol;
+    if (protocol === 'http:') {
+      throw new Error("'host' protocol (" + protocol + ") is invalid - only 'https:' is accepted");
+    }
+  }
+  napi.validate_package_json(package_json, opts);
+}
+
+module.exports.validate_config = validate_config;
+
+function eval_template(template, opts) {
+  Object.keys(opts).forEach((key) => {
+    const pattern = '{' + key + '}';
+    while (template.indexOf(pattern) > -1) {
+      template = template.replace(pattern, opts[key]);
+    }
+  });
+  return template;
+}
+
+// url.resolve needs single trailing slash
+// to behave correctly, otherwise a double slash
+// may end up in the url which breaks requests
+// and a lacking slash may not lead to proper joining
+function fix_slashes(pathname) {
+  if (pathname.slice(-1) !== '/') {
+    return pathname + '/';
+  }
+  return pathname;
+}
+
+// remove double slashes
+// note: path.normalize will not work because
+// it will convert forward to back slashes
+function drop_double_slashes(pathname) {
+  return pathname.replace(/\/\//g, '/');
+}
+
+function get_process_runtime(versions) {
+  let runtime = 'node';
+  if (versions['node-webkit']) {
+    runtime = 'node-webkit';
+  } else if (versions.electron) {
+    runtime = 'electron';
+  }
+  return runtime;
+}
+
+module.exports.get_process_runtime = get_process_runtime;
+
+const default_package_name = '{module_name}-v{version}-{node_abi}-{platform}-{arch}.tar.gz';
+const default_remote_path = '';
+
+module.exports.evaluate = function(package_json, options, napi_build_version) {
+  options = options || {};
+  validate_config(package_json, options); // options is a suitable substitute for opts in this case
+  const v = package_json.version;
+  const module_version = semver.parse(v);
+  const runtime = options.runtime || get_process_runtime(process.versions);
+  const opts = {
+    name: package_json.name,
+    configuration: options.debug ? 'Debug' : 'Release',
+    debug: options.debug,
+    module_name: package_json.binary.module_name,
+    version: module_version.version,
+    prerelease: module_version.prerelease.length ? module_version.prerelease.join('.') : '',
+    build: module_version.build.length ? module_version.build.join('.') : '',
+    major: module_version.major,
+    minor: module_version.minor,
+    patch: module_version.patch,
+    runtime: runtime,
+    node_abi: get_runtime_abi(runtime, options.target),
+    node_abi_napi: napi.get_napi_version(options.target) ? 'napi' : get_runtime_abi(runtime, options.target),
+    napi_version: napi.get_napi_version(options.target), // non-zero numeric, undefined if unsupported
+    napi_build_version: napi_build_version || '',
+    node_napi_label: napi_build_version ? 'napi-v' + napi_build_version : get_runtime_abi(runtime, options.target),
+    target: options.target || '',
+    platform: options.target_platform || process.platform,
+    target_platform: options.target_platform || process.platform,
+    arch: options.target_arch || process.arch,
+    target_arch: options.target_arch || process.arch,
+    libc: options.target_libc || detect_libc.familySync() || 'unknown',
+    module_main: package_json.main,
+    toolset: options.toolset || '', // address https://github.com/mapbox/node-pre-gyp/issues/119
+    bucket: package_json.binary.bucket,
+    region: package_json.binary.region,
+    s3ForcePathStyle: package_json.binary.s3ForcePathStyle || false
+  };
+    // support host mirror with npm config `--{module_name}_binary_host_mirror`
+    // e.g.: https://github.com/node-inspector/v8-profiler/blob/master/package.json#L25
+    // > npm install v8-profiler --profiler_binary_host_mirror=https://npm.taobao.org/mirrors/node-inspector/
+  const validModuleName = opts.module_name.replace('-', '_');
+  const host = process.env['npm_config_' + validModuleName + '_binary_host_mirror'] || package_json.binary.host;
+  opts.host = fix_slashes(eval_template(host, opts));
+  opts.module_path = eval_template(package_json.binary.module_path, opts);
+  // now we resolve the module_path to ensure it is absolute so that binding.gyp variables work predictably
+  if (options.module_root) {
+    // resolve relative to known module root: works for pre-binding require
+    opts.module_path = path.join(options.module_root, opts.module_path);
+  } else {
+    // resolve relative to current working directory: works for node-pre-gyp commands
+    opts.module_path = path.resolve(opts.module_path);
+  }
+  opts.module = path.join(opts.module_path, opts.module_name + '.node');
+  opts.remote_path = package_json.binary.remote_path ? drop_double_slashes(fix_slashes(eval_template(package_json.binary.remote_path, opts))) : default_remote_path;
+  const package_name = package_json.binary.package_name ? package_json.binary.package_name : default_package_name;
+  opts.package_name = eval_template(package_name, opts);
+  opts.staged_tarball = path.join('build/stage', opts.remote_path, opts.package_name);
+  opts.hosted_path = url.resolve(opts.host, opts.remote_path);
+  opts.hosted_tarball = url.resolve(opts.hosted_path, opts.package_name);
+  return opts;
+};

+ 54 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/nopt/bin/nopt.js

@@ -0,0 +1,54 @@
+#!/usr/bin/env node
+var nopt = require("../lib/nopt")
+  , path = require("path")
+  , types = { num: Number
+            , bool: Boolean
+            , help: Boolean
+            , list: Array
+            , "num-list": [Number, Array]
+            , "str-list": [String, Array]
+            , "bool-list": [Boolean, Array]
+            , str: String
+            , clear: Boolean
+            , config: Boolean
+            , length: Number
+            , file: path
+            }
+  , shorthands = { s: [ "--str", "astring" ]
+                 , b: [ "--bool" ]
+                 , nb: [ "--no-bool" ]
+                 , tft: [ "--bool-list", "--no-bool-list", "--bool-list", "true" ]
+                 , "?": ["--help"]
+                 , h: ["--help"]
+                 , H: ["--help"]
+                 , n: [ "--num", "125" ]
+                 , c: ["--config"]
+                 , l: ["--length"]
+                 , f: ["--file"]
+                 }
+  , parsed = nopt( types
+                 , shorthands
+                 , process.argv
+                 , 2 )
+
+console.log("parsed", parsed)
+
+if (parsed.help) {
+  console.log("")
+  console.log("nopt cli tester")
+  console.log("")
+  console.log("types")
+  console.log(Object.keys(types).map(function M (t) {
+    var type = types[t]
+    if (Array.isArray(type)) {
+      return [t, type.map(function (type) { return type.name })]
+    }
+    return [t, type && type.name]
+  }).reduce(function (s, i) {
+    s[i[0]] = i[1]
+    return s
+  }, {}))
+  console.log("")
+  console.log("shorthands")
+  console.log(shorthands)
+}

+ 441 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/nopt/lib/nopt.js

@@ -0,0 +1,441 @@
+// info about each config option.
+
+var debug = process.env.DEBUG_NOPT || process.env.NOPT_DEBUG
+  ? function () { console.error.apply(console, arguments) }
+  : function () {}
+
+var url = require("url")
+  , path = require("path")
+  , Stream = require("stream").Stream
+  , abbrev = require("abbrev")
+  , os = require("os")
+
+module.exports = exports = nopt
+exports.clean = clean
+
+exports.typeDefs =
+  { String  : { type: String,  validate: validateString  }
+  , Boolean : { type: Boolean, validate: validateBoolean }
+  , url     : { type: url,     validate: validateUrl     }
+  , Number  : { type: Number,  validate: validateNumber  }
+  , path    : { type: path,    validate: validatePath    }
+  , Stream  : { type: Stream,  validate: validateStream  }
+  , Date    : { type: Date,    validate: validateDate    }
+  }
+
+function nopt (types, shorthands, args, slice) {
+  args = args || process.argv
+  types = types || {}
+  shorthands = shorthands || {}
+  if (typeof slice !== "number") slice = 2
+
+  debug(types, shorthands, args, slice)
+
+  args = args.slice(slice)
+  var data = {}
+    , key
+    , argv = {
+        remain: [],
+        cooked: args,
+        original: args.slice(0)
+      }
+
+  parse(args, data, argv.remain, types, shorthands)
+  // now data is full
+  clean(data, types, exports.typeDefs)
+  data.argv = argv
+  Object.defineProperty(data.argv, 'toString', { value: function () {
+    return this.original.map(JSON.stringify).join(" ")
+  }, enumerable: false })
+  return data
+}
+
+function clean (data, types, typeDefs) {
+  typeDefs = typeDefs || exports.typeDefs
+  var remove = {}
+    , typeDefault = [false, true, null, String, Array]
+
+  Object.keys(data).forEach(function (k) {
+    if (k === "argv") return
+    var val = data[k]
+      , isArray = Array.isArray(val)
+      , type = types[k]
+    if (!isArray) val = [val]
+    if (!type) type = typeDefault
+    if (type === Array) type = typeDefault.concat(Array)
+    if (!Array.isArray(type)) type = [type]
+
+    debug("val=%j", val)
+    debug("types=", type)
+    val = val.map(function (val) {
+      // if it's an unknown value, then parse false/true/null/numbers/dates
+      if (typeof val === "string") {
+        debug("string %j", val)
+        val = val.trim()
+        if ((val === "null" && ~type.indexOf(null))
+            || (val === "true" &&
+               (~type.indexOf(true) || ~type.indexOf(Boolean)))
+            || (val === "false" &&
+               (~type.indexOf(false) || ~type.indexOf(Boolean)))) {
+          val = JSON.parse(val)
+          debug("jsonable %j", val)
+        } else if (~type.indexOf(Number) && !isNaN(val)) {
+          debug("convert to number", val)
+          val = +val
+        } else if (~type.indexOf(Date) && !isNaN(Date.parse(val))) {
+          debug("convert to date", val)
+          val = new Date(val)
+        }
+      }
+
+      if (!types.hasOwnProperty(k)) {
+        return val
+      }
+
+      // allow `--no-blah` to set 'blah' to null if null is allowed
+      if (val === false && ~type.indexOf(null) &&
+          !(~type.indexOf(false) || ~type.indexOf(Boolean))) {
+        val = null
+      }
+
+      var d = {}
+      d[k] = val
+      debug("prevalidated val", d, val, types[k])
+      if (!validate(d, k, val, types[k], typeDefs)) {
+        if (exports.invalidHandler) {
+          exports.invalidHandler(k, val, types[k], data)
+        } else if (exports.invalidHandler !== false) {
+          debug("invalid: "+k+"="+val, types[k])
+        }
+        return remove
+      }
+      debug("validated val", d, val, types[k])
+      return d[k]
+    }).filter(function (val) { return val !== remove })
+
+    // if we allow Array specifically, then an empty array is how we
+    // express 'no value here', not null.  Allow it.
+    if (!val.length && type.indexOf(Array) === -1) {
+      debug('VAL HAS NO LENGTH, DELETE IT', val, k, type.indexOf(Array))
+      delete data[k]
+    }
+    else if (isArray) {
+      debug(isArray, data[k], val)
+      data[k] = val
+    } else data[k] = val[0]
+
+    debug("k=%s val=%j", k, val, data[k])
+  })
+}
+
+function validateString (data, k, val) {
+  data[k] = String(val)
+}
+
+function validatePath (data, k, val) {
+  if (val === true) return false
+  if (val === null) return true
+
+  val = String(val)
+
+  var isWin       = process.platform === 'win32'
+    , homePattern = isWin ? /^~(\/|\\)/ : /^~\//
+    , home        = os.homedir()
+
+  if (home && val.match(homePattern)) {
+    data[k] = path.resolve(home, val.substr(2))
+  } else {
+    data[k] = path.resolve(val)
+  }
+  return true
+}
+
+function validateNumber (data, k, val) {
+  debug("validate Number %j %j %j", k, val, isNaN(val))
+  if (isNaN(val)) return false
+  data[k] = +val
+}
+
+function validateDate (data, k, val) {
+  var s = Date.parse(val)
+  debug("validate Date %j %j %j", k, val, s)
+  if (isNaN(s)) return false
+  data[k] = new Date(val)
+}
+
+function validateBoolean (data, k, val) {
+  if (val instanceof Boolean) val = val.valueOf()
+  else if (typeof val === "string") {
+    if (!isNaN(val)) val = !!(+val)
+    else if (val === "null" || val === "false") val = false
+    else val = true
+  } else val = !!val
+  data[k] = val
+}
+
+function validateUrl (data, k, val) {
+  val = url.parse(String(val))
+  if (!val.host) return false
+  data[k] = val.href
+}
+
+function validateStream (data, k, val) {
+  if (!(val instanceof Stream)) return false
+  data[k] = val
+}
+
+function validate (data, k, val, type, typeDefs) {
+  // arrays are lists of types.
+  if (Array.isArray(type)) {
+    for (var i = 0, l = type.length; i < l; i ++) {
+      if (type[i] === Array) continue
+      if (validate(data, k, val, type[i], typeDefs)) return true
+    }
+    delete data[k]
+    return false
+  }
+
+  // an array of anything?
+  if (type === Array) return true
+
+  // NaN is poisonous.  Means that something is not allowed.
+  if (type !== type) {
+    debug("Poison NaN", k, val, type)
+    delete data[k]
+    return false
+  }
+
+  // explicit list of values
+  if (val === type) {
+    debug("Explicitly allowed %j", val)
+    // if (isArray) (data[k] = data[k] || []).push(val)
+    // else data[k] = val
+    data[k] = val
+    return true
+  }
+
+  // now go through the list of typeDefs, validate against each one.
+  var ok = false
+    , types = Object.keys(typeDefs)
+  for (var i = 0, l = types.length; i < l; i ++) {
+    debug("test type %j %j %j", k, val, types[i])
+    var t = typeDefs[types[i]]
+    if (t &&
+      ((type && type.name && t.type && t.type.name) ? (type.name === t.type.name) : (type === t.type))) {
+      var d = {}
+      ok = false !== t.validate(d, k, val)
+      val = d[k]
+      if (ok) {
+        // if (isArray) (data[k] = data[k] || []).push(val)
+        // else data[k] = val
+        data[k] = val
+        break
+      }
+    }
+  }
+  debug("OK? %j (%j %j %j)", ok, k, val, types[i])
+
+  if (!ok) delete data[k]
+  return ok
+}
+
+function parse (args, data, remain, types, shorthands) {
+  debug("parse", args, data, remain)
+
+  var key = null
+    , abbrevs = abbrev(Object.keys(types))
+    , shortAbbr = abbrev(Object.keys(shorthands))
+
+  for (var i = 0; i < args.length; i ++) {
+    var arg = args[i]
+    debug("arg", arg)
+
+    if (arg.match(/^-{2,}$/)) {
+      // done with keys.
+      // the rest are args.
+      remain.push.apply(remain, args.slice(i + 1))
+      args[i] = "--"
+      break
+    }
+    var hadEq = false
+    if (arg.charAt(0) === "-" && arg.length > 1) {
+      var at = arg.indexOf('=')
+      if (at > -1) {
+        hadEq = true
+        var v = arg.substr(at + 1)
+        arg = arg.substr(0, at)
+        args.splice(i, 1, arg, v)
+      }
+
+      // see if it's a shorthand
+      // if so, splice and back up to re-parse it.
+      var shRes = resolveShort(arg, shorthands, shortAbbr, abbrevs)
+      debug("arg=%j shRes=%j", arg, shRes)
+      if (shRes) {
+        debug(arg, shRes)
+        args.splice.apply(args, [i, 1].concat(shRes))
+        if (arg !== shRes[0]) {
+          i --
+          continue
+        }
+      }
+      arg = arg.replace(/^-+/, "")
+      var no = null
+      while (arg.toLowerCase().indexOf("no-") === 0) {
+        no = !no
+        arg = arg.substr(3)
+      }
+
+      if (abbrevs[arg]) arg = abbrevs[arg]
+
+      var argType = types[arg]
+      var isTypeArray = Array.isArray(argType)
+      if (isTypeArray && argType.length === 1) {
+        isTypeArray = false
+        argType = argType[0]
+      }
+
+      var isArray = argType === Array ||
+        isTypeArray && argType.indexOf(Array) !== -1
+
+      // allow unknown things to be arrays if specified multiple times.
+      if (!types.hasOwnProperty(arg) && data.hasOwnProperty(arg)) {
+        if (!Array.isArray(data[arg]))
+          data[arg] = [data[arg]]
+        isArray = true
+      }
+
+      var val
+        , la = args[i + 1]
+
+      var isBool = typeof no === 'boolean' ||
+        argType === Boolean ||
+        isTypeArray && argType.indexOf(Boolean) !== -1 ||
+        (typeof argType === 'undefined' && !hadEq) ||
+        (la === "false" &&
+         (argType === null ||
+          isTypeArray && ~argType.indexOf(null)))
+
+      if (isBool) {
+        // just set and move along
+        val = !no
+        // however, also support --bool true or --bool false
+        if (la === "true" || la === "false") {
+          val = JSON.parse(la)
+          la = null
+          if (no) val = !val
+          i ++
+        }
+
+        // also support "foo":[Boolean, "bar"] and "--foo bar"
+        if (isTypeArray && la) {
+          if (~argType.indexOf(la)) {
+            // an explicit type
+            val = la
+            i ++
+          } else if ( la === "null" && ~argType.indexOf(null) ) {
+            // null allowed
+            val = null
+            i ++
+          } else if ( !la.match(/^-{2,}[^-]/) &&
+                      !isNaN(la) &&
+                      ~argType.indexOf(Number) ) {
+            // number
+            val = +la
+            i ++
+          } else if ( !la.match(/^-[^-]/) && ~argType.indexOf(String) ) {
+            // string
+            val = la
+            i ++
+          }
+        }
+
+        if (isArray) (data[arg] = data[arg] || []).push(val)
+        else data[arg] = val
+
+        continue
+      }
+
+      if (argType === String) {
+        if (la === undefined) {
+          la = ""
+        } else if (la.match(/^-{1,2}[^-]+/)) {
+          la = ""
+          i --
+        }
+      }
+
+      if (la && la.match(/^-{2,}$/)) {
+        la = undefined
+        i --
+      }
+
+      val = la === undefined ? true : la
+      if (isArray) (data[arg] = data[arg] || []).push(val)
+      else data[arg] = val
+
+      i ++
+      continue
+    }
+    remain.push(arg)
+  }
+}
+
+function resolveShort (arg, shorthands, shortAbbr, abbrevs) {
+  // handle single-char shorthands glommed together, like
+  // npm ls -glp, but only if there is one dash, and only if
+  // all of the chars are single-char shorthands, and it's
+  // not a match to some other abbrev.
+  arg = arg.replace(/^-+/, '')
+
+  // if it's an exact known option, then don't go any further
+  if (abbrevs[arg] === arg)
+    return null
+
+  // if it's an exact known shortopt, same deal
+  if (shorthands[arg]) {
+    // make it an array, if it's a list of words
+    if (shorthands[arg] && !Array.isArray(shorthands[arg]))
+      shorthands[arg] = shorthands[arg].split(/\s+/)
+
+    return shorthands[arg]
+  }
+
+  // first check to see if this arg is a set of single-char shorthands
+  var singles = shorthands.___singles
+  if (!singles) {
+    singles = Object.keys(shorthands).filter(function (s) {
+      return s.length === 1
+    }).reduce(function (l,r) {
+      l[r] = true
+      return l
+    }, {})
+    shorthands.___singles = singles
+    debug('shorthand singles', singles)
+  }
+
+  var chrs = arg.split("").filter(function (c) {
+    return singles[c]
+  })
+
+  if (chrs.join("") === arg) return chrs.map(function (c) {
+    return shorthands[c]
+  }).reduce(function (l, r) {
+    return l.concat(r)
+  }, [])
+
+
+  // if it's an arg abbrev, and not a literal shorthand, then prefer the arg
+  if (abbrevs[arg] && !shorthands[arg])
+    return null
+
+  // if it's an abbr for a shorthand, then use that
+  if (shortAbbr[arg])
+    arg = shortAbbr[arg]
+
+  // make it an array, if it's a list of words
+  if (shorthands[arg] && !Array.isArray(shorthands[arg]))
+    shorthands[arg] = shorthands[arg].split(/\s+/)
+
+  return shorthands[arg]
+}

+ 34 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/nopt/package.json

@@ -0,0 +1,34 @@
+{
+  "name": "nopt",
+  "version": "5.0.0",
+  "description": "Option parsing for Node, supporting types, shorthands, etc. Used by npm.",
+  "author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me/)",
+  "main": "lib/nopt.js",
+  "scripts": {
+    "preversion": "npm test",
+    "postversion": "npm publish",
+    "prepublishOnly": "git push origin --follow-tags",
+    "test": "tap test/*.js"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/npm/nopt.git"
+  },
+  "bin": {
+    "nopt": "bin/nopt.js"
+  },
+  "license": "ISC",
+  "dependencies": {
+    "abbrev": "1"
+  },
+  "devDependencies": {
+    "tap": "^14.10.6"
+  },
+  "files": [
+    "bin",
+    "lib"
+  ],
+  "engines": {
+    "node": ">=6"
+  }
+}

+ 183 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/bin/semver.js

@@ -0,0 +1,183 @@
+#!/usr/bin/env node
+// Standalone semver comparison program.
+// Exits successfully and prints matching version(s) if
+// any supplied version is valid and passes all tests.
+
+const argv = process.argv.slice(2)
+
+let versions = []
+
+const range = []
+
+let inc = null
+
+const version = require('../package.json').version
+
+let loose = false
+
+let includePrerelease = false
+
+let coerce = false
+
+let rtl = false
+
+let identifier
+
+const semver = require('../')
+
+let reverse = false
+
+let options = {}
+
+const main = () => {
+  if (!argv.length) {
+    return help()
+  }
+  while (argv.length) {
+    let a = argv.shift()
+    const indexOfEqualSign = a.indexOf('=')
+    if (indexOfEqualSign !== -1) {
+      const value = a.slice(indexOfEqualSign + 1)
+      a = a.slice(0, indexOfEqualSign)
+      argv.unshift(value)
+    }
+    switch (a) {
+      case '-rv': case '-rev': case '--rev': case '--reverse':
+        reverse = true
+        break
+      case '-l': case '--loose':
+        loose = true
+        break
+      case '-p': case '--include-prerelease':
+        includePrerelease = true
+        break
+      case '-v': case '--version':
+        versions.push(argv.shift())
+        break
+      case '-i': case '--inc': case '--increment':
+        switch (argv[0]) {
+          case 'major': case 'minor': case 'patch': case 'prerelease':
+          case 'premajor': case 'preminor': case 'prepatch':
+            inc = argv.shift()
+            break
+          default:
+            inc = 'patch'
+            break
+        }
+        break
+      case '--preid':
+        identifier = argv.shift()
+        break
+      case '-r': case '--range':
+        range.push(argv.shift())
+        break
+      case '-c': case '--coerce':
+        coerce = true
+        break
+      case '--rtl':
+        rtl = true
+        break
+      case '--ltr':
+        rtl = false
+        break
+      case '-h': case '--help': case '-?':
+        return help()
+      default:
+        versions.push(a)
+        break
+    }
+  }
+
+  options = { loose: loose, includePrerelease: includePrerelease, rtl: rtl }
+
+  versions = versions.map((v) => {
+    return coerce ? (semver.coerce(v, options) || { version: v }).version : v
+  }).filter((v) => {
+    return semver.valid(v)
+  })
+  if (!versions.length) {
+    return fail()
+  }
+  if (inc && (versions.length !== 1 || range.length)) {
+    return failInc()
+  }
+
+  for (let i = 0, l = range.length; i < l; i++) {
+    versions = versions.filter((v) => {
+      return semver.satisfies(v, range[i], options)
+    })
+    if (!versions.length) {
+      return fail()
+    }
+  }
+  return success(versions)
+}
+
+const failInc = () => {
+  console.error('--inc can only be used on a single version with no range')
+  fail()
+}
+
+const fail = () => process.exit(1)
+
+const success = () => {
+  const compare = reverse ? 'rcompare' : 'compare'
+  versions.sort((a, b) => {
+    return semver[compare](a, b, options)
+  }).map((v) => {
+    return semver.clean(v, options)
+  }).map((v) => {
+    return inc ? semver.inc(v, inc, options, identifier) : v
+  }).forEach((v, i, _) => {
+    console.log(v)
+  })
+}
+
+const help = () => console.log(
+`SemVer ${version}
+
+A JavaScript implementation of the https://semver.org/ specification
+Copyright Isaac Z. Schlueter
+
+Usage: semver [options] <version> [<version> [...]]
+Prints valid versions sorted by SemVer precedence
+
+Options:
+-r --range <range>
+        Print versions that match the specified range.
+
+-i --increment [<level>]
+        Increment a version by the specified level.  Level can
+        be one of: major, minor, patch, premajor, preminor,
+        prepatch, or prerelease.  Default level is 'patch'.
+        Only one version may be specified.
+
+--preid <identifier>
+        Identifier to be used to prefix premajor, preminor,
+        prepatch or prerelease version increments.
+
+-l --loose
+        Interpret versions and ranges loosely
+
+-p --include-prerelease
+        Always include prerelease versions in range matching
+
+-c --coerce
+        Coerce a string into SemVer if possible
+        (does not imply --loose)
+
+--rtl
+        Coerce version strings right to left
+
+--ltr
+        Coerce version strings left to right (default)
+
+Program exits successfully if any valid version satisfies
+all supplied ranges, and prints all satisfying versions.
+
+If no satisfying versions are found, then exits failure.
+
+Versions are printed in ascending order, so supplying
+multiple versions to the utility will just sort them.`)
+
+main()

+ 136 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/classes/comparator.js

@@ -0,0 +1,136 @@
+const ANY = Symbol('SemVer ANY')
+// hoisted class for cyclic dependency
+class Comparator {
+  static get ANY () {
+    return ANY
+  }
+
+  constructor (comp, options) {
+    options = parseOptions(options)
+
+    if (comp instanceof Comparator) {
+      if (comp.loose === !!options.loose) {
+        return comp
+      } else {
+        comp = comp.value
+      }
+    }
+
+    debug('comparator', comp, options)
+    this.options = options
+    this.loose = !!options.loose
+    this.parse(comp)
+
+    if (this.semver === ANY) {
+      this.value = ''
+    } else {
+      this.value = this.operator + this.semver.version
+    }
+
+    debug('comp', this)
+  }
+
+  parse (comp) {
+    const r = this.options.loose ? re[t.COMPARATORLOOSE] : re[t.COMPARATOR]
+    const m = comp.match(r)
+
+    if (!m) {
+      throw new TypeError(`Invalid comparator: ${comp}`)
+    }
+
+    this.operator = m[1] !== undefined ? m[1] : ''
+    if (this.operator === '=') {
+      this.operator = ''
+    }
+
+    // if it literally is just '>' or '' then allow anything.
+    if (!m[2]) {
+      this.semver = ANY
+    } else {
+      this.semver = new SemVer(m[2], this.options.loose)
+    }
+  }
+
+  toString () {
+    return this.value
+  }
+
+  test (version) {
+    debug('Comparator.test', version, this.options.loose)
+
+    if (this.semver === ANY || version === ANY) {
+      return true
+    }
+
+    if (typeof version === 'string') {
+      try {
+        version = new SemVer(version, this.options)
+      } catch (er) {
+        return false
+      }
+    }
+
+    return cmp(version, this.operator, this.semver, this.options)
+  }
+
+  intersects (comp, options) {
+    if (!(comp instanceof Comparator)) {
+      throw new TypeError('a Comparator is required')
+    }
+
+    if (!options || typeof options !== 'object') {
+      options = {
+        loose: !!options,
+        includePrerelease: false,
+      }
+    }
+
+    if (this.operator === '') {
+      if (this.value === '') {
+        return true
+      }
+      return new Range(comp.value, options).test(this.value)
+    } else if (comp.operator === '') {
+      if (comp.value === '') {
+        return true
+      }
+      return new Range(this.value, options).test(comp.semver)
+    }
+
+    const sameDirectionIncreasing =
+      (this.operator === '>=' || this.operator === '>') &&
+      (comp.operator === '>=' || comp.operator === '>')
+    const sameDirectionDecreasing =
+      (this.operator === '<=' || this.operator === '<') &&
+      (comp.operator === '<=' || comp.operator === '<')
+    const sameSemVer = this.semver.version === comp.semver.version
+    const differentDirectionsInclusive =
+      (this.operator === '>=' || this.operator === '<=') &&
+      (comp.operator === '>=' || comp.operator === '<=')
+    const oppositeDirectionsLessThan =
+      cmp(this.semver, '<', comp.semver, options) &&
+      (this.operator === '>=' || this.operator === '>') &&
+        (comp.operator === '<=' || comp.operator === '<')
+    const oppositeDirectionsGreaterThan =
+      cmp(this.semver, '>', comp.semver, options) &&
+      (this.operator === '<=' || this.operator === '<') &&
+        (comp.operator === '>=' || comp.operator === '>')
+
+    return (
+      sameDirectionIncreasing ||
+      sameDirectionDecreasing ||
+      (sameSemVer && differentDirectionsInclusive) ||
+      oppositeDirectionsLessThan ||
+      oppositeDirectionsGreaterThan
+    )
+  }
+}
+
+module.exports = Comparator
+
+const parseOptions = require('../internal/parse-options')
+const { re, t } = require('../internal/re')
+const cmp = require('../functions/cmp')
+const debug = require('../internal/debug')
+const SemVer = require('./semver')
+const Range = require('./range')

+ 5 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/classes/index.js

@@ -0,0 +1,5 @@
+module.exports = {
+  SemVer: require('./semver.js'),
+  Range: require('./range.js'),
+  Comparator: require('./comparator.js'),
+}

+ 519 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/classes/range.js

@@ -0,0 +1,519 @@
+// hoisted class for cyclic dependency
+class Range {
+  constructor (range, options) {
+    options = parseOptions(options)
+
+    if (range instanceof Range) {
+      if (
+        range.loose === !!options.loose &&
+        range.includePrerelease === !!options.includePrerelease
+      ) {
+        return range
+      } else {
+        return new Range(range.raw, options)
+      }
+    }
+
+    if (range instanceof Comparator) {
+      // just put it in the set and return
+      this.raw = range.value
+      this.set = [[range]]
+      this.format()
+      return this
+    }
+
+    this.options = options
+    this.loose = !!options.loose
+    this.includePrerelease = !!options.includePrerelease
+
+    // First, split based on boolean or ||
+    this.raw = range
+    this.set = range
+      .split('||')
+      // map the range to a 2d array of comparators
+      .map(r => this.parseRange(r.trim()))
+      // throw out any comparator lists that are empty
+      // this generally means that it was not a valid range, which is allowed
+      // in loose mode, but will still throw if the WHOLE range is invalid.
+      .filter(c => c.length)
+
+    if (!this.set.length) {
+      throw new TypeError(`Invalid SemVer Range: ${range}`)
+    }
+
+    // if we have any that are not the null set, throw out null sets.
+    if (this.set.length > 1) {
+      // keep the first one, in case they're all null sets
+      const first = this.set[0]
+      this.set = this.set.filter(c => !isNullSet(c[0]))
+      if (this.set.length === 0) {
+        this.set = [first]
+      } else if (this.set.length > 1) {
+        // if we have any that are *, then the range is just *
+        for (const c of this.set) {
+          if (c.length === 1 && isAny(c[0])) {
+            this.set = [c]
+            break
+          }
+        }
+      }
+    }
+
+    this.format()
+  }
+
+  format () {
+    this.range = this.set
+      .map((comps) => {
+        return comps.join(' ').trim()
+      })
+      .join('||')
+      .trim()
+    return this.range
+  }
+
+  toString () {
+    return this.range
+  }
+
+  parseRange (range) {
+    range = range.trim()
+
+    // memoize range parsing for performance.
+    // this is a very hot path, and fully deterministic.
+    const memoOpts = Object.keys(this.options).join(',')
+    const memoKey = `parseRange:${memoOpts}:${range}`
+    const cached = cache.get(memoKey)
+    if (cached) {
+      return cached
+    }
+
+    const loose = this.options.loose
+    // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4`
+    const hr = loose ? re[t.HYPHENRANGELOOSE] : re[t.HYPHENRANGE]
+    range = range.replace(hr, hyphenReplace(this.options.includePrerelease))
+    debug('hyphen replace', range)
+    // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5`
+    range = range.replace(re[t.COMPARATORTRIM], comparatorTrimReplace)
+    debug('comparator trim', range)
+
+    // `~ 1.2.3` => `~1.2.3`
+    range = range.replace(re[t.TILDETRIM], tildeTrimReplace)
+
+    // `^ 1.2.3` => `^1.2.3`
+    range = range.replace(re[t.CARETTRIM], caretTrimReplace)
+
+    // normalize spaces
+    range = range.split(/\s+/).join(' ')
+
+    // At this point, the range is completely trimmed and
+    // ready to be split into comparators.
+
+    let rangeList = range
+      .split(' ')
+      .map(comp => parseComparator(comp, this.options))
+      .join(' ')
+      .split(/\s+/)
+      // >=0.0.0 is equivalent to *
+      .map(comp => replaceGTE0(comp, this.options))
+
+    if (loose) {
+      // in loose mode, throw out any that are not valid comparators
+      rangeList = rangeList.filter(comp => {
+        debug('loose invalid filter', comp, this.options)
+        return !!comp.match(re[t.COMPARATORLOOSE])
+      })
+    }
+    debug('range list', rangeList)
+
+    // if any comparators are the null set, then replace with JUST null set
+    // if more than one comparator, remove any * comparators
+    // also, don't include the same comparator more than once
+    const rangeMap = new Map()
+    const comparators = rangeList.map(comp => new Comparator(comp, this.options))
+    for (const comp of comparators) {
+      if (isNullSet(comp)) {
+        return [comp]
+      }
+      rangeMap.set(comp.value, comp)
+    }
+    if (rangeMap.size > 1 && rangeMap.has('')) {
+      rangeMap.delete('')
+    }
+
+    const result = [...rangeMap.values()]
+    cache.set(memoKey, result)
+    return result
+  }
+
+  intersects (range, options) {
+    if (!(range instanceof Range)) {
+      throw new TypeError('a Range is required')
+    }
+
+    return this.set.some((thisComparators) => {
+      return (
+        isSatisfiable(thisComparators, options) &&
+        range.set.some((rangeComparators) => {
+          return (
+            isSatisfiable(rangeComparators, options) &&
+            thisComparators.every((thisComparator) => {
+              return rangeComparators.every((rangeComparator) => {
+                return thisComparator.intersects(rangeComparator, options)
+              })
+            })
+          )
+        })
+      )
+    })
+  }
+
+  // if ANY of the sets match ALL of its comparators, then pass
+  test (version) {
+    if (!version) {
+      return false
+    }
+
+    if (typeof version === 'string') {
+      try {
+        version = new SemVer(version, this.options)
+      } catch (er) {
+        return false
+      }
+    }
+
+    for (let i = 0; i < this.set.length; i++) {
+      if (testSet(this.set[i], version, this.options)) {
+        return true
+      }
+    }
+    return false
+  }
+}
+module.exports = Range
+
+const LRU = require('lru-cache')
+const cache = new LRU({ max: 1000 })
+
+const parseOptions = require('../internal/parse-options')
+const Comparator = require('./comparator')
+const debug = require('../internal/debug')
+const SemVer = require('./semver')
+const {
+  re,
+  t,
+  comparatorTrimReplace,
+  tildeTrimReplace,
+  caretTrimReplace,
+} = require('../internal/re')
+
+const isNullSet = c => c.value === '<0.0.0-0'
+const isAny = c => c.value === ''
+
+// take a set of comparators and determine whether there
+// exists a version which can satisfy it
+const isSatisfiable = (comparators, options) => {
+  let result = true
+  const remainingComparators = comparators.slice()
+  let testComparator = remainingComparators.pop()
+
+  while (result && remainingComparators.length) {
+    result = remainingComparators.every((otherComparator) => {
+      return testComparator.intersects(otherComparator, options)
+    })
+
+    testComparator = remainingComparators.pop()
+  }
+
+  return result
+}
+
+// comprised of xranges, tildes, stars, and gtlt's at this point.
+// already replaced the hyphen ranges
+// turn into a set of JUST comparators.
+const parseComparator = (comp, options) => {
+  debug('comp', comp, options)
+  comp = replaceCarets(comp, options)
+  debug('caret', comp)
+  comp = replaceTildes(comp, options)
+  debug('tildes', comp)
+  comp = replaceXRanges(comp, options)
+  debug('xrange', comp)
+  comp = replaceStars(comp, options)
+  debug('stars', comp)
+  return comp
+}
+
+const isX = id => !id || id.toLowerCase() === 'x' || id === '*'
+
+// ~, ~> --> * (any, kinda silly)
+// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0-0
+// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0-0
+// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0-0
+// ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0-0
+// ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0-0
+const replaceTildes = (comp, options) =>
+  comp.trim().split(/\s+/).map((c) => {
+    return replaceTilde(c, options)
+  }).join(' ')
+
+const replaceTilde = (comp, options) => {
+  const r = options.loose ? re[t.TILDELOOSE] : re[t.TILDE]
+  return comp.replace(r, (_, M, m, p, pr) => {
+    debug('tilde', comp, _, M, m, p, pr)
+    let ret
+
+    if (isX(M)) {
+      ret = ''
+    } else if (isX(m)) {
+      ret = `>=${M}.0.0 <${+M + 1}.0.0-0`
+    } else if (isX(p)) {
+      // ~1.2 == >=1.2.0 <1.3.0-0
+      ret = `>=${M}.${m}.0 <${M}.${+m + 1}.0-0`
+    } else if (pr) {
+      debug('replaceTilde pr', pr)
+      ret = `>=${M}.${m}.${p}-${pr
+      } <${M}.${+m + 1}.0-0`
+    } else {
+      // ~1.2.3 == >=1.2.3 <1.3.0-0
+      ret = `>=${M}.${m}.${p
+      } <${M}.${+m + 1}.0-0`
+    }
+
+    debug('tilde return', ret)
+    return ret
+  })
+}
+
+// ^ --> * (any, kinda silly)
+// ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0-0
+// ^2.0, ^2.0.x --> >=2.0.0 <3.0.0-0
+// ^1.2, ^1.2.x --> >=1.2.0 <2.0.0-0
+// ^1.2.3 --> >=1.2.3 <2.0.0-0
+// ^1.2.0 --> >=1.2.0 <2.0.0-0
+const replaceCarets = (comp, options) =>
+  comp.trim().split(/\s+/).map((c) => {
+    return replaceCaret(c, options)
+  }).join(' ')
+
+const replaceCaret = (comp, options) => {
+  debug('caret', comp, options)
+  const r = options.loose ? re[t.CARETLOOSE] : re[t.CARET]
+  const z = options.includePrerelease ? '-0' : ''
+  return comp.replace(r, (_, M, m, p, pr) => {
+    debug('caret', comp, _, M, m, p, pr)
+    let ret
+
+    if (isX(M)) {
+      ret = ''
+    } else if (isX(m)) {
+      ret = `>=${M}.0.0${z} <${+M + 1}.0.0-0`
+    } else if (isX(p)) {
+      if (M === '0') {
+        ret = `>=${M}.${m}.0${z} <${M}.${+m + 1}.0-0`
+      } else {
+        ret = `>=${M}.${m}.0${z} <${+M + 1}.0.0-0`
+      }
+    } else if (pr) {
+      debug('replaceCaret pr', pr)
+      if (M === '0') {
+        if (m === '0') {
+          ret = `>=${M}.${m}.${p}-${pr
+          } <${M}.${m}.${+p + 1}-0`
+        } else {
+          ret = `>=${M}.${m}.${p}-${pr
+          } <${M}.${+m + 1}.0-0`
+        }
+      } else {
+        ret = `>=${M}.${m}.${p}-${pr
+        } <${+M + 1}.0.0-0`
+      }
+    } else {
+      debug('no pr')
+      if (M === '0') {
+        if (m === '0') {
+          ret = `>=${M}.${m}.${p
+          }${z} <${M}.${m}.${+p + 1}-0`
+        } else {
+          ret = `>=${M}.${m}.${p
+          }${z} <${M}.${+m + 1}.0-0`
+        }
+      } else {
+        ret = `>=${M}.${m}.${p
+        } <${+M + 1}.0.0-0`
+      }
+    }
+
+    debug('caret return', ret)
+    return ret
+  })
+}
+
+const replaceXRanges = (comp, options) => {
+  debug('replaceXRanges', comp, options)
+  return comp.split(/\s+/).map((c) => {
+    return replaceXRange(c, options)
+  }).join(' ')
+}
+
+const replaceXRange = (comp, options) => {
+  comp = comp.trim()
+  const r = options.loose ? re[t.XRANGELOOSE] : re[t.XRANGE]
+  return comp.replace(r, (ret, gtlt, M, m, p, pr) => {
+    debug('xRange', comp, ret, gtlt, M, m, p, pr)
+    const xM = isX(M)
+    const xm = xM || isX(m)
+    const xp = xm || isX(p)
+    const anyX = xp
+
+    if (gtlt === '=' && anyX) {
+      gtlt = ''
+    }
+
+    // if we're including prereleases in the match, then we need
+    // to fix this to -0, the lowest possible prerelease value
+    pr = options.includePrerelease ? '-0' : ''
+
+    if (xM) {
+      if (gtlt === '>' || gtlt === '<') {
+        // nothing is allowed
+        ret = '<0.0.0-0'
+      } else {
+        // nothing is forbidden
+        ret = '*'
+      }
+    } else if (gtlt && anyX) {
+      // we know patch is an x, because we have any x at all.
+      // replace X with 0
+      if (xm) {
+        m = 0
+      }
+      p = 0
+
+      if (gtlt === '>') {
+        // >1 => >=2.0.0
+        // >1.2 => >=1.3.0
+        gtlt = '>='
+        if (xm) {
+          M = +M + 1
+          m = 0
+          p = 0
+        } else {
+          m = +m + 1
+          p = 0
+        }
+      } else if (gtlt === '<=') {
+        // <=0.7.x is actually <0.8.0, since any 0.7.x should
+        // pass.  Similarly, <=7.x is actually <8.0.0, etc.
+        gtlt = '<'
+        if (xm) {
+          M = +M + 1
+        } else {
+          m = +m + 1
+        }
+      }
+
+      if (gtlt === '<') {
+        pr = '-0'
+      }
+
+      ret = `${gtlt + M}.${m}.${p}${pr}`
+    } else if (xm) {
+      ret = `>=${M}.0.0${pr} <${+M + 1}.0.0-0`
+    } else if (xp) {
+      ret = `>=${M}.${m}.0${pr
+      } <${M}.${+m + 1}.0-0`
+    }
+
+    debug('xRange return', ret)
+
+    return ret
+  })
+}
+
+// Because * is AND-ed with everything else in the comparator,
+// and '' means "any version", just remove the *s entirely.
+const replaceStars = (comp, options) => {
+  debug('replaceStars', comp, options)
+  // Looseness is ignored here.  star is always as loose as it gets!
+  return comp.trim().replace(re[t.STAR], '')
+}
+
+const replaceGTE0 = (comp, options) => {
+  debug('replaceGTE0', comp, options)
+  return comp.trim()
+    .replace(re[options.includePrerelease ? t.GTE0PRE : t.GTE0], '')
+}
+
+// This function is passed to string.replace(re[t.HYPHENRANGE])
+// M, m, patch, prerelease, build
+// 1.2 - 3.4.5 => >=1.2.0 <=3.4.5
+// 1.2.3 - 3.4 => >=1.2.0 <3.5.0-0 Any 3.4.x will do
+// 1.2 - 3.4 => >=1.2.0 <3.5.0-0
+const hyphenReplace = incPr => ($0,
+  from, fM, fm, fp, fpr, fb,
+  to, tM, tm, tp, tpr, tb) => {
+  if (isX(fM)) {
+    from = ''
+  } else if (isX(fm)) {
+    from = `>=${fM}.0.0${incPr ? '-0' : ''}`
+  } else if (isX(fp)) {
+    from = `>=${fM}.${fm}.0${incPr ? '-0' : ''}`
+  } else if (fpr) {
+    from = `>=${from}`
+  } else {
+    from = `>=${from}${incPr ? '-0' : ''}`
+  }
+
+  if (isX(tM)) {
+    to = ''
+  } else if (isX(tm)) {
+    to = `<${+tM + 1}.0.0-0`
+  } else if (isX(tp)) {
+    to = `<${tM}.${+tm + 1}.0-0`
+  } else if (tpr) {
+    to = `<=${tM}.${tm}.${tp}-${tpr}`
+  } else if (incPr) {
+    to = `<${tM}.${tm}.${+tp + 1}-0`
+  } else {
+    to = `<=${to}`
+  }
+
+  return (`${from} ${to}`).trim()
+}
+
+const testSet = (set, version, options) => {
+  for (let i = 0; i < set.length; i++) {
+    if (!set[i].test(version)) {
+      return false
+    }
+  }
+
+  if (version.prerelease.length && !options.includePrerelease) {
+    // Find the set of versions that are allowed to have prereleases
+    // For example, ^1.2.3-pr.1 desugars to >=1.2.3-pr.1 <2.0.0
+    // That should allow `1.2.3-pr.2` to pass.
+    // However, `1.2.4-alpha.notready` should NOT be allowed,
+    // even though it's within the range set by the comparators.
+    for (let i = 0; i < set.length; i++) {
+      debug(set[i].semver)
+      if (set[i].semver === Comparator.ANY) {
+        continue
+      }
+
+      if (set[i].semver.prerelease.length > 0) {
+        const allowed = set[i].semver
+        if (allowed.major === version.major &&
+            allowed.minor === version.minor &&
+            allowed.patch === version.patch) {
+          return true
+        }
+      }
+    }
+
+    // Version has a -pre, but it's not one of the ones we like.
+    return false
+  }
+
+  return true
+}

+ 287 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/classes/semver.js

@@ -0,0 +1,287 @@
+const debug = require('../internal/debug')
+const { MAX_LENGTH, MAX_SAFE_INTEGER } = require('../internal/constants')
+const { re, t } = require('../internal/re')
+
+const parseOptions = require('../internal/parse-options')
+const { compareIdentifiers } = require('../internal/identifiers')
+class SemVer {
+  constructor (version, options) {
+    options = parseOptions(options)
+
+    if (version instanceof SemVer) {
+      if (version.loose === !!options.loose &&
+          version.includePrerelease === !!options.includePrerelease) {
+        return version
+      } else {
+        version = version.version
+      }
+    } else if (typeof version !== 'string') {
+      throw new TypeError(`Invalid Version: ${version}`)
+    }
+
+    if (version.length > MAX_LENGTH) {
+      throw new TypeError(
+        `version is longer than ${MAX_LENGTH} characters`
+      )
+    }
+
+    debug('SemVer', version, options)
+    this.options = options
+    this.loose = !!options.loose
+    // this isn't actually relevant for versions, but keep it so that we
+    // don't run into trouble passing this.options around.
+    this.includePrerelease = !!options.includePrerelease
+
+    const m = version.trim().match(options.loose ? re[t.LOOSE] : re[t.FULL])
+
+    if (!m) {
+      throw new TypeError(`Invalid Version: ${version}`)
+    }
+
+    this.raw = version
+
+    // these are actually numbers
+    this.major = +m[1]
+    this.minor = +m[2]
+    this.patch = +m[3]
+
+    if (this.major > MAX_SAFE_INTEGER || this.major < 0) {
+      throw new TypeError('Invalid major version')
+    }
+
+    if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) {
+      throw new TypeError('Invalid minor version')
+    }
+
+    if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) {
+      throw new TypeError('Invalid patch version')
+    }
+
+    // numberify any prerelease numeric ids
+    if (!m[4]) {
+      this.prerelease = []
+    } else {
+      this.prerelease = m[4].split('.').map((id) => {
+        if (/^[0-9]+$/.test(id)) {
+          const num = +id
+          if (num >= 0 && num < MAX_SAFE_INTEGER) {
+            return num
+          }
+        }
+        return id
+      })
+    }
+
+    this.build = m[5] ? m[5].split('.') : []
+    this.format()
+  }
+
+  format () {
+    this.version = `${this.major}.${this.minor}.${this.patch}`
+    if (this.prerelease.length) {
+      this.version += `-${this.prerelease.join('.')}`
+    }
+    return this.version
+  }
+
+  toString () {
+    return this.version
+  }
+
+  compare (other) {
+    debug('SemVer.compare', this.version, this.options, other)
+    if (!(other instanceof SemVer)) {
+      if (typeof other === 'string' && other === this.version) {
+        return 0
+      }
+      other = new SemVer(other, this.options)
+    }
+
+    if (other.version === this.version) {
+      return 0
+    }
+
+    return this.compareMain(other) || this.comparePre(other)
+  }
+
+  compareMain (other) {
+    if (!(other instanceof SemVer)) {
+      other = new SemVer(other, this.options)
+    }
+
+    return (
+      compareIdentifiers(this.major, other.major) ||
+      compareIdentifiers(this.minor, other.minor) ||
+      compareIdentifiers(this.patch, other.patch)
+    )
+  }
+
+  comparePre (other) {
+    if (!(other instanceof SemVer)) {
+      other = new SemVer(other, this.options)
+    }
+
+    // NOT having a prerelease is > having one
+    if (this.prerelease.length && !other.prerelease.length) {
+      return -1
+    } else if (!this.prerelease.length && other.prerelease.length) {
+      return 1
+    } else if (!this.prerelease.length && !other.prerelease.length) {
+      return 0
+    }
+
+    let i = 0
+    do {
+      const a = this.prerelease[i]
+      const b = other.prerelease[i]
+      debug('prerelease compare', i, a, b)
+      if (a === undefined && b === undefined) {
+        return 0
+      } else if (b === undefined) {
+        return 1
+      } else if (a === undefined) {
+        return -1
+      } else if (a === b) {
+        continue
+      } else {
+        return compareIdentifiers(a, b)
+      }
+    } while (++i)
+  }
+
+  compareBuild (other) {
+    if (!(other instanceof SemVer)) {
+      other = new SemVer(other, this.options)
+    }
+
+    let i = 0
+    do {
+      const a = this.build[i]
+      const b = other.build[i]
+      debug('prerelease compare', i, a, b)
+      if (a === undefined && b === undefined) {
+        return 0
+      } else if (b === undefined) {
+        return 1
+      } else if (a === undefined) {
+        return -1
+      } else if (a === b) {
+        continue
+      } else {
+        return compareIdentifiers(a, b)
+      }
+    } while (++i)
+  }
+
+  // preminor will bump the version up to the next minor release, and immediately
+  // down to pre-release. premajor and prepatch work the same way.
+  inc (release, identifier) {
+    switch (release) {
+      case 'premajor':
+        this.prerelease.length = 0
+        this.patch = 0
+        this.minor = 0
+        this.major++
+        this.inc('pre', identifier)
+        break
+      case 'preminor':
+        this.prerelease.length = 0
+        this.patch = 0
+        this.minor++
+        this.inc('pre', identifier)
+        break
+      case 'prepatch':
+        // If this is already a prerelease, it will bump to the next version
+        // drop any prereleases that might already exist, since they are not
+        // relevant at this point.
+        this.prerelease.length = 0
+        this.inc('patch', identifier)
+        this.inc('pre', identifier)
+        break
+      // If the input is a non-prerelease version, this acts the same as
+      // prepatch.
+      case 'prerelease':
+        if (this.prerelease.length === 0) {
+          this.inc('patch', identifier)
+        }
+        this.inc('pre', identifier)
+        break
+
+      case 'major':
+        // If this is a pre-major version, bump up to the same major version.
+        // Otherwise increment major.
+        // 1.0.0-5 bumps to 1.0.0
+        // 1.1.0 bumps to 2.0.0
+        if (
+          this.minor !== 0 ||
+          this.patch !== 0 ||
+          this.prerelease.length === 0
+        ) {
+          this.major++
+        }
+        this.minor = 0
+        this.patch = 0
+        this.prerelease = []
+        break
+      case 'minor':
+        // If this is a pre-minor version, bump up to the same minor version.
+        // Otherwise increment minor.
+        // 1.2.0-5 bumps to 1.2.0
+        // 1.2.1 bumps to 1.3.0
+        if (this.patch !== 0 || this.prerelease.length === 0) {
+          this.minor++
+        }
+        this.patch = 0
+        this.prerelease = []
+        break
+      case 'patch':
+        // If this is not a pre-release version, it will increment the patch.
+        // If it is a pre-release it will bump up to the same patch version.
+        // 1.2.0-5 patches to 1.2.0
+        // 1.2.0 patches to 1.2.1
+        if (this.prerelease.length === 0) {
+          this.patch++
+        }
+        this.prerelease = []
+        break
+      // This probably shouldn't be used publicly.
+      // 1.0.0 'pre' would become 1.0.0-0 which is the wrong direction.
+      case 'pre':
+        if (this.prerelease.length === 0) {
+          this.prerelease = [0]
+        } else {
+          let i = this.prerelease.length
+          while (--i >= 0) {
+            if (typeof this.prerelease[i] === 'number') {
+              this.prerelease[i]++
+              i = -2
+            }
+          }
+          if (i === -1) {
+            // didn't increment anything
+            this.prerelease.push(0)
+          }
+        }
+        if (identifier) {
+          // 1.2.0-beta.1 bumps to 1.2.0-beta.2,
+          // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0
+          if (compareIdentifiers(this.prerelease[0], identifier) === 0) {
+            if (isNaN(this.prerelease[1])) {
+              this.prerelease = [identifier, 0]
+            }
+          } else {
+            this.prerelease = [identifier, 0]
+          }
+        }
+        break
+
+      default:
+        throw new Error(`invalid increment argument: ${release}`)
+    }
+    this.format()
+    this.raw = this.version
+    return this
+  }
+}
+
+module.exports = SemVer

+ 6 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/clean.js

@@ -0,0 +1,6 @@
+const parse = require('./parse')
+const clean = (version, options) => {
+  const s = parse(version.trim().replace(/^[=v]+/, ''), options)
+  return s ? s.version : null
+}
+module.exports = clean

+ 52 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/cmp.js

@@ -0,0 +1,52 @@
+const eq = require('./eq')
+const neq = require('./neq')
+const gt = require('./gt')
+const gte = require('./gte')
+const lt = require('./lt')
+const lte = require('./lte')
+
+const cmp = (a, op, b, loose) => {
+  switch (op) {
+    case '===':
+      if (typeof a === 'object') {
+        a = a.version
+      }
+      if (typeof b === 'object') {
+        b = b.version
+      }
+      return a === b
+
+    case '!==':
+      if (typeof a === 'object') {
+        a = a.version
+      }
+      if (typeof b === 'object') {
+        b = b.version
+      }
+      return a !== b
+
+    case '':
+    case '=':
+    case '==':
+      return eq(a, b, loose)
+
+    case '!=':
+      return neq(a, b, loose)
+
+    case '>':
+      return gt(a, b, loose)
+
+    case '>=':
+      return gte(a, b, loose)
+
+    case '<':
+      return lt(a, b, loose)
+
+    case '<=':
+      return lte(a, b, loose)
+
+    default:
+      throw new TypeError(`Invalid operator: ${op}`)
+  }
+}
+module.exports = cmp

+ 52 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/coerce.js

@@ -0,0 +1,52 @@
+const SemVer = require('../classes/semver')
+const parse = require('./parse')
+const { re, t } = require('../internal/re')
+
+const coerce = (version, options) => {
+  if (version instanceof SemVer) {
+    return version
+  }
+
+  if (typeof version === 'number') {
+    version = String(version)
+  }
+
+  if (typeof version !== 'string') {
+    return null
+  }
+
+  options = options || {}
+
+  let match = null
+  if (!options.rtl) {
+    match = version.match(re[t.COERCE])
+  } else {
+    // Find the right-most coercible string that does not share
+    // a terminus with a more left-ward coercible string.
+    // Eg, '1.2.3.4' wants to coerce '2.3.4', not '3.4' or '4'
+    //
+    // Walk through the string checking with a /g regexp
+    // Manually set the index so as to pick up overlapping matches.
+    // Stop when we get a match that ends at the string end, since no
+    // coercible string can be more right-ward without the same terminus.
+    let next
+    while ((next = re[t.COERCERTL].exec(version)) &&
+        (!match || match.index + match[0].length !== version.length)
+    ) {
+      if (!match ||
+            next.index + next[0].length !== match.index + match[0].length) {
+        match = next
+      }
+      re[t.COERCERTL].lastIndex = next.index + next[1].length + next[2].length
+    }
+    // leave it in a clean state
+    re[t.COERCERTL].lastIndex = -1
+  }
+
+  if (match === null) {
+    return null
+  }
+
+  return parse(`${match[2]}.${match[3] || '0'}.${match[4] || '0'}`, options)
+}
+module.exports = coerce

+ 7 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/compare-build.js

@@ -0,0 +1,7 @@
+const SemVer = require('../classes/semver')
+const compareBuild = (a, b, loose) => {
+  const versionA = new SemVer(a, loose)
+  const versionB = new SemVer(b, loose)
+  return versionA.compare(versionB) || versionA.compareBuild(versionB)
+}
+module.exports = compareBuild

+ 3 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/compare-loose.js

@@ -0,0 +1,3 @@
+const compare = require('./compare')
+const compareLoose = (a, b) => compare(a, b, true)
+module.exports = compareLoose

+ 5 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/compare.js

@@ -0,0 +1,5 @@
+const SemVer = require('../classes/semver')
+const compare = (a, b, loose) =>
+  new SemVer(a, loose).compare(new SemVer(b, loose))
+
+module.exports = compare

+ 23 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/diff.js

@@ -0,0 +1,23 @@
+const parse = require('./parse')
+const eq = require('./eq')
+
+const diff = (version1, version2) => {
+  if (eq(version1, version2)) {
+    return null
+  } else {
+    const v1 = parse(version1)
+    const v2 = parse(version2)
+    const hasPre = v1.prerelease.length || v2.prerelease.length
+    const prefix = hasPre ? 'pre' : ''
+    const defaultResult = hasPre ? 'prerelease' : ''
+    for (const key in v1) {
+      if (key === 'major' || key === 'minor' || key === 'patch') {
+        if (v1[key] !== v2[key]) {
+          return prefix + key
+        }
+      }
+    }
+    return defaultResult // may be undefined
+  }
+}
+module.exports = diff

+ 3 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/eq.js

@@ -0,0 +1,3 @@
+const compare = require('./compare')
+const eq = (a, b, loose) => compare(a, b, loose) === 0
+module.exports = eq

+ 3 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/gt.js

@@ -0,0 +1,3 @@
+const compare = require('./compare')
+const gt = (a, b, loose) => compare(a, b, loose) > 0
+module.exports = gt

+ 3 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/gte.js

@@ -0,0 +1,3 @@
+const compare = require('./compare')
+const gte = (a, b, loose) => compare(a, b, loose) >= 0
+module.exports = gte

+ 18 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/inc.js

@@ -0,0 +1,18 @@
+const SemVer = require('../classes/semver')
+
+const inc = (version, release, options, identifier) => {
+  if (typeof (options) === 'string') {
+    identifier = options
+    options = undefined
+  }
+
+  try {
+    return new SemVer(
+      version instanceof SemVer ? version.version : version,
+      options
+    ).inc(release, identifier).version
+  } catch (er) {
+    return null
+  }
+}
+module.exports = inc

+ 3 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/lt.js

@@ -0,0 +1,3 @@
+const compare = require('./compare')
+const lt = (a, b, loose) => compare(a, b, loose) < 0
+module.exports = lt

+ 3 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/lte.js

@@ -0,0 +1,3 @@
+const compare = require('./compare')
+const lte = (a, b, loose) => compare(a, b, loose) <= 0
+module.exports = lte

+ 3 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/major.js

@@ -0,0 +1,3 @@
+const SemVer = require('../classes/semver')
+const major = (a, loose) => new SemVer(a, loose).major
+module.exports = major

+ 3 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/minor.js

@@ -0,0 +1,3 @@
+const SemVer = require('../classes/semver')
+const minor = (a, loose) => new SemVer(a, loose).minor
+module.exports = minor

+ 3 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/neq.js

@@ -0,0 +1,3 @@
+const compare = require('./compare')
+const neq = (a, b, loose) => compare(a, b, loose) !== 0
+module.exports = neq

+ 33 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/parse.js

@@ -0,0 +1,33 @@
+const { MAX_LENGTH } = require('../internal/constants')
+const { re, t } = require('../internal/re')
+const SemVer = require('../classes/semver')
+
+const parseOptions = require('../internal/parse-options')
+const parse = (version, options) => {
+  options = parseOptions(options)
+
+  if (version instanceof SemVer) {
+    return version
+  }
+
+  if (typeof version !== 'string') {
+    return null
+  }
+
+  if (version.length > MAX_LENGTH) {
+    return null
+  }
+
+  const r = options.loose ? re[t.LOOSE] : re[t.FULL]
+  if (!r.test(version)) {
+    return null
+  }
+
+  try {
+    return new SemVer(version, options)
+  } catch (er) {
+    return null
+  }
+}
+
+module.exports = parse

+ 3 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/patch.js

@@ -0,0 +1,3 @@
+const SemVer = require('../classes/semver')
+const patch = (a, loose) => new SemVer(a, loose).patch
+module.exports = patch

+ 6 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/prerelease.js

@@ -0,0 +1,6 @@
+const parse = require('./parse')
+const prerelease = (version, options) => {
+  const parsed = parse(version, options)
+  return (parsed && parsed.prerelease.length) ? parsed.prerelease : null
+}
+module.exports = prerelease

+ 3 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/rcompare.js

@@ -0,0 +1,3 @@
+const compare = require('./compare')
+const rcompare = (a, b, loose) => compare(b, a, loose)
+module.exports = rcompare

+ 3 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/rsort.js

@@ -0,0 +1,3 @@
+const compareBuild = require('./compare-build')
+const rsort = (list, loose) => list.sort((a, b) => compareBuild(b, a, loose))
+module.exports = rsort

+ 10 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/satisfies.js

@@ -0,0 +1,10 @@
+const Range = require('../classes/range')
+const satisfies = (version, range, options) => {
+  try {
+    range = new Range(range, options)
+  } catch (er) {
+    return false
+  }
+  return range.test(version)
+}
+module.exports = satisfies

+ 3 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/sort.js

@@ -0,0 +1,3 @@
+const compareBuild = require('./compare-build')
+const sort = (list, loose) => list.sort((a, b) => compareBuild(a, b, loose))
+module.exports = sort

+ 6 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/functions/valid.js

@@ -0,0 +1,6 @@
+const parse = require('./parse')
+const valid = (version, options) => {
+  const v = parse(version, options)
+  return v ? v.version : null
+}
+module.exports = valid

+ 48 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/index.js

@@ -0,0 +1,48 @@
+// just pre-load all the stuff that index.js lazily exports
+const internalRe = require('./internal/re')
+module.exports = {
+  re: internalRe.re,
+  src: internalRe.src,
+  tokens: internalRe.t,
+  SEMVER_SPEC_VERSION: require('./internal/constants').SEMVER_SPEC_VERSION,
+  SemVer: require('./classes/semver'),
+  compareIdentifiers: require('./internal/identifiers').compareIdentifiers,
+  rcompareIdentifiers: require('./internal/identifiers').rcompareIdentifiers,
+  parse: require('./functions/parse'),
+  valid: require('./functions/valid'),
+  clean: require('./functions/clean'),
+  inc: require('./functions/inc'),
+  diff: require('./functions/diff'),
+  major: require('./functions/major'),
+  minor: require('./functions/minor'),
+  patch: require('./functions/patch'),
+  prerelease: require('./functions/prerelease'),
+  compare: require('./functions/compare'),
+  rcompare: require('./functions/rcompare'),
+  compareLoose: require('./functions/compare-loose'),
+  compareBuild: require('./functions/compare-build'),
+  sort: require('./functions/sort'),
+  rsort: require('./functions/rsort'),
+  gt: require('./functions/gt'),
+  lt: require('./functions/lt'),
+  eq: require('./functions/eq'),
+  neq: require('./functions/neq'),
+  gte: require('./functions/gte'),
+  lte: require('./functions/lte'),
+  cmp: require('./functions/cmp'),
+  coerce: require('./functions/coerce'),
+  Comparator: require('./classes/comparator'),
+  Range: require('./classes/range'),
+  satisfies: require('./functions/satisfies'),
+  toComparators: require('./ranges/to-comparators'),
+  maxSatisfying: require('./ranges/max-satisfying'),
+  minSatisfying: require('./ranges/min-satisfying'),
+  minVersion: require('./ranges/min-version'),
+  validRange: require('./ranges/valid'),
+  outside: require('./ranges/outside'),
+  gtr: require('./ranges/gtr'),
+  ltr: require('./ranges/ltr'),
+  intersects: require('./ranges/intersects'),
+  simplifyRange: require('./ranges/simplify'),
+  subset: require('./ranges/subset'),
+}

+ 17 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/internal/constants.js

@@ -0,0 +1,17 @@
+// Note: this is the semver.org version of the spec that it implements
+// Not necessarily the package version of this code.
+const SEMVER_SPEC_VERSION = '2.0.0'
+
+const MAX_LENGTH = 256
+const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER ||
+/* istanbul ignore next */ 9007199254740991
+
+// Max safe segment length for coercion.
+const MAX_SAFE_COMPONENT_LENGTH = 16
+
+module.exports = {
+  SEMVER_SPEC_VERSION,
+  MAX_LENGTH,
+  MAX_SAFE_INTEGER,
+  MAX_SAFE_COMPONENT_LENGTH,
+}

+ 9 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/internal/debug.js

@@ -0,0 +1,9 @@
+const debug = (
+  typeof process === 'object' &&
+  process.env &&
+  process.env.NODE_DEBUG &&
+  /\bsemver\b/i.test(process.env.NODE_DEBUG)
+) ? (...args) => console.error('SEMVER', ...args)
+  : () => {}
+
+module.exports = debug

+ 23 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/internal/identifiers.js

@@ -0,0 +1,23 @@
+const numeric = /^[0-9]+$/
+const compareIdentifiers = (a, b) => {
+  const anum = numeric.test(a)
+  const bnum = numeric.test(b)
+
+  if (anum && bnum) {
+    a = +a
+    b = +b
+  }
+
+  return a === b ? 0
+    : (anum && !bnum) ? -1
+    : (bnum && !anum) ? 1
+    : a < b ? -1
+    : 1
+}
+
+const rcompareIdentifiers = (a, b) => compareIdentifiers(b, a)
+
+module.exports = {
+  compareIdentifiers,
+  rcompareIdentifiers,
+}

+ 11 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/internal/parse-options.js

@@ -0,0 +1,11 @@
+// parse out just the options we care about so we always get a consistent
+// obj with keys in a consistent order.
+const opts = ['includePrerelease', 'loose', 'rtl']
+const parseOptions = options =>
+  !options ? {}
+  : typeof options !== 'object' ? { loose: true }
+  : opts.filter(k => options[k]).reduce((o, k) => {
+    o[k] = true
+    return o
+  }, {})
+module.exports = parseOptions

+ 182 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/internal/re.js

@@ -0,0 +1,182 @@
+const { MAX_SAFE_COMPONENT_LENGTH } = require('./constants')
+const debug = require('./debug')
+exports = module.exports = {}
+
+// The actual regexps go on exports.re
+const re = exports.re = []
+const src = exports.src = []
+const t = exports.t = {}
+let R = 0
+
+const createToken = (name, value, isGlobal) => {
+  const index = R++
+  debug(name, index, value)
+  t[name] = index
+  src[index] = value
+  re[index] = new RegExp(value, isGlobal ? 'g' : undefined)
+}
+
+// The following Regular Expressions can be used for tokenizing,
+// validating, and parsing SemVer version strings.
+
+// ## Numeric Identifier
+// A single `0`, or a non-zero digit followed by zero or more digits.
+
+createToken('NUMERICIDENTIFIER', '0|[1-9]\\d*')
+createToken('NUMERICIDENTIFIERLOOSE', '[0-9]+')
+
+// ## Non-numeric Identifier
+// Zero or more digits, followed by a letter or hyphen, and then zero or
+// more letters, digits, or hyphens.
+
+createToken('NONNUMERICIDENTIFIER', '\\d*[a-zA-Z-][a-zA-Z0-9-]*')
+
+// ## Main Version
+// Three dot-separated numeric identifiers.
+
+createToken('MAINVERSION', `(${src[t.NUMERICIDENTIFIER]})\\.` +
+                   `(${src[t.NUMERICIDENTIFIER]})\\.` +
+                   `(${src[t.NUMERICIDENTIFIER]})`)
+
+createToken('MAINVERSIONLOOSE', `(${src[t.NUMERICIDENTIFIERLOOSE]})\\.` +
+                        `(${src[t.NUMERICIDENTIFIERLOOSE]})\\.` +
+                        `(${src[t.NUMERICIDENTIFIERLOOSE]})`)
+
+// ## Pre-release Version Identifier
+// A numeric identifier, or a non-numeric identifier.
+
+createToken('PRERELEASEIDENTIFIER', `(?:${src[t.NUMERICIDENTIFIER]
+}|${src[t.NONNUMERICIDENTIFIER]})`)
+
+createToken('PRERELEASEIDENTIFIERLOOSE', `(?:${src[t.NUMERICIDENTIFIERLOOSE]
+}|${src[t.NONNUMERICIDENTIFIER]})`)
+
+// ## Pre-release Version
+// Hyphen, followed by one or more dot-separated pre-release version
+// identifiers.
+
+createToken('PRERELEASE', `(?:-(${src[t.PRERELEASEIDENTIFIER]
+}(?:\\.${src[t.PRERELEASEIDENTIFIER]})*))`)
+
+createToken('PRERELEASELOOSE', `(?:-?(${src[t.PRERELEASEIDENTIFIERLOOSE]
+}(?:\\.${src[t.PRERELEASEIDENTIFIERLOOSE]})*))`)
+
+// ## Build Metadata Identifier
+// Any combination of digits, letters, or hyphens.
+
+createToken('BUILDIDENTIFIER', '[0-9A-Za-z-]+')
+
+// ## Build Metadata
+// Plus sign, followed by one or more period-separated build metadata
+// identifiers.
+
+createToken('BUILD', `(?:\\+(${src[t.BUILDIDENTIFIER]
+}(?:\\.${src[t.BUILDIDENTIFIER]})*))`)
+
+// ## Full Version String
+// A main version, followed optionally by a pre-release version and
+// build metadata.
+
+// Note that the only major, minor, patch, and pre-release sections of
+// the version string are capturing groups.  The build metadata is not a
+// capturing group, because it should not ever be used in version
+// comparison.
+
+createToken('FULLPLAIN', `v?${src[t.MAINVERSION]
+}${src[t.PRERELEASE]}?${
+  src[t.BUILD]}?`)
+
+createToken('FULL', `^${src[t.FULLPLAIN]}$`)
+
+// like full, but allows v1.2.3 and =1.2.3, which people do sometimes.
+// also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty
+// common in the npm registry.
+createToken('LOOSEPLAIN', `[v=\\s]*${src[t.MAINVERSIONLOOSE]
+}${src[t.PRERELEASELOOSE]}?${
+  src[t.BUILD]}?`)
+
+createToken('LOOSE', `^${src[t.LOOSEPLAIN]}$`)
+
+createToken('GTLT', '((?:<|>)?=?)')
+
+// Something like "2.*" or "1.2.x".
+// Note that "x.x" is a valid xRange identifer, meaning "any version"
+// Only the first item is strictly required.
+createToken('XRANGEIDENTIFIERLOOSE', `${src[t.NUMERICIDENTIFIERLOOSE]}|x|X|\\*`)
+createToken('XRANGEIDENTIFIER', `${src[t.NUMERICIDENTIFIER]}|x|X|\\*`)
+
+createToken('XRANGEPLAIN', `[v=\\s]*(${src[t.XRANGEIDENTIFIER]})` +
+                   `(?:\\.(${src[t.XRANGEIDENTIFIER]})` +
+                   `(?:\\.(${src[t.XRANGEIDENTIFIER]})` +
+                   `(?:${src[t.PRERELEASE]})?${
+                     src[t.BUILD]}?` +
+                   `)?)?`)
+
+createToken('XRANGEPLAINLOOSE', `[v=\\s]*(${src[t.XRANGEIDENTIFIERLOOSE]})` +
+                        `(?:\\.(${src[t.XRANGEIDENTIFIERLOOSE]})` +
+                        `(?:\\.(${src[t.XRANGEIDENTIFIERLOOSE]})` +
+                        `(?:${src[t.PRERELEASELOOSE]})?${
+                          src[t.BUILD]}?` +
+                        `)?)?`)
+
+createToken('XRANGE', `^${src[t.GTLT]}\\s*${src[t.XRANGEPLAIN]}$`)
+createToken('XRANGELOOSE', `^${src[t.GTLT]}\\s*${src[t.XRANGEPLAINLOOSE]}$`)
+
+// Coercion.
+// Extract anything that could conceivably be a part of a valid semver
+createToken('COERCE', `${'(^|[^\\d])' +
+              '(\\d{1,'}${MAX_SAFE_COMPONENT_LENGTH}})` +
+              `(?:\\.(\\d{1,${MAX_SAFE_COMPONENT_LENGTH}}))?` +
+              `(?:\\.(\\d{1,${MAX_SAFE_COMPONENT_LENGTH}}))?` +
+              `(?:$|[^\\d])`)
+createToken('COERCERTL', src[t.COERCE], true)
+
+// Tilde ranges.
+// Meaning is "reasonably at or greater than"
+createToken('LONETILDE', '(?:~>?)')
+
+createToken('TILDETRIM', `(\\s*)${src[t.LONETILDE]}\\s+`, true)
+exports.tildeTrimReplace = '$1~'
+
+createToken('TILDE', `^${src[t.LONETILDE]}${src[t.XRANGEPLAIN]}$`)
+createToken('TILDELOOSE', `^${src[t.LONETILDE]}${src[t.XRANGEPLAINLOOSE]}$`)
+
+// Caret ranges.
+// Meaning is "at least and backwards compatible with"
+createToken('LONECARET', '(?:\\^)')
+
+createToken('CARETTRIM', `(\\s*)${src[t.LONECARET]}\\s+`, true)
+exports.caretTrimReplace = '$1^'
+
+createToken('CARET', `^${src[t.LONECARET]}${src[t.XRANGEPLAIN]}$`)
+createToken('CARETLOOSE', `^${src[t.LONECARET]}${src[t.XRANGEPLAINLOOSE]}$`)
+
+// A simple gt/lt/eq thing, or just "" to indicate "any version"
+createToken('COMPARATORLOOSE', `^${src[t.GTLT]}\\s*(${src[t.LOOSEPLAIN]})$|^$`)
+createToken('COMPARATOR', `^${src[t.GTLT]}\\s*(${src[t.FULLPLAIN]})$|^$`)
+
+// An expression to strip any whitespace between the gtlt and the thing
+// it modifies, so that `> 1.2.3` ==> `>1.2.3`
+createToken('COMPARATORTRIM', `(\\s*)${src[t.GTLT]
+}\\s*(${src[t.LOOSEPLAIN]}|${src[t.XRANGEPLAIN]})`, true)
+exports.comparatorTrimReplace = '$1$2$3'
+
+// Something like `1.2.3 - 1.2.4`
+// Note that these all use the loose form, because they'll be
+// checked against either the strict or loose comparator form
+// later.
+createToken('HYPHENRANGE', `^\\s*(${src[t.XRANGEPLAIN]})` +
+                   `\\s+-\\s+` +
+                   `(${src[t.XRANGEPLAIN]})` +
+                   `\\s*$`)
+
+createToken('HYPHENRANGELOOSE', `^\\s*(${src[t.XRANGEPLAINLOOSE]})` +
+                        `\\s+-\\s+` +
+                        `(${src[t.XRANGEPLAINLOOSE]})` +
+                        `\\s*$`)
+
+// Star ranges basically just allow anything at all.
+createToken('STAR', '(<|>)?=?\\s*\\*')
+// >=0.0.0 is like a star
+createToken('GTE0', '^\\s*>=\\s*0\\.0\\.0\\s*$')
+createToken('GTE0PRE', '^\\s*>=\\s*0\\.0\\.0-0\\s*$')

+ 75 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/package.json

@@ -0,0 +1,75 @@
+{
+  "name": "semver",
+  "version": "7.3.7",
+  "description": "The semantic version parser used by npm.",
+  "main": "index.js",
+  "scripts": {
+    "test": "tap",
+    "snap": "tap",
+    "preversion": "npm test",
+    "postversion": "npm publish",
+    "postpublish": "git push origin --follow-tags",
+    "lint": "eslint \"**/*.js\"",
+    "postlint": "template-oss-check",
+    "lintfix": "npm run lint -- --fix",
+    "prepublishOnly": "git push origin --follow-tags",
+    "posttest": "npm run lint",
+    "template-oss-apply": "template-oss-apply --force"
+  },
+  "devDependencies": {
+    "@npmcli/eslint-config": "^3.0.1",
+    "@npmcli/template-oss": "3.3.2",
+    "tap": "^16.0.0"
+  },
+  "license": "ISC",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/npm/node-semver.git"
+  },
+  "bin": {
+    "semver": "bin/semver.js"
+  },
+  "files": [
+    "bin/",
+    "classes/",
+    "functions/",
+    "internal/",
+    "ranges/",
+    "index.js",
+    "preload.js",
+    "range.bnf"
+  ],
+  "tap": {
+    "check-coverage": true,
+    "coverage-map": "map.js"
+  },
+  "engines": {
+    "node": ">=10"
+  },
+  "dependencies": {
+    "lru-cache": "^6.0.0"
+  },
+  "author": "GitHub Inc.",
+  "templateOSS": {
+    "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.",
+    "version": "3.3.2",
+    "engines": ">=10",
+    "ciVersions": [
+      "10.0.0",
+      "10.x",
+      "12.x",
+      "14.x",
+      "16.x"
+    ],
+    "distPaths": [
+      "bin/",
+      "classes/",
+      "functions/",
+      "internal/",
+      "ranges/",
+      "index.js",
+      "preload.js",
+      "range.bnf"
+    ]
+  }
+}

+ 2 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/preload.js

@@ -0,0 +1,2 @@
+// XXX remove in v8 or beyond
+module.exports = require('./index.js')

+ 4 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/ranges/gtr.js

@@ -0,0 +1,4 @@
+// Determine if version is greater than all the versions possible in the range.
+const outside = require('./outside')
+const gtr = (version, range, options) => outside(version, range, '>', options)
+module.exports = gtr

+ 7 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/ranges/intersects.js

@@ -0,0 +1,7 @@
+const Range = require('../classes/range')
+const intersects = (r1, r2, options) => {
+  r1 = new Range(r1, options)
+  r2 = new Range(r2, options)
+  return r1.intersects(r2)
+}
+module.exports = intersects

+ 4 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/ranges/ltr.js

@@ -0,0 +1,4 @@
+const outside = require('./outside')
+// Determine if version is less than all the versions possible in the range
+const ltr = (version, range, options) => outside(version, range, '<', options)
+module.exports = ltr

+ 25 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/ranges/max-satisfying.js

@@ -0,0 +1,25 @@
+const SemVer = require('../classes/semver')
+const Range = require('../classes/range')
+
+const maxSatisfying = (versions, range, options) => {
+  let max = null
+  let maxSV = null
+  let rangeObj = null
+  try {
+    rangeObj = new Range(range, options)
+  } catch (er) {
+    return null
+  }
+  versions.forEach((v) => {
+    if (rangeObj.test(v)) {
+      // satisfies(v, range, options)
+      if (!max || maxSV.compare(v) === -1) {
+        // compare(max, v, true)
+        max = v
+        maxSV = new SemVer(max, options)
+      }
+    }
+  })
+  return max
+}
+module.exports = maxSatisfying

+ 24 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/ranges/min-satisfying.js

@@ -0,0 +1,24 @@
+const SemVer = require('../classes/semver')
+const Range = require('../classes/range')
+const minSatisfying = (versions, range, options) => {
+  let min = null
+  let minSV = null
+  let rangeObj = null
+  try {
+    rangeObj = new Range(range, options)
+  } catch (er) {
+    return null
+  }
+  versions.forEach((v) => {
+    if (rangeObj.test(v)) {
+      // satisfies(v, range, options)
+      if (!min || minSV.compare(v) === 1) {
+        // compare(min, v, true)
+        min = v
+        minSV = new SemVer(min, options)
+      }
+    }
+  })
+  return min
+}
+module.exports = minSatisfying

+ 61 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/ranges/min-version.js

@@ -0,0 +1,61 @@
+const SemVer = require('../classes/semver')
+const Range = require('../classes/range')
+const gt = require('../functions/gt')
+
+const minVersion = (range, loose) => {
+  range = new Range(range, loose)
+
+  let minver = new SemVer('0.0.0')
+  if (range.test(minver)) {
+    return minver
+  }
+
+  minver = new SemVer('0.0.0-0')
+  if (range.test(minver)) {
+    return minver
+  }
+
+  minver = null
+  for (let i = 0; i < range.set.length; ++i) {
+    const comparators = range.set[i]
+
+    let setMin = null
+    comparators.forEach((comparator) => {
+      // Clone to avoid manipulating the comparator's semver object.
+      const compver = new SemVer(comparator.semver.version)
+      switch (comparator.operator) {
+        case '>':
+          if (compver.prerelease.length === 0) {
+            compver.patch++
+          } else {
+            compver.prerelease.push(0)
+          }
+          compver.raw = compver.format()
+          /* fallthrough */
+        case '':
+        case '>=':
+          if (!setMin || gt(compver, setMin)) {
+            setMin = compver
+          }
+          break
+        case '<':
+        case '<=':
+          /* Ignore maximum versions */
+          break
+        /* istanbul ignore next */
+        default:
+          throw new Error(`Unexpected operation: ${comparator.operator}`)
+      }
+    })
+    if (setMin && (!minver || gt(minver, setMin))) {
+      minver = setMin
+    }
+  }
+
+  if (minver && range.test(minver)) {
+    return minver
+  }
+
+  return null
+}
+module.exports = minVersion

+ 80 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/ranges/outside.js

@@ -0,0 +1,80 @@
+const SemVer = require('../classes/semver')
+const Comparator = require('../classes/comparator')
+const { ANY } = Comparator
+const Range = require('../classes/range')
+const satisfies = require('../functions/satisfies')
+const gt = require('../functions/gt')
+const lt = require('../functions/lt')
+const lte = require('../functions/lte')
+const gte = require('../functions/gte')
+
+const outside = (version, range, hilo, options) => {
+  version = new SemVer(version, options)
+  range = new Range(range, options)
+
+  let gtfn, ltefn, ltfn, comp, ecomp
+  switch (hilo) {
+    case '>':
+      gtfn = gt
+      ltefn = lte
+      ltfn = lt
+      comp = '>'
+      ecomp = '>='
+      break
+    case '<':
+      gtfn = lt
+      ltefn = gte
+      ltfn = gt
+      comp = '<'
+      ecomp = '<='
+      break
+    default:
+      throw new TypeError('Must provide a hilo val of "<" or ">"')
+  }
+
+  // If it satisfies the range it is not outside
+  if (satisfies(version, range, options)) {
+    return false
+  }
+
+  // From now on, variable terms are as if we're in "gtr" mode.
+  // but note that everything is flipped for the "ltr" function.
+
+  for (let i = 0; i < range.set.length; ++i) {
+    const comparators = range.set[i]
+
+    let high = null
+    let low = null
+
+    comparators.forEach((comparator) => {
+      if (comparator.semver === ANY) {
+        comparator = new Comparator('>=0.0.0')
+      }
+      high = high || comparator
+      low = low || comparator
+      if (gtfn(comparator.semver, high.semver, options)) {
+        high = comparator
+      } else if (ltfn(comparator.semver, low.semver, options)) {
+        low = comparator
+      }
+    })
+
+    // If the edge version comparator has a operator then our version
+    // isn't outside it
+    if (high.operator === comp || high.operator === ecomp) {
+      return false
+    }
+
+    // If the lowest version comparator has an operator and our version
+    // is less than it then it isn't higher than the range
+    if ((!low.operator || low.operator === comp) &&
+        ltefn(version, low.semver)) {
+      return false
+    } else if (low.operator === ecomp && ltfn(version, low.semver)) {
+      return false
+    }
+  }
+  return true
+}
+
+module.exports = outside

+ 47 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/ranges/simplify.js

@@ -0,0 +1,47 @@
+// given a set of versions and a range, create a "simplified" range
+// that includes the same versions that the original range does
+// If the original range is shorter than the simplified one, return that.
+const satisfies = require('../functions/satisfies.js')
+const compare = require('../functions/compare.js')
+module.exports = (versions, range, options) => {
+  const set = []
+  let first = null
+  let prev = null
+  const v = versions.sort((a, b) => compare(a, b, options))
+  for (const version of v) {
+    const included = satisfies(version, range, options)
+    if (included) {
+      prev = version
+      if (!first) {
+        first = version
+      }
+    } else {
+      if (prev) {
+        set.push([first, prev])
+      }
+      prev = null
+      first = null
+    }
+  }
+  if (first) {
+    set.push([first, null])
+  }
+
+  const ranges = []
+  for (const [min, max] of set) {
+    if (min === max) {
+      ranges.push(min)
+    } else if (!max && min === v[0]) {
+      ranges.push('*')
+    } else if (!max) {
+      ranges.push(`>=${min}`)
+    } else if (min === v[0]) {
+      ranges.push(`<=${max}`)
+    } else {
+      ranges.push(`${min} - ${max}`)
+    }
+  }
+  const simplified = ranges.join(' || ')
+  const original = typeof range.raw === 'string' ? range.raw : String(range)
+  return simplified.length < original.length ? simplified : range
+}

+ 244 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/ranges/subset.js

@@ -0,0 +1,244 @@
+const Range = require('../classes/range.js')
+const Comparator = require('../classes/comparator.js')
+const { ANY } = Comparator
+const satisfies = require('../functions/satisfies.js')
+const compare = require('../functions/compare.js')
+
+// Complex range `r1 || r2 || ...` is a subset of `R1 || R2 || ...` iff:
+// - Every simple range `r1, r2, ...` is a null set, OR
+// - Every simple range `r1, r2, ...` which is not a null set is a subset of
+//   some `R1, R2, ...`
+//
+// Simple range `c1 c2 ...` is a subset of simple range `C1 C2 ...` iff:
+// - If c is only the ANY comparator
+//   - If C is only the ANY comparator, return true
+//   - Else if in prerelease mode, return false
+//   - else replace c with `[>=0.0.0]`
+// - If C is only the ANY comparator
+//   - if in prerelease mode, return true
+//   - else replace C with `[>=0.0.0]`
+// - Let EQ be the set of = comparators in c
+// - If EQ is more than one, return true (null set)
+// - Let GT be the highest > or >= comparator in c
+// - Let LT be the lowest < or <= comparator in c
+// - If GT and LT, and GT.semver > LT.semver, return true (null set)
+// - If any C is a = range, and GT or LT are set, return false
+// - If EQ
+//   - If GT, and EQ does not satisfy GT, return true (null set)
+//   - If LT, and EQ does not satisfy LT, return true (null set)
+//   - If EQ satisfies every C, return true
+//   - Else return false
+// - If GT
+//   - If GT.semver is lower than any > or >= comp in C, return false
+//   - If GT is >=, and GT.semver does not satisfy every C, return false
+//   - If GT.semver has a prerelease, and not in prerelease mode
+//     - If no C has a prerelease and the GT.semver tuple, return false
+// - If LT
+//   - If LT.semver is greater than any < or <= comp in C, return false
+//   - If LT is <=, and LT.semver does not satisfy every C, return false
+//   - If GT.semver has a prerelease, and not in prerelease mode
+//     - If no C has a prerelease and the LT.semver tuple, return false
+// - Else return true
+
+const subset = (sub, dom, options = {}) => {
+  if (sub === dom) {
+    return true
+  }
+
+  sub = new Range(sub, options)
+  dom = new Range(dom, options)
+  let sawNonNull = false
+
+  OUTER: for (const simpleSub of sub.set) {
+    for (const simpleDom of dom.set) {
+      const isSub = simpleSubset(simpleSub, simpleDom, options)
+      sawNonNull = sawNonNull || isSub !== null
+      if (isSub) {
+        continue OUTER
+      }
+    }
+    // the null set is a subset of everything, but null simple ranges in
+    // a complex range should be ignored.  so if we saw a non-null range,
+    // then we know this isn't a subset, but if EVERY simple range was null,
+    // then it is a subset.
+    if (sawNonNull) {
+      return false
+    }
+  }
+  return true
+}
+
+const simpleSubset = (sub, dom, options) => {
+  if (sub === dom) {
+    return true
+  }
+
+  if (sub.length === 1 && sub[0].semver === ANY) {
+    if (dom.length === 1 && dom[0].semver === ANY) {
+      return true
+    } else if (options.includePrerelease) {
+      sub = [new Comparator('>=0.0.0-0')]
+    } else {
+      sub = [new Comparator('>=0.0.0')]
+    }
+  }
+
+  if (dom.length === 1 && dom[0].semver === ANY) {
+    if (options.includePrerelease) {
+      return true
+    } else {
+      dom = [new Comparator('>=0.0.0')]
+    }
+  }
+
+  const eqSet = new Set()
+  let gt, lt
+  for (const c of sub) {
+    if (c.operator === '>' || c.operator === '>=') {
+      gt = higherGT(gt, c, options)
+    } else if (c.operator === '<' || c.operator === '<=') {
+      lt = lowerLT(lt, c, options)
+    } else {
+      eqSet.add(c.semver)
+    }
+  }
+
+  if (eqSet.size > 1) {
+    return null
+  }
+
+  let gtltComp
+  if (gt && lt) {
+    gtltComp = compare(gt.semver, lt.semver, options)
+    if (gtltComp > 0) {
+      return null
+    } else if (gtltComp === 0 && (gt.operator !== '>=' || lt.operator !== '<=')) {
+      return null
+    }
+  }
+
+  // will iterate one or zero times
+  for (const eq of eqSet) {
+    if (gt && !satisfies(eq, String(gt), options)) {
+      return null
+    }
+
+    if (lt && !satisfies(eq, String(lt), options)) {
+      return null
+    }
+
+    for (const c of dom) {
+      if (!satisfies(eq, String(c), options)) {
+        return false
+      }
+    }
+
+    return true
+  }
+
+  let higher, lower
+  let hasDomLT, hasDomGT
+  // if the subset has a prerelease, we need a comparator in the superset
+  // with the same tuple and a prerelease, or it's not a subset
+  let needDomLTPre = lt &&
+    !options.includePrerelease &&
+    lt.semver.prerelease.length ? lt.semver : false
+  let needDomGTPre = gt &&
+    !options.includePrerelease &&
+    gt.semver.prerelease.length ? gt.semver : false
+  // exception: <1.2.3-0 is the same as <1.2.3
+  if (needDomLTPre && needDomLTPre.prerelease.length === 1 &&
+      lt.operator === '<' && needDomLTPre.prerelease[0] === 0) {
+    needDomLTPre = false
+  }
+
+  for (const c of dom) {
+    hasDomGT = hasDomGT || c.operator === '>' || c.operator === '>='
+    hasDomLT = hasDomLT || c.operator === '<' || c.operator === '<='
+    if (gt) {
+      if (needDomGTPre) {
+        if (c.semver.prerelease && c.semver.prerelease.length &&
+            c.semver.major === needDomGTPre.major &&
+            c.semver.minor === needDomGTPre.minor &&
+            c.semver.patch === needDomGTPre.patch) {
+          needDomGTPre = false
+        }
+      }
+      if (c.operator === '>' || c.operator === '>=') {
+        higher = higherGT(gt, c, options)
+        if (higher === c && higher !== gt) {
+          return false
+        }
+      } else if (gt.operator === '>=' && !satisfies(gt.semver, String(c), options)) {
+        return false
+      }
+    }
+    if (lt) {
+      if (needDomLTPre) {
+        if (c.semver.prerelease && c.semver.prerelease.length &&
+            c.semver.major === needDomLTPre.major &&
+            c.semver.minor === needDomLTPre.minor &&
+            c.semver.patch === needDomLTPre.patch) {
+          needDomLTPre = false
+        }
+      }
+      if (c.operator === '<' || c.operator === '<=') {
+        lower = lowerLT(lt, c, options)
+        if (lower === c && lower !== lt) {
+          return false
+        }
+      } else if (lt.operator === '<=' && !satisfies(lt.semver, String(c), options)) {
+        return false
+      }
+    }
+    if (!c.operator && (lt || gt) && gtltComp !== 0) {
+      return false
+    }
+  }
+
+  // if there was a < or >, and nothing in the dom, then must be false
+  // UNLESS it was limited by another range in the other direction.
+  // Eg, >1.0.0 <1.0.1 is still a subset of <2.0.0
+  if (gt && hasDomLT && !lt && gtltComp !== 0) {
+    return false
+  }
+
+  if (lt && hasDomGT && !gt && gtltComp !== 0) {
+    return false
+  }
+
+  // we needed a prerelease range in a specific tuple, but didn't get one
+  // then this isn't a subset.  eg >=1.2.3-pre is not a subset of >=1.0.0,
+  // because it includes prereleases in the 1.2.3 tuple
+  if (needDomGTPre || needDomLTPre) {
+    return false
+  }
+
+  return true
+}
+
+// >=1.2.3 is lower than >1.2.3
+const higherGT = (a, b, options) => {
+  if (!a) {
+    return b
+  }
+  const comp = compare(a.semver, b.semver, options)
+  return comp > 0 ? a
+    : comp < 0 ? b
+    : b.operator === '>' && a.operator === '>=' ? b
+    : a
+}
+
+// <=1.2.3 is higher than <1.2.3
+const lowerLT = (a, b, options) => {
+  if (!a) {
+    return b
+  }
+  const comp = compare(a.semver, b.semver, options)
+  return comp < 0 ? a
+    : comp > 0 ? b
+    : b.operator === '<' && a.operator === '<=' ? b
+    : a
+}
+
+module.exports = subset

+ 8 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/ranges/to-comparators.js

@@ -0,0 +1,8 @@
+const Range = require('../classes/range')
+
+// Mostly just for testing and legacy API reasons
+const toComparators = (range, options) =>
+  new Range(range, options).set
+    .map(comp => comp.map(c => c.value).join(' ').trim().split(' '))
+
+module.exports = toComparators

+ 11 - 0
backend/node_modules/@mapbox/node-pre-gyp/node_modules/semver/ranges/valid.js

@@ -0,0 +1,11 @@
+const Range = require('../classes/range')
+const validRange = (range, options) => {
+  try {
+    // Return '*' instead of '' so that truthiness works.
+    // This will throw if it's invalid anyway
+    return new Range(range, options).range || '*'
+  } catch (er) {
+    return null
+  }
+}
+module.exports = validRange

+ 62 - 0
backend/node_modules/@mapbox/node-pre-gyp/package.json

@@ -0,0 +1,62 @@
+{
+  "name": "@mapbox/node-pre-gyp",
+  "description": "Node.js native addon binary install tool",
+  "version": "1.0.9",
+  "keywords": [
+    "native",
+    "addon",
+    "module",
+    "c",
+    "c++",
+    "bindings",
+    "binary"
+  ],
+  "license": "BSD-3-Clause",
+  "author": "Dane Springmeyer <dane@mapbox.com>",
+  "repository": {
+    "type": "git",
+    "url": "git://github.com/mapbox/node-pre-gyp.git"
+  },
+  "bin": "./bin/node-pre-gyp",
+  "main": "./lib/node-pre-gyp.js",
+  "dependencies": {
+    "detect-libc": "^2.0.0",
+    "https-proxy-agent": "^5.0.0",
+    "make-dir": "^3.1.0",
+    "node-fetch": "^2.6.7",
+    "nopt": "^5.0.0",
+    "npmlog": "^5.0.1",
+    "rimraf": "^3.0.2",
+    "semver": "^7.3.5",
+    "tar": "^6.1.11"
+  },
+  "devDependencies": {
+    "@mapbox/cloudfriend": "^5.1.0",
+    "@mapbox/eslint-config-mapbox": "^3.0.0",
+    "aws-sdk": "^2.1087.0",
+    "codecov": "^3.8.3",
+    "eslint": "^7.32.0",
+    "eslint-plugin-node": "^11.1.0",
+    "mock-aws-s3": "^4.0.2",
+    "nock": "^12.0.3",
+    "node-addon-api": "^4.3.0",
+    "nyc": "^15.1.0",
+    "tape": "^5.5.2",
+    "tar-fs": "^2.1.1"
+  },
+  "nyc": {
+    "all": true,
+    "skip-full": false,
+    "exclude": [
+      "test/**"
+    ]
+  },
+  "scripts": {
+    "coverage": "nyc --all --include index.js --include lib/ npm test",
+    "upload-coverage": "nyc report --reporter json && codecov --clear --flags=unit --file=./coverage/coverage-final.json",
+    "lint": "eslint bin/node-pre-gyp lib/*js lib/util/*js test/*js scripts/*js",
+    "fix": "npm run lint -- --fix",
+    "update-crosswalk": "node scripts/abi_crosswalk.js",
+    "test": "tape test/*test.js"
+  }
+}

+ 245 - 0
backend/node_modules/@sindresorhus/is/dist/index.js

@@ -0,0 +1,245 @@
+"use strict";
+/// <reference lib="es2016"/>
+/// <reference lib="es2017.sharedmemory"/>
+/// <reference lib="esnext.asynciterable"/>
+/// <reference lib="dom"/>
+Object.defineProperty(exports, "__esModule", { value: true });
+// TODO: Use the `URL` global when targeting Node.js 10
+// tslint:disable-next-line
+const URLGlobal = typeof URL === 'undefined' ? require('url').URL : URL;
+const toString = Object.prototype.toString;
+const isOfType = (type) => (value) => typeof value === type;
+const isBuffer = (input) => !is.nullOrUndefined(input) && !is.nullOrUndefined(input.constructor) && is.function_(input.constructor.isBuffer) && input.constructor.isBuffer(input);
+const getObjectType = (value) => {
+    const objectName = toString.call(value).slice(8, -1);
+    if (objectName) {
+        return objectName;
+    }
+    return null;
+};
+const isObjectOfType = (type) => (value) => getObjectType(value) === type;
+function is(value) {
+    switch (value) {
+        case null:
+            return "null" /* null */;
+        case true:
+        case false:
+            return "boolean" /* boolean */;
+        default:
+    }
+    switch (typeof value) {
+        case 'undefined':
+            return "undefined" /* undefined */;
+        case 'string':
+            return "string" /* string */;
+        case 'number':
+            return "number" /* number */;
+        case 'symbol':
+            return "symbol" /* symbol */;
+        default:
+    }
+    if (is.function_(value)) {
+        return "Function" /* Function */;
+    }
+    if (is.observable(value)) {
+        return "Observable" /* Observable */;
+    }
+    if (Array.isArray(value)) {
+        return "Array" /* Array */;
+    }
+    if (isBuffer(value)) {
+        return "Buffer" /* Buffer */;
+    }
+    const tagType = getObjectType(value);
+    if (tagType) {
+        return tagType;
+    }
+    if (value instanceof String || value instanceof Boolean || value instanceof Number) {
+        throw new TypeError('Please don\'t use object wrappers for primitive types');
+    }
+    return "Object" /* Object */;
+}
+(function (is) {
+    // tslint:disable-next-line:strict-type-predicates
+    const isObject = (value) => typeof value === 'object';
+    // tslint:disable:variable-name
+    is.undefined = isOfType('undefined');
+    is.string = isOfType('string');
+    is.number = isOfType('number');
+    is.function_ = isOfType('function');
+    // tslint:disable-next-line:strict-type-predicates
+    is.null_ = (value) => value === null;
+    is.class_ = (value) => is.function_(value) && value.toString().startsWith('class ');
+    is.boolean = (value) => value === true || value === false;
+    is.symbol = isOfType('symbol');
+    // tslint:enable:variable-name
+    is.numericString = (value) => is.string(value) && value.length > 0 && !Number.isNaN(Number(value));
+    is.array = Array.isArray;
+    is.buffer = isBuffer;
+    is.nullOrUndefined = (value) => is.null_(value) || is.undefined(value);
+    is.object = (value) => !is.nullOrUndefined(value) && (is.function_(value) || isObject(value));
+    is.iterable = (value) => !is.nullOrUndefined(value) && is.function_(value[Symbol.iterator]);
+    is.asyncIterable = (value) => !is.nullOrUndefined(value) && is.function_(value[Symbol.asyncIterator]);
+    is.generator = (value) => is.iterable(value) && is.function_(value.next) && is.function_(value.throw);
+    is.nativePromise = (value) => isObjectOfType("Promise" /* Promise */)(value);
+    const hasPromiseAPI = (value) => !is.null_(value) &&
+        isObject(value) &&
+        is.function_(value.then) &&
+        is.function_(value.catch);
+    is.promise = (value) => is.nativePromise(value) || hasPromiseAPI(value);
+    is.generatorFunction = isObjectOfType("GeneratorFunction" /* GeneratorFunction */);
+    is.asyncFunction = isObjectOfType("AsyncFunction" /* AsyncFunction */);
+    is.boundFunction = (value) => is.function_(value) && !value.hasOwnProperty('prototype');
+    is.regExp = isObjectOfType("RegExp" /* RegExp */);
+    is.date = isObjectOfType("Date" /* Date */);
+    is.error = isObjectOfType("Error" /* Error */);
+    is.map = (value) => isObjectOfType("Map" /* Map */)(value);
+    is.set = (value) => isObjectOfType("Set" /* Set */)(value);
+    is.weakMap = (value) => isObjectOfType("WeakMap" /* WeakMap */)(value);
+    is.weakSet = (value) => isObjectOfType("WeakSet" /* WeakSet */)(value);
+    is.int8Array = isObjectOfType("Int8Array" /* Int8Array */);
+    is.uint8Array = isObjectOfType("Uint8Array" /* Uint8Array */);
+    is.uint8ClampedArray = isObjectOfType("Uint8ClampedArray" /* Uint8ClampedArray */);
+    is.int16Array = isObjectOfType("Int16Array" /* Int16Array */);
+    is.uint16Array = isObjectOfType("Uint16Array" /* Uint16Array */);
+    is.int32Array = isObjectOfType("Int32Array" /* Int32Array */);
+    is.uint32Array = isObjectOfType("Uint32Array" /* Uint32Array */);
+    is.float32Array = isObjectOfType("Float32Array" /* Float32Array */);
+    is.float64Array = isObjectOfType("Float64Array" /* Float64Array */);
+    is.arrayBuffer = isObjectOfType("ArrayBuffer" /* ArrayBuffer */);
+    is.sharedArrayBuffer = isObjectOfType("SharedArrayBuffer" /* SharedArrayBuffer */);
+    is.dataView = isObjectOfType("DataView" /* DataView */);
+    is.directInstanceOf = (instance, klass) => Object.getPrototypeOf(instance) === klass.prototype;
+    is.urlInstance = (value) => isObjectOfType("URL" /* URL */)(value);
+    is.urlString = (value) => {
+        if (!is.string(value)) {
+            return false;
+        }
+        try {
+            new URLGlobal(value); // tslint:disable-line no-unused-expression
+            return true;
+        }
+        catch (_a) {
+            return false;
+        }
+    };
+    is.truthy = (value) => Boolean(value);
+    is.falsy = (value) => !value;
+    is.nan = (value) => Number.isNaN(value);
+    const primitiveTypes = new Set([
+        'undefined',
+        'string',
+        'number',
+        'boolean',
+        'symbol'
+    ]);
+    is.primitive = (value) => is.null_(value) || primitiveTypes.has(typeof value);
+    is.integer = (value) => Number.isInteger(value);
+    is.safeInteger = (value) => Number.isSafeInteger(value);
+    is.plainObject = (value) => {
+        // From: https://github.com/sindresorhus/is-plain-obj/blob/master/index.js
+        let prototype;
+        return getObjectType(value) === "Object" /* Object */ &&
+            (prototype = Object.getPrototypeOf(value), prototype === null || // tslint:disable-line:ban-comma-operator
+                prototype === Object.getPrototypeOf({}));
+    };
+    const typedArrayTypes = new Set([
+        "Int8Array" /* Int8Array */,
+        "Uint8Array" /* Uint8Array */,
+        "Uint8ClampedArray" /* Uint8ClampedArray */,
+        "Int16Array" /* Int16Array */,
+        "Uint16Array" /* Uint16Array */,
+        "Int32Array" /* Int32Array */,
+        "Uint32Array" /* Uint32Array */,
+        "Float32Array" /* Float32Array */,
+        "Float64Array" /* Float64Array */
+    ]);
+    is.typedArray = (value) => {
+        const objectType = getObjectType(value);
+        if (objectType === null) {
+            return false;
+        }
+        return typedArrayTypes.has(objectType);
+    };
+    const isValidLength = (value) => is.safeInteger(value) && value > -1;
+    is.arrayLike = (value) => !is.nullOrUndefined(value) && !is.function_(value) && isValidLength(value.length);
+    is.inRange = (value, range) => {
+        if (is.number(range)) {
+            return value >= Math.min(0, range) && value <= Math.max(range, 0);
+        }
+        if (is.array(range) && range.length === 2) {
+            return value >= Math.min(...range) && value <= Math.max(...range);
+        }
+        throw new TypeError(`Invalid range: ${JSON.stringify(range)}`);
+    };
+    const NODE_TYPE_ELEMENT = 1;
+    const DOM_PROPERTIES_TO_CHECK = [
+        'innerHTML',
+        'ownerDocument',
+        'style',
+        'attributes',
+        'nodeValue'
+    ];
+    is.domElement = (value) => is.object(value) && value.nodeType === NODE_TYPE_ELEMENT && is.string(value.nodeName) &&
+        !is.plainObject(value) && DOM_PROPERTIES_TO_CHECK.every(property => property in value);
+    is.observable = (value) => {
+        if (!value) {
+            return false;
+        }
+        if (value[Symbol.observable] && value === value[Symbol.observable]()) {
+            return true;
+        }
+        if (value['@@observable'] && value === value['@@observable']()) {
+            return true;
+        }
+        return false;
+    };
+    is.nodeStream = (value) => !is.nullOrUndefined(value) && isObject(value) && is.function_(value.pipe) && !is.observable(value);
+    is.infinite = (value) => value === Infinity || value === -Infinity;
+    const isAbsoluteMod2 = (rem) => (value) => is.integer(value) && Math.abs(value % 2) === rem;
+    is.even = isAbsoluteMod2(0);
+    is.odd = isAbsoluteMod2(1);
+    const isWhiteSpaceString = (value) => is.string(value) && /\S/.test(value) === false;
+    is.emptyArray = (value) => is.array(value) && value.length === 0;
+    is.nonEmptyArray = (value) => is.array(value) && value.length > 0;
+    is.emptyString = (value) => is.string(value) && value.length === 0;
+    is.nonEmptyString = (value) => is.string(value) && value.length > 0;
+    is.emptyStringOrWhitespace = (value) => is.emptyString(value) || isWhiteSpaceString(value);
+    is.emptyObject = (value) => is.object(value) && !is.map(value) && !is.set(value) && Object.keys(value).length === 0;
+    is.nonEmptyObject = (value) => is.object(value) && !is.map(value) && !is.set(value) && Object.keys(value).length > 0;
+    is.emptySet = (value) => is.set(value) && value.size === 0;
+    is.nonEmptySet = (value) => is.set(value) && value.size > 0;
+    is.emptyMap = (value) => is.map(value) && value.size === 0;
+    is.nonEmptyMap = (value) => is.map(value) && value.size > 0;
+    const predicateOnArray = (method, predicate, values) => {
+        if (is.function_(predicate) === false) {
+            throw new TypeError(`Invalid predicate: ${JSON.stringify(predicate)}`);
+        }
+        if (values.length === 0) {
+            throw new TypeError('Invalid number of values');
+        }
+        return method.call(values, predicate);
+    };
+    // tslint:disable variable-name
+    is.any = (predicate, ...values) => predicateOnArray(Array.prototype.some, predicate, values);
+    is.all = (predicate, ...values) => predicateOnArray(Array.prototype.every, predicate, values);
+    // tslint:enable variable-name
+})(is || (is = {}));
+// Some few keywords are reserved, but we'll populate them for Node.js users
+// See https://github.com/Microsoft/TypeScript/issues/2536
+Object.defineProperties(is, {
+    class: {
+        value: is.class_
+    },
+    function: {
+        value: is.function_
+    },
+    null: {
+        value: is.null_
+    }
+});
+exports.default = is;
+// For CommonJS default export support
+module.exports = is;
+module.exports.default = is;
+//# sourceMappingURL=index.js.map

+ 63 - 0
backend/node_modules/@sindresorhus/is/package.json

@@ -0,0 +1,63 @@
+{
+	"name": "@sindresorhus/is",
+	"version": "0.14.0",
+	"description": "Type check values: `is.string('🦄') //=> true`",
+	"license": "MIT",
+	"repository": "sindresorhus/is",
+	"author": {
+		"name": "Sindre Sorhus",
+		"email": "sindresorhus@gmail.com",
+		"url": "sindresorhus.com"
+	},
+	"main": "dist/index.js",
+	"engines": {
+		"node": ">=6"
+	},
+	"scripts": {
+		"lint": "tslint --format stylish --project .",
+		"build": "del dist && tsc",
+		"test": "npm run lint && npm run build && ava dist/tests",
+		"prepublish": "npm run build && del dist/tests"
+	},
+	"files": [
+		"dist"
+	],
+	"keywords": [
+		"type",
+		"types",
+		"is",
+		"check",
+		"checking",
+		"validate",
+		"validation",
+		"utility",
+		"util",
+		"typeof",
+		"instanceof",
+		"object",
+		"assert",
+		"assertion",
+		"test",
+		"kind",
+		"primitive",
+		"verify",
+		"compare"
+	],
+	"devDependencies": {
+		"@sindresorhus/tsconfig": "^0.1.0",
+		"@types/jsdom": "^11.12.0",
+		"@types/node": "^10.12.10",
+		"@types/tempy": "^0.2.0",
+		"@types/zen-observable": "^0.8.0",
+		"ava": "^0.25.0",
+		"del-cli": "^1.1.0",
+		"jsdom": "^11.6.2",
+		"rxjs": "^6.3.3",
+		"tempy": "^0.2.1",
+		"tslint": "^5.9.1",
+		"tslint-xo": "^0.10.0",
+		"typescript": "^3.2.1",
+		"zen-observable": "^0.8.8"
+	},
+	"types": "dist/index.d.ts"
+}

+ 47 - 0
backend/node_modules/@szmarczak/http-timer/package.json

@@ -0,0 +1,47 @@
+{
+	"name": "@szmarczak/http-timer",
+	"version": "1.1.2",
+	"description": "Timings for HTTP requests",
+	"main": "source",
+	"engines": {
+		"node": ">=6"
+	},
+	"scripts": {
+		"test": "xo && nyc ava",
+		"coveralls": "nyc report --reporter=text-lcov | coveralls"
+	},
+	"files": [
+		"source"
+	],
+	"keywords": [
+		"http",
+		"https",
+		"timer",
+		"timings"
+	],
+	"repository": {
+		"type": "git",
+		"url": "git+https://github.com/szmarczak/http-timer.git"
+	},
+	"author": "Szymon Marczak",
+	"license": "MIT",
+	"bugs": {
+		"url": "https://github.com/szmarczak/http-timer/issues"
+	},
+	"homepage": "https://github.com/szmarczak/http-timer#readme",
+	"xo": {
+		"rules": {
+			"unicorn/filename-case": "camelCase"
+		}
+	},
+	"devDependencies": {
+		"ava": "^0.25.0",
+		"coveralls": "^3.0.2",
+		"p-event": "^2.1.0",
+		"nyc": "^12.0.2",
+		"xo": "^0.22.0"
+	},
+	"dependencies": {
+		"defer-to-connect": "^1.0.1"
+	}
+}

+ 99 - 0
backend/node_modules/@szmarczak/http-timer/source/index.js

@@ -0,0 +1,99 @@
+'use strict';
+const deferToConnect = require('defer-to-connect');
+
+module.exports = request => {
+	const timings = {
+		start: Date.now(),
+		socket: null,
+		lookup: null,
+		connect: null,
+		upload: null,
+		response: null,
+		end: null,
+		error: null,
+		phases: {
+			wait: null,
+			dns: null,
+			tcp: null,
+			request: null,
+			firstByte: null,
+			download: null,
+			total: null
+		}
+	};
+
+	const handleError = origin => {
+		const emit = origin.emit.bind(origin);
+		origin.emit = (event, ...args) => {
+			// Catches the `error` event
+			if (event === 'error') {
+				timings.error = Date.now();
+				timings.phases.total = timings.error - timings.start;
+
+				origin.emit = emit;
+			}
+
+			// Saves the original behavior
+			return emit(event, ...args);
+		};
+	};
+
+	let uploadFinished = false;
+	const onUpload = () => {
+		timings.upload = Date.now();
+		timings.phases.request = timings.upload - timings.connect;
+	};
+
+	handleError(request);
+
+	request.once('socket', socket => {
+		timings.socket = Date.now();
+		timings.phases.wait = timings.socket - timings.start;
+
+		const lookupListener = () => {
+			timings.lookup = Date.now();
+			timings.phases.dns = timings.lookup - timings.socket;
+		};
+
+		socket.once('lookup', lookupListener);
+
+		deferToConnect(socket, () => {
+			timings.connect = Date.now();
+
+			if (timings.lookup === null) {
+				socket.removeListener('lookup', lookupListener);
+				timings.lookup = timings.connect;
+				timings.phases.dns = timings.lookup - timings.socket;
+			}
+
+			timings.phases.tcp = timings.connect - timings.lookup;
+
+			if (uploadFinished && !timings.upload) {
+				onUpload();
+			}
+		});
+	});
+
+	request.once('finish', () => {
+		uploadFinished = true;
+
+		if (timings.connect) {
+			onUpload();
+		}
+	});
+
+	request.once('response', response => {
+		timings.response = Date.now();
+		timings.phases.firstByte = timings.response - timings.upload;
+
+		handleError(response);
+
+		response.once('end', () => {
+			timings.end = Date.now();
+			timings.phases.download = timings.end - timings.response;
+			timings.phases.total = timings.end - timings.start;
+		});
+	});
+
+	return timings;
+};

+ 25 - 0
backend/node_modules/@types/component-emitter/package.json

@@ -0,0 +1,25 @@
+{
+    "name": "@types/component-emitter",
+    "version": "1.2.11",
+    "description": "TypeScript definitions for component-emitter",
+    "homepage": "https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/component-emitter",
+    "license": "MIT",
+    "contributors": [
+        {
+            "name": "Peter Snider",
+            "url": "https://github.com/psnider",
+            "githubUsername": "psnider"
+        }
+    ],
+    "main": "",
+    "types": "index.d.ts",
+    "repository": {
+        "type": "git",
+        "url": "https://github.com/DefinitelyTyped/DefinitelyTyped.git",
+        "directory": "types/component-emitter"
+    },
+    "scripts": {},
+    "dependencies": {},
+    "typesPublisherContentHash": "d86d217b63101effae1228ebbfe02ac682ee4eb8abd6fc0dcc9948a4b6fdf572",
+    "typeScriptVersion": "3.7"
+}

+ 30 - 0
backend/node_modules/@types/cookie/package.json

@@ -0,0 +1,30 @@
+{
+    "name": "@types/cookie",
+    "version": "0.4.1",
+    "description": "TypeScript definitions for cookie",
+    "homepage": "https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/cookie",
+    "license": "MIT",
+    "contributors": [
+        {
+            "name": "Pine Mizune",
+            "url": "https://github.com/pine",
+            "githubUsername": "pine"
+        },
+        {
+            "name": "Piotr Błażejewicz",
+            "url": "https://github.com/peterblazejewicz",
+            "githubUsername": "peterblazejewicz"
+        }
+    ],
+    "main": "",
+    "types": "index.d.ts",
+    "repository": {
+        "type": "git",
+        "url": "https://github.com/DefinitelyTyped/DefinitelyTyped.git",
+        "directory": "types/cookie"
+    },
+    "scripts": {},
+    "dependencies": {},
+    "typesPublisherContentHash": "7d4a6dd505c896319459ae131b5fa8fc0a2ed25552db53dac87946119bb21559",
+    "typeScriptVersion": "3.6"
+}

+ 30 - 0
backend/node_modules/@types/cors/package.json

@@ -0,0 +1,30 @@
+{
+    "name": "@types/cors",
+    "version": "2.8.12",
+    "description": "TypeScript definitions for cors",
+    "homepage": "https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/cors",
+    "license": "MIT",
+    "contributors": [
+        {
+            "name": "Alan Plum",
+            "url": "https://github.com/pluma",
+            "githubUsername": "pluma"
+        },
+        {
+            "name": "Gaurav Sharma",
+            "url": "https://github.com/gtpan77",
+            "githubUsername": "gtpan77"
+        }
+    ],
+    "main": "",
+    "types": "index.d.ts",
+    "repository": {
+        "type": "git",
+        "url": "https://github.com/DefinitelyTyped/DefinitelyTyped.git",
+        "directory": "types/cors"
+    },
+    "scripts": {},
+    "dependencies": {},
+    "typesPublisherContentHash": "53ea51a6543d58d3c1b9035a9c361d8f06d7be01973be2895820b2fb7ad9563a",
+    "typeScriptVersion": "3.6"
+}

+ 230 - 0
backend/node_modules/@types/node/package.json

@@ -0,0 +1,230 @@
+{
+    "name": "@types/node",
+    "version": "17.0.8",
+    "description": "TypeScript definitions for Node.js",
+    "homepage": "https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/node",
+    "license": "MIT",
+    "contributors": [
+        {
+            "name": "Microsoft TypeScript",
+            "url": "https://github.com/Microsoft",
+            "githubUsername": "Microsoft"
+        },
+        {
+            "name": "DefinitelyTyped",
+            "url": "https://github.com/DefinitelyTyped",
+            "githubUsername": "DefinitelyTyped"
+        },
+        {
+            "name": "Alberto Schiabel",
+            "url": "https://github.com/jkomyno",
+            "githubUsername": "jkomyno"
+        },
+        {
+            "name": "Alvis HT Tang",
+            "url": "https://github.com/alvis",
+            "githubUsername": "alvis"
+        },
+        {
+            "name": "Andrew Makarov",
+            "url": "https://github.com/r3nya",
+            "githubUsername": "r3nya"
+        },
+        {
+            "name": "Benjamin Toueg",
+            "url": "https://github.com/btoueg",
+            "githubUsername": "btoueg"
+        },
+        {
+            "name": "Chigozirim C.",
+            "url": "https://github.com/smac89",
+            "githubUsername": "smac89"
+        },
+        {
+            "name": "David Junger",
+            "url": "https://github.com/touffy",
+            "githubUsername": "touffy"
+        },
+        {
+            "name": "Deividas Bakanas",
+            "url": "https://github.com/DeividasBakanas",
+            "githubUsername": "DeividasBakanas"
+        },
+        {
+            "name": "Eugene Y. Q. Shen",
+            "url": "https://github.com/eyqs",
+            "githubUsername": "eyqs"
+        },
+        {
+            "name": "Hannes Magnusson",
+            "url": "https://github.com/Hannes-Magnusson-CK",
+            "githubUsername": "Hannes-Magnusson-CK"
+        },
+        {
+            "name": "Huw",
+            "url": "https://github.com/hoo29",
+            "githubUsername": "hoo29"
+        },
+        {
+            "name": "Kelvin Jin",
+            "url": "https://github.com/kjin",
+            "githubUsername": "kjin"
+        },
+        {
+            "name": "Klaus Meinhardt",
+            "url": "https://github.com/ajafff",
+            "githubUsername": "ajafff"
+        },
+        {
+            "name": "Lishude",
+            "url": "https://github.com/islishude",
+            "githubUsername": "islishude"
+        },
+        {
+            "name": "Mariusz Wiktorczyk",
+            "url": "https://github.com/mwiktorczyk",
+            "githubUsername": "mwiktorczyk"
+        },
+        {
+            "name": "Mohsen Azimi",
+            "url": "https://github.com/mohsen1",
+            "githubUsername": "mohsen1"
+        },
+        {
+            "name": "Nicolas Even",
+            "url": "https://github.com/n-e",
+            "githubUsername": "n-e"
+        },
+        {
+            "name": "Nikita Galkin",
+            "url": "https://github.com/galkin",
+            "githubUsername": "galkin"
+        },
+        {
+            "name": "Parambir Singh",
+            "url": "https://github.com/parambirs",
+            "githubUsername": "parambirs"
+        },
+        {
+            "name": "Sebastian Silbermann",
+            "url": "https://github.com/eps1lon",
+            "githubUsername": "eps1lon"
+        },
+        {
+            "name": "Seth Westphal",
+            "url": "https://github.com/westy92",
+            "githubUsername": "westy92"
+        },
+        {
+            "name": "Simon Schick",
+            "url": "https://github.com/SimonSchick",
+            "githubUsername": "SimonSchick"
+        },
+        {
+            "name": "Thomas den Hollander",
+            "url": "https://github.com/ThomasdenH",
+            "githubUsername": "ThomasdenH"
+        },
+        {
+            "name": "Wilco Bakker",
+            "url": "https://github.com/WilcoBakker",
+            "githubUsername": "WilcoBakker"
+        },
+        {
+            "name": "wwwy3y3",
+            "url": "https://github.com/wwwy3y3",
+            "githubUsername": "wwwy3y3"
+        },
+        {
+            "name": "Samuel Ainsworth",
+            "url": "https://github.com/samuela",
+            "githubUsername": "samuela"
+        },
+        {
+            "name": "Kyle Uehlein",
+            "url": "https://github.com/kuehlein",
+            "githubUsername": "kuehlein"
+        },
+        {
+            "name": "Thanik Bhongbhibhat",
+            "url": "https://github.com/bhongy",
+            "githubUsername": "bhongy"
+        },
+        {
+            "name": "Marcin Kopacz",
+            "url": "https://github.com/chyzwar",
+            "githubUsername": "chyzwar"
+        },
+        {
+            "name": "Trivikram Kamat",
+            "url": "https://github.com/trivikr",
+            "githubUsername": "trivikr"
+        },
+        {
+            "name": "Junxiao Shi",
+            "url": "https://github.com/yoursunny",
+            "githubUsername": "yoursunny"
+        },
+        {
+            "name": "Ilia Baryshnikov",
+            "url": "https://github.com/qwelias",
+            "githubUsername": "qwelias"
+        },
+        {
+            "name": "ExE Boss",
+            "url": "https://github.com/ExE-Boss",
+            "githubUsername": "ExE-Boss"
+        },
+        {
+            "name": "Surasak Chaisurin",
+            "url": "https://github.com/Ryan-Willpower",
+            "githubUsername": "Ryan-Willpower"
+        },
+        {
+            "name": "Piotr Błażejewicz",
+            "url": "https://github.com/peterblazejewicz",
+            "githubUsername": "peterblazejewicz"
+        },
+        {
+            "name": "Anna Henningsen",
+            "url": "https://github.com/addaleax",
+            "githubUsername": "addaleax"
+        },
+        {
+            "name": "Victor Perin",
+            "url": "https://github.com/victorperin",
+            "githubUsername": "victorperin"
+        },
+        {
+            "name": "Yongsheng Zhang",
+            "url": "https://github.com/ZYSzys",
+            "githubUsername": "ZYSzys"
+        },
+        {
+            "name": "NodeJS Contributors",
+            "url": "https://github.com/NodeJS",
+            "githubUsername": "NodeJS"
+        },
+        {
+            "name": "Linus Unnebäck",
+            "url": "https://github.com/LinusU",
+            "githubUsername": "LinusU"
+        },
+        {
+            "name": "wafuwafu13",
+            "url": "https://github.com/wafuwafu13",
+            "githubUsername": "wafuwafu13"
+        }
+    ],
+    "main": "",
+    "types": "index.d.ts",
+    "repository": {
+        "type": "git",
+        "url": "https://github.com/DefinitelyTyped/DefinitelyTyped.git",
+        "directory": "types/node"
+    },
+    "scripts": {},
+    "dependencies": {},
+    "typesPublisherContentHash": "133a958e0122898f222d66bf1e776d90a38ee878a694bbe229cf5e02db71deef",
+    "typeScriptVersion": "3.8"
+}

+ 25 - 0
backend/node_modules/@types/webidl-conversions/package.json

@@ -0,0 +1,25 @@
+{
+    "name": "@types/webidl-conversions",
+    "version": "6.1.1",
+    "description": "TypeScript definitions for webidl-conversions",
+    "homepage": "https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/webidl-conversions",
+    "license": "MIT",
+    "contributors": [
+        {
+            "name": "ExE Boss",
+            "url": "https://github.com/ExE-Boss",
+            "githubUsername": "ExE-Boss"
+        }
+    ],
+    "main": "",
+    "types": "index.d.ts",
+    "repository": {
+        "type": "git",
+        "url": "https://github.com/DefinitelyTyped/DefinitelyTyped.git",
+        "directory": "types/webidl-conversions"
+    },
+    "scripts": {},
+    "dependencies": {},
+    "typesPublisherContentHash": "bc47f919faf031afa91cea9b170f96f05eeac452057ba17794386552a99d0ad7",
+    "typeScriptVersion": "3.6"
+}

+ 33 - 0
backend/node_modules/@types/whatwg-url/package.json

@@ -0,0 +1,33 @@
+{
+    "name": "@types/whatwg-url",
+    "version": "8.2.2",
+    "description": "TypeScript definitions for whatwg-url",
+    "homepage": "https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/whatwg-url",
+    "license": "MIT",
+    "contributors": [
+        {
+            "name": "Alexander Marks",
+            "url": "https://github.com/aomarks",
+            "githubUsername": "aomarks"
+        },
+        {
+            "name": "ExE Boss",
+            "url": "https://github.com/ExE-Boss",
+            "githubUsername": "ExE-Boss"
+        }
+    ],
+    "main": "",
+    "types": "index.d.ts",
+    "repository": {
+        "type": "git",
+        "url": "https://github.com/DefinitelyTyped/DefinitelyTyped.git",
+        "directory": "types/whatwg-url"
+    },
+    "scripts": {},
+    "dependencies": {
+        "@types/node": "*",
+        "@types/webidl-conversions": "*"
+    },
+    "typesPublisherContentHash": "ea85d67c501583ff421cfbf206fafac0410c464edef169a6d88e06ecfe226dfc",
+    "typeScriptVersion": "4.0"
+}

+ 61 - 0
backend/node_modules/abbrev/abbrev.js

@@ -0,0 +1,61 @@
+module.exports = exports = abbrev.abbrev = abbrev
+
+abbrev.monkeyPatch = monkeyPatch
+
+function monkeyPatch () {
+  Object.defineProperty(Array.prototype, 'abbrev', {
+    value: function () { return abbrev(this) },
+    enumerable: false, configurable: true, writable: true
+  })
+
+  Object.defineProperty(Object.prototype, 'abbrev', {
+    value: function () { return abbrev(Object.keys(this)) },
+    enumerable: false, configurable: true, writable: true
+  })
+}
+
+function abbrev (list) {
+  if (arguments.length !== 1 || !Array.isArray(list)) {
+    list = Array.prototype.slice.call(arguments, 0)
+  }
+  for (var i = 0, l = list.length, args = [] ; i < l ; i ++) {
+    args[i] = typeof list[i] === "string" ? list[i] : String(list[i])
+  }
+
+  // sort them lexicographically, so that they're next to their nearest kin
+  args = args.sort(lexSort)
+
+  // walk through each, seeing how much it has in common with the next and previous
+  var abbrevs = {}
+    , prev = ""
+  for (var i = 0, l = args.length ; i < l ; i ++) {
+    var current = args[i]
+      , next = args[i + 1] || ""
+      , nextMatches = true
+      , prevMatches = true
+    if (current === next) continue
+    for (var j = 0, cl = current.length ; j < cl ; j ++) {
+      var curChar = current.charAt(j)
+      nextMatches = nextMatches && curChar === next.charAt(j)
+      prevMatches = prevMatches && curChar === prev.charAt(j)
+      if (!nextMatches && !prevMatches) {
+        j ++
+        break
+      }
+    }
+    prev = current
+    if (j === cl) {
+      abbrevs[current] = current
+      continue
+    }
+    for (var a = current.substr(0, j) ; j <= cl ; j ++) {
+      abbrevs[a] = current
+      a += current.charAt(j)
+    }
+  }
+  return abbrevs
+}
+
+function lexSort (a, b) {
+  return a === b ? 0 : a > b ? 1 : -1
+}

+ 21 - 0
backend/node_modules/abbrev/package.json

@@ -0,0 +1,21 @@
+{
+  "name": "abbrev",
+  "version": "1.1.1",
+  "description": "Like ruby's abbrev module, but in js",
+  "author": "Isaac Z. Schlueter <i@izs.me>",
+  "main": "abbrev.js",
+  "scripts": {
+    "test": "tap test.js --100",
+    "preversion": "npm test",
+    "postversion": "npm publish",
+    "postpublish": "git push origin --all; git push origin --tags"
+  },
+  "repository": "http://github.com/isaacs/abbrev-js",
+  "license": "ISC",
+  "devDependencies": {
+    "tap": "^10.1"
+  },
+  "files": [
+    "abbrev.js"
+  ]
+}

+ 238 - 0
backend/node_modules/accepts/index.js

@@ -0,0 +1,238 @@
+/*!
+ * accepts
+ * Copyright(c) 2014 Jonathan Ong
+ * Copyright(c) 2015 Douglas Christopher Wilson
+ * MIT Licensed
+ */
+
+'use strict'
+
+/**
+ * Module dependencies.
+ * @private
+ */
+
+var Negotiator = require('negotiator')
+var mime = require('mime-types')
+
+/**
+ * Module exports.
+ * @public
+ */
+
+module.exports = Accepts
+
+/**
+ * Create a new Accepts object for the given req.
+ *
+ * @param {object} req
+ * @public
+ */
+
+function Accepts (req) {
+  if (!(this instanceof Accepts)) {
+    return new Accepts(req)
+  }
+
+  this.headers = req.headers
+  this.negotiator = new Negotiator(req)
+}
+
+/**
+ * Check if the given `type(s)` is acceptable, returning
+ * the best match when true, otherwise `undefined`, in which
+ * case you should respond with 406 "Not Acceptable".
+ *
+ * The `type` value may be a single mime type string
+ * such as "application/json", the extension name
+ * such as "json" or an array `["json", "html", "text/plain"]`. When a list
+ * or array is given the _best_ match, if any is returned.
+ *
+ * Examples:
+ *
+ *     // Accept: text/html
+ *     this.types('html');
+ *     // => "html"
+ *
+ *     // Accept: text/*, application/json
+ *     this.types('html');
+ *     // => "html"
+ *     this.types('text/html');
+ *     // => "text/html"
+ *     this.types('json', 'text');
+ *     // => "json"
+ *     this.types('application/json');
+ *     // => "application/json"
+ *
+ *     // Accept: text/*, application/json
+ *     this.types('image/png');
+ *     this.types('png');
+ *     // => undefined
+ *
+ *     // Accept: text/*;q=.5, application/json
+ *     this.types(['html', 'json']);
+ *     this.types('html', 'json');
+ *     // => "json"
+ *
+ * @param {String|Array} types...
+ * @return {String|Array|Boolean}
+ * @public
+ */
+
+Accepts.prototype.type =
+Accepts.prototype.types = function (types_) {
+  var types = types_
+
+  // support flattened arguments
+  if (types && !Array.isArray(types)) {
+    types = new Array(arguments.length)
+    for (var i = 0; i < types.length; i++) {
+      types[i] = arguments[i]
+    }
+  }
+
+  // no types, return all requested types
+  if (!types || types.length === 0) {
+    return this.negotiator.mediaTypes()
+  }
+
+  // no accept header, return first given type
+  if (!this.headers.accept) {
+    return types[0]
+  }
+
+  var mimes = types.map(extToMime)
+  var accepts = this.negotiator.mediaTypes(mimes.filter(validMime))
+  var first = accepts[0]
+
+  return first
+    ? types[mimes.indexOf(first)]
+    : false
+}
+
+/**
+ * Return accepted encodings or best fit based on `encodings`.
+ *
+ * Given `Accept-Encoding: gzip, deflate`
+ * an array sorted by quality is returned:
+ *
+ *     ['gzip', 'deflate']
+ *
+ * @param {String|Array} encodings...
+ * @return {String|Array}
+ * @public
+ */
+
+Accepts.prototype.encoding =
+Accepts.prototype.encodings = function (encodings_) {
+  var encodings = encodings_
+
+  // support flattened arguments
+  if (encodings && !Array.isArray(encodings)) {
+    encodings = new Array(arguments.length)
+    for (var i = 0; i < encodings.length; i++) {
+      encodings[i] = arguments[i]
+    }
+  }
+
+  // no encodings, return all requested encodings
+  if (!encodings || encodings.length === 0) {
+    return this.negotiator.encodings()
+  }
+
+  return this.negotiator.encodings(encodings)[0] || false
+}
+
+/**
+ * Return accepted charsets or best fit based on `charsets`.
+ *
+ * Given `Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5`
+ * an array sorted by quality is returned:
+ *
+ *     ['utf-8', 'utf-7', 'iso-8859-1']
+ *
+ * @param {String|Array} charsets...
+ * @return {String|Array}
+ * @public
+ */
+
+Accepts.prototype.charset =
+Accepts.prototype.charsets = function (charsets_) {
+  var charsets = charsets_
+
+  // support flattened arguments
+  if (charsets && !Array.isArray(charsets)) {
+    charsets = new Array(arguments.length)
+    for (var i = 0; i < charsets.length; i++) {
+      charsets[i] = arguments[i]
+    }
+  }
+
+  // no charsets, return all requested charsets
+  if (!charsets || charsets.length === 0) {
+    return this.negotiator.charsets()
+  }
+
+  return this.negotiator.charsets(charsets)[0] || false
+}
+
+/**
+ * Return accepted languages or best fit based on `langs`.
+ *
+ * Given `Accept-Language: en;q=0.8, es, pt`
+ * an array sorted by quality is returned:
+ *
+ *     ['es', 'pt', 'en']
+ *
+ * @param {String|Array} langs...
+ * @return {Array|String}
+ * @public
+ */
+
+Accepts.prototype.lang =
+Accepts.prototype.langs =
+Accepts.prototype.language =
+Accepts.prototype.languages = function (languages_) {
+  var languages = languages_
+
+  // support flattened arguments
+  if (languages && !Array.isArray(languages)) {
+    languages = new Array(arguments.length)
+    for (var i = 0; i < languages.length; i++) {
+      languages[i] = arguments[i]
+    }
+  }
+
+  // no languages, return all requested languages
+  if (!languages || languages.length === 0) {
+    return this.negotiator.languages()
+  }
+
+  return this.negotiator.languages(languages)[0] || false
+}
+
+/**
+ * Convert extnames to mime.
+ *
+ * @param {String} type
+ * @return {String}
+ * @private
+ */
+
+function extToMime (type) {
+  return type.indexOf('/') === -1
+    ? mime.lookup(type)
+    : type
+}
+
+/**
+ * Check if mime is valid.
+ *
+ * @param {String} type
+ * @return {String}
+ * @private
+ */
+
+function validMime (type) {
+  return typeof type === 'string'
+}

+ 47 - 0
backend/node_modules/accepts/package.json

@@ -0,0 +1,47 @@
+{
+  "name": "accepts",
+  "description": "Higher-level content negotiation",
+  "version": "1.3.7",
+  "contributors": [
+    "Douglas Christopher Wilson <doug@somethingdoug.com>",
+    "Jonathan Ong <me@jongleberry.com> (http://jongleberry.com)"
+  ],
+  "license": "MIT",
+  "repository": "jshttp/accepts",
+  "dependencies": {
+    "mime-types": "~2.1.24",
+    "negotiator": "0.6.2"
+  },
+  "devDependencies": {
+    "deep-equal": "1.0.1",
+    "eslint": "5.16.0",
+    "eslint-config-standard": "12.0.0",
+    "eslint-plugin-import": "2.17.2",
+    "eslint-plugin-markdown": "1.0.0",
+    "eslint-plugin-node": "8.0.1",
+    "eslint-plugin-promise": "4.1.1",
+    "eslint-plugin-standard": "4.0.0",
+    "mocha": "6.1.4",
+    "nyc": "14.0.0"
+  },
+  "files": [
+    "LICENSE",
+    "HISTORY.md",
+    "index.js"
+  ],
+  "engines": {
+    "node": ">= 0.6"
+  },
+  "scripts": {
+    "lint": "eslint --plugin markdown --ext js,md .",
+    "test": "mocha --reporter spec --check-leaks --bail test/",
+    "test-cov": "nyc --reporter=html --reporter=text npm test",
+    "test-travis": "nyc --reporter=text npm test"
+  },
+  "keywords": [
+    "content",
+    "negotiation",
+    "accept",
+    "accepts"
+  ]
+}

+ 203 - 0
backend/node_modules/agent-base/dist/src/index.js

@@ -0,0 +1,203 @@
+"use strict";
+var __importDefault = (this && this.__importDefault) || function (mod) {
+    return (mod && mod.__esModule) ? mod : { "default": mod };
+};
+const events_1 = require("events");
+const debug_1 = __importDefault(require("debug"));
+const promisify_1 = __importDefault(require("./promisify"));
+const debug = debug_1.default('agent-base');
+function isAgent(v) {
+    return Boolean(v) && typeof v.addRequest === 'function';
+}
+function isSecureEndpoint() {
+    const { stack } = new Error();
+    if (typeof stack !== 'string')
+        return false;
+    return stack.split('\n').some(l => l.indexOf('(https.js:') !== -1 || l.indexOf('node:https:') !== -1);
+}
+function createAgent(callback, opts) {
+    return new createAgent.Agent(callback, opts);
+}
+(function (createAgent) {
+    /**
+     * Base `http.Agent` implementation.
+     * No pooling/keep-alive is implemented by default.
+     *
+     * @param {Function} callback
+     * @api public
+     */
+    class Agent extends events_1.EventEmitter {
+        constructor(callback, _opts) {
+            super();
+            let opts = _opts;
+            if (typeof callback === 'function') {
+                this.callback = callback;
+            }
+            else if (callback) {
+                opts = callback;
+            }
+            // Timeout for the socket to be returned from the callback
+            this.timeout = null;
+            if (opts && typeof opts.timeout === 'number') {
+                this.timeout = opts.timeout;
+            }
+            // These aren't actually used by `agent-base`, but are required
+            // for the TypeScript definition files in `@types/node` :/
+            this.maxFreeSockets = 1;
+            this.maxSockets = 1;
+            this.maxTotalSockets = Infinity;
+            this.sockets = {};
+            this.freeSockets = {};
+            this.requests = {};
+            this.options = {};
+        }
+        get defaultPort() {
+            if (typeof this.explicitDefaultPort === 'number') {
+                return this.explicitDefaultPort;
+            }
+            return isSecureEndpoint() ? 443 : 80;
+        }
+        set defaultPort(v) {
+            this.explicitDefaultPort = v;
+        }
+        get protocol() {
+            if (typeof this.explicitProtocol === 'string') {
+                return this.explicitProtocol;
+            }
+            return isSecureEndpoint() ? 'https:' : 'http:';
+        }
+        set protocol(v) {
+            this.explicitProtocol = v;
+        }
+        callback(req, opts, fn) {
+            throw new Error('"agent-base" has no default implementation, you must subclass and override `callback()`');
+        }
+        /**
+         * Called by node-core's "_http_client.js" module when creating
+         * a new HTTP request with this Agent instance.
+         *
+         * @api public
+         */
+        addRequest(req, _opts) {
+            const opts = Object.assign({}, _opts);
+            if (typeof opts.secureEndpoint !== 'boolean') {
+                opts.secureEndpoint = isSecureEndpoint();
+            }
+            if (opts.host == null) {
+                opts.host = 'localhost';
+            }
+            if (opts.port == null) {
+                opts.port = opts.secureEndpoint ? 443 : 80;
+            }
+            if (opts.protocol == null) {
+                opts.protocol = opts.secureEndpoint ? 'https:' : 'http:';
+            }
+            if (opts.host && opts.path) {
+                // If both a `host` and `path` are specified then it's most
+                // likely the result of a `url.parse()` call... we need to
+                // remove the `path` portion so that `net.connect()` doesn't
+                // attempt to open that as a unix socket file.
+                delete opts.path;
+            }
+            delete opts.agent;
+            delete opts.hostname;
+            delete opts._defaultAgent;
+            delete opts.defaultPort;
+            delete opts.createConnection;
+            // Hint to use "Connection: close"
+            // XXX: non-documented `http` module API :(
+            req._last = true;
+            req.shouldKeepAlive = false;
+            let timedOut = false;
+            let timeoutId = null;
+            const timeoutMs = opts.timeout || this.timeout;
+            const onerror = (err) => {
+                if (req._hadError)
+                    return;
+                req.emit('error', err);
+                // For Safety. Some additional errors might fire later on
+                // and we need to make sure we don't double-fire the error event.
+                req._hadError = true;
+            };
+            const ontimeout = () => {
+                timeoutId = null;
+                timedOut = true;
+                const err = new Error(`A "socket" was not created for HTTP request before ${timeoutMs}ms`);
+                err.code = 'ETIMEOUT';
+                onerror(err);
+            };
+            const callbackError = (err) => {
+                if (timedOut)
+                    return;
+                if (timeoutId !== null) {
+                    clearTimeout(timeoutId);
+                    timeoutId = null;
+                }
+                onerror(err);
+            };
+            const onsocket = (socket) => {
+                if (timedOut)
+                    return;
+                if (timeoutId != null) {
+                    clearTimeout(timeoutId);
+                    timeoutId = null;
+                }
+                if (isAgent(socket)) {
+                    // `socket` is actually an `http.Agent` instance, so
+                    // relinquish responsibility for this `req` to the Agent
+                    // from here on
+                    debug('Callback returned another Agent instance %o', socket.constructor.name);
+                    socket.addRequest(req, opts);
+                    return;
+                }
+                if (socket) {
+                    socket.once('free', () => {
+                        this.freeSocket(socket, opts);
+                    });
+                    req.onSocket(socket);
+                    return;
+                }
+                const err = new Error(`no Duplex stream was returned to agent-base for \`${req.method} ${req.path}\``);
+                onerror(err);
+            };
+            if (typeof this.callback !== 'function') {
+                onerror(new Error('`callback` is not defined'));
+                return;
+            }
+            if (!this.promisifiedCallback) {
+                if (this.callback.length >= 3) {
+                    debug('Converting legacy callback function to promise');
+                    this.promisifiedCallback = promisify_1.default(this.callback);
+                }
+                else {
+                    this.promisifiedCallback = this.callback;
+                }
+            }
+            if (typeof timeoutMs === 'number' && timeoutMs > 0) {
+                timeoutId = setTimeout(ontimeout, timeoutMs);
+            }
+            if ('port' in opts && typeof opts.port !== 'number') {
+                opts.port = Number(opts.port);
+            }
+            try {
+                debug('Resolving socket for %o request: %o', opts.protocol, `${req.method} ${req.path}`);
+                Promise.resolve(this.promisifiedCallback(req, opts)).then(onsocket, callbackError);
+            }
+            catch (err) {
+                Promise.reject(err).catch(callbackError);
+            }
+        }
+        freeSocket(socket, opts) {
+            debug('Freeing socket %o %o', socket.constructor.name, opts);
+            socket.destroy();
+        }
+        destroy() {
+            debug('Destroying agent %o', this.constructor.name);
+        }
+    }
+    createAgent.Agent = Agent;
+    // So that `instanceof` works correctly
+    createAgent.prototype = createAgent.Agent.prototype;
+})(createAgent || (createAgent = {}));
+module.exports = createAgent;
+//# sourceMappingURL=index.js.map

+ 18 - 0
backend/node_modules/agent-base/dist/src/promisify.js

@@ -0,0 +1,18 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+function promisify(fn) {
+    return function (req, opts) {
+        return new Promise((resolve, reject) => {
+            fn.call(this, req, opts, (err, rtn) => {
+                if (err) {
+                    reject(err);
+                }
+                else {
+                    resolve(rtn);
+                }
+            });
+        });
+    };
+}
+exports.default = promisify;
+//# sourceMappingURL=promisify.js.map

+ 59 - 0
backend/node_modules/agent-base/node_modules/debug/package.json

@@ -0,0 +1,59 @@
+{
+  "name": "debug",
+  "version": "4.3.4",
+  "repository": {
+    "type": "git",
+    "url": "git://github.com/debug-js/debug.git"
+  },
+  "description": "Lightweight debugging utility for Node.js and the browser",
+  "keywords": [
+    "debug",
+    "log",
+    "debugger"
+  ],
+  "files": [
+    "src",
+    "LICENSE",
+    "README.md"
+  ],
+  "author": "Josh Junon <josh.junon@protonmail.com>",
+  "contributors": [
+    "TJ Holowaychuk <tj@vision-media.ca>",
+    "Nathan Rajlich <nathan@tootallnate.net> (http://n8.io)",
+    "Andrew Rhyne <rhyneandrew@gmail.com>"
+  ],
+  "license": "MIT",
+  "scripts": {
+    "lint": "xo",
+    "test": "npm run test:node && npm run test:browser && npm run lint",
+    "test:node": "istanbul cover _mocha -- test.js",
+    "test:browser": "karma start --single-run",
+    "test:coverage": "cat ./coverage/lcov.info | coveralls"
+  },
+  "dependencies": {
+    "ms": "2.1.2"
+  },
+  "devDependencies": {
+    "brfs": "^2.0.1",
+    "browserify": "^16.2.3",
+    "coveralls": "^3.0.2",
+    "istanbul": "^0.4.5",
+    "karma": "^3.1.4",
+    "karma-browserify": "^6.0.0",
+    "karma-chrome-launcher": "^2.2.0",
+    "karma-mocha": "^1.3.0",
+    "mocha": "^5.2.0",
+    "mocha-lcov-reporter": "^1.2.0",
+    "xo": "^0.23.0"
+  },
+  "peerDependenciesMeta": {
+    "supports-color": {
+      "optional": true
+    }
+  },
+  "main": "./src/index.js",
+  "browser": "./src/browser.js",
+  "engines": {
+    "node": ">=6.0"
+  }
+}

+ 269 - 0
backend/node_modules/agent-base/node_modules/debug/src/browser.js

@@ -0,0 +1,269 @@
+/* eslint-env browser */
+
+/**
+ * This is the web browser implementation of `debug()`.
+ */
+
+exports.formatArgs = formatArgs;
+exports.save = save;
+exports.load = load;
+exports.useColors = useColors;
+exports.storage = localstorage();
+exports.destroy = (() => {
+	let warned = false;
+
+	return () => {
+		if (!warned) {
+			warned = true;
+			console.warn('Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.');
+		}
+	};
+})();
+
+/**
+ * Colors.
+ */
+
+exports.colors = [
+	'#0000CC',
+	'#0000FF',
+	'#0033CC',
+	'#0033FF',
+	'#0066CC',
+	'#0066FF',
+	'#0099CC',
+	'#0099FF',
+	'#00CC00',
+	'#00CC33',
+	'#00CC66',
+	'#00CC99',
+	'#00CCCC',
+	'#00CCFF',
+	'#3300CC',
+	'#3300FF',
+	'#3333CC',
+	'#3333FF',
+	'#3366CC',
+	'#3366FF',
+	'#3399CC',
+	'#3399FF',
+	'#33CC00',
+	'#33CC33',
+	'#33CC66',
+	'#33CC99',
+	'#33CCCC',
+	'#33CCFF',
+	'#6600CC',
+	'#6600FF',
+	'#6633CC',
+	'#6633FF',
+	'#66CC00',
+	'#66CC33',
+	'#9900CC',
+	'#9900FF',
+	'#9933CC',
+	'#9933FF',
+	'#99CC00',
+	'#99CC33',
+	'#CC0000',
+	'#CC0033',
+	'#CC0066',
+	'#CC0099',
+	'#CC00CC',
+	'#CC00FF',
+	'#CC3300',
+	'#CC3333',
+	'#CC3366',
+	'#CC3399',
+	'#CC33CC',
+	'#CC33FF',
+	'#CC6600',
+	'#CC6633',
+	'#CC9900',
+	'#CC9933',
+	'#CCCC00',
+	'#CCCC33',
+	'#FF0000',
+	'#FF0033',
+	'#FF0066',
+	'#FF0099',
+	'#FF00CC',
+	'#FF00FF',
+	'#FF3300',
+	'#FF3333',
+	'#FF3366',
+	'#FF3399',
+	'#FF33CC',
+	'#FF33FF',
+	'#FF6600',
+	'#FF6633',
+	'#FF9900',
+	'#FF9933',
+	'#FFCC00',
+	'#FFCC33'
+];
+
+/**
+ * Currently only WebKit-based Web Inspectors, Firefox >= v31,
+ * and the Firebug extension (any Firefox version) are known
+ * to support "%c" CSS customizations.
+ *
+ * TODO: add a `localStorage` variable to explicitly enable/disable colors
+ */
+
+// eslint-disable-next-line complexity
+function useColors() {
+	// NB: In an Electron preload script, document will be defined but not fully
+	// initialized. Since we know we're in Chrome, we'll just detect this case
+	// explicitly
+	if (typeof window !== 'undefined' && window.process && (window.process.type === 'renderer' || window.process.__nwjs)) {
+		return true;
+	}
+
+	// Internet Explorer and Edge do not support colors.
+	if (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) {
+		return false;
+	}
+
+	// Is webkit? http://stackoverflow.com/a/16459606/376773
+	// document is undefined in react-native: https://github.com/facebook/react-native/pull/1632
+	return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) ||
+		// Is firebug? http://stackoverflow.com/a/398120/376773
+		(typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) ||
+		// Is firefox >= v31?
+		// https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
+		(typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) ||
+		// Double check webkit in userAgent just in case we are in a worker
+		(typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/));
+}
+
+/**
+ * Colorize log arguments if enabled.
+ *
+ * @api public
+ */
+
+function formatArgs(args) {
+	args[0] = (this.useColors ? '%c' : '') +
+		this.namespace +
+		(this.useColors ? ' %c' : ' ') +
+		args[0] +
+		(this.useColors ? '%c ' : ' ') +
+		'+' + module.exports.humanize(this.diff);
+
+	if (!this.useColors) {
+		return;
+	}
+
+	const c = 'color: ' + this.color;
+	args.splice(1, 0, c, 'color: inherit');
+
+	// The final "%c" is somewhat tricky, because there could be other
+	// arguments passed either before or after the %c, so we need to
+	// figure out the correct index to insert the CSS into
+	let index = 0;
+	let lastC = 0;
+	args[0].replace(/%[a-zA-Z%]/g, match => {
+		if (match === '%%') {
+			return;
+		}
+		index++;
+		if (match === '%c') {
+			// We only are interested in the *last* %c
+			// (the user may have provided their own)
+			lastC = index;
+		}
+	});
+
+	args.splice(lastC, 0, c);
+}
+
+/**
+ * Invokes `console.debug()` when available.
+ * No-op when `console.debug` is not a "function".
+ * If `console.debug` is not available, falls back
+ * to `console.log`.
+ *
+ * @api public
+ */
+exports.log = console.debug || console.log || (() => {});
+
+/**
+ * Save `namespaces`.
+ *
+ * @param {String} namespaces
+ * @api private
+ */
+function save(namespaces) {
+	try {
+		if (namespaces) {
+			exports.storage.setItem('debug', namespaces);
+		} else {
+			exports.storage.removeItem('debug');
+		}
+	} catch (error) {
+		// Swallow
+		// XXX (@Qix-) should we be logging these?
+	}
+}
+
+/**
+ * Load `namespaces`.
+ *
+ * @return {String} returns the previously persisted debug modes
+ * @api private
+ */
+function load() {
+	let r;
+	try {
+		r = exports.storage.getItem('debug');
+	} catch (error) {
+		// Swallow
+		// XXX (@Qix-) should we be logging these?
+	}
+
+	// If debug isn't set in LS, and we're in Electron, try to load $DEBUG
+	if (!r && typeof process !== 'undefined' && 'env' in process) {
+		r = process.env.DEBUG;
+	}
+
+	return r;
+}
+
+/**
+ * Localstorage attempts to return the localstorage.
+ *
+ * This is necessary because safari throws
+ * when a user disables cookies/localstorage
+ * and you attempt to access it.
+ *
+ * @return {LocalStorage}
+ * @api private
+ */
+
+function localstorage() {
+	try {
+		// TVMLKit (Apple TV JS Runtime) does not have a window object, just localStorage in the global context
+		// The Browser also has localStorage in the global context.
+		return localStorage;
+	} catch (error) {
+		// Swallow
+		// XXX (@Qix-) should we be logging these?
+	}
+}
+
+module.exports = require('./common')(exports);
+
+const {formatters} = module.exports;
+
+/**
+ * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.
+ */
+
+formatters.j = function (v) {
+	try {
+		return JSON.stringify(v);
+	} catch (error) {
+		return '[UnexpectedJSONParseError]: ' + error.message;
+	}
+};

+ 274 - 0
backend/node_modules/agent-base/node_modules/debug/src/common.js

@@ -0,0 +1,274 @@
+
+/**
+ * This is the common logic for both the Node.js and web browser
+ * implementations of `debug()`.
+ */
+
+function setup(env) {
+	createDebug.debug = createDebug;
+	createDebug.default = createDebug;
+	createDebug.coerce = coerce;
+	createDebug.disable = disable;
+	createDebug.enable = enable;
+	createDebug.enabled = enabled;
+	createDebug.humanize = require('ms');
+	createDebug.destroy = destroy;
+
+	Object.keys(env).forEach(key => {
+		createDebug[key] = env[key];
+	});
+
+	/**
+	* The currently active debug mode names, and names to skip.
+	*/
+
+	createDebug.names = [];
+	createDebug.skips = [];
+
+	/**
+	* Map of special "%n" handling functions, for the debug "format" argument.
+	*
+	* Valid key names are a single, lower or upper-case letter, i.e. "n" and "N".
+	*/
+	createDebug.formatters = {};
+
+	/**
+	* Selects a color for a debug namespace
+	* @param {String} namespace The namespace string for the debug instance to be colored
+	* @return {Number|String} An ANSI color code for the given namespace
+	* @api private
+	*/
+	function selectColor(namespace) {
+		let hash = 0;
+
+		for (let i = 0; i < namespace.length; i++) {
+			hash = ((hash << 5) - hash) + namespace.charCodeAt(i);
+			hash |= 0; // Convert to 32bit integer
+		}
+
+		return createDebug.colors[Math.abs(hash) % createDebug.colors.length];
+	}
+	createDebug.selectColor = selectColor;
+
+	/**
+	* Create a debugger with the given `namespace`.
+	*
+	* @param {String} namespace
+	* @return {Function}
+	* @api public
+	*/
+	function createDebug(namespace) {
+		let prevTime;
+		let enableOverride = null;
+		let namespacesCache;
+		let enabledCache;
+
+		function debug(...args) {
+			// Disabled?
+			if (!debug.enabled) {
+				return;
+			}
+
+			const self = debug;
+
+			// Set `diff` timestamp
+			const curr = Number(new Date());
+			const ms = curr - (prevTime || curr);
+			self.diff = ms;
+			self.prev = prevTime;
+			self.curr = curr;
+			prevTime = curr;
+
+			args[0] = createDebug.coerce(args[0]);
+
+			if (typeof args[0] !== 'string') {
+				// Anything else let's inspect with %O
+				args.unshift('%O');
+			}
+
+			// Apply any `formatters` transformations
+			let index = 0;
+			args[0] = args[0].replace(/%([a-zA-Z%])/g, (match, format) => {
+				// If we encounter an escaped % then don't increase the array index
+				if (match === '%%') {
+					return '%';
+				}
+				index++;
+				const formatter = createDebug.formatters[format];
+				if (typeof formatter === 'function') {
+					const val = args[index];
+					match = formatter.call(self, val);
+
+					// Now we need to remove `args[index]` since it's inlined in the `format`
+					args.splice(index, 1);
+					index--;
+				}
+				return match;
+			});
+
+			// Apply env-specific formatting (colors, etc.)
+			createDebug.formatArgs.call(self, args);
+
+			const logFn = self.log || createDebug.log;
+			logFn.apply(self, args);
+		}
+
+		debug.namespace = namespace;
+		debug.useColors = createDebug.useColors();
+		debug.color = createDebug.selectColor(namespace);
+		debug.extend = extend;
+		debug.destroy = createDebug.destroy; // XXX Temporary. Will be removed in the next major release.
+
+		Object.defineProperty(debug, 'enabled', {
+			enumerable: true,
+			configurable: false,
+			get: () => {
+				if (enableOverride !== null) {
+					return enableOverride;
+				}
+				if (namespacesCache !== createDebug.namespaces) {
+					namespacesCache = createDebug.namespaces;
+					enabledCache = createDebug.enabled(namespace);
+				}
+
+				return enabledCache;
+			},
+			set: v => {
+				enableOverride = v;
+			}
+		});
+
+		// Env-specific initialization logic for debug instances
+		if (typeof createDebug.init === 'function') {
+			createDebug.init(debug);
+		}
+
+		return debug;
+	}
+
+	function extend(namespace, delimiter) {
+		const newDebug = createDebug(this.namespace + (typeof delimiter === 'undefined' ? ':' : delimiter) + namespace);
+		newDebug.log = this.log;
+		return newDebug;
+	}
+
+	/**
+	* Enables a debug mode by namespaces. This can include modes
+	* separated by a colon and wildcards.
+	*
+	* @param {String} namespaces
+	* @api public
+	*/
+	function enable(namespaces) {
+		createDebug.save(namespaces);
+		createDebug.namespaces = namespaces;
+
+		createDebug.names = [];
+		createDebug.skips = [];
+
+		let i;
+		const split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/);
+		const len = split.length;
+
+		for (i = 0; i < len; i++) {
+			if (!split[i]) {
+				// ignore empty strings
+				continue;
+			}
+
+			namespaces = split[i].replace(/\*/g, '.*?');
+
+			if (namespaces[0] === '-') {
+				createDebug.skips.push(new RegExp('^' + namespaces.slice(1) + '$'));
+			} else {
+				createDebug.names.push(new RegExp('^' + namespaces + '$'));
+			}
+		}
+	}
+
+	/**
+	* Disable debug output.
+	*
+	* @return {String} namespaces
+	* @api public
+	*/
+	function disable() {
+		const namespaces = [
+			...createDebug.names.map(toNamespace),
+			...createDebug.skips.map(toNamespace).map(namespace => '-' + namespace)
+		].join(',');
+		createDebug.enable('');
+		return namespaces;
+	}
+
+	/**
+	* Returns true if the given mode name is enabled, false otherwise.
+	*
+	* @param {String} name
+	* @return {Boolean}
+	* @api public
+	*/
+	function enabled(name) {
+		if (name[name.length - 1] === '*') {
+			return true;
+		}
+
+		let i;
+		let len;
+
+		for (i = 0, len = createDebug.skips.length; i < len; i++) {
+			if (createDebug.skips[i].test(name)) {
+				return false;
+			}
+		}
+
+		for (i = 0, len = createDebug.names.length; i < len; i++) {
+			if (createDebug.names[i].test(name)) {
+				return true;
+			}
+		}
+
+		return false;
+	}
+
+	/**
+	* Convert regexp to namespace
+	*
+	* @param {RegExp} regxep
+	* @return {String} namespace
+	* @api private
+	*/
+	function toNamespace(regexp) {
+		return regexp.toString()
+			.substring(2, regexp.toString().length - 2)
+			.replace(/\.\*\?$/, '*');
+	}
+
+	/**
+	* Coerce `val`.
+	*
+	* @param {Mixed} val
+	* @return {Mixed}
+	* @api private
+	*/
+	function coerce(val) {
+		if (val instanceof Error) {
+			return val.stack || val.message;
+		}
+		return val;
+	}
+
+	/**
+	* XXX DO NOT USE. This is a temporary stub function.
+	* XXX It WILL be removed in the next major release.
+	*/
+	function destroy() {
+		console.warn('Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.');
+	}
+
+	createDebug.enable(createDebug.load());
+
+	return createDebug;
+}
+
+module.exports = setup;

+ 0 - 0
backend/node_modules/agent-base/node_modules/debug/src/index.js


Some files were not shown because too many files changed in this diff