diff --git a/.gitignore b/.gitignore index 9752a88d..7af470c1 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,16 @@ sharedPlugins/* !sharedPlugins/remoteCommands/ !sharedPlugins/researchSync/ !sharedPlugins/globalChat/ +!sharedPlugins/clusterioMod/ +!sharedPlugins/serverManager/ +sharedPlugins/serverManager/database/* +sharedPlugins/serverManager/config.json +sharedPlugins/serverManager/secret-api-token.txt +!sharedPlugins/scenario_loader/ +sharedPlugins/scenario_loader/scenarios +!sharedPlugins/statisticsExporter/ + +# File used for marking plugins as disabled +sharedPlugins/*/DISABLED + +\.cache-require-paths\.json diff --git a/.snyk b/.snyk new file mode 100644 index 00000000..fcb500f1 --- /dev/null +++ b/.snyk @@ -0,0 +1,8 @@ +# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities. +version: v1.12.0 +ignore: {} +# patches apply the minimum changes required to fix a vulnerability +patch: + 'npm:lodash:20180130': + - html5-validator > caporal > cli-table2 > lodash: + patched: '2018-07-03T06:07:55.532Z' diff --git a/.travis.yml b/.travis.yml index 793b482f..93725d69 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: node_js node_js: - - "node" + - "9" before_install: - sudo apt-get -qq update - sudo apt-get install -y curl git tar diff --git a/Dockerfile b/Dockerfile index 949a4018..399c1259 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,19 +1,21 @@ -FROM node:latest -RUN apt-get update && apt install git -y - +FROM node:9.11.2 +RUN apt-get update && apt install git curl tar -y RUN mkdir factorioClusterio -RUN git clone -b dev https://github.com/Danielv123/factorioClusterio.git && cd factorioClusterio && npm install && curl -o factorio.tar.gz -L https://www.factorio.com/get-download/latest/headless/linux64 && tar -xvzf factorio.tar.gz + +RUN git clone -b master https://github.com/Danielv123/factorioClusterio.git && cd factorioClusterio && npm install +RUN cd factorioClusterio && curl -o factorio.tar.gz -L https://www.factorio.com/get-download/latest/headless/linux64 && tar -xf factorio.tar.gz WORKDIR factorioClusterio -RUN mkdir instances sharedMods sharedPlugins +RUN mkdir instances sharedMods +RUN cp config.json.dist config.json RUN node client.js download -LABEL maintainer "danielv@live.no" +LABEL maintainer "Sir3lit@gmail.com" EXPOSE 8080 34167 VOLUME /factorioClusterio/instances VOLUME /factorioClusterio/sharedMods VOLUME /factorioClusterio/sharedPlugins -CMD RCONPORT="$RCONPORT" FACTORIOPORT="$FACTORIOPORT" MODE="$MODE" node $MODE\.js start $INSTANCE \ No newline at end of file +CMD RCONPORT="$RCONPORT" FACTORIOPORT="$FACTORIOPORT" MODE="$MODE" node $MODE\.js start $INSTANCE diff --git a/README.md b/README.md index 2c50731b..03de0c11 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ # factorioClusterio -[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2FDanielv123%2FfactorioClusterio.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2FDanielv123%2FfactorioClusterio?ref=badge_shield) -| Branch | Coverage | Build | -|--------|----------| ----- | -| Master | [![codecov](https://codecov.io/gh/Danielv123/factorioClusterio/branch/master/graph/badge.svg)](https://codecov.io/gh/Danielv123/factorioClusterio) | [![Build Status](https://travis-ci.org/Danielv123/factorioClusterio.svg?branch=master)](https://travis-ci.org/Danielv123/factorioClusterio) | -| Dev | [![codecov](https://codecov.io/gh/Danielv123/factorioClusterio/branch/dev/graph/badge.svg)](https://codecov.io/gh/Danielv123/factorioClusterio) | [![Build Status](https://travis-ci.org/Danielv123/factorioClusterio.svg?branch=dev)](https://travis-ci.org/Danielv123/factorioClusterio) | +Discord for development/support/play: https://discord.gg/5XuDkje -If you want to connect to a clusterio cluster, please reffer to the [client](https://github.com/Danielv123/factorioClusterioClient) +### Ways to support me/the project: + +* Contribute with code/documentation. Always nice to move the project forward + +* Support me monetarily on [patreon](https://www.patreon.com/danielv123) or paypal: danielv@live.no ### Table of contents @@ -16,6 +16,10 @@ If you want to connect to a clusterio cluster, please reffer to the [client](htt * [Windows setup](#windows-setup) +* [Optional plugins](#Plugins) + +* [Common problems](#Common-problems) + * [Command cheatsheet](#cheatsheet) ## Introduction @@ -32,7 +36,16 @@ Features: - Inventory combinator to display item levels in the cluster (and epoch time) -- Reporting of graphs and UPS on master interface +- Reporting of graphs and UPS on master interface (Also has extensive Prometheus reporting) + +Optional extras (see [Plugins](#Plugins)) + +- Have your inventory synchronize across servers + +- Teleport trans from the border of one world to te next + +- Show in-game chat in discord + Connection diagram: @@ -70,27 +83,43 @@ NodeJS does not support EOL ubuntu releases. Make sure you are on the most recen Master and all slaves: - sudo curl -sL https://deb.nodesource.com/setup_9.x | sudo -E bash - \ - sudo apt install -y nodejs python-dev git wget curl tar build-essential \ - sudo npm install pm2 -g \ - git clone https://github.com/Danielv123/factorioClusterio.git \ - cd factorioClusterio \ - npm install \ - curl -o factorio.tar.gz -L https://www.factorio.com/get-download/latest/headless/linux64 \ + sudo curl -sL https://deb.nodesource.com/setup_9.x | sudo -E bash - + sudo apt install -y nodejs python-dev git wget curl tar build-essential + sudo npm install pm2 -g + git clone https://github.com/Danielv123/factorioClusterio.git + cd factorioClusterio + curl -o factorio.tar.gz -L https://www.factorio.com/get-download/latest/headless/linux64 tar -xf factorio.tar.gz + npm install + cp config.json.dist config.json + node ./lib/npmPostinstall.js + downloads and installs nodejs, pm2, git and clusterio. To specify a version, change "latest" in the link to a version number like 0.14.21. +Now you need to edit the `config.json` file. If you skip this step nothing will work. +Pretty much all the blank fields should be filled in, except on the master where a few can be omitted. + +* You get the `masterAuthToken` from `secret-api-token.txt` in the master install dir after running the master twice. + +* You get your factorio matchmaking token from factorio.com + +* The `masterAuthSecret` should never be touched unless you want to invalidate everyones authentication tokens + **Master** pm2 start master.js --name master + +OR + + node master.js **Server Host** -To download the mod for all its non vanilla features and items, (optional) - - node client.js download +To download the mod for all its non vanilla features and items, (optional, but very recommended) + node client.js manage shared mods add clusterio + To create a new instance (its own save, set of mods and config files) node client.js start [instancename] @@ -103,7 +132,7 @@ use `nano config.json` to change settings. **Ubuntu with Docker** -Clusterio has limited support for using docker. +Clusterio has *very* limited support for using docker. sudo docker build -t clusterio --no-cache --force-rm factorioClusterio @@ -123,13 +152,9 @@ Server host (Slave) = client.js + factorio server Game Client = The people connecting to the server -All Server Hosts (Slaves) AND Game Clients need to be running the clusterio mod located at [github](https://github.com/Danielv123/factorioClusterioMod) Install it by dropping it into either the sharedMods folder or instances/[instanceName]/instanceMods folder. - -There are no requirements for other mods, they can be ran in any configuration allowed by the base game. - **Requirements** -download and install nodeJS 6+ from http://nodejs.org +download and install nodeJS 8 or 9 from http://nodejs.org download and install git from https://git-scm.com/ @@ -139,29 +164,40 @@ reboot when you are done, then proceed to the next steps. *reboots matter* 1. download and run https://puu.sh/toFHl/01eebbb333.bat -2. You do not *need* to follow the given instructions, but you should +2. Copy config.json.dist to config.json -3. type `node master.js` +3. Follow the instructions in the bat file +3. Some of the instructions are outdated. If you get stuck somewhere, look at the Ubuntu section. + +4. Run `node client.js manage shared mods add clusterio` + +5. type `node master.js` to start the server **Server Host** 1. download and run https://puu.sh/toFHl/01eebbb333.bat -2. Follow the instructions given +2. Copy config.json.dist to config.json -3. Type `node client.js start [instancename]` to create a new instance. +3. Follow the instructions given. + +3.5 Some of the instructions are outdated. If you get stuck somewhere, look at the Ubuntu section. + +4. Type `node client.js start [instancename]` to create a new instance. To connect to a master server running on a remote machine, open config.json with your favourite text editor (notepad++). You can also set it up to use the official server browser. -Change masterIP to something like 31.152.123.14 (provided by master server owner) +Change `masterIP `to something like `31.152.123.14` (provided by master server owner) -Change masterPort to something like 8080 (provided by master server owner) +Change `masterPort` to something like `8080` (provided by master server owner) -Repeat step 3 for more servers on one machine. You should be able to find its port by looking at the slave section on master:8080 (the web interface) +Change `masterAuthToken` to the value found in `secret-api-token.txt` on the master server + +Repeat step 4 for more servers on one machine. You should be able to find its port by looking at the slave section on master:8080 (the web interface) **GameClient** -Fancy game client that does the following steps automatically: [clusterioClient](https://github.com/Danielv123/factorioClusterioClient) +Fancy game client that does the following steps automatically, but is really old so be warned: [clusterioClient](https://github.com/Danielv123/factorioClusterioClient) 1. Download the same version of the mod as the slave is running from [the mod portal](https://mods.factorio.com/mods/Danielv123/clusterio) or [github](https://github.com/Danielv123/factorioClusterioMod @@ -169,6 +205,55 @@ Fancy game client that does the following steps automatically: [clusterioClient] 3. Run factorio and connect to slave as a normal MP game. You will find the port number to connect to at http://[masterAddress]:8080 +## Plugins +Here are the known Clusterio plugins in the wild: +1. [Player Manager](https://github.com/Danielv123/playerManager) - Adds player management to the Web UI and shared inventory handling (beta) +2. [DiscordChat](https://github.com/jakedraddy/ClusterioDiscordChat) - Logs in-game chat/joins/leave messages on all instances to a Discord webhook. +3. [TrainTeleports](https://github.com/Godmave/clusterioTrainTeleports) - Allows you to teleport cargotrains between servers. + +## Common problems + +### Cannot find module: `/../../config` + +Copy your config.json.dist to config.json and configure it. + +### EACCESS [...] LISTEN 443 + +Some systems don't let non root processes listen to ports below 1000. Either run with `sudo` or change config.json to use higher port numbers. + +### InitializationError: First argument must be an abstract-leveldown compliant store + +do: + + npm install linvodb3@3.25.1 + +if that does not suffice do + + npm install levelup@1.3.9 + +If it still doesn't work, come over to our support discord linked at the top of this readme. + +### Portforwarding doesn't work on the master server when running under WSL + +If you follow the ubuntu guide on WSL (Windows Subsystem for Linux, Bash on Ubuntu on Windows specifically), you will find that the website works on localhost and on your local ip, but not on the global ip. This is also true when you correctly port-forwarded the correct ports. Even when routing this server through nginx in WSL, the issue persists. Then, on a hunch, I tried to run nginx from windows itself and found that this DID work. It came to me that the only usage difference between the 2 versions of nginx is that I got a Windows Firewall popup. + +TLDR: the tested fix is: + +- open your windows firewall and go to advanced settings + +- click on inbound rules and click on new rule... + +- select port and click next > + +- select TCP and select specific local ports and type in the ports that you want to open (comma separated) and click next > 3 times + +- give the rule a name (like 'web server' or something), give it a description (optionally) and click finish + +### Other fixes for other potential problems: + +Sometimes the install fails. Try `node ./lib/npmPostinstall.js` to complete it. + + ## Cheatsheet **To create a new instance/start it** @@ -194,5 +279,5 @@ npm install 3. Download the latest version of the factorioClusterioMod from its github repo ``` -node client.js download +node client.js manage shared mods add clusterio ``` diff --git a/client.js b/client.js index 3f050152..aded5348 100644 --- a/client.js +++ b/client.js @@ -18,15 +18,17 @@ const ioClient = require("socket.io-client"); const objectOps = require("./lib/objectOps.js"); const fileOps = require("_app/fileOps"); const stringUtils = require("./lib/stringUtils.js"); -const modManager = require("./lib/manager/modManager.js"); const configManager = require("./lib/manager/configManager.js"); +// argument parsing +const args = require('minimist')(process.argv.slice(2)); + // require config.json -var config = require('./config'); +var config = require(args.config || './config'); var global = {}; -if (!fs.existsSync("./instances/")) { - fs.mkdirSync("instances"); +if (!fs.existsSync(config.instanceDirectory)) { + fs.mkdirSync(config.instanceDirectory); } if (!fs.existsSync("./sharedPlugins/")) { fs.mkdirSync("sharedPlugins"); @@ -35,18 +37,21 @@ if (!fs.existsSync("./sharedMods/")) { fs.mkdirSync("sharedMods"); } const instance = process.argv[3]; -const instancedirectory = './instances/' + instance; +const instancedirectory = config.instanceDirectory + '/' + instance; const command = process.argv[2]; // Set the process title, shows up as the title of the CMD window on windows // and as the process name in ps/top on linux. process.title = "clusterioClient "+instance; +// add better stack traces on promise rejection +process.on('unhandledRejection', r => console.log(r)); + // make sure we have the master access token (can't write to master without it since clusterio 2.0) if(!config.masterAuthToken || typeof config.masterAuthToken !== "string"){ - console.log("ERROR invalid config!"); - console.log("Master server now needs an access token for write operations. As clusterio slaves depends \ - upon this, please add your token to config.js in the field named masterAuthToken. \ + console.error("ERROR invalid config!"); + console.error("Master server now needs an access token for write operations. As clusterio slaves depends \ + upon this, please add your token to config.json in the field named masterAuthToken. \ You can retrieve your auth token from the master in secret-api-token.txt after running it once."); } const needleOptionsWithTokenAuthHeader = { @@ -57,8 +62,16 @@ const needleOptionsWithTokenAuthHeader = { var instanceInfo = {}; var commandBuffer=[]; +// messageInterface Management +setInterval(function(){ + let command=commandBuffer.shift(); + if(command){ + messageInterfaceInternal(command[0], command[1], command[2], command[3]); + } +},config.msBetweenCommands || 50); + // function to handle sending commands into the game -function messageInterfaceInternal(command, callback) { +async function messageInterfaceInternal(command, callback, resolve, reject) { // try to save us if you send a buffer instead of string if(typeof command == "object") { command = command.toString('utf8'); @@ -73,20 +86,28 @@ function messageInterfaceInternal(command, callback) { if(typeof callback == "function"){ callback(); } + resolve(); } else if(typeof command == "string" && client && client.send && typeof client.send == "function") { try { - client.send(command+"\n").then(str => { - if(typeof callback == "function") callback(str) - }); + let str = await client.send(command+"\n"); + if(typeof callback == "function") callback(str) + resolve(str) } catch (err) { - console.log(err); + console.log("RCON failed, server might be paused or shutting down"); + // console.log(err); + // reject(err); if(typeof callback == "function"){ callback(); } + reject(err) } } } -function messageInterface(command, callback) {commandBuffer.push([command,callback]);} +function messageInterface(command, callback) { + return new Promise((resolve,reject) => { + commandBuffer.push([command,callback, resolve, reject]); + }); +} // handle commandline parameters @@ -97,11 +118,13 @@ if (!command || command == "help" || command == "--help") { console.error("node client.js delete [instance]"); console.error("To download the latest version of the Clusterio lua mod, do"); console.error("node client.js manage shared mods download clusterio"); + console.error("To download a clusterio plugin, do"); + console.error("node client.js manage shared plugins install https://github.com/Danielv123/playerManager"); console.error("For more management options, do"); console.error("node client.js manage"); process.exit(1); } else if (command == "list") { - let instanceNames = fileOps.getDirectoriesSync("./instances/"); + let instanceNames = fileOps.getDirectoriesSync(config.instanceDirectory); instanceNames.unshift("Name:"); let longestInstanceName = 0; // determine longest instance name @@ -119,12 +142,12 @@ if (!command || command == "help" || command == "--help") { // create port colloumn let factorioPorts = []; instanceNames.forEach(function(instance){ - let factorioPort + let factorioPort; if(instance.includes("Name:")){ factorioPort = "Port:" } else { - factorioPort = require("./instances/"+instance+"/config").factorioPort; + factorioPort = require(path.resolve(config.instanceDirectory, instance, 'config')).factorioPort; } factorioPorts.push(factorioPort); }); @@ -143,7 +166,7 @@ if (!command || command == "help" || command == "--help") { }); displayLines.forEach(line => console.log(line)); - process.exit(1); + process.exit(0); } else if (command == "manage"){ // console.log("Launching mod manager"); //const fullUsage = 'node client.js manage [instance, "shared"] ["mods", "config"] ...'; @@ -152,28 +175,39 @@ if (!command || command == "help" || command == "--help") { console.log('node client.js manage '+instance+' '+tool+' ["list", "search", "add", "remove", "update"]'); } else if(tool && tool == "config") { console.log('node client.js manage '+instance+' '+tool+' ["list", "edit"]'); + } else if(tool && tool == "plugins") { + console.log(`node client.js manage ${instance} ${tool} ["list", "add", "remove"]`); } else { - console.log('node client.js manage '+(instance || '[instance, "shared"]') +' '+ (tool || '["mods", "config"]') + ' ...'); + console.log('node client.js manage '+(instance || '[instance, "shared"]') +' '+ (tool || '["mods", "config", "plugins"]') + ' ...'); } } const tool = process.argv[4] || ""; const action = process.argv[5] || ""; if(instance){ if(tool == "mods"){ - // allow managing mods - if(action == "list"){ - modManager.listMods(instance); - } else if(action == "search"){ - modManager.findMods(process.argv[6]); - } else if(action == "add" || action == "download"){ - modManager.addMod(process.argv[6], instance); - } else if(action == "remove" || action == "rm" || action == "delete"){ - modManager.removeMod(process.argv[6], instance); - } else if(action == "update"){ - modManager.updateAllMods(); - } else { - usage(instance, tool); - } + (async function(){try{ + // do require down here to reduce master load time + const modManager = require("./lib/manager/modManager.js")(config); + + // allow managing mods + if(action == "list"){ + console.log(await modManager.listMods(instance)); + } else if(action == "search"){ + console.log(await modManager.findMods(process.argv[6])); + } else if(action == "add" || action == "download"){ + await modManager.addMod(process.argv[6], instance); + } else if(action == "remove" || action == "rm" || action == "delete"){ + await modManager.removeMod(process.argv[6], instance); + } else if(action == "update"){ + await modManager.updateAllMods(); + } else { + usage(instance, tool); + } + process.exit(0); + }catch(e){ + console.log("Got error from modManager:") + console.log(e); + }})(); } else if(tool == "config"){ // allow managing the config if(action == "list" || action == "show" || action == "display"){ @@ -191,6 +225,28 @@ if (!command || command == "help" || command == "--help") { } else { usage(instance, tool); } + } else if(tool == "plugins"){ + (async function(){try{ + const pluginManager = require("./lib/manager/pluginManager.js")(config); + if(action == "list"){ + await pluginManager.listPlugins(); + } else if(action == "add" || action == "install" || action == "download"){ + let status = await pluginManager.addPlugin(process.argv[6]); + } else if(action == "remove" || action == "uninstall" || action == "delete"){ + let status = await pluginManager.removePlugin(process.argv[6]); + if(status && status.msg) console.log(status.msg); + } else if(action == "enable"){ + let status = await pluginManager.enablePlugin(process.argv[6]); + if(status && status.msg) console.log(status.msg); + } else if(action == "disable"){ + let status = await pluginManager.disablePlugin(process.argv[6]); + if(status && status.msg) console.log(status.msg); + } + process.exit(0); + }catch(e){ + console.error(e); + process.exit(1); + }})(); } else { usage(instance); } @@ -198,18 +254,18 @@ if (!command || command == "help" || command == "--help") { console.log('Usage:'); usage(instance); } - // process.exit(1); + // process.exit(0); } else if (command == "delete") { if (!process.argv[3]) { console.error("Usage: node client.js delete [instance]"); process.exit(1); - } else if (typeof process.argv[3] == "string" && fs.existsSync("./instances/" + process.argv[3]) && process.argv[3] != "/" && process.argv[3] != "") { - fileOps.deleteFolderRecursiveSync("./instances/" + process.argv[3]); + } else if (typeof process.argv[3] == "string" && fs.existsSync(config.instanceDirectory+"/" + process.argv[3]) && process.argv[3] != "/" && process.argv[3] != "") { + fileOps.deleteFolderRecursiveSync(path.resolve(config.instanceDirectory, process.argv[3])); // TODO: Check if this can cause i-craft users to format their server by using wrong paths console.log("Deleted instance " + process.argv[3]); - process.exit(1); + process.exit(0); } else { console.error("Instance not found: " + process.argv[3]); - process.exit(1); + process.exit(0); } } else if (command == "download") { console.log("Downloading mods..."); @@ -221,13 +277,16 @@ if (!command || command == "help" || command == "--help") { console.log(url); let file = fs.createWriteStream("sharedMods/"+name); https.get(url, function(response) { + response.on('end', function () { + console.log("Downloaded "+name); + process.exit(0); + }); response.pipe(file); - console.log("Downloaded "+name); - }); + }).end(); } } else if (command == "start" && instance === undefined) { - console.log("ERROR: No instanceName provided!"); - console.log("Usage: node client.js start [instanceName]"); + console.error("ERROR: No instanceName provided!"); + console.error("Usage: node client.js start [instanceName]"); process.exit(0); } else if (command == "start" && typeof instance == "string" && instance != "/" && !fs.existsSync(instancedirectory)) { // if instance does not exist, create it @@ -240,25 +299,31 @@ if (!command || command == "help" || command == "--help") { fs.writeFileSync(instancedirectory + "/script-output/txbuffer.txt", ""); fs.mkdirSync(instancedirectory + "/mods/"); fs.mkdirSync(instancedirectory + "/instanceMods/"); + fs.mkdirSync(instancedirectory + "/scenarios/"); + ncp("./lib/scenarios", path.resolve(instancedirectory, "scenarios"), err => { + if (err) console.error(err) + }); + // fs.symlinkSync('../../../sharedMods', instancedirectory + "/mods", 'junction') // This is broken because it can only take a file as first argument, not a folder - fs.writeFileSync(instancedirectory + "/config.ini", "[path]\r\n\ -read-data=__PATH__executable__/../../data\r\n\ -write-data=__PATH__executable__/../../../instances/" + instance + "\r\n\ - "); - + fs.writeFileSync(instancedirectory + `/config.ini`, `[path]\r\n +read-data=${ path.resolve(config.factorioDirectory, "data") }\r\n +write-data=${ path.resolve(config.instanceDirectory, instance) }\r\n + `); + // this line is probably not needed anymore but Im not gonna remove it - fs.copySync('sharedMods', instancedirectory + "/mods"); + fs.copySync('sharedMods', path.join(instancedirectory, "mods")); let instconf = { - "factorioPort": process.env.FACTORIOPORT || Math.floor(Math.random() * 65535), - "clientPort": process.env.RCONPORT || Math.floor(Math.random() * 65535), - "clientPassword": Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 8), + "factorioPort": args.port || process.env.FACTORIOPORT || Math.floor(Math.random() * 65535), + "clientPort": args["rcon-port"] || process.env.RCONPORT || Math.floor(Math.random() * 65535), + "__comment_clientPassword": "This is the rcon password. Its also used for making an instanceID. Make sure its unique and not blank.", + "clientPassword": args["rcon-password"] || Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 8), } console.log("Clusterio | Created instance with settings:") console.log(instconf); - + // create instance config fs.writeFileSync(instancedirectory + "/config.json", JSON.stringify(instconf, null, 4)); - + let name = "Clusterio instance: " + instance; if (config.username) { name = config.username + "'s clusterio " + instance; @@ -279,19 +344,59 @@ write-data=__PATH__executable__/../../../instances/" + instance + "\r\n\ "autosave_slots": 5, "afk_autokick_interval": 0, "auto_pause": config.auto_pause, - } + }; fs.writeFileSync(instancedirectory + "/server-settings.json", JSON.stringify(serversettings, null, 4)); - let createSave = child_process.spawnSync( - './' + config.factorioDirectory + '/bin/x64/factorio', [ - '-c', instancedirectory + '/config.ini', - '--create', instancedirectory + '/saves/save.zip', - ] - ); - console.log("Instance created!"); -} else if (command == "start" && typeof instance == "string" && instance != "/" && fs.existsSync(instancedirectory)) { + console.log("Server settings: "+JSON.stringify(serversettings, null, 4)); + console.log("Creating save ....."); + let factorio = child_process.spawn( + './' + config.factorioDirectory + '/bin/x64/factorio', [ + '-c', instancedirectory + '/config.ini', + // '--create', instancedirectory + '/saves/save.zip', + '--start-server-load-scenario', 'Hotpatch', + '--server-settings', instancedirectory + '/server-settings.json', + '--rcon-port', Number(process.env.RCONPORT) || instconf.clientPort, + '--rcon-password', instconf.clientPassword, + ], { + 'stdio': ['pipe', 'pipe', 'pipe'] + } + ); + factorio.stdout.on("data", data => { + data = data.toString("utf8").replace(/(\r\n\t|\n|\r\t)/gm,""); + console.log(data); + if(data.includes("Starting RCON interface")){ + let client = new Rcon(); + client.connect({ + host: 'localhost', + port: Number(process.env.RCONPORT) || instconf.clientPort, + password: instconf.clientPassword, + timeout: 5000 + }); + client.onDidAuthenticate(() => { + console.log('Clusterio | RCON Authenticated!'); + }); + client.onDidConnect(() => { + console.log('Clusterio | RCON Connected, starting save'); + client.send("/c game.server_save('hotpachSave')"); + }); + } + if(data.includes("Saving finished")){ + console.log("Map saved as hotpachSave.zip, exiting..."); + console.log("Instance created!") + process.exit(0); + } + if(data.includes("Downloading from auth server failed")){ + console.error("Instance creation failed, unable to establish auth server connection."); + console.error("Deleting broken instance..."); + + fileOps.deleteFolderRecursiveSync(path.resolve(config.instanceDirectory, process.argv[3])); + + process.exit(0); + } + }); +} else if (command == "start" && typeof instance == "string" && instance != "/" && fs.existsSync(instancedirectory)){(async () => { // Exit if no instance specified (it should be, just a safeguard); - if(instancedirectory != "./instances/undefined"){ - var instanceconfig = require(instancedirectory + '/config'); + if(instancedirectory != config.instanceDirectory+"/undefined"){ + var instanceconfig = require(path.resolve(instancedirectory,'config')); instanceconfig.unique = stringUtils.hashCode(instanceconfig.clientPassword); if(process.env.FACTORIOPORT){ instanceconfig.factorioPort = process.env.FACTORIOPORT; @@ -303,50 +408,53 @@ write-data=__PATH__executable__/../../../instances/" + instance + "\r\n\ process.exit(1); } console.log("Deleting .tmp.zip files"); - let savefiles = fs.readdirSync(instancedirectory + "/saves/"); + let savefiles = fs.readdirSync(path.join(instancedirectory,"saves")); for(i = 0; i < savefiles.length; i++){ if(savefiles[i].substr(savefiles[i].length - 8, 8) == ".tmp.zip") { - fs.unlinkSync(instancedirectory + "/saves/" + savefiles[i]); + fs.unlinkSync(path.resolve(instancedirectory, "saves", savefiles[i])); } } - console.log("Deleting logs"); + console.log("Clusterio | Rotating old logs..."); // clean old log file to avoid crash - // file exists, delete so we don't get in trouble - try { - fs.unlinkSync(instancedirectory+'/factorio-current.log'); - } catch (err){ - if(err){ - console.log(err); - } else { - console.log("Clusterio | Deleting old logs..."); + try{ + let logPath = path.join(instancedirectory,'factorio-current.log'); + let stat = await fs.stat(logPath); + console.log(stat) + console.log(stat.isFile()) + if(stat.isFile()){ + let logFilename = `factorio-${Math.floor(Date.parse(stat.mtime)/1000)}.log`; + await fs.rename(logPath, path.join(instancedirectory, logFilename)); + console.log(`Log rotated as ${logFilename}`); } - } + }catch(e){} + // Math.floor(Date.now()/1000) + // (new Date).toGMTString() // move mods from ./sharedMods to the instances mod directory - try { - fs.mkdirSync(instancedirectory + "/instanceMods/"); - } catch(e){} - try{rmdirSync(instancedirectory + "/mods");}catch(e){} + try{fs.mkdirSync(path.join(instancedirectory, "instanceMods"));}catch(e){} + try{rmdirSync(path.join(instancedirectory, "mods"));}catch(e){} try { // mods directory that will be emptied (deleted) when closing the server to facilitate seperation of instanceMods and sharedMods - fs.mkdirSync(instancedirectory + "/mods/"); + fs.mkdirSync(path.join(instancedirectory, "mods")); } catch(e){} console.log("Clusterio | Moving shared mods from sharedMods/ to instance/mods..."); - fs.copySync('sharedMods', instancedirectory + "/mods"); + fs.copySync('sharedMods', path.join(instancedirectory, "mods")); console.log("Clusterio | Moving instance specific mods from instance/instanceMods to instance/mods..."); - fs.copySync(instancedirectory + "/instanceMods", instancedirectory + "/mods"); - - process.on('SIGINT', function () { - console.log("Caught interrupt signal"); - messageInterface("/quit"); - }); - + fs.copySync(path.join(instancedirectory, "instanceMods"), path.join(instancedirectory, "mods")); + + // Set paths for factorio so it reads and writes from the correct place even if the instance is imported from somewhere else + fs.writeFileSync(instancedirectory + `/config.ini`, `[path]\r\n +read-data=${ path.resolve(config.factorioDirectory, "data") }\r\n +write-data=${ path.resolve(config.instanceDirectory, instance) }\r\n + `); + // Spawn factorio server //var serverprocess = child_process.exec(commandline); fileOps.getNewestFile(instancedirectory + "/saves/", fs.readdirSync(instancedirectory + "/saves/"),function(err, latestSave) { if(err) { - console.log("Your savefile seems to be missing. This might because you created an instance without having factorio\ - installed and configured properly. Try installing factorio and adding your savefile to instances/[instancename]/saves/"); + console.error("ERROR!"); + console.error("Your savefile seems to be missing. This might because you created an instance without having factorio\ + installed and configured properly. Try installing factorio and adding your savefile to instances/[instancename]/saves/\n"); throw err; } // implicit global @@ -354,31 +462,29 @@ write-data=__PATH__executable__/../../../instances/" + instance + "\r\n\ './' + config.factorioDirectory + '/bin/x64/factorio', [ '-c', instancedirectory + '/config.ini', '--start-server', latestSave.file, - '--rcon-port', Number(process.env.RCONPORT) || instanceconfig.clientPort, - '--rcon-password', instanceconfig.clientPassword, + '--rcon-port', args["rcon-port"] || Number(process.env.RCONPORT) || instanceconfig.clientPort, + '--rcon-password', args["rcon-password"] || instanceconfig.clientPassword, '--server-settings', instancedirectory + '/server-settings.json', - '--port', Number(process.env.FACTORIOPORT) || instanceconfig.factorioPort + '--port', args.port || Number(process.env.FACTORIOPORT) || instanceconfig.factorioPort ], { 'stdio': ['pipe', 'pipe', 'pipe'] } ); - serverprocess.on('close', (code) => { + serverprocess.on('close', code => { console.log(`child process exited with code ${code}`); process.exit(); }); - serverprocess.stdout.on("data", (data) => { + serverprocess.stdout.on("data", data => { // log("Stdout: " + data); if(data.toString('utf8').includes("Couldn't parse RCON data: Maximum payload size exceeded")){ console.error("ERROR: RCON CONNECTION BROKE DUE TO TOO LARGE PACKET!"); console.error("Attempting reconnect..."); - client.close(); + client.disconnect(); client.connect(); } - if(process.platform == "linux"){ - // we have to log on linux because linux only shows stdout of the first process launched - console.log('Factorio: ' + data); - } + // we have to do this to make logs visible on linux and in powershell. Causes log duplication for people with CMD. + console.log('Fact: ' + data.toString("utf8").replace("\n", "")); }); serverprocess.stderr.on('data', (chunk) => { console.log('ERR: ' + chunk); @@ -393,12 +499,12 @@ write-data=__PATH__executable__/../../../instances/" + instance + "\r\n\ // we read the log every 2 seconds and stop looping when we start connecting to factorio function checkRcon() { fs.readFile(instancedirectory+"/factorio-current.log", function (err, data) { - // if (err) console.log(err); + // if (err) console.error(err); if(data && data.indexOf('Starting RCON interface') > 0){ client.connect({ host: 'localhost', - port: Number(process.env.RCONPORT) || instanceconfig.clientPort, - password: instanceconfig.clientPassword, + port: args["rcon-port"] || Number(process.env.RCONPORT) || instanceconfig.clientPort, + password: args["rcon-password"] || instanceconfig.clientPassword, timeout: 5000 }); } else { @@ -412,7 +518,7 @@ write-data=__PATH__executable__/../../../instances/" + instance + "\r\n\ client.onDidAuthenticate(() => { console.log('Clusterio | RCON Authenticated!'); - instanceManagement(); // start using rcons + instanceManagement(instanceconfig); // start using rcons }); client.onDidConnect(() => { console.log('Clusterio | RCON Connected!'); @@ -420,70 +526,89 @@ write-data=__PATH__executable__/../../../instances/" + instance + "\r\n\ }); client.onDidDisconnect(() => { console.log('Clusterio | RCON Disconnected!'); - process.exit(0); // exit because RCON disconnecting is undefined behaviour and we rather just wanna restart now + // process.exit(0); // exit because RCON disconnecting is undefined behaviour and we rather just wanna restart now + }); + process.on('SIGINT', function () { + console.log("Caught interrupt signal, disconnecting RCON"); + client.disconnect().then(()=>{ + console.log("Rcon disconnected, Sending ^C"); + // We don't actually do this on windows, because ctrl+c in windows CMD sends it to all subprocesses as well. Doing it twice will abort factorios save. + if(process.platform == "linux"){ + serverprocess.kill("SIGINT"); + } + }); }); } else if(process.platform == "linux"){ - // TODO: Check if this works fine on linux or if it has to be delayed until the server starts properly - instanceManagement(); + // don't open an RCON connection and just use stdio instead, does not work on windows. + instanceManagement(instanceconfig); + process.on('SIGINT', function () { + console.log("Caught interrupt signal, sending ^C"); + serverprocess.kill("SIGINT"); + }); } // set some globals confirmedOrders = []; lastSignalCheck = Date.now(); }); +})()} else { + console.error("Invalid arguments, quitting."); + process.exit(1); } // ensure instancemanagement only ever runs once _.once(instanceManagement); -function instanceManagement() { - /* Open websocket connection to master */ +async function instanceManagement(instanceconfig) { + console.log("Started instanceManagement();"); + + /* Open websocket connection to master */ var socket = ioClient("http://"+config.masterIP+":"+config.masterPort); socket.on("hello", data => { console.log("SOCKET | registering slave!"); socket.emit("registerSlave", { instanceID: instanceconfig.unique, }); - setInterval(B=> socket.emit("heartbeat"), 10000); }); - - console.log("Started instanceManagement();"); + setInterval(B=> socket.emit("heartbeat"), 10000); + // load plugins and execute onLoad event - let pluginDirectories = fileOps.getDirectoriesSync("./sharedPlugins/"); + const pluginManager = require("./lib/manager/pluginManager.js")(config); + let pluginsToLoad = await pluginManager.getPlugins(); let plugins = []; - for(let i=0; i plugins[I].factorioOutput(data.toString())); + serverprocess.stdout.on("data", data => plugins[i].factorioOutput(data.toString())); } if(pluginConfig.scriptOutputFileSubscription && typeof pluginConfig.scriptOutputFileSubscription == "string"){ if(global.subscribedFiles[pluginConfig.scriptOutputFileSubscription]) { // please choose a unique file to subscribe to. If you need plugins to share this interface, set up a direct communication // between those plugins instead. - throw "FATAL ERROR IN " + pluginDirectories[i] + " FILE ALREADY SUBSCRIBED " + pluginConfig.scriptOutputFileSubscription; + throw "FATAL ERROR IN " + pluginConfig.name + " FILE ALREADY SUBSCRIBED " + pluginConfig.scriptOutputFileSubscription; } if (!fs.existsSync(instancedirectory + "/script-output/" + pluginConfig.scriptOutputFileSubscription)) { @@ -492,10 +617,13 @@ function instanceManagement() { } global.subscribedFiles[pluginConfig.scriptOutputFileSubscription] = true; console.log("Clusterio | Registered file subscription on "+instancedirectory + "/script-output/" + pluginConfig.scriptOutputFileSubscription); + fs.watch(instancedirectory + "/script-output/" + pluginConfig.scriptOutputFileSubscription, fileChangeHandler); // run once in case a plugin wrote out information before the plugin loaded fully - fileChangeHandler(false, pluginConfig.scriptOutputFileSubscription); - + // delay, so the socket got enough time to connect + setTimeout(()=> { + fileChangeHandler(false, pluginConfig.scriptOutputFileSubscription); + }, 500); // send file contents to plugin for processing function fileChangeHandler(eventType, filename) { if(filename != null){ @@ -508,11 +636,11 @@ function instanceManagement() { if (stuff[0]) { fs.writeFileSync(instancedirectory + "/script-output/" + filename, ""); } - for(let i = 0; i < stuff.length; i++) { - if(stuff[i] && !stuff[i].includes('\u0000\u0000')) { + for(let o = 0; o < stuff.length; o++) { + if(stuff[o] && !stuff[o].includes('\u0000\u0000')) { try{ - plugins[I].scriptOutput(stuff[i]); - }catch(e){console.log(e)} + plugins[i].scriptOutput(stuff[o]); + }catch(e){console.error(e)} } } }, @@ -521,71 +649,12 @@ function instanceManagement() { } } } - console.log("Clusterio | Loaded plugin " + pluginDirectories[i]); - } else if(pluginConfig.binary != "nodePackage"){ - // handle as fragile executable plugin - let args = pluginConfig.args || []; - plugins[I]=child_process.spawn(pluginConfig.binary, args, { - cwd: "./sharedPlugins/"+pluginDirectories[i], - stdio: ['pipe', 'pipe', 'pipe'], - }); - - /* - to send to stdin, use: - spawn.stdin.write("text\n"); - */ - // If plugin has subscribed to a file, send any text appearing in that file to stdin - if(pluginConfig.scriptOutputFileSubscription && typeof pluginConfig.scriptOutputFileSubscription == "string") { - if(global.subscribedFiles[pluginConfig.scriptOutputFileSubscription]) { - // please choose a unique file to subscribe to. If you need plugins to share this interface, set up a direct communication - // between those plugins instead. - throw "FATAL ERROR IN " + pluginDirectories[i] + " FILE ALREADY SUBSCRIBED " + pluginConfig.scriptOutputFileSubscription; - } - - if (!fs.existsSync(instancedirectory + "/script-output/" + pluginConfig.scriptOutputFileSubscription)) { - // Do something - fs.writeFileSync(instancedirectory + "/script-output/" + pluginConfig.scriptOutputFileSubscription, ""); - } - global.subscribedFiles[pluginConfig.scriptOutputFileSubscription] = true; - fs.watch(instancedirectory + "/script-output/" + pluginConfig.scriptOutputFileSubscription, function (eventType, filename) { - // get array of lines in file - // TODO async - let stuff = fs.readFileSync(instancedirectory + "/script-output/" + filename, "utf8").split("\n"); - // if you found anything, reset the file - if (stuff[0]) { - fs.writeFileSync(instancedirectory + "/script-output/" + filename, ""); - } - for (let i = 0; i < stuff.length; i++) { - if (stuff[i]) { - plugins[I].stdin.write(stuff[i]); - } - } - }); - } - // these are our two config files. We need to send these in case plugin - // wants to contact master or know something. - // send through script-output file, maybe more compat? - fs.writeFileSync(instancedirectory + "/script-output/" + pluginConfig.scriptOutputFileSubscription, JSON.stringify(combinedConfig)+"\r\n"); - // send directly through stdin - // plugins[i].stdin.write(JSON.stringify(combinedConfig)+"\n"); - - console.log("Clusterio | Loaded plugin " + pluginDirectories[i]); - plugins[i].stdout.on("data", (data) => { - if(data.toString('utf8')[0] != "/") { - log("Stdout: " + data.toString('utf8')) - } else { - messageInterface(data.toString('utf8')); - } - }); - plugins[i].stderr.on("data", (data) => { - log("STDERR: " + data); - }); - plugins[i].on('close', (code) => { - log(`child process exited with code ${code}`); - }); + console.log(`Clusterio | Loaded plugin ${pluginsToLoad[i].name} in ${Date.now() - pluginLoadStarted}ms`); + } else { + // this plugin doesn't have a client portion. Maybe it runs on the master only? } } - + // world IDs ------------------------------------------------------------------ hashMods(instance, function(modHashes){ setInterval(getID, 10000); @@ -601,7 +670,11 @@ function instanceManagement() { publicIP: config.publicIP, // IP of the server should be global for all instances, so we pull that straight from the config mods:modHashes, instanceName: instance, - playerCount, + } + if(playerCount){ + payload.playerCount = playerCount.replace(/(\r\n\t|\n|\r\t)/gm, ""); + } else { + payload.playerCount = 0; } function callback(err, mac) { @@ -609,15 +682,19 @@ function instanceManagement() { mac = "unknown"; console.log("##### getMac crashed, but we don't really give a shit because we are probably closing down #####"); } + global.mac = mac; payload.mac = mac; - console.log("Registered our precense with master "+config.masterIP+" at " + payload.time); + // console.log("Registered our presence with master "+config.masterIP+" at " + payload.time); needle.post(config.masterIP + ":" + config.masterPort + '/api/getID', payload, needleOptionsWithTokenAuthHeader, function (err, response, body) { if (err && err.code != "ECONNRESET"){ - console.error("We got problems, something went wrong when contacting master"); + console.error("We got problems, something went wrong when contacting master"+config.masterIP+" at " + payload.time); console.error(err); } else if (response && response.body) { // In the future we might be interested in whether or not we actually manage to send it, but honestly I don't care. - console.log(response.body); + if(response.body !== "ok") { + console.log("Got no \"ok\" while registering our precense with master "+config.masterIP+" at " + payload.time); + console.log(response.body); + } } }); } @@ -629,10 +706,6 @@ function instanceManagement() { }); } }); - - // messageInterface Management - setInterval(function(){let command=commandBuffer.pop();if(command){messageInterfaceInternal(command[0],command[1])}},config.msBetweenCommands || 50); - // Mod uploading and management ----------------------------------------------- // get mod names and hashes // string: instance, function: callback @@ -646,9 +719,9 @@ function instanceManagement() { hash: modHashes[i].hash, } needle.post(config.masterIP + ":" + config.masterPort + '/api/checkMod', payload, needleOptionsWithTokenAuthHeader, function (err, response, body) { - if(err) console.log(new Error("Unable to contact master server! Please check your config.json.")); + if(err) console.error("Unable to contact master server /api/checkMod! Please check your config.json."); if(response && body && body == "found") { - console.log("master has mod"); + console.log("master has mod "+modHashes[i].modName); } else if (response && body && typeof body == "string") { let mod = response.body; if(config.uploadModsToMaster){ @@ -661,13 +734,13 @@ function instanceManagement() { }, }, function (err, resp, body) { if (err) { - console.log(new Error("Unable to contact master server! Please check your config.json.")); + console.error(new Error("Unable to contact master server /api/uploadMod! Please check your config.json.")); } else { console.log('URL: ' + body); } }); var form = req.form(); - form.append('file', fs.createReadStream("./instances/"+instance+"/mods/"+mod)); + form.append('file', fs.createReadStream(config.instanceDirectory+"/"+instance+"/mods/"+mod)); } else { console.log("Not sending mod: " + mod + " to master because config.uploadModsToMaster is not enabled") } @@ -675,262 +748,6 @@ function instanceManagement() { }); } } - - // flow/production statistics ------------------------------------------------------------ - oldFlowStats = false - setInterval(function(){ - fs.readFile(instancedirectory + "/script-output/flows.txt", {encoding: "utf8"}, function(err, data) { - if(!err && data) { - let timestamp = Date.now(); - data = data.split("\n"); - let flowStats = []; - for(let i = 0; i < data.length; i++) { - // try catch to remove any invalid json - try{ - flowStats[flowStats.length] = JSON.parse(data[i]); - } catch (e) { - // console.log(" invalid json: " + i); - // some lines of JSON are invalid but don't worry, we just filter em out - } - } - // fluids - let flowStat1 = flowStats[flowStats.length-1].flows.player.input_counts - // items - let flowStat2 = flowStats[flowStats.length-2].flows.player.input_counts - // merge fluid and item flows - let totalFlows = {}; - for(let key in flowStat1) totalFlows[key] = flowStat1[key]; - for(let key in flowStat2) totalFlows[key] = flowStat2[key]; - if(oldFlowStats && totalFlows && oldTimestamp) { - let payload = objectOps.deepclone(totalFlows); - // change from total reported to per time unit - for(let key in oldFlowStats) { - // get production per minute - payload[key] = Math.floor((payload[key] - oldFlowStats[key])/(timestamp - oldTimestamp)*60000); - if(payload[key] < 0) { - payload[key] = 0; - } - } - for(let key in payload) { - if(payload[key] == '0') { - delete payload[key]; - } - } - console.log("Recorded flows, copper plate since last time: " + payload["copper-plate"]); - needle.post(config.masterIP + ":" + config.masterPort + '/api/logStats', {timestamp: timestamp, instanceID: instanceconfig.unique,data: payload}, needleOptionsWithTokenAuthHeader, function (err, response, body) { - // we did it, keep going - }); - } - oldTimestamp = timestamp; - oldFlowStats = totalFlows; - fs.writeFileSync(instancedirectory + "/script-output/flows.txt", ""); - } - }); - // we don't need to update stats quickly as that could be expensive - }, 60000*5); - - // provide items -------------------------------------------------------------- - // trigger when something happens to output.txt - fs.watch(instancedirectory + "/script-output/output.txt", function (eventType, filename) { - // get array of lines in file - let items = fs.readFileSync(instancedirectory + "/script-output/output.txt", "utf8").split("\n"); - // if you found anything, reset the file - if (items[0]) { - fs.writeFileSync(instancedirectory + "/script-output/output.txt", ""); - } - for (let i = 0; i < items.length; i++) { - if (items[i]) { - let g = items[i].split(" "); - g[0] = g[0].replace("\u0000", ""); - // console.log("exporting " + JSON.stringify(g)); - // send our entity and count to the master for him to keep track of - needle.post(config.masterIP + ":" + config.masterPort + '/api/place', { - name: g[0], - count: g[1], - instanceName: instance, // name of instance - instanceID: instanceconfig.unique, // a hash computed from the randomly generated rcon password - }, needleOptionsWithTokenAuthHeader, function (err, resp, body) { - if(body == "failure") console.error("#### Export failed! Lost: "+g[1]+" "+g[0]); - if(config.logItemTransfers){ - if(body == "success") console.log(`Exported ${g[1]} ${g[0]} to master`); - } - }); - } - } - }); - // request items -------------------------------------------------------------- - setInterval(function () { - // get array of lines in file - let items = fs.readFileSync(instancedirectory + "/script-output/orders.txt", "utf8").split("\n"); - // if we actually got anything from the file, proceed and reset file - if (items[0]) { - fs.writeFileSync(instancedirectory + "/script-output/orders.txt", ""); - // prepare a package of all our requested items in a more tranfer friendly format - var preparedPackage = {}; - for (let i = 0; i < items.length; i++) { - (function (i) { - if (items[i]) { - items[i] = items[i].split(" "); - items[i][0] = items[i][0].replace("\u0000", ""); - items[i][0] = items[i][0].replace(",", ""); - if (preparedPackage[items[i][0]]) { - // if we have buffered some already, sum the new items - if (typeof Number(preparedPackage[items[i][0]].count) == "number" && typeof Number(items[i][1]) == "number") { - preparedPackage[items[i][0]] = { - "name": items[i][0], - "count": Number(preparedPackage[items[i][0]].count) + Number(items[i][1]), - "instanceName":instance, - "instanceID":instanceconfig.unique, - }; - // else just add em in without summing - } else if (typeof Number(items[i][1]) == "number") { - preparedPackage[items[i][0]] = { - "name": items[i][0], - "count": Number(items[i][1]), - "instanceName":instance, - "instanceID":instanceconfig.unique, - }; - } - // this condition will NEVER be triggered but we know how that goes - } else if (typeof Number(items[i][1]) == "number") { - preparedPackage[items[i][0]] = { - "name": items[i][0], - "count": Number(items[i][1]), - "instanceName":instance, - "instanceID":instanceconfig.unique, - }; - } - } - })(i); - } - // request our items, one item at a time - for (let i = 0; i < Object.keys(preparedPackage).length; i++) { - // console.log(preparedPackage[Object.keys(preparedPackage)[i]]); - needle.post(config.masterIP + ":" + config.masterPort + '/api/remove', preparedPackage[Object.keys(preparedPackage)[i]], needleOptionsWithTokenAuthHeader, function (err, response, body) { - if (response && response.body && typeof response.body == "object") { - // buffer confirmed orders - confirmedOrders[confirmedOrders.length] = {name:response.body.name,count:response.body.count} - if(config.logItemTransfers){ - console.log(`Imported ${response.body.count} ${response.body.name} from master`); - } - } - }); - } - // if we got some confirmed orders - // console.log("Importing " + confirmedOrders.length + " items! " + JSON.stringify(confirmedOrders)); - //if (!(confirmedOrders.length>0)){return;} - let cmd="local t={"; - for(let i=0;i320) // Factorio max packet size is 508 - { - messageInterface("/silent-command remote.call('clusterio', 'runcode', '"+cmd.slice(0, -1)+"}"+ " for k, item in pairs(t) do GiveItemsToStorage(k, item) end')"); - cmd="local t={"; - } - } - if (!(cmd==="local t={")){ - messageInterface("/silent-command remote.call('clusterio', 'runcode', '"+cmd.slice(0, -1)+"}"+ " for k, item in pairs(t) do GiveItemsToStorage(k, item) end')"); - } - confirmedOrders=[]; - } - }, 1000); - // COMBINATOR SIGNALS --------------------------------------------------------- - // get inventory from Master and RCON it to our slave - setInterval(function () { - needle.get(config.masterIP + ":" + config.masterPort + '/api/inventory', function (err, response, body) { - if(err){ - console.log("Unable to get JSON master/api/inventory, master might be unaccessible"); - } else if (response && response.body) { - // Take the inventory we (hopefully) got and turn it into the format LUA accepts - if(Buffer.isBuffer(response.body)) {console.log(response.body.toString("utf-8")); throw new Error();} // We are probably contacting the wrong webserver - try { - var inventory = JSON.parse(response.body); - var inventoryFrame = {}; - for (let i = 0; i < inventory.length; i++) { - inventoryFrame[inventory[i].name] = Number(inventory[i].count); - if(inventoryFrame[inventory[i].name] >= Math.pow(2, 31)){ - inventoryFrame[inventory[i].name] = Math.pow(2, 30); // set it waaay lower, 31 -1 would probably suffice - } - } - inventoryFrame["signal-unixtime"] = Math.floor(Date.now()/1000); - // console.log("RCONing inventory! " + JSON.stringify(inventoryFrame)); - let first = true; - let cmd="local s={"; - for (let key in inventoryFrame) - { - cmd+='["'+key+'"]='+inventoryFrame[key]+","; - if(first && cmd.length>300 || !first && cmd.length > 320) // Factorio max packet size is 508 - { - messageInterface("/silent-command remote.call('clusterio', 'runcode', '"+(first ? 'global.ticksSinceMasterPinged=0 ':'')+cmd.slice(0, -1)+"}"+ " for name,count in pairs(s) do global.invdata[name]=count end')"); - cmd="local s={"; - first = false; - } - } - if (!(cmd==="local s={")){ - messageInterface("/silent-command remote.call('clusterio', 'runcode', '"+(first ? 'global.ticksSinceMasterPinged=0 ':'')+cmd.slice(0, -1)+"}"+ " for name,count in pairs(s) do global.invdata[name]=count end')"); - } - messageInterface("/silent-command remote.call('clusterio', 'runcode', 'UpdateInvCombinators()')"); - } catch (e){ - console.log(e); - } - } - }); - }, 1550); - // Make sure world has its worldID - setTimeout(function(){ - messageInterface("/silent-command remote.call('clusterio','setWorldID',"+instanceconfig.unique+")") - }, 20000); - /* REMOTE SIGNALLING - * send any signals the slave has been told to send - * Fetch combinator signals from the server - */ - socket.on("processCombinatorSignal", circuitFrameWithMeta => { - if(circuitFrameWithMeta && typeof circuitFrameWithMeta == "object" && circuitFrameWithMeta.frame && Array.isArray(circuitFrameWithMeta.frame)){ - messageInterface("/silent-command remote.call('clusterio', 'receiveFrame', '"+JSON.stringify(circuitFrameWithMeta.frame)+"')"); - } - }); - // get outbound frames from file and send to master - // get array of lines in file, each line should correspond to a JSON encoded frame - let signals = fs.readFileSync(instancedirectory + "/script-output/txbuffer.txt", "utf8").split("\n"); - // if we actually got anything from the file, proceed and reset file - let readingTxBufferSoon = false; - let txBufferClearCounter = 0; - fs.watch(instancedirectory + "/script-output/txbuffer.txt", "utf-8", (eventType, filename) => { - if(!readingTxBufferSoon){ // use a 100ms delay to avoid messing with rapid sequential writes from factorio (I think that might be a problem maybe?) - readingTxBufferSoon = true; - setTimeout(()=>{ - txBufferClearCounter++; - fs.readFile(instancedirectory + "/script-output/txbuffer.txt", "utf-8", (err, signals) => { - signals = signals.split("\n"); - if (signals[0]) { - //if(txBufferClearCounter > 500){ - fs.writeFileSync(instancedirectory + "/script-output/txbuffer.txt", ""); - // txBufferClearCounter = 0; - //} - - // loop through all our frames - for (let i = 0; i < signals.length; i++) { - if (signals[i] && objectOps.isJSON(signals[i])) { - // signals[i] is a JSON array called a "frame" of signals. We timestamp it for storage on master - // then we unpack and RCON in this.frame to the game later. - let framepart = JSON.parse(signals[i]); - let doneframe = { - time: Date.now(), - frame: framepart, // thats our array of objects(single signals); - } - // send to master using socket.io, opened at the top of instanceManagement() - socket.emit("combinatorSignal", doneframe); - } else { - // console.log("Invalid jsony: "+typeof signals[i]) - } - } - } - }); - readingTxBufferSoon = false; - },100); - } - }); } // END OF INSTANCE START --------------------------------------------------------------------- // string, function @@ -952,7 +769,7 @@ function hashMods(instanceName, callback) { } let hashedMods = []; /*let mods = fs.readdirSync("./sharedMods/")*/ - let instanceMods = fs.readdirSync("./instances/"+instanceName+"/mods/"); + let instanceMods = fs.readdirSync(config.instanceDirectory+"/"+instanceName+"/mods/"); if(instanceMods.length == 0){ callback({}); // there are no mods installed, return an empty object } @@ -962,7 +779,7 @@ function hashMods(instanceName, callback) { } } for(let i=0; i{ @@ -24,7 +27,7 @@ module.exports.displayConfig = function(instance){ } else configArray.push({entry: key, value: config[key]}); } }); - console.log(asTable(configArray)); + return configArray; } } module.exports.editConfig = function(instance, entry, newValue){ @@ -33,16 +36,34 @@ module.exports.editConfig = function(instance, entry, newValue){ if(objectOps.isJSON(newValue)){ config[entry] = JSON.parse(newValue); console.log("Changed config entry "+entry+" from "+JSON.stringify(config[entry])+" to "+JSON.stringify(newValue)); - console.log("new config file:"); - console.log(JSON.stringify(config, null, " ")) + // console.log("new config file:"); + // console.log(JSON.stringify(config, null, " ")) fs.writeFileSync("config.json", JSON.stringify(config, null, 4)); + return { + ok:true, + msg:`Changed config entry ${entry} from Object to ${JSON.stringify(newValue)}`, + } } else { config[entry] = newValue; console.log("Changed config entry "+entry+" from "+config[entry]+" to '"+newValue+"'"); - console.log("new config file:"); - console.log(JSON.stringify(config, null, " ")) + // console.log("new config file:"); + // console.log(JSON.stringify(config, null, " ")) fs.writeFileSync("config.json", JSON.stringify(config, null, 4)); + return { + ok:true, + msg:`Changed config entry ${entry} from ***** to '${newValue}'`, + }; } + } else { + return { + ok:false, + msg:`No such config parameter`, + } + } + } else { + return { + ok:false, + msg:`Editing config for single instances is not yet supported`, } } }; diff --git a/lib/manager/modManager.js b/lib/manager/modManager.js index fec45562..78a07ca4 100644 --- a/lib/manager/modManager.js +++ b/lib/manager/modManager.js @@ -7,15 +7,15 @@ const rightpad = require("right-pad"); const asTable = require("as-table").configure({delimiter: ' | '}); const moment = require("moment"); const syncRequest = require("sync-request"); -const fs = require("fs"); +const fs = require("fs-extra"); const https = require('follow-redirects').https; const factorioAPI = require("node-factorio-api"); const sLog = require("single-line-log").stdout; +const query = require('cli-interact').getYesNo; const fileOps = require("_app/fileOps"); -const config = require("./../../config"); +// const config = require("./../../config"); -const instances = require("./../getInstances.js").sync(); function ellipse(str, max){ return str.length > (max - 3) ? str.substring(0,max-3) + '...' : str; @@ -23,209 +23,248 @@ function ellipse(str, max){ // https://stackoverflow.com/questions/15900485/correct-way-to-convert-size-in-bytes-to-kb-mb-gb-in-javascript function formatBytes(a,b){if(0==a)return"0 Bytes";var c=1024,d=b||2,e=["Bytes","KB","MB","GB","TB","PB","EB","ZB","YB"],f=Math.floor(Math.log(a)/Math.log(c));return parseFloat((a/Math.pow(c,f)).toFixed(d))+" "+e[f]}; -/** -Lists mods for an instance as a table. Warning, handles printing internally. -@param {string} instance - Name of the instance or "shared" -@example -var modManager = require("modManager"); -modManager.listMods("testInstance"); -// console.logs table of mods installed on instance -modManager.listMods("shared"); -// console.logs mods located in /sharedMods as a nice table -*/ -module.exports.listMods = function(instance){ - if(instance && instances.includes(instance)){ - factorioAPI.init(false, 'instances/'+instance+'/instanceMods', 'instances/'+instance+'/saves', config.factorio_version); - factorioAPI.authenticate({ - username: config.username, - token: config.token, - require_ownership: false - }).then(token => { - console.log("* Currently installed:"); - factorioAPI.readModZips().then(list => { - console.log(asTable(list.map(mod => { - return { - name:(mod.title || mod.name), - version:mod.version, - date:mod.date, - factorio_ver:mod.factorio_version, - author:mod.author, - } - }))); - }); - // Had issues earlier due to bug in node-factorio-api, see https://github.com/Danacus/node-factorio-api/issues/2 - // bug is resolved, thanks to @Danacus and @brotherpot - factorioAPI.getModsFromSaves().then(list => { - console.log("* Mods used in save:"); - console.log(asTable(list[0].mods)) - }); - }).catch(reason => { - console.log("Failed to authenticate to factorio.com! Please check your connection and the username+token used for authentication in config.json"); - }); - } else { - // list shared mods in /sharedMods/ - factorioAPI.init(false, 'sharedMods/', '', config.factorio_version); - factorioAPI.authenticate({ - username: config.username, - token: config.token, - require_ownership: false - }).then(() => { - console.log("\r\n") - console.log("* Mods shared by all instances:") - factorioAPI.getInstalledMods().then(mods => { - console.log(asTable(mods)); - }); - }); - } - - // get JSON data about GH releases - /*let res = syncRequest('GET', 'https://api.github.com/repos/Danielv123/factorioClusterioMod/releases', {"headers":{"User-Agent":"Fuck you for requiring user agents!"}}); - let url = JSON.parse(res.getBody())[0].assets[0].browser_download_url; - let name = JSON.parse(res.getBody())[0].assets[0].name; - console.log(JSON.parse(res.getBody())) - */ -} -/** -Searches mods.factorio.com for mods matching a search term and prints -the results to console as a neat looking table +module.exports = function(config){ + if(!config) config = require("./../../config"); + const instances = require("./../getInstances.js")(config).sync(); + return { + /** + Lists mods for an instance as a table. Warning, handles printing internally. + @param {string} instance - Name of the instance or "shared" -@param {string} searchTerm What to search for -*/ -module.exports.findMods = function(searchTerm){ - factorioAPI.init(false); - factorioAPI.authenticate({ - username: config.username, - token: config.token, - require_ownership: false - }).then(token => { - factorioAPI.searchMods(searchTerm).then(mods => { - console.log(asTable(mods.reverse().map(mod => { - return { - title:ellipse(mod.title, 50), - name:ellipse(mod.name, 30), - downloads:mod.downloads_count, - updated: moment(mod.updated_at).format("DD.MM.YY"), - latest_version: mod.latest_release.version, - owner:mod.owner, - //license: mod.license_name, - } - }))) - }) - }); -} - -module.exports.addMod = function(term, instance){ - if(instance == "shared"){ // modDirectory has to end in / - var modDirectory = "sharedMods/"; - } else { - var modDirectory = "instances/"+instance+"/instanceMods/"; - } - if(term && term.length > 0){ - if(term.includes("mods.factorio.com/mods/")){ // https://mods.factorio.com/mods/Danielv123/ChooChoo - // treat it as a link download - factorioAPI.init(false, modDirectory, "", config.factorio_version); - factorioAPI.authenticate({ - username: config.username, - token: config.token, - require_ownership: false - }).then(() => { - factorioAPI.searchMods(term.split("/")[term.split("/").length-1]).then(body => { - console.log("Im gonna download: "+body[0].name + " in version: " + body[0].latest_release.version); - factorioAPI.downloadMod( {name: body[0].name, version: body[0].latest_release.version}).then(()=>{ - console.log("Mod downloaded"); + @example + var modManager = require("modManager")(config); + modManager.listMods("testInstance"); + // console.logs table of mods installed on instance + modManager.listMods("shared"); + // console.logs mods located in /sharedMods as a nice table + */ + listMods: function(instance){ + return new Promise((resolve, reject) => { + if(instance && instances.includes(instance)){ + factorioAPI.init(false, config.instanceDirectory+'/'+instance+'/instanceMods', config.instanceDirectory+'/'+instance+'/saves', config.factorio_version); + factorioAPI.authenticate({ + username: config.username, + token: config.token, + require_ownership: false + }).then(token => { + console.log("* Currently installed:"); + factorioAPI.readModZips().then(list => { + console.log(asTable(list.map(mod => { + return { + name:(mod.title || mod.name), + version:mod.version, + date:mod.date, + factorio_ver:mod.factorio_version, + author:mod.author, + } + }))); + }); + // Had issues earlier due to bug in node-factorio-api, see https://github.com/Danacus/node-factorio-api/issues/2 + // bug is resolved, thanks to @Danacus and @brotherpot + factorioAPI.getModsFromSaves().then(list => { + console.log("* Mods used in save:"); + console.log(asTable(list[0].mods)) + }); + }).catch(reason => { + console.log("Failed to authenticate to factorio.com! Please check your connection and the username+token used for authentication in config.json"); + resolve(); }); + } else { + // list shared mods in /sharedMods/ + factorioAPI.init(false, 'sharedMods/', '', config.factorio_version); + factorioAPI.authenticate({ + username: config.username, + token: config.token, + require_ownership: false + }).then(() => { + console.log("\r\n") + console.log("* Mods shared by all instances:") + factorioAPI.getInstalledMods().then(mods => { + console.log(asTable(mods)); + resolve(); + }); + }).catch(e => { + reject(new Error(e)); + }); + } + + // get JSON data about GH releases + /*let res = syncRequest('GET', 'https://api.github.com/repos/Danielv123/factorioClusterioMod/releases', {"headers":{"User-Agent":"Fuck you for requiring user agents!"}}); + let url = JSON.parse(res.getBody())[0].assets[0].browser_download_url; + let name = JSON.parse(res.getBody())[0].assets[0].name; + console.log(JSON.parse(res.getBody())) + */ + }); + }, + /** + Searches mods.factorio.com for mods matching a search term and prints + the results to console as a neat looking table + + @param {string} searchTerm What to search for + */ + findMods: function(searchTerm){ + return new Promise((resolve, reject) => { + factorioAPI.init(false); + factorioAPI.authenticate({ + username: config.username, + token: config.token, + require_ownership: false + }).then(token => { + factorioAPI.searchMods(searchTerm).then(mods => { + let result = asTable(mods.reverse().map(mod => { + return { + title:ellipse(mod.title, 50), + name:ellipse(mod.name, 30), + downloads:mod.downloads_count, + updated: moment(mod.updated_at).format("DD.MM.YY"), + latest_version: mod.latest_release.version, + owner:mod.owner, + //license: mod.license_name, + } + })); + resolve(result); + // console.log(result); + }); + }).catch(e => { + if(e.contains("Insufficient information")) console.log("We are missing some parameters required for logging in! Make sure your factorio username and token are specified in config.json."); + reject(new Error(e)); }); }); - } else if(term.includes("github.com")){ // https://github.com/Danielv123/factorioClusterioMod - // treat it as a github releases download - let apiURL = "https://api.github.com/repos/" + term.split("/")[3] + "/" + term.split("/")[4] + "/releases"; // https://api.github.com/repos/Danielv123/factorioClusterioMod/releases - - let res = syncRequest('GET', apiURL, {"headers":{"User-Agent":"Fuck you for requiring user agents!"}}); - let url = JSON.parse(res.getBody())[0].assets[0].browser_download_url; + }, + addMod: function(term, instance){ + return new Promise((resolve, reject) => { + if(instance == "shared"){ // modDirectory has to end in / + var modDirectory = "sharedMods/"; + } else { + var modDirectory = config.instanceDirectory+"/"+instance+"/instanceMods/"; + } + if(term && term.length > 0){ + if(term.includes("mods.factorio.com/mods/")){ // https://mods.factorio.com/mods/Danielv123/ChooChoo + // treat it as a link download + factorioAPI.init(false, modDirectory, "", config.factorio_version); + factorioAPI.authenticate({ + username: config.username, + token: config.token, + require_ownership: false + }).then(() => { + factorioAPI.searchMods(term.split("/")[term.split("/").length-1]).then(body => { + console.log("Im gonna download: "+body[0].name + " in version: " + body[0].latest_release.version); + factorioAPI.downloadMod( {name: body[0].name, version: body[0].latest_release.version}).then(()=>{ + console.log("Mod downloaded"); + resolve(); + }); + }); + }); + } else if(term.includes("github.com")){ // https://github.com/Danielv123/factorioClusterioMod + // treat it as a github releases download + let apiURL = "https://api.github.com/repos/" + term.split("/")[3] + "/" + term.split("/")[4] + "/releases"; // https://api.github.com/repos/Danielv123/factorioClusterioMod/releases + + let res = syncRequest('GET', apiURL, {"headers":{"User-Agent":"Fuck you for requiring user agents!"}}); + let url = JSON.parse(res.getBody())[0].assets[0].browser_download_url; + let name = JSON.parse(res.getBody())[0].assets[0].name; + if(url) { + console.log("Downloading " + url); + let file = fs.createWriteStream(modDirectory + name); + let bytesDownloaded = 0; + let downloadStartTime = Date.now(); + https.get(url, function(response) { + let bytesToDownload = response.headers['content-length']; + response.pipe(file); + response.on("data", chunk => { + bytesDownloaded += chunk.length; + let downloadTimeElapsed = Date.now() - downloadStartTime; + let bytesPerSecond = bytesDownloaded / (downloadTimeElapsed/1000); + let timeToComplete = Math.floor((bytesToDownload - bytesDownloaded)/bytesPerSecond); + sLog("Downloading "+name+" "+(bytesDownloaded/bytesToDownload*100).toPrecision(3)+"% ("+formatBytes(bytesDownloaded)+" / "+formatBytes(bytesToDownload)+")\r\n"+ + "about "+timeToComplete+" seconds remaining ("+formatBytes(bytesPerSecond)+"/s)"); + }).on("end", x => { + sLog.clear(); + console.log(" Downloaded in "+moment.utc(moment.duration(Date.now()-downloadStartTime).as('milliseconds')).format('mm:ss')); // because sLog.clear() doesn't seem to do the job of ending the line properly + resolve(); + }); + }); + } + } else { + // treat it as a search and download the top result + factorioAPI.init(false, modDirectory, "", config.factorio_version); + factorioAPI.authenticate({ + username: config.username, + token: config.token, + require_ownership: false + }).then(() => { + factorioAPI.searchMods(term).then(body => { + console.log(body) + console.log("Im gonna download: "+body[0].name + " in version: " + body[0].latest_release.version); + factorioAPI.downloadMod( {name: body[0].name, version: body[0].latest_release.version}).then(()=>{ + console.log("Mod downloaded"); + resolve(); + }); + }); + }); + } + } else { + reject(new Error("Invalid search term")); + } + }); + }, + removeMod: function removeMod(term, instance, force = false){ + return new Promise((resolve, reject) => { + if(instance == "shared"){ // modDirectory has to end in / + var modDirectory = "sharedMods/"; + } else { + var modDirectory = config.instanceDirectory+"/"+instance+"/instanceMods/"; + } + if(term && typeof term == "string" && term.length > 0){ + let mods = fs.readdirSync(modDirectory); + let modsMatchingFilter = mods.filter(mod => mod.toLowerCase().includes(term.toLowerCase())); + if(modsMatchingFilter.length < 1){ + console.log("No mods found matching search"); + } else if(modsMatchingFilter.length == 1){ + console.log("Deleting "+modsMatchingFilter[0]); + fs.unlinkSync(modDirectory+modsMatchingFilter[0]); + console.log("Deleted."); + } else if(modsMatchingFilter.length > 1){ + console.log("Found multiple mods"); + modsMatchingFilter.forEach(mod => console.log(mod)); + if(query("Delete all mods matching filter?")){ + let modsToDelete = modsMatchingFilter.length; + for(let i = 0; i < modsToDelete; i++){ + fs.unlinkSync(modDirectory+modsMatchingFilter[i]); + console.log("Deleted "+modsMatchingFilter[i]); + } + } else console.log("Did not delete mods.") + } + resolve(); + } else { + reject(new Error("Invalid search term")); + } + }); + }, + + // 'https://api.github.com/repos/Danielv123/factorioClusterioMod/releases' + /** + Download mod from github repo by looking for the first binary download in + the latest release, saving it to /sharedMods + + @param {string} url a github repo URL, for example https://api.github.com/repos/Danielv123/factorioClusterioMod/releases. Must link to /releases + */ + downloadFromGitURL: function(url){ + console.log("Downloading mod from "+url); + // get JSON data about releases + let res = syncRequest('GET', url, {"headers":{"User-Agent":"Please don't require setting a user agent"}}); + let downloadUrl = JSON.parse(res.getBody())[0].assets[0].browser_download_url; let name = JSON.parse(res.getBody())[0].assets[0].name; - if(url) { - console.log("Downloading " + url); - let file = fs.createWriteStream(modDirectory + name); - let bytesDownloaded = 0; - let downloadStartTime = Date.now(); - https.get(url, function(response) { - let bytesToDownload = response.headers['content-length']; + console.log(JSON.parse(res.getBody())) + if(downloadUrl) { + console.log(downloadUrl); + let file = fs.createWriteStream("sharedMods/"+name); + https.get(downloadUrl, function(response) { response.pipe(file); - response.on("data", chunk => { - bytesDownloaded += chunk.length; - let downloadTimeElapsed = Date.now() - downloadStartTime; - let bytesPerSecond = bytesDownloaded / (downloadTimeElapsed/1000); - let timeToComplete = Math.floor((bytesToDownload - bytesDownloaded)/bytesPerSecond); - sLog("Downloading "+name+" "+(bytesDownloaded/bytesToDownload*100).toPrecision(3)+"% ("+formatBytes(bytesDownloaded)+" / "+formatBytes(bytesToDownload)+")\r\n"+ - "about "+timeToComplete+" seconds remaining ("+formatBytes(bytesPerSecond)+"/s)"); - }).on("end", x => { - sLog.clear(); - console.log(" Downloaded in "+moment.utc(moment.duration(Date.now()-downloadStartTime).as('milliseconds')).format('mm:ss')); // because sLog.clear() doesn't seem to do the job of ending the line properly - }); + console.log("Downloaded "+name); }); } - } else { - // treat it as a search and download the top result - factorioAPI.init(false, modDirectory, "", config.factorio_version); - factorioAPI.authenticate({ - username: config.username, - token: config.token, - require_ownership: false - }).then(() => { - factorioAPI.searchMods(term).then(body => { - console.log("Im gonna download: "+body[0].name + " in version: " + body[0].latest_release.version); - factorioAPI.downloadMod( {name: body[0].name, version: body[0].latest_release.version}).then(()=>{ - console.log("Mod downloaded"); - }); - }); - }); - } - } -} -module.exports.removeMod = function removeMod(term, instance, force = false){ - if(instance == "shared"){ // modDirectory has to end in / - var modDirectory = "sharedMods/"; - } else { - var modDirectory = "instances/"+instance+"/instanceMods/"; - } - if(term && typeof term == "string" && term.length > 0){ - let mods = fs.readdirSync(modDirectory); - let modsMatchingFilter = mods.filter(mod => mod.toLowerCase().includes(term.toLowerCase())); - if(modsMatchingFilter.length < 1){ - console.log("No mods found matching search"); - } else if(modsMatchingFilter.length == 1){ - console.log("Deleting "+modsMatchingFilter[0]); - fs.unlinkSync(modDirectory+modsMatchingFilter[0]); - console.log("Deleted."); - } else if(modsMatchingFilter.length > 1){ - console.log("Found multiple mods, not doing anything"); - modsMatchingFilter.forEach(mod => console.log(mod)); - } - } -} - -// 'https://api.github.com/repos/Danielv123/factorioClusterioMod/releases' -/** -Download mod from github repo by looking for the first binary download in -the latest release, saving it to /sharedMods - -@param {string} url a github repo URL, for example https://api.github.com/repos/Danielv123/factorioClusterioMod/releases. Must link to /releases -*/ -module.exports.downloadFromGitURL = function(url){ - console.log("Downloading mod from "+url); - // get JSON data about releases - let res = syncRequest('GET', url, {"headers":{"User-Agent":"Please don't require setting a user agent"}}); - let downloadUrl = JSON.parse(res.getBody())[0].assets[0].browser_download_url; - let name = JSON.parse(res.getBody())[0].assets[0].name; - console.log(JSON.parse(res.getBody())) - if(downloadUrl) { - console.log(downloadUrl); - let file = fs.createWriteStream("sharedMods/"+name); - https.get(downloadUrl, function(response) { - response.pipe(file); - console.log("Downloaded "+name); - }); + }, } } /* diff --git a/lib/manager/pluginManager.js b/lib/manager/pluginManager.js new file mode 100644 index 00000000..7907d65d --- /dev/null +++ b/lib/manager/pluginManager.js @@ -0,0 +1,116 @@ +const fs = require("fs-extra"); +const path = require("path"); +const git = require("simple-git")("./sharedPlugins"); + + +module.exports = function(config){ + if(!config) config = require("./../../config"); + + return { + listPlugins: async function(){ + let plugins = await this.getPlugins(); + plugins.forEach(plug => { + console.log(`${plug.name}`.padEnd(22).substr(0,22)+` - ${plug.description}`.substr(0,80-22)); + }); + }, + getPlugins: async function(){ + let directories = await fs.readdir("./sharedPlugins"); + let returnArray = []; + for(let i in directories){ + let dir = directories[i]; + let pluginContents = await fs.readdir(path.join("./sharedPlugins", dir)); + // console.log(pluginContents) + if(pluginContents.includes("config.js") || pluginContents.includes("config.json")){ + try{ + var pluginConfig = require(path.join("./../../sharedPlugins", dir, "config")); + pluginConfig.pluginPath = path.join("./sharedPlugins", dir); + }catch(e){ + console.log(e) + } + + // check if plugin is enabled + try{ + await fs.stat(path.join(pluginConfig.pluginPath, "DISABLED")) + pluginConfig.enabled = false; + }catch(e){ + pluginConfig.enabled = true; + } + returnArray.push(pluginConfig); + } + } + return returnArray; + }, + addPlugin: function(path){ + return new Promise((resolve, reject) => { + if(path.includes("git")){ // we are decently sure this is a git repo + git.clone(path, data => resolve(data)) + } + }); + }, + removePlugin: async function(name){ + let plugins = await this.getPlugins(); + for(let i in plugins){ + let plug = plugins[i]; + if(plug.name.includes(name)){ + // console.log(`Removing plugin ${plug.name}`); + await fs.remove(plug.pluginPath); + return { + ok:true, + msg:`Deleted plugin ${plug.name}` + } + } + } + return { + ok:false, + msg:`Plugin matching "${name}" not found`, + } + }, + enablePlugin: async function(name, instance){ + let plugins = await this.getPlugins(); + let plugin = plugins[findInArray("name", name, plugins)[0]]; + if(plugin){ + let files = await fs.readdir(plugin.pluginPath); + if(files.includes("DISABLED")){ + await fs.unlink(path.join(plugin.pluginPath, "DISABLED")); + return { + ok:true, + msg:`Globally enabled plugin ${name}`, + } + } else { + return { + ok:true, + msg:`Plugin ${name} already enabled`, + } + } + } else { + return { + ok:false, + msg:`Found no excact match for plugin ${name}`, + } + } + }, + disablePlugin: async function(name, instance){ + let plugins = await this.getPlugins(); + let plugin = plugins[findInArray("name", name, plugins)[0]]; + if(plugin){ + await fs.writeFile(path.join(plugin.pluginPath, "DISABLED"), ""); + return { + ok:true, + msg:`Plugin ${name} is now disabled on this host`, + } + } else { + return { + ok:false, + msg:`Found no excact match for plugin ${name}`, + } + } + } + } +} +function findInArray(key, value, array){ + let indexes = []; + for(let i in array){ + if(array[i][key] && array[i][key] === value) indexes.push(i); + } + return indexes; +} diff --git a/lib/scenarios/Hotpatch/control.lua b/lib/scenarios/Hotpatch/control.lua new file mode 100644 index 00000000..b5b66946 --- /dev/null +++ b/lib/scenarios/Hotpatch/control.lua @@ -0,0 +1,109 @@ +--[[ + +Copyright 2018 Chrisgbk +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +]] +-- MIT License, https://opensource.org/licenses/MIT + +-- Hotpatchable control.lua for scenarios +-- Supports multiple simultaneously loaded softmods +-- This is a WIP +-- Version 1.1 alpha +-- probably some performance improvements to be made + +_ENV.debug_settings = { + level = 'info', + log_to_console_only = false, + log_to_RCON = false, + log_on_tick = false, +} + +local hotpatch_tools = require 'hotpatch.mod-tools' +require 'hotpatch.remote-interface' +require 'hotpatch.commands' +require 'hotpatch.gui' + +-- mod code goes here + +-- This is the Factorio Freeplay scenario +-- This code, and the freeplay locale files in the zip file, is the property of Wube +-- As this is part of the base mod, the developers have given permission for modders to use/adapt the assets therein, for modding Factorio +-- Any other use would require licensing/permission from Wube +-- If you don't want to run this, either comment this out before creating your scenario, OR uninstall freeplay at runtime +hotpatch_tools.static_mod('freeplay', '1.0.0', [===[ +local silo_script = require("silo-script") +local version = 1 + +script.on_event(defines.events.on_player_created, function(event) + local player = game.players[event.player_index] + player.insert{name="iron-plate", count=8} + player.insert{name="pistol", count=1} + player.insert{name="firearm-magazine", count=10} + player.insert{name="burner-mining-drill", count = 1} + player.insert{name="stone-furnace", count = 1} + player.force.chart(player.surface, {{player.position.x - 200, player.position.y - 200}, {player.position.x + 200, player.position.y + 200}}) + if (#game.players <= 1) then + game.show_message_dialog{text = {"msg-intro"}} + else + player.print({"msg-intro"}) + end + silo_script.on_player_created(event) +end) + +script.on_event(defines.events.on_player_respawned, function(event) + local player = game.players[event.player_index] + player.insert{name="pistol", count=1} + player.insert{name="firearm-magazine", count=10} +end) + +script.on_event(defines.events.on_gui_click, function(event) + silo_script.on_gui_click(event) +end) + +script.on_init(function() + global.version = version + silo_script.on_init() +end) + +script.on_event(defines.events.on_rocket_launched, function(event) + silo_script.on_rocket_launched(event) +end) + +script.on_configuration_changed(function(event) + if global.version ~= version then + global.version = version + end + silo_script.on_configuration_changed(event) +end) + +silo_script.add_remote_interface() +silo_script.add_commands() +]===]) + +-- Multi-file test +--[[ +local files = {} + +files['testfolder.anothertest.test'] = [===[ + script.on_event(defines.events.on_player_changed_position, function(e) game.print('position changed') end) +]===] + +files['testfolder.test'] = [===[ + require 'anothertest.test' +]===] + +files['test'] = [===[ + require 'testfolder.test' +]===] + +hotpatch_tools.static_mod('require-test', '1.0.0', [===[ + require 'test' +]===], files) +--]] + +-- end of mod code \ No newline at end of file diff --git a/lib/scenarios/Hotpatch/hotpatch/commands.lua b/lib/scenarios/Hotpatch/hotpatch/commands.lua new file mode 100644 index 00000000..43ffbc80 --- /dev/null +++ b/lib/scenarios/Hotpatch/hotpatch/commands.lua @@ -0,0 +1,135 @@ +local hotpatch_tools = require 'hotpatch.mod-tools' +hotpatch_tools.static_mod('hotpatch-commands', '1.0.1', [===[ +--[[ + +Copyright 2018 Chrisgbk +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +]] +-- MIT License, https://opensource.org/licenses/MIT + +local hotpatch_tools = require 'hotpatch.mod-tools' + +--load private API +-- mod installation/uninstallation support functions +-- These take a mod NAME as a first argument +local install_mod = hotpatch_tools.install_mod +local find_installed_mod = hotpatch_tools.find_installed_mod +local install_mod_file = hotpatch_tools.install_mod_file +local uninstall_mod = hotpatch_tools.uninstall_mod + +-- mod interaction functions +-- These take a LOADED INDEX as a first argument, except load_mod, which takes an INSTALLED INDEX +local load_mod = hotpatch_tools.load_mod +local find_loaded_mod = hotpatch_tools.find_loaded_mod +local run_mod = hotpatch_tools.run_mod +local reset_mod = hotpatch_tools.reset_mod +local reset_mod_events = hotpatch_tools.reset_mod_events +local register_mod_events = hotpatch_tools.register_mod_events +local unload_mod = hotpatch_tools.unload_mod + +-- internal callbacks when a mod registers events +local register_event = hotpatch_tools.register_event +local register_nth_tick = hotpatch_tools.register_nth_tick +local register_on_tick = hotpatch_tools.register_on_tick + +-- mod bootstrap functions +-- These take a LOADED INDEX as a first argument +local mod_on_init = hotpatch_tools.mod_on_init +local mod_on_load = hotpatch_tools.mod_on_load +local mod_on_configuration_changed = hotpatch_tools.mod_on_configuration_changed + +local static_mods = hotpatch_tools.static_mods +local console = hotpatch_tools.console +local debug_log = hotpatch_tools.debug_log +local loaded_mods = hotpatch_tools.loaded_mods +local installed_mods = hotpatch_tools.installed_mods + +local help_commands = { + help = '/hotpatch help\nYou got this far, you probably know all you need to know', + list = '/hotpatch list [type]\nLists \'installed\', \'loaded\' or \'running\' mods', +} + +local sub_commands +sub_commands = { + help = function(player_index, param) + local caller = (player_index and game.players[player_index]) or console + if (not param) or (param == '') then + caller.print('Usage: /hotpatch [command] [parameters]') + caller.print('Available commands:') + for k, v in pairs(sub_commands) do caller.print(k) end + caller.print('Use /hotpatch help [command] for help on a particular command') + else + local text = help_commands[param] + if text then + caller.print(text) + end + end + end, + list = function(player_index, param) + local caller = (player_index and game.players[player_index]) or console + if not param or (param == '') then + caller.print('Installed mods:') + for k, v in pairs(installed_mods) do + caller.print(table.concat{'[', k, '] ', v.name, ' ', v.version}) + end + caller.print('Loaded/Running mods:') + for k, v in pairs(loaded_mods) do + caller.print(table.concat{'[', k, '] ', v.name, ' ', v.version, ' ', (v.loaded and 'loaded') or 'not loaded', ' ', (v.running and 'running') or 'not running'}) + end + else + + end + end, +} + +local admin_commands = { + reinstall = function(player_index, param) + local caller = (player_index and game.players[player_index]) or console + local mod + local installed_index + local loaded_index + for i = 1, #static_mods do + mod = static_mods[i] + if mod.name:match('^hotpatch%-.*$') then + loaded_index = find_loaded_mod(mod.name) + if loaded_index then + unload_mod(loaded_index) + end + install_mod(mod.name, mod.version, mod.code, mod.files) + installed_index = find_installed_mod(mod.name) + load_mod(installed_index) + loaded_index = find_loaded_mod(mod.name) + run_mod(loaded_index) + mod_on_init(loaded_index) + end + end + end, +} + +_ENV.commands.add_command('hotpatch', 'Commands for hotpatch. Run /hotpatch help for details.', function(e) + local caller = (e.player_index and game.players[e.player_index]) or console + if (not e.parameter) or (e.parameter == '') then + sub_commands['help'](e.player_index) + return + end + local sub_command, rest = e.parameter:match('(.-)%s(.*)') + if not sub_command then + sub_command = e.parameter + end + + local f = sub_commands[sub_command] + if f then f(e.player_index, rest) end + if caller.admin then + f = admin_commands[sub_command] + if f then f(e.player_index, rest) end + end +end) + +]===]) + +return true \ No newline at end of file diff --git a/lib/scenarios/Hotpatch/hotpatch/gui.lua b/lib/scenarios/Hotpatch/hotpatch/gui.lua new file mode 100644 index 00000000..06acdbb1 --- /dev/null +++ b/lib/scenarios/Hotpatch/hotpatch/gui.lua @@ -0,0 +1,375 @@ +local hotpatch_tools = require 'hotpatch.mod-tools' +hotpatch_tools.static_mod('hotpatch-gui', '1.0.4', [===[ +--[[ + +Copyright 2018 Chrisgbk +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +]] +-- MIT License, https://opensource.org/licenses/MIT + +local hotpatch_tools = require 'hotpatch.mod-tools' +local mod_gui = require 'mod-gui' + +--load private API +-- mod installation/uninstallation support functions +-- These take a mod NAME as a first argument +local install_mod = hotpatch_tools.install_mod +local find_installed_mod = hotpatch_tools.find_installed_mod +local install_mod_file = hotpatch_tools.install_mod_file +local uninstall_mod = hotpatch_tools.uninstall_mod + +-- mod interaction functions +-- These take a LOADED INDEX as a first argument, except load_mod, which takes an INSTALLED INDEX +local load_mod = hotpatch_tools.load_mod +local find_loaded_mod = hotpatch_tools.find_loaded_mod +local run_mod = hotpatch_tools.run_mod +local reset_mod = hotpatch_tools.reset_mod +local reset_mod_events = hotpatch_tools.reset_mod_events +local register_mod_events = hotpatch_tools.register_mod_events +local unload_mod = hotpatch_tools.unload_mod + +-- internal callbacks when a mod registers events +local register_event = hotpatch_tools.register_event +local register_nth_tick = hotpatch_tools.register_nth_tick +local register_on_tick = hotpatch_tools.register_on_tick + +-- mod bootstrap functions +-- These take a LOADED INDEX as a first argument +local mod_on_init = hotpatch_tools.mod_on_init +local mod_on_load = hotpatch_tools.mod_on_load +local mod_on_configuration_changed = hotpatch_tools.mod_on_configuration_changed + +local static_mods = hotpatch_tools.static_mods +local console = hotpatch_tools.console +local debug_log = hotpatch_tools.debug_log +local loaded_mods = hotpatch_tools.loaded_mods +local installed_mods = hotpatch_tools.installed_mods + +script.on_event(defines.events.on_player_joined_game, function(e) + local player = game.players[e.player_index] + local top = mod_gui.get_button_flow(player) + local left = mod_gui.get_frame_flow(player) + local center = player.gui.center + + local button = top['hotpatch-button'] + if button then + button.destroy() + end + local menu = left['hotpatch-menu'] + if menu then + menu.destroy() + end + + local IDE = center['hotpatch-IDE'] + if IDE then + IDE.destroy() + end + + local main = center['hotpatch-main'] + if main then + main.destroy() + end + + button = top.add{type = 'sprite-button', name = 'hotpatch-button', sprite='utility/heat_exchange_indication', tooltip = 'Hotpatch', style = mod_gui.button_style} +end) + +local on_gui_click_handlers +local on_gui_selection_state_changed_handlers + +on_gui_click_handlers = { + ['hotpatch-button'] = function(e) + --on_gui_click_handlers['hotpatch-menu.IDE'](e) + --do return end + local player = game.players[e.player_index] + local top = mod_gui.get_button_flow(player) + local left = mod_gui.get_frame_flow(player) + local center = player.gui.center + + local menu = left['hotpatch-menu'] + if not menu then + menu = left.add{type = 'frame', name = 'hotpatch-menu', direction = 'vertical'} + menu.add{type = 'button', name = 'hotpatch-menu.IDE', caption = 'Mod IDE', tooltip = 'Open debugging GUI'} + menu.add{type = 'button', name = 'hotpatch-menu.console', caption = 'Mod Console', tooltip = 'Open debugging console'} + menu.style.visible = true + return + end + menu.style.visible = not menu.style.visible + end, + ['hotpatch-menu'] = function(e) + local player = game.players[e.player_index] + local top = mod_gui.get_button_flow(player) + local left = mod_gui.get_frame_flow(player) + local center = player.gui.center + + local menu = left['hotpatch-menu'] + menu.style.visible = not menu.style.visible + + on_gui_click_handlers[e.element.name](e) + end, + ['hotpatch-menu.IDE'] = function(e) + local player = game.players[e.player_index] + local top = mod_gui.get_button_flow(player) + local left = mod_gui.get_frame_flow(player) + local center = player.gui.center + + local IDE = center['hotpatch-IDE'] + if not IDE then + IDE = center.add{type = 'frame', name = 'hotpatch-IDE', direction = 'vertical', caption = 'Hotpatch IDE', style = mod_gui.frame_style} + local top_flow = IDE.add{type = 'flow', name = 'hotpatch-IDE-top', direction = 'horizontal'} + top_flow.add{type = 'label', name = 'hotpatch-IDE-mod-label', caption = 'Mod: '} + local IDE_dropdown = top_flow.add{type = 'drop-down', name = 'hotpatch-IDE-mod-selector'} + top_flow.add{type = 'label', name = 'hotpatch-IDE-mod-version', caption = 'Version: No mod selected'} + local IDE_table = IDE.add{type = 'table', name = 'hotpatch-IDE-table', column_count = 2} + local files = IDE_table.add{type = 'scroll-pane', name = 'hotpatch-IDE-files', direction='vertical'} + files.style.height = 600 + files.vertical_scroll_policy = 'always' + files = files.add{type = 'table', name = 'hotpatch-IDE-files-table', column_count = 1} + local code = IDE_table.add{type = 'text-box', name = 'hotpatch-IDE-code'} + code.word_wrap = true + files.style.width = 400 + files.style.cell_spacing = 0 + files.style.top_padding = 0 + files.style.bottom_padding = 0 + code.style.width = 600 + code.style.height = 600 + IDE.style.visible = false + end + + local IDE_dropdown = IDE['hotpatch-IDE-top']['hotpatch-IDE-mod-selector'] + IDE_dropdown.clear_items() + for k, v in ipairs(installed_mods) do + IDE_dropdown.add_item(v.name) + end + + IDE.style.visible = not IDE.style.visible + if IDE.style.visible then + player.opened = IDE + else + player.opened = nil + end + end, + ['hotpatch-menu.console'] = function(e) + local player = game.players[e.player_index] + local top = mod_gui.get_button_flow(player) + local left = mod_gui.get_frame_flow(player) + local center = player.gui.center + + local console = center['hotpatch-console'] + if not console then + console = center.add{type = 'frame', name = 'hotpatch-console', direction = 'vertical', caption = 'Hotpatch Console'} + local top_flow = console.add{type = 'flow', name = 'hotpatch-console-top', direction = 'horizontal'} + top_flow.add{type = 'label', name = 'hotpatch-console-mod-label', caption = 'Mod: '} + local console_dropdown = top_flow.add{type = 'drop-down', name = 'hotpatch-console-mod-selector'} + top_flow.add{type = 'label', name = 'hotpatch-console-mod-version', caption = 'Version: No mod selected'} + local bottom_flow = console.add{type = 'flow', name = 'hotpatch-console-bottom', direction = 'vertical'} + local output = bottom_flow.add{type = 'text-box', name = 'hotpatch-console-output'} + local bottom_input_flow = bottom_flow.add{type = 'flow', name = 'hotpatch-console-bottom-input', direction = 'horizontal'} + local input = bottom_input_flow.add{type = 'textfield', name = 'hotpatch-console-input'} + bottom_input_flow.add{type = 'button', name = 'hotpatch-console-run', caption = 'Run', tooltip = 'Run Lua Code'} + output.style.width = 800 + output.style.height = 600 + input.style.width = 600 + end + + local console_dropdown = console['hotpatch-console-top']['hotpatch-console-mod-selector'] + console_dropdown.clear_items() + for k, v in ipairs(loaded_mods) do + console_dropdown.add_item(v.name) + end + + console.style.visible = not console.style.visible + if console.style.visible then + player.opened = console + else + player.opened = nil + end + end, + ['hotpatch-console-run'] = function(e) + local player = game.players[e.player_index] + + local top = mod_gui.get_button_flow(player) + local left = mod_gui.get_frame_flow(player) + local center = player.gui.center + + local console = center['hotpatch-console'] + local output = console['hotpatch-console-bottom']['hotpatch-console-output'] + local input = console['hotpatch-console-bottom']['hotpatch-console-bottom-input']['hotpatch-console-input'] + local console_dropdown = console['hotpatch-console-top']['hotpatch-console-mod-selector'] + + if not player.admin then + output.text = table.concat{output.text, '\n', 'Only admins!'} + return + end + + if not (console_dropdown.selected_index > 0) then + output.text = table.concat{output.text, '\n', 'Select a mod!'} + return + end + + local mod_name = console_dropdown.items[console_dropdown.selected_index] + local loaded_index = find_loaded_mod(mod_name) + local env = loaded_mods[loaded_index].env + local old_print = env.print + local old_log = env.log + + + + + + env.print = function(...) + --local count = select('#', ...) + local t = table.pack(...) + for k, v in pairs(t) do + t[k] = tostring(v) + end + local text = table.concat(t, '\t') + output.text = table.concat{output.text, '\n', text} + end + + env.log = function(text) + output.text = table.concat{output.text, '\n', text} + end + + local code, err = load(input.text, input.text, 't', env) + if code then + local success, err = pcall(code) + if not success then + output.text = table.concat{output.text, '\n', err} + end + else + output.text = table.concat{output.text, '\n', err} + end + + env.print = old_print + env.log = old_log + end, + ['hotpatch-IDE-file'] = function(e) + local player = game.players[e.player_index] + local top = mod_gui.get_button_flow(player) + local left = mod_gui.get_frame_flow(player) + local center = player.gui.center + + local element = e.element + local previous + for k, v in pairs(center['hotpatch-IDE']['hotpatch-IDE-table']['hotpatch-IDE-files']['hotpatch-IDE-files-table'].children) do + if table.compare(v.style.font_color or {}, {r=1.0, g=1.0, b=0.0, a=1.0}) then + previous = v + break + end + end + if previous then + previous.style.font_color = {r=1.0, g=1.0, b=1.0, a=1.0} + end + element.style.font_color = {r=1.0, g=1.0, b=0.0, a=1.0} + local file = element.name:match('.-%.(.*)') + local selected = center['hotpatch-IDE']['hotpatch-IDE-top']['hotpatch-IDE-mod-selector'] + local mod_name = selected.items[selected.selected_index] + local code = center['hotpatch-IDE']['hotpatch-IDE-table']['hotpatch-IDE-code'] + local index = find_installed_mod(mod_name) + if index then + local mod = installed_mods[index] + if file == 'control' then + code.text = mod.code + else + code.text = mod.files[file] + end + end + end, +} + +script.on_event(defines.events.on_gui_click, function(e) + local element = e.element + if element.valid then + local name = element.name:match('([^%.]*)%.?.-') + local handler = on_gui_click_handlers[name] + if handler then handler(e) end + end +end) + +on_gui_selection_state_changed_handlers = { + ['hotpatch-console-mod-selector'] = function(e) + local player = game.players[e.player_index] + local top = mod_gui.get_button_flow(player) + local left = mod_gui.get_frame_flow(player) + local center = player.gui.center + + local element = e.element + local name = element.items[element.selected_index] + if name then + local index = find_loaded_mod(name) + if index then + local mod = loaded_mods[index] + local version_label = center['hotpatch-console']['hotpatch-console-top']['hotpatch-console-mod-version'] + version_label.caption = 'Version: ' .. mod.version + local console = center['hotpatch-console'] + local output = console['hotpatch-console-bottom']['hotpatch-console-output'] + output.text = '' + end + end + end, + ['hotpatch-IDE-mod-selector'] = function(e) + local player = game.players[e.player_index] + local top = mod_gui.get_button_flow(player) + local left = mod_gui.get_frame_flow(player) + local center = player.gui.center + + local element = e.element + + local name = element.items[element.selected_index] + if name then + local index = find_installed_mod(name) + if index then + local mod = installed_mods[index] + local version_label = center['hotpatch-IDE']['hotpatch-IDE-top']['hotpatch-IDE-mod-version'] + version_label.caption = 'Version: ' .. mod.version + local list = center['hotpatch-IDE']['hotpatch-IDE-table']['hotpatch-IDE-files']['hotpatch-IDE-files-table'] + list.clear() + local file = list.add{type = 'label', style = 'hoverable_bold_label', caption = 'control', name = 'hotpatch-IDE-file.control'} + file.style.bottom_padding = 0 + file.style.top_padding = 0 + for k, v in pairs(mod.files) do + file = list.add{type = 'label', style = 'hoverable_bold_label', caption = k, name = 'hotpatch-IDE-file.' .. k} + file.style.bottom_padding = 0 + file.style.top_padding = 0 + end + local code = center['hotpatch-IDE']['hotpatch-IDE-table']['hotpatch-IDE-code'] + code.text = '' + end + end + end, +} + +script.on_event(defines.events.on_gui_selection_state_changed, function(e) + local element = e.element + local handler = on_gui_selection_state_changed_handlers[element.name] + if handler then handler(e) end +end) + +script.on_event(defines.events.on_gui_closed, function(e) + local player = game.players[e.player_index] + local element = e.element + if e.gui_type == defines.gui_type.custom then + if element and element.valid then + if element.name == 'hotpatch-IDE' then + if element.style.visible then + player.play_sound{path = "utility/gui_click"} + end + element.style.visible = false + elseif element.name == 'hotpatch-console' then + if element.style.visible then + player.play_sound{path = "utility/gui_click"} + end + element.style.visible = false + end + end + end +end) + +]===]) + +return true \ No newline at end of file diff --git a/lib/scenarios/Hotpatch/hotpatch/mod-tools.lua b/lib/scenarios/Hotpatch/hotpatch/mod-tools.lua new file mode 100644 index 00000000..c75d77ad --- /dev/null +++ b/lib/scenarios/Hotpatch/hotpatch/mod-tools.lua @@ -0,0 +1,1281 @@ +--[[ + +Copyright 2018 Chrisgbk +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +]] +-- MIT License, https://opensource.org/licenses/MIT + +-- Hotpatch-MultiMod: a tool to load multiple scenarios side-by-side, with support for both static loading and dynamic loading, as well as run-time patching + +log('Hotpatch runtime initializing...') +local util = require 'util' + +local debug_levels = {['disabled'] = -1, ['severe'] = 1, ['error'] = 2, ['warning'] = 3, ['info'] = 4, ['verbose'] = 5, ['trace'] = 6} + +-- configuration options; these should be exposed via API one day +local debug_level = (tonumber(_ENV.debug_settings.level) or debug_levels[_ENV.debug_settings.level]) or 0 +local debug_log_to_console_only = _ENV.debug_settings.log_to_console_only +local debug_log_to_RCON = _ENV.debug_settings.log_to_RCON --only affects when log_to_console_only is in effect +local debug_log_on_tick = _ENV.debug_settings.log_on_tick + +local compat_mode = false -- enable some compatibility settings, which can help some mods load +local strict_mode = false -- causes hotpatch to hard-stop on mods doing bad things + +-- convenience object(rcon.print also prints to stdout when called from server console) +local console = {name = 'Console', admin = true, print = function(...) rcon.print(...) end, color = {1,1,1,1}} + +-- these represent the mods as statically packaged with the scenario +local static_mods = {} +-- static_mods[i] = {} +-- static_mods[i].name = '' +-- static_mods[i].version = '' +-- static_mods[i].code = '' +-- static_mods[i].files = {} + +-- installed mods are represented with: +-- global.mods = {} +-- global.mods[i] = {} +-- global.mods[i].name = '' +-- global.mods[i].version = '' +-- global.mods[i].code = '' +-- global.mods[i].files = {} +-- global.mods[i].global = {} + +-- loaded/running mods are represented with: +local loaded_mods = {} -- this holds a reference to the mods internal object +-- loaded_mods[i] = mod_obj_template + +-- internal mod object: +local mod_obj_template = { + name = '', -- name of the current mod + version = '', + env = {}, -- environment of the mod + loaded = false, + running = false, + on_init = nil, -- called once after first install, or when specifically requested to be re-ran (good practice is to never request a re-run) + on_load = nil, -- called every time the scenario loads from disk + on_configuration_changed = nil, -- called every time the external mod configuration changes, OR when the hotpatch mod configuration changes + on_tick = nil, -- cache the on-tick event handler, because it is ID 0, which causes it to be stored in the hash part, which causes a 50% increase in access time + on_event = {}, -- list of on_event handlers registered + on_nth_tick = {}, -- list of on_nth_tick handlers registered + loaded_files = {}, -- list of files loaded by require() that were installed into the virtual file system +} + +-- this is for the core factorio libraries; hotpatch MUST load these now, as they cannot be dynamically loaded due to factorio limitation +local loaded_libraries = {} + +-- mod installation/uninstallation support functions +-- These take a mod NAME as a first argument +local install_mod +local find_installed_mod +local install_mod_file +local uninstall_mod + +-- mod interaction functions +-- These take a LOADED INDEX as a first argument, except load_mod, which takes an INSTALLED INDEX +local load_mod +local find_loaded_mod +local run_mod +local reset_mod +local reset_mod_events +local register_mod_events +local unload_mod + +-- internal callbacks when a mod registers events +local register_event +local register_nth_tick +local register_on_tick + +-- mod bootstrap functions +-- These take a LOADED INDEX as a first argument +local mod_on_init +local mod_on_load +local mod_on_configuration_changed + +-- local event handler to proxy to mods, these call the mods events +local on_event +local on_nth_tick + +-- core handlers +local on_init -- installs static mods, loads, runs, and calls mod_on_init +local on_load -- loads, runs, and calls mod_on_load +local on_configuration_changed -- calls mod_on_configuration_changed + +-- this is dual-purpose, handles core needs and mod on_tick +local on_tick + +-- this installs a new mod statically, only installed during on_init +-- Installed mod will still be updatable +local function static_mod(name, version, code, files) + local mod = {} + mod.name = name + mod.version = version + mod.code = code + mod.files = {} + if files then + for k, v in pairs(files) do + mod.files[k] = v + end + end + table.insert(static_mods, mod) +end + +-- mapping of events to names for logging +local event_names = {} +for k,v in pairs(defines.events) do + event_names[v] = k +end + +-- This scenario contains an embedded locale, but it's unavailable until this file finishes loading +-- ie: calls to log{...} will fail right now, but will work in events +-- Rseding91 fixed it for 0.17 (https://forums.factorio.com/viewtopic.php?f=23&t=60767) +-- I'll make it work myself in 0.16 ¯\_(ツ)_/¯ +local static_cfg = [[ +[hotpatch] +log=@__1__:__2__] __3__ +log-mod=@__1__:__2__][__4__] __3__ +severe=SEVERE ERROR: __1__ +error=ERROR: __1__ +warning=WARNING: __1__ +info=INFO: __1__ +verbose=INFO: __1__ +trace=TRACE: __1__ + +[hotpatch-info] +logging-enabled=Logging enabled +metatable-installed=_ENV metatable installed +complete=__1__ Complete! +on-init=initializing... +on-load=loading... +on-configuration-changed=mod configuration changed... +installing-included-mods=installing included mods... +loading-installed-mods=loading installed mods... +loading-libs=loading Factorio.data.core.lualib... +loading-library=loading library: __1__ +uninstalling=Uninstalling mod... +installing=Installing version __1__... +installing-file=Installing file __1__... +script-shim=setting up mod script shim... +setting-env=setting up mod _ENV... +loading=loading... +unloading=unloading... +running=running... +must-be-admin=You must be an admin to run this command +remote-installing=installing remote interface + +[hotpatch-trace] +nil-var-access=_ENV nil variable access: __1__ +nil-var-assignment=_ENV variable assignment: __1__ +event-registering=registering events... +on-tick-event-registered=registered on_tick event... +on-event-registered=registered event __1__... +on-nth-tick-event-registered=registered nth_tick event __1__... +event-running=running event __1__... +nth-tick-event-running=running nth_event __1__... +mod-on-init=running on_init... +mod-on-load=running on_load... +mod-on-configuration-changed=running on_configuration_changed... +adding-event=adding event __1__ +adding-nth-tick-event=adding nth_tick event __1__ +caching-event=caching event: __1__... +caching-nth-tick-event=caching nth_tick event: __1__... +event-processing=processing event __1__... +nth-tick-event-processing=processing nth_tick event __1__... +nth-tick-handler-added=added nth_tick handler: __1__ +nth-tick-handler-removed=removed nth_tick handler: __1__ +on-tick-handler-added=added on_tick handler +on-tick-handler-removed=removed on_tick handler +on-event-handler-added=added event handler: __1__ +on-event-handler-removed=removed event handler: __1__ +cached-load-require=loading cached require'd file: __1__... +load-require=loading require'd file: __1__... +load-core-lib=loading from Factorio.data.core.lualib: __1__... + +[hotpatch-warning] +contains-comments=mod code contains comments! +contains-comments-no-lf=mod contains comments and no linefeed! +contains-comments-console=comments from console will comment out the entire code! +reset-events-not-running=tried to reset events for mod that isn't running! +remote-interface-exists=remote interface __1__ already exists, removing... +command-exists=command __1__ already exists, removing... +already-loaded=mod already loaded +reinitializing=reinitializing... +already-exists=mod already exists +reinstalling=reinstalling mod in-place + +[hotpatch-error] +invalid-API-access=Invalid API access: __1__ +not-installed=mod not installed, cannot install file for mod that does not exist +compilation-failed=compilation failed for mod +execution-failed=execution failed for mod + +[test-pluralization] +test=__1:(^1$)=singular;(^[2-9]$)=plural single digit;([1-2][0-9]$)=plural ends with double digit <30;(.*)=fallback case%; plural with embedded %;;__ +]] + +local function build_locale(ini) + local t = {} + local section = '' + local temp + + -- line must end with a linefeed - single line file with no LF will fail + if not ini:match('[\r\n]+$') then + ini = ini .. '\n' + end + + -- for each non-empty line do + local key, value + for l in ini:gmatch('[\r\n]*(.-)[\r\n]+') do + -- header? + temp = l:match('^%[(.-)%].*$') + if temp then + section = temp + temp = nil + else + --key=value + key, value = l:match('^(.-)=(.+)$') + t[table.concat{section, '.', key}] = value + end + end + return t +end + +local static_locale = build_locale(static_cfg) + +local function escape(s) + return (s:gsub('([%^%$%(%)%%%.%[%]%*%+%-%?])', '%%%1')) +end + +local function unescape(s) + return (s:gsub('(%%)', '')) +end + +local function static_translate(t, recursive) + -- only translate tables + if type(t) ~= 'table' then return t end + -- only translate tables that have a string as the first item + local k = t[1] + if type(k) ~= 'string' then return t end + + -- make a copy, don't destroy the original table, after we copy we can translate in place + if not recursive then + t = table.deepcopy(t) + end + -- translate any arguments as well + local v + for i = 2, #t do + v = t[i] + if type(v) == 'table' then + t[i] = static_translate(v, true) + end + end + -- special case: whitespace token causes concatenation with that token + -- slightly better than factorio, where only '' is supported + if k:find('^%s*$') then table.remove(t, 1) return table.concat(t, k) end + local pattern = static_locale[k] + -- if not translatable return normal table ref; factorio does following instead: + -- if not pattern then return 'Unknown key: ' .. k end + -- by returning the table we pass off to factorio runtime translation, where available + if not pattern then return t end + -- substitution of parameters: use literal value of parameter n + -- __n__ + local result = (pattern:gsub('__(%d+)__', function(s) return tostring(t[tonumber(s)+1]) end)) + + -- re-substitution engine: match value of parameter n to provide additional translation; use for pluralization + -- __n:(pattern-1)=substitution-1;(pattern-2)=substitution-2;...(pattern-i)=substitution-i;__ + for n, p in result:gmatch('__(%d+)(:.-;)__') do + for x, y in p:gmatch('%((.-)%)=(.-[^%%]);') do + if t[tonumber(n)+1]:match(x) then + result = result:gsub(table.concat{'__', n, escape(p), '__'}, unescape(y)) + break + end + end + end + + return result +end + +-- override print to make it support our translation efforts +-- still doesn't support Factorio locales, because devs don't patch it like I do +-- this means unknown keys will be printed as 'table: 0x...' +local real_print = print +local print = function(...) + if select('#', ...) == 1 then + real_print(static_translate(...)) + else + local t = table.pack(...) + table.insert(t, 1, '\t') + real_print(static_translate(t)) + end +end + +-- override log to make it support our translation efforts +-- any unknown keys are passed to Factorio to translate + +local hidden = load([===[ + local real_log = log; local static_translate = select(1, ...); local log = function(...); if select('#', ...) == 1 then real_log(static_translate(...)) else local t = table.pack(...); table.insert(t, 1, '\t'); real_log(static_translate(t)) end end return log +]===], '[HOTPATCH') +local hidden_log = hidden(static_translate) + +-- logs localized data +local function debug_log(message, mod_name, stack_level) + if debug_level > -1 then + if not stack_level then stack_level = 2 end + local di = debug.getinfo(stack_level) + local line = di.currentline + local file = (di.source:gsub('%@.*/', '')) + local class = 'hotpatch.info' + local log_type = (mod_name and 'hotpatch.log-mod') or 'hotpatch.log' + local severity + if type(message) == 'table' then + severity = message[1]:match('.-%-([^%-]*)%.') + if not severity then + severity = message[1]:match('.-%-.-%-(.*)%.') + if not severity then + severity = 'always' + end + end + class = table.concat{'hotpatch', '.', severity} + else + severity = 'always' + end + local level = ((severity == 'always') and 0) or debug_levels[severity] + if debug_level >= level then + if debug_log_to_console_only then + if debug_log_to_RCON then + rcon.print{log_type, file, line, {class, message}, mod_name} + end + print{log_type, file, line, {class, message}, mod_name} + + else + hidden_log{log_type, file, line, {class, message}, mod_name} + end + end + end +end + +debug_log({'hotpatch-info.logging-enabled'}) + +-- track _ENV accesses +setmetatable(_ENV, { + __index = function(t, k) + debug_log({'hotpatch-trace.nil-var-access', k}, nil, 3) + return nil + end, + __newindex = function(t, k, v) + debug_log({'hotpatch-trace.nil-var-assignment', k}, nil, 3) + rawset(t,k,v) + end, + __metatable = false +}) + +debug_log({'hotpatch-info.metatable-installed'}) + +-- load all possible libraries +debug_log({'hotpatch-info.loading-libs'}) + +local libraries = { + 'camera', + 'flying_tags', + 'inspect', + 'math3d', + 'mod-gui', + 'noise', + 'production-score', + 'silo-script', + 'story', + 'util', +} + +for k, v in pairs(libraries) do + debug_log({'hotpatch-info.loading-library', v}) + loaded_libraries[k] = require(v) +end + +debug_log({'hotpatch-info.complete', {'hotpatch-info.loading-libs'}}) + +find_installed_mod = function(mod_name) + local mod + local mods = global.mods + for i = 1, #mods do + mod = mods[i] + if mod.name == mod_name then + return i + end + end + return nil +end + +find_loaded_mod = function(mod_name) + local mod + for i = 1, #loaded_mods do + mod = loaded_mods[i] + if mod.name == mod_name then + return i + end + end + return nil +end + +uninstall_mod = function(mod_name) + local index = find_installed_mod(mod_name) + if not index then + -- TODO: notify that mod doesn't exist + return + end + local loaded_index = find_loaded_mod(mod_name) + + if loaded_index then + unload_mod(loaded_index) + end + + + debug_log({'hotpatch-info.uninstalling', mod_name}) + + table.remove(global.mods, index) +end + +install_mod_file = function(mod_name, mod_file, mod_file_code) + local index = find_installed_mod(mod_name) + if not index then + debug_log({'hotpatch-error.not-installed'}) + return + end + local mod = global.mods[index] + + debug_log({'hotpatch-info.installing-file', mod_file}, mod_name) + + mod_file = mod_file:gsub('/', '.') + mod_file = mod_file:gsub('\\', '.') + mod_file_code = mod_file_code:gsub('\t', ' ') + mod.files[mod_file] = mod_file_code +end + +install_mod = function(mod_name, mod_version, mod_code, mod_files) + local index = find_installed_mod(mod_name) + local mod = {} + if index then + -- TODO: notify about installing over top of existing mod + mod = global.mods[index] + else + --next free index + table.insert(global.mods, mod) + end + debug_log({'hotpatch-info.installing', mod_version}, mod_name) + if mod_code:find('--', 1, true) then + + debug_log({'hotpatch-warning.contains-comments'}, mod_name) + if not mod_code:find("\n", 1, true) then + debug_log({'hotpatch-warning.contains-comments-no-lf'}, mod_name) + end + debug_log({'hotpatch-warning.contains-comments-console'}, mod_name) + end + + mod_code = mod_code:gsub('\t', ' ') + + mod.name = mod_name + mod.files = mod.files or {} + mod.code = mod_code + mod.version = mod_version + mod.global = mod.global or {} + + if mod_files then + for k,v in pairs(mod_files) do + install_mod_file(mod_name, k, v) + end + end +end + +load_mod = function(installed_index) + local mod = global.mods[installed_index] + local mod_name = mod.name + if mod.code then + -- TODO: integrity check, verify each mods global table to detect guaranteed desync mods + local loaded_index = find_loaded_mod(mod_name) + if loaded_index then + --TODO notify that mod was already loaded + unload_mod(loaded_index) + end + + local mod_obj = { + name = mod_name, -- name of the current mod + version = mod.version, + env = {}, -- environment of the mod + loaded = false, + running = false, + on_init = nil, -- called once after first install, or when specifically requested to be re-ran (good practice is to never request a re-run) + on_load = nil, -- called every time the scenario loads from disk + on_configuration_changed = nil, -- called every time the external mod configuration changes, OR when the hotpatch mod configuration changes + on_tick = nil, -- cache the on-tick event handler, because it is ID 0, which causes it to be stored in the hash part, which causes a 50% increase in access time + on_event = {}, -- list of on_event handlers registered + on_nth_tick = {}, -- list of on_nth_tick handlers registered + loaded_files = {}, -- list of files loaded by require() that were installed into the virtual file system + } + + debug_log({'hotpatch-info.script-shim'}, mod_name) + --mods private script table/shim + local mod_script = {} + + mod_script.on_init = function(f) + mod_obj.on_init = f + end + mod_script.on_load = function(f) + mod_obj.on_load = f + end + mod_script.on_configuration_changed = function(f) + mod_obj.on_configuration_changed = f + end + if not compat_mode then + mod_script.on_event = function(event, f) + if event == defines.events.on_tick then + mod_obj.on_tick = f + else + mod_obj.on_event[event] = f + end + end + mod_script.on_nth_tick = function(tick, f) + if tick then + if type(tick) == 'table' then + for k, v in pairs(tick) do + mod_script.on_nth_tick(v, f) + end + return + end + mod_obj.on_nth_tick[tick] = f + else + mod_obj.on_nth_tick = {} + end + end + else + mod_script.on_event = function(event, f) + if event == defines.events.on_tick then + mod_obj.on_tick = f + if mod_obj.running then + register_on_tick(mod_name, event) + end + else + mod_obj.on_event[event] = f + if mod_obj.running then + register_event(mod_name, event) + end + end + end + mod_script.on_nth_tick = function(tick, f) + if tick then + if type(tick) == 'table' then + for k, v in pairs(tick) do + mod_script.on_nth_tick(v, f) + end + return + end + mod_obj.on_nth_tick[tick] = f + if mod_obj.running then + register_nth_tick(mod_name, tick) + end + else + local on_nth_tick = mod_obj.on_nth_tick + mod_obj.on_nth_tick = {} + if mod_obj.running then + for k, v in pairs(on_nth_tick) do + register_nth_tick(mod_name, tick) + end + end + end + end + end + mod_script.generate_event_name = function() + return script.generate_event_name() + end + mod_script.get_event_handler = function(event) + return mod_obj.on_event[event] + end + mod_script.raise_event = function(event, table) + script.raise_event(event, table) + end + --TODO: replace these with mod-provided versions, so multi-mod aware softmods can easily detect other loaded softmods + mod_script.get_event_order = function() + return script.get_event_order() + end + mod_script.mod_name = function() + return script.mod_name() + end + + debug_log({'hotpatch-info.setting-env'}, mod_name) + -- mods private env + local env = mod_obj.env + -- mods private package + local pack = {} + -- mods private package.loaded + local loaded = {} + -- copy the current environment + for k,v in pairs(_ENV) do + env[k] = v + end + -- copy package.loaded + for k,v in pairs(_ENV.package.loaded) do + loaded[k] = v + end + loaded._G = env + + -- so many ways to escape sandboxes... + + for k,v in pairs(_ENV.package) do + pack[k] = v + end + pack.loaded = loaded + env.package = pack + loaded.package = pack + + env.script = mod_script + env.global = mod.global + env._G = env + + env['remote'] = { + add_interface = function(name, functions) + if remote.interfaces[name] then + debug_log({'hotpatch-warning.remote-interface-exists', name}, mod_name) + remote.remove_interface(name) + end + remote.add_interface(name, functions) + end, + remove_interface = function(name) + return remote.remove_interface(name) + end, + call = function(...) + return remote.call(...) + end, + interfaces = setmetatable({}, { + __index = function(t, k) return remote.interfaces[k] end, + __pairs = function(t) local function iter(t, k) local v; k, v = next(remote.interfaces, k); if v then return k, t[k] end; end; return iter, t, nil end + }) + } + env['commands'] = { + add_command = function(name, help, func) + if commands.commands[name] then + debug_log({'hotpatch-warning.command-exists', name}, mod_name) + commands.remove_command(name) + end + commands.add_command(name, help, func) + end, + remove_command = function(name) + return commands.remove_command(name) + end, + commands = setmetatable({}, { + __index = function(t, k) return commands.commands[k] end, + __pairs = function(t) local function iter(t, k) local v; k, v = next(commands.commands, k); if v then return k, t[k] end; end; return iter, t, nil end + }), + game_commands = setmetatable({}, { + __index = function(t, k) return commands.game_commands[k] end, + __pairs = function(t) local function iter(t, k) local v; k, v = next(commands.game_commands, k); if v then return k, t[k] end; end; return iter, t, nil end + }) + } + + + env.require = function(path) + -- I blame Nexela for this + path = path:gsub('/', '.') + path = path:gsub('\\', '.') + if env.package._current_path_in_package then + path = env.package._current_path_in_package .. path + end + if mod_obj.loaded_files[path] then + debug_log({'hotpatch-trace.cached-load-require', path}, mod_name) + return mod_obj.loaded_files[path] + else + local oldbase = env.package._current_path_in_package + env.package._current_path_in_package = path:match('.+%..+%.') + if not env.package._current_path_in_package then + env.package._current_path_in_package = path:match('.+%.') + end + local file = global.mods[installed_index].files[path] + if file then + debug_log({'hotpatch-trace.load-require', path}, mod_name) + local code, err = load(file, '[' .. mod_name .. '] ' .. path .. '.lua', 'bt', env) + if code then + local result = code() + mod_obj.loaded_files[path] = result or true + env.package._current_path_in_package = oldbase + return mod_obj.loaded_files[path] + else + debug_log(err, nil, 3) + error(err) + end + end + debug_log({'hotpatch-trace.load-core-lib', path}, mod_name) + env.package._current_path_in_package = oldbase + return package.loaded[path] + end + end + + env['load'] = function(l, s, m, e) + return load(l, s, m, e or env) + end + env['loadstring'] = env['load'] + + + env['game'] = setmetatable({}, { + __index = function(t, k) return game[k] end, + __pairs = function(t) local function iter(t, k) local v; k, v = next(game, k); if v then return k, t[k] end; end; return iter, t, nil end + }) + + local mt = {} + mt.__index = function(t, k) + debug_log({'hotpatch-trace.nil-var-access', k}, nil, 3) + return nil + end + mt.__newindex = function(t, k, v) + debug_log({'hotpatch-trace.nil-var-assignment', k}, nil, 3) + rawset(t,k,v) + end + -- Don't let mods break this + mt.__metatable = false + setmetatable(env, mt) + + --load/run code + debug_log({'hotpatch-info.loading'}, mod_name) + + local mod_code, message = load(mod.code, '[' .. mod_name .. '] control.lua', 'bt', env) + if not mod_code then + debug_log({'hotpatch-error.compilation-failed'}, mod_name) + if game and game.player then + game.player.print(message) + end + debug_log(message, mod_name) + return false + end + + mod_obj.code = mod_code + mod_obj.loaded = true + table.insert(loaded_mods, mod_obj) + return true + end + return false +end + +unload_mod = function(loaded_index) + local mod = loaded_mods[loaded_index] + if not mod then + --TODO notify that mod isn't loaded + return + end + local mod_name = mod.name + -- TODO + -- stop mod running, unregister handlers from being called + debug_log({'hotpatch-info.unloading'}, mod_name) + -- TODO: + mod.loaded = false + mod.running = false + table.remove(loaded_mods, loaded_index) +end + + +--TODO: pretty much all of this routine +-- This should unregister events and clear the globals +reset_mod = function(loaded_index) + local new_global = {} + local mod = loaded_mods[loaded_index] + mod.mod_env.global = new_global + local install_index = find_installed_mod(mod.name) + mod = global.mods[install_index] + mod.global = new_global + --local mod_global = global.mod_global[mod_name] + --for k, v in pairs(mod_global) do + -- mod_global[k] = nil + --end + + reset_mod_events(loaded_index) +end + +reset_mod_events = function(loaded_index) + local loaded_index = find_loaded_mod(mod_name) + + if not loaded_index then + debug_log({'hotpatch-warning.reset-events-not-running'}, mod_name) + else + local loaded_mod = loaded_mods[loaded_index] + loaded_mod.on_event = {} + loaded_mod.on_nth_tick = {} + loaded_mod.on_init = nil + loaded_mod.on_load = nil + loaded_mod.on_configuration_changed = nil + loaded_mod.on_tick = nil + register_all_events() + end +end + +run_mod = function(loaded_index) + local mod = loaded_mods[loaded_index] + if mod then + local mod_name = mod.name + local old_global = mod.env.global + if strict_mode then + mod.env.global = {} + end + debug_log({'hotpatch-info.running'}, mod_name) + + local success, result = xpcall(mod.code, debug.traceback) + if not success then + debug_log({'hotpatch-error.execution-failed'}, mod_name) + debug_log(result, mod_name) + --local caller = (game and game.player) or console + if game and game.player then + game.player.print(result) + end + unload_mod(loaded_index) + + return false + end + + + if strict_mode then + if mod.env.global ~= {} then + --TODO: error, mod touched global inappropriately during load + end + mod.env.global = old_global + end + + mod.running = true + debug_log({'hotpatch-info.complete', {'hotpatch-info.running'}}, mod_name) + + --load complete, start notifying on event subscriptions + if not compat_mode then + mod.env.script.on_event = function(event, f) + if event == defines.events.on_tick then + mod.on_tick = f + register_on_tick(mod_name, event) + else + mod.on_event[event] = f + register_event(mod_name, event) + end + end + mod.env.script.on_nth_tick = function(tick, f) + if tick then + if type(tick) == 'table' then + for k, v in pairs(tick) do + mod_script.on_nth_tick(v, f) + end + return + end + mod.on_nth_tick[tick] = f + register_nth_tick(mod_name, tick) + else + local on_nth_tick = mod.on_nth_tick + mod.on_nth_tick = {} + for k, v in pairs(on_nth_tick) do + register_nth_tick(mod_name, tick) + end + end + end + end + return true + end + return false +end + +-- Note: might be able to optimize this a bit +-- event handlers to call into mods requested event handlers +on_event = function(event) + local event_name = (event_names[event.name] or event.name) + local f + debug_log({'hotpatch-trace.event-processing', event_name}) + local mod + for i = 1, #loaded_mods do + mod = loaded_mods[i] + f = mod.on_event[event.name] + if f then + debug_log({'hotpatch-trace.event-running', event_name}, mod.name) + f(event) + end + end +end + +if debug_log_on_tick then + on_nth_tick = function(event) + local tick = event.nth_tick + local f + debug_log({'hotpatch-trace.nth-tick-event-processing', tick}) + local mod + for i = 1, #loaded_mods do + mod = loaded_mods[i] + f = mod.on_nth_tick[event.nth_tick] + if f then + debug_log({'hotpatch-trace.nth-tick-event-running', tick}, mod.name) + f(event) + end + end + end +else + on_nth_tick = function(event) + local tick = event.nth_tick + local f + local mod + for i = 1, #loaded_mods do + mod = loaded_mods[i] + f = mod.on_nth_tick[event.nth_tick] + if f then + debug_log({'hotpatch-trace.nth-tick-event-running', tick}, mod.name) + f(event) + end + end + end +end + +local register_all_events = function() + --unregister all events + script.on_event(defines.events, nil) + script.on_nth_tick(nil, nil) + --re-register all mod events + for i = 1, #loaded_mods do + register_mod_events(i) + end +end + +register_mod_events = function(loaded_index) + + local mod = loaded_mods[loaded_index] + local mod_name = mod.name + debug_log({'hotpatch-trace.event-registering'}, mod_name) + if mod.on_tick then + debug_log({'hotpatch-trace.on-tick-event-registered'}, mod_name) + script.on_event(defines.events.on_tick, on_tick) + end + for k,v in pairs(mod.on_event) do + local event_name = (event_names[k] or k) + debug_log({'hotpatch-trace.on-event-registered', event_name}, mod_name) + script.on_event(k, on_event) + end + for k,v in pairs(mod.on_nth_tick) do + debug_log({'hotpatch-trace.on-nth-tick-event-registered', k}, mod_name) + script.on_nth_tick(k, on_nth_tick) + end +end + +mod_on_init = function(loaded_index) + local mod = loaded_mods[loaded_index] + + if mod then + debug_log({'hotpatch-trace.mod-on-init'}, mod.name) + if mod.on_init then + local success, result = xpcall(mod.on_init, debug.traceback) + if not success then + debug_log('on_init failed for ' .. mod.name) + unload_mod(loaded_index) + return false + end + end + register_mod_events(loaded_index) + return true + end + return false +end + +mod_on_load = function(loaded_index) + local mod = loaded_mods[loaded_index] + + if mod then + debug_log({'hotpatch-trace.mod-on-load'}, mod.name) + if mod.on_load then + local success, result = xpcall(mod.on_load, debug.traceback) + if not success then + debug_log('on_load failed for ' .. mod.name) + unload_mod(loaded_index) + return false + end + end + register_mod_events(loaded_index) + return true + end + return false +end + +mod_on_configuration_changed = function(loaded_index, config) + local mod = loaded_mods[loaded_index] + + if mod then + debug_log({'hotpatch-trace.mod-on-configuration-changed'}, mod.name) + if mod.on_configuration_changed then + local success, result = xpcall(mod.on_configuration_changed, debug.traceback, config) + if not success then + debug_log('on_configuration_changed failed for ' .. mod.name) + unload_mod(loaded_index) + return false + end + end + return true + end + return false +end + +-- callbacks from mods to tell hotpatch when to enable handlers + +register_on_tick = function(mod_name) + local found_event + -- + local mod + for i = 1, #loaded_mods do + mod = loaded_mods[i] + local f = mod.on_tick + if f then + found_event = true + break + end + end + debug_log({'hotpatch-trace.on-tick-event-registered'}, mod_name) + if found_event then + debug_log({'hotpatch-trace.on-tick-handler-added'}) + else + debug_log({'hotpatch-trace.on-tick-handler-removed'}) + end +end + +register_nth_tick = function(mod_name, nth_tick) + local found_event + -- + local mod + for i = 1, #loaded_mods do + mod = loaded_mods[i] + local f = mod.on_nth_tick[nth_tick] + if f then + found_event = true + break + end + end + debug_log({'hotpatch-trace.on-nth-tick-event-registered', nth_tick}, mod_name) + if found_event then + debug_log({'hotpatch-trace.nth-tick-handler-added', nth_tick}) + script.on_nth_tick(event.nth_tick, on_nth_tick) + else + debug_log({'hotpatch-trace.nth-tick-handler-removed', nth_tick}) + script.on_nth_tick(event.nth_tick, nil) + end +end + +register_event = function(mod_name, event_name) + local found_event + local mod + for i = 1, #loaded_mods do + mod = loaded_mods[i] + local f = mod.on_event[event_name] + if f then found_event = true break end + end + debug_log({'hotpatch-trace.on-event-registered', nth_tick}, mod_name) + if found_event then + if script.get_event_handler(event_name) then + -- handler already installed + return + else + debug_log({'hotpatch-trace.on-event-handler-added', (event_names[event_name] or event_name)}) + script.on_event(event.event, on_event) + end + else + debug_log({'hotpatch-trace.on-event-handler-removed', (event_names[event_name] or event_name)}) + script.on_event(event.event, nil) + end +end + +-- Core registration + +on_init = function() + hidden = load([===[ + return function(...) log(...) end + ]===], '[HOTPATCH') + hidden_log = hidden() + debug_log({'hotpatch-info.on-init'}) + --juuuuust in case + global.mods = global.mods or {} + + debug_log({'hotpatch-info.installing-included-mods'}) + local mod + for i = 1, #static_mods do + mod = static_mods[i] + install_mod(mod.name, mod.version, mod.code, mod.files) + end + + for i = 1, #global.mods do + load_mod(i) + end + + for i = 1, #loaded_mods do + run_mod(i) + mod_on_init(i) + end + debug_log({'hotpatch-info.complete', {'hotpatch-info.installing-included-mods'}}) + debug_log({'hotpatch-info.complete', {'hotpatch-info.on-init'}}) +end + +on_load = function() + hidden = load([===[ + return function(...) log(...) end + ]===], '[HOTPATCH') + hidden_log = hidden() + debug_log({'hotpatch-info.on-load'}) + debug_log({'hotpatch-info.loading-installed-mods'}) + + if global.globals then + error('Upgrading from Hotpatch 1.0.X to 1.1.0 is not currently supported!') + + -- untested migration: use a tick to do the migration + script.on_event(defines.events.on_tick, function(e) + global.mods = {} + local mod + for k, v in pairs(global.mod_version) do + mod = {} + mod.name = k + mod.version = v + mod.code = global.mod_code[k] + mod.files = global.mod_files[k] + mod.global = global.globals[k] + table.insert(global.mods, mod) + end + global.mod_version = nil + global.mod_code = nil + global.mod_files = nil + global.globals = nil + + on_load() -- jump back into loading + on_tick(e) -- need to test if factorio auto calls this or not after assigning the handler + script.on_event(defines.events.on_tick, on_tick) -- restore old on_tick handler + end) + return + end + + -- load installed mods + for i = 1, #global.mods do + load_mod(i) + end + + -- run mods which loaded successfully + for i = 1, #loaded_mods do + run_mod(i) + mod_on_load(i) + end + + debug_log({'hotpatch-info.complete', {'hotpatch-info.loading-installed-mods'}}) + debug_log({'hotpatch-info.complete', {'hotpatch-info.on-load'}}) +end + +on_configuration_changed = function(config) + debug_log({'hotpatch-info.on-configuration-changed'}) + for i = 1, #loaded_mods do + mod_on_configuration_changed(i) + end + debug_log({'hotpatch-info.complete', {'hotpatch-info.on-configuration-changed'}}) +end + +if debug_log_on_tick then + on_tick = function(e) + debug_log({'hotpatch-trace.event-processing', 'on_tick'}) + local mod + local f + for i = 1, #loaded_mods do + mod = loaded_mods[i] + f = mod.on_tick + if f then + debug_log({'hotpatch-trace.event-running', 'on_tick'}, mod.name) + f(e) + end + end + end +else + on_tick = function(e) + local mod + local f + for i = 1, #loaded_mods do + mod = loaded_mods[i] + f = mod.on_tick + if f then + f(e) + end + end + end +end + + +script.on_init(on_init) +script.on_load(on_load) +script.on_configuration_changed(on_configuration_changed) +script.on_event(defines.events.on_tick, on_tick) + +--private API, don't use this +local mod_tools_internal = setmetatable({ + -- mod installation/uninstallation support functions + -- These take a mod NAME as a first argument + install_mod = install_mod, + find_installed_mod = find_installed_mod, + install_mod_file = install_mod_file, + uninstall_mod = uninstall_mod, + + -- mod interaction functions + -- These take a LOADED INDEX as a first argument, except load_mod, which takes an INSTALLED INDEX + load_mod = load_mod, + find_loaded_mod = find_loaded_mod, + run_mod = run_mod, + reset_mod = reset_mod, + reset_mod_events = reset_mod_events, + register_mod_events = register_mod_events, + unload_mod = unload_mod, + + -- internal callbacks when a mod registers events + register_event = register_event, + register_nth_tick = register_nth_tick, + register_on_tick = register_on_tick, + + -- mod bootstrap functions + -- These take a LOADED INDEX as a first argument + mod_on_init = mod_on_init, + mod_on_load = mod_on_load, + mod_on_configuration_changed = mod_on_configuration_changed, + + static_mods = static_mods, + console = console, + debug_log = debug_log, + loaded_mods = loaded_mods, + loaded_libraries = loaded_libraries, + installed_mods = setmetatable({}, { + __index = function(t, k) + return global.mods[k] + end, + __newindex = function(t, k, v) + -- do nothing + end, + __len = function(t) + return #global.mods + end, + __pairs = function(t) local function iter(t, k) local v; v = global.mods[k+1]; if v then return k+1, v end; end; return iter, t, 0 end, + __ipairs = function(t) local function iter(t, k) local v; v = global.mods[k+1]; if v then return k+1, v end; end; return iter, t, 0 end, + __metatable = false, + }) +},{ + __index = function(t, k) + debug_log({'hotpatch-error.invalid-API-access', k}, nil, 3) + end, + __newindex = function(t, k, v) + -- do nothing + end, + -- Don't let mods muck around + __metatable = false, +}) + +--public API +local mod_tools = setmetatable({ + -- most code should use static_mod + static_mod = static_mod, -- (name, version, code, files) + set_debug_level = function(level) + debug_level = tonumber(_ENV.debug_settings.level) or debug_levels[_ENV.debug_settings.level] + end, +},{ + __index = mod_tools_internal, + __newindex = function(t, k, v) + -- do nothing, read-only table + end, + -- Don't let mods muck around + __metatable = false, +}) + + +return mod_tools \ No newline at end of file diff --git a/lib/scenarios/Hotpatch/hotpatch/remote-interface.lua b/lib/scenarios/Hotpatch/hotpatch/remote-interface.lua new file mode 100644 index 00000000..2c2c6aba --- /dev/null +++ b/lib/scenarios/Hotpatch/hotpatch/remote-interface.lua @@ -0,0 +1,266 @@ +local hotpatch_tools = require 'hotpatch.mod-tools' +hotpatch_tools.static_mod('hotpatch-remote-interface', '1.0.1', [===[ +--[[ + +Copyright 2018 Chrisgbk +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +]] +-- MIT License, https://opensource.org/licenses/MIT + +local hotpatch_tools = require 'hotpatch.mod-tools' + +--load private API +-- mod installation/uninstallation support functions +-- These take a mod NAME as a first argument +local install_mod = hotpatch_tools.install_mod +local find_installed_mod = hotpatch_tools.find_installed_mod +local install_mod_file = hotpatch_tools.install_mod_file +local uninstall_mod = hotpatch_tools.uninstall_mod + +-- mod interaction functions +-- These take a LOADED INDEX as a first argument, except load_mod, which takes an INSTALLED INDEX +local load_mod = hotpatch_tools.load_mod +local find_loaded_mod = hotpatch_tools.find_loaded_mod +local run_mod = hotpatch_tools.run_mod +local reset_mod = hotpatch_tools.reset_mod +local reset_mod_events = hotpatch_tools.reset_mod_events +local register_mod_events = hotpatch_tools.register_mod_events +local unload_mod = hotpatch_tools.unload_mod + +-- internal callbacks when a mod registers events +local register_event = hotpatch_tools.register_event +local register_nth_tick = hotpatch_tools.register_nth_tick +local register_on_tick = hotpatch_tools.register_on_tick + +-- mod bootstrap functions +-- These take a LOADED INDEX as a first argument +local mod_on_init = hotpatch_tools.mod_on_init +local mod_on_load = hotpatch_tools.mod_on_load +local mod_on_configuration_changed = hotpatch_tools.mod_on_configuration_changed + +local static_mods = hotpatch_tools.static_mods +local console = hotpatch_tools.console +local debug_log = hotpatch_tools.debug_log +local loaded_mods = hotpatch_tools.loaded_mods +local installed_mods = hotpatch_tools.installed_mods + +local remote_interface = {} + +--IMPORTANT: WHEN COPY PASTING CODE TO CONSOLE THE CODE MUST HAVE SINGLE LINE COMMENTS REMOVED +-- FACTORIO CONSOLE STRIPS LINEFEEDS WHICH MAKES ALL THE CODE BECOME COMMENTED OUT +--TODO: gate these behind admin permissions; if (game.player and game.player.admin) should work for console commands? + +debug_log({'hotpatch-info.remote-installing'}) + +remote_interface['install'] = function(mod_name, mod_version, mod_code, mod_files, only_install) + -- this installs a new mod and runs on_init, then registers events + -- Note that mods may expect that certain events haven't been called yet when their on_init is ran + -- This may prevent them from functioning properly, without manually calling the events they expect + -- examples: on_player_created + local caller = game.player or console + if caller.admin then + local installed_index = find_installed_mod(mod_name) + local loaded_index = find_loaded_mod(mod_name) + if installed_index then + local old_version = installed_mods[installed_index].version + if old_version then + debug_log('WARNING: mod already exists: ' .. mod_name .. ' ' .. old_version) + debug_log('WARNING: reinstalling mod in-place: ' .. mod_name .. ' ' .. mod_version) + end + if loaded_index then + unload_mod(loaded_index) + end + end + + install_mod(mod_name, mod_version, mod_code, mod_files) + if not only_install then + installed_index = find_installed_mod(mod_name) + if installed_index then + if not load_mod(installed_index) then + caller.print('compilation failed for mod ' .. mod_name) + return + end + loaded_index = find_loaded_mod(mod_name) + if loaded_index then + if not run_mod(loaded_index) then + caller.print('execution failed for mod ' .. mod_name) + return + end + if not mod_on_init(loaded_index) then + caller.print('on_init failed for mod ' .. mod_name) + return + end + -- TODO: notify all mods + -- TODO: determine vanilla behaviour and replicate it + if not mod_on_configuration_changed(loaded_index, {mod_changes = {mod_name={new_version=version}}}) then + caller.print('on_configuration_changed failed for mod ' .. mod_name) + return + end + else + end + else + end + end + else + caller.print('You must be an admin to run this command.') + end +end + +remote_interface['run'] = function(mod_name) + -- this runs on_init, then registers events + -- Note that mods may expect that certain events haven't been called yet when their on_init is ran + -- This may prevent them from functioning properly, without manually calling the events they expect + -- examples: on_player_created + local caller = game.player or console + if caller.admin then + local installed_index = find_installed_mod(mod_name) + local loaded_index = find_loaded_mod(mod_name) + if installed_index and not loaded_index then + if not load_mod(installed_index) then + caller.print('compilation failed for mod ' .. mod_name) + return + end + loaded_index = find_loaded_mod(mod_name) + else + if loaded_index then + local mod = loaded_mods[loaded_index] + local version = mod.version + debug_log('WARNING: mod already loaded: ' .. mod_name .. ' ' .. version) + debug_log('WARNING: reinitializing: ' .. mod_name .. ' ' .. version) + end + end + + if loaded_index then + if not run_mod(loaded_index) then + caller.print('execution failed for mod ' .. mod_name) + return + end + if not mod_on_init(loaded_index) then + caller.print('on_init failed for mod ' .. mod_name) + return + end + -- TODO: notify all mods + -- TODO: determine vanilla behaviour and replicate it + if not mod_on_configuration_changed(loaded_index, {mod_changes = {mod_name={new_version=version}}}) then + caller.print('on_configuration_changed failed for mod ' .. mod_name) + return + end + end + else + caller.print('You must be an admin to run this command.') + end +end + +remote_interface['update'] = function(mod_name, mod_version, mod_code, mod_files) + -- this updates an existing mod + -- the current mods events are de-registered, the new code is installed, on_load is triggered, and then events are registered + -- finally, the mod is informed of the update, so it can run migrations from the previous version + -- TODO: validation + local caller = game.player or console + if caller.admin then + local installed_index = find_installed_mod(mod_name) + local loaded_index = find_loaded_mod(mod_name) + local old_version + if loaded_index then + old_version = loaded_mods[loaded_index].version + unload_mod(loaded_index) + end + install_mod(mod_name, mod_version, mod_code, mod_files) + installed_index = find_installed_mod(mod_name) + if not load_mod(installed_index) then + caller.print('compilation failed for mod ' .. mod_name) + return + end + loaded_index = find_loaded_mod(mod_name) + if not run_mod(loaded_index) then + caller.print('execution failed for mod ' .. mod_name) + return + end + if old_version then + if not mod_on_load(installed_index) then + caller.print('on_load failed for mod ' .. mod_name) + return + end + -- The mod must do any migrations here + -- TODO: notify all mods + if not mod_on_configuration_changed(installed_index, {mod_changes = {mod_name={old_version=old_version, new_version=mod_version}}}) then + caller.print('on_configuration_changed failed for mod ' .. mod_name) + return + end + else + -- first time install + if not mod_on_init(installed_index) then + caller.print('on_init failed for mod ' .. mod_name) + return + end + -- TODO: notify all mods + if not mod_on_configuration_changed(installed_index, {mod_changes = {mod_name={new_version=mod_version}}}) then + caller.print('on_configuration_changed failed for mod ' .. mod_name) + return + end + end + else + caller.print('You must be an admin to run this command.') + end +end + +remote_interface['install_mod_file'] = function(mod_name, mod_file, mod_file_code) + local caller = game.player or console + if caller.admin then + install_mod_file(mod_name, mod_file, mod_file_code) + else + caller.print('You must be an admin to run this command.') + end +end + +--TODO: most of this function +remote_interface['clean'] = function(mod_name) + -- Removes ALL mod data and reinitializes + -- doesn't remove things a mod may add like surfaces, etc + local caller = game.player or console + if caller.admin then + mod_reset(mod_name) + run_mod(mod_name) + mod_on_init(mod_name) + mod_on_configuration_changed(mod_name, {mod_changes = {mod_name={new_version=mod_version}}}) + else + caller.print('You must be an admin to run this command.') + end +end + +remote_interface['uninstall'] = function(mod_name) + -- Uninstalls a mod + local caller = game.player or console + if caller.admin then + uninstall_mod(mod_name) + else + caller.print('You must be an admin to run this command.') + end +end + +remote.add_interface('hotpatch', remote_interface) + +debug_log({'hotpatch-info.complete', {'hotpatch-info.remote-installing'}}) + +]===]) + +--[[ + +remote.call('hotpatch, 'install', 'test', '1.0.0', [===[ +script.on_event(defines.events.on_player_changed_position, function(e) + game.print('changed position') +end +]===] + +local updated_code = .... +remote.call('hotpatch', 'update', 'test', '1.0.1', updated_code) + + +]] + +return true \ No newline at end of file diff --git a/lib/scenarios/Hotpatch/locale/af/freeplay.cfg b/lib/scenarios/Hotpatch/locale/af/freeplay.cfg new file mode 100644 index 00000000..e3aafe93 --- /dev/null +++ b/lib/scenarios/Hotpatch/locale/af/freeplay.cfg @@ -0,0 +1,3 @@ +msg-intro=Hierdie is Factorio vryspel. Jou taak is om 'n vuurpyl in die buitenste ruimte in te stuur. Doen dit deur die bou van 'n Vuurpyl Silo en van daar af 'n vuurpyl te lanseer met a satelliet aan boord. Jy sal baie gevorderde tegnologiëe moet navors om die Vuurpyl Silo te ontsluit. Begin klein, werk jou pad op met outomatisering en moenie vergeet om jouself te beskerm teen die inboorlinge nie. + + diff --git a/lib/scenarios/Hotpatch/locale/ar/freeplay.cfg b/lib/scenarios/Hotpatch/locale/ar/freeplay.cfg new file mode 100644 index 00000000..86b6801a --- /dev/null +++ b/lib/scenarios/Hotpatch/locale/ar/freeplay.cfg @@ -0,0 +1,3 @@ +msg-intro=.نييلصألا ناكسلا نم كسفن ةيامح ىسنت الو ةياهنلا ىلإ لصتل كتقيرطب لمعأو ،رفصلا نم أدبتس .خيراوصلا عماوص حتف لجأ نم ةمدقتم ايجولونكت نع ثحبلل جاتحت فوس .ةيعانصلا رامقألاب خيراوصلا قالطإو خيراوصلا عماوص ءانب قيرط نع كلذب مايقلا عيطتست .ءاضفلل اخوراص قلطت نا يه كتمهم .Factorio ـل رحلا بعللا وه اذه + + diff --git a/lib/scenarios/Hotpatch/locale/be/freeplay.cfg b/lib/scenarios/Hotpatch/locale/be/freeplay.cfg new file mode 100644 index 00000000..04f8637c --- /dev/null +++ b/lib/scenarios/Hotpatch/locale/be/freeplay.cfg @@ -0,0 +1,3 @@ +msg-intro=Гэта рэжым свабоднай гульні Factorio. Вашая мэта - запусціць ракету ў космас. Дзеля гэтага вам трэба пабудаваць ракетную шахту і саму ракету з спадарожнікам. Вы мусіце даследваць перадавыя тэхналогіі, каб навучыцца будаваць ракетную шахту. Пачынайце з малога, працуйце над аўтаматызацыяй вытворчасці, даследуйце новыя тэхналогіі і не забывайцеся на абарону ад "тутэйшых"! + + diff --git a/lib/scenarios/Hotpatch/locale/bg/freeplay.cfg b/lib/scenarios/Hotpatch/locale/bg/freeplay.cfg new file mode 100644 index 00000000..50ca9458 --- /dev/null +++ b/lib/scenarios/Hotpatch/locale/bg/freeplay.cfg @@ -0,0 +1,3 @@ +msg-intro=Това е режимът на свободна игра. Задачата ви е да изстреляте ракета в космоса. За това ще е нужно да построите ракетна шахта и изстреляте ракетата със спътник. Ще трябва да проучите развити технологии, които ще отключат ракетната шахта. Започнете с малко и постепенно автоматизирайте процеса, като не забравяте да се защитавате от местните форми на живот. + + diff --git a/lib/scenarios/Hotpatch/locale/ca/freeplay.cfg b/lib/scenarios/Hotpatch/locale/ca/freeplay.cfg new file mode 100644 index 00000000..e563ebae --- /dev/null +++ b/lib/scenarios/Hotpatch/locale/ca/freeplay.cfg @@ -0,0 +1,3 @@ +msg-intro=Aquest és el mode lliure del Factorio. La vostra tasca és construir un coet i enviar-lo a l'espai. Ho haureu de fer construint un centre de coets espacials i llençant un coet amb un satèl·lit. Necessitareu investigar tecnologies avançades per poder-ho fer. Aneu pas a pas, avanceu cap a l'automatització i no oblideu protegir-vos dels natius. + + diff --git a/lib/scenarios/Hotpatch/locale/cs/freeplay.cfg b/lib/scenarios/Hotpatch/locale/cs/freeplay.cfg new file mode 100644 index 00000000..145e382e --- /dev/null +++ b/lib/scenarios/Hotpatch/locale/cs/freeplay.cfg @@ -0,0 +1,3 @@ +msg-intro=Toto je volná hra Factorio. Tvým úkolem je vypustit raketu do vesmíru. Postav raketové silo a vypusť raketu se satelitem. Pro odemknutí raketového sila budeš muset vyzkoumat pokročilé technologie. Začni v malém, postupuj s automatizací a nezapomeň se chránit před domorodci. + + diff --git a/lib/scenarios/Hotpatch/locale/da/freeplay.cfg b/lib/scenarios/Hotpatch/locale/da/freeplay.cfg new file mode 100644 index 00000000..f06d6237 --- /dev/null +++ b/lib/scenarios/Hotpatch/locale/da/freeplay.cfg @@ -0,0 +1,3 @@ +msg-intro=Det her er Factorio fri leg. Din opgave er at affyre en raket for at udforske rummet. For at gøre det skal du bygge en raket silo og affyre en raket med en satellit i. Du bliver nød til at udforske avancerede teknologier for at at kunne bygge en raket silo. Start småt, og arbejd din vej op med hjælp af automatisering, glem ikke at beskytte dig fra de indfødte. + + diff --git a/lib/scenarios/Hotpatch/locale/de/freeplay.cfg b/lib/scenarios/Hotpatch/locale/de/freeplay.cfg new file mode 100644 index 00000000..b1f81ade --- /dev/null +++ b/lib/scenarios/Hotpatch/locale/de/freeplay.cfg @@ -0,0 +1,3 @@ +msg-intro=Dies ist das freie Spiel von Factorio. Deine Aufgabe ist es, eine Rakete in den Weltraum zu starten. Dazu musst Du ein Raketensilo bauen und eine Rakete mit einem Satelliten starten. Du wirst erweiterte Technologien erforschen müssen, um das Raketensilo freizuschalten. Fange klein an, arbeite Dich durch Automatisierung hoch und vergiss nicht, Dich vor den Einheimischen zu schützen. + + diff --git a/lib/scenarios/Hotpatch/locale/el/freeplay.cfg b/lib/scenarios/Hotpatch/locale/el/freeplay.cfg new file mode 100644 index 00000000..769bc117 --- /dev/null +++ b/lib/scenarios/Hotpatch/locale/el/freeplay.cfg @@ -0,0 +1,3 @@ +msg-intro=Αυτό είναι το ελεύθερο παιχνίδι του Factorio. Ο στόχος σου είναι να εκτοξεύσεις έναν πύραυλο στο διάστημα. Αυτό το κάνεις κατασκευάζοντας ένα σιλό πυραύλου και εκτοξεύοντας έναν πυραύλο με δορυφόρο. Θα πρέπει να ερευνήσεις προηγμένες τεχνολογίες για να ξεκλειδώσεις το σιλό πυραύλου. Ξεκινάς περιορισμένος, δούλεψε με αυτοματισμούς για την άνοδο σου και μην ξεχνάς να προστατέψεις τον εαυτό σου από τους ντόπιους. + + diff --git a/lib/scenarios/Hotpatch/locale/en/freeplay.cfg b/lib/scenarios/Hotpatch/locale/en/freeplay.cfg new file mode 100644 index 00000000..10cc632d --- /dev/null +++ b/lib/scenarios/Hotpatch/locale/en/freeplay.cfg @@ -0,0 +1,2 @@ +msg-intro=This is the Factorio freeplay. Your task is to launch a rocket into space. Do this by constructing a Rocket Silo and launching a rocket with a satellite. You will need to research advanced technologies in order to unlock the Rocket Silo. Start small, work your way up with automation and don't forget to protect yourself from the natives. + diff --git a/lib/scenarios/Hotpatch/locale/en/hotpatch.cfg b/lib/scenarios/Hotpatch/locale/en/hotpatch.cfg new file mode 100644 index 00000000..d0c51230 --- /dev/null +++ b/lib/scenarios/Hotpatch/locale/en/hotpatch.cfg @@ -0,0 +1,77 @@ +[hotpatch] +log=@__1__:__2__] __3__ +log-mod=@__1__:__2__][__4__] __3__ +severe=SEVERE ERROR: __1__ +error=ERROR: __1__ +warning=WARNING: __1__ +info=INFO: __1__ +verbose=INFO: __1__ +trace=TRACE: __1__ + +[hotpatch-info] +logging-enabled=Logging enabled +metatable-installed=_ENV metatable installed +complete=__1__ Complete! +on-init=initializing... +on-load=loading... +on-configuration-changed=mod configuration changed... +installing-included-mods=installing included mods... +loading-installed-mods=loading installed mods... +loading-libs=loading Factorio.data.core.lualib... +loading-library=loading library: __1__ +uninstalling=Uninstalling mod... +installing=Installing version __1__... +installing-file=Installing file __1__... +script-shim=setting up mod script shim... +setting-env=setting up mod _ENV... +loading=loading... +unloading=unloading... +running=running... +must-be-admin=You must be an admin to run this command +remote-installing=installing remote interface + +[hotpatch-trace] +nil-var-access=_ENV nil variable access: __1__ +nil-var-assignment=_ENV variable assignment: __1__ +event-registering=registering events... +on-tick-event-registered=registered on_tick event... +on-event-registered=registered event __1__... +on-nth-tick-event-registered=registered nth_tick event __1__... +event-running=running event __1__... +nth-tick-event-running=running nth_event __1__... +mod-on-init=running on_init... +mod-on-load=running on_load... +mod-on-configuration-changed=running on_configuration_changed... +adding-event=adding event __1__ +adding-nth-tick-event=adding nth_tick event __1__ +caching-event=caching event: __1__... +caching-nth-tick-event=caching nth_tick event: __1__... +event-processing=processing event __1__... +nth-tick-event-processing=processing nth_tick event __1__... +nth-tick-handler-added=added nth_tick handler: __1__ +nth-tick-handler-removed=removed nth_tick handler: __1__ +on-tick-handler-added=added on_tick handler +on-tick-handler-removed=removed on_tick handler +on-event-handler-added=added event handler: __1__ +on-event-handler-removed=removed event handler: __1__ +cached-load-require=loading cached require'd file: __1__... +load-require=loading require'd file: __1__... +load-core-lib=loading from Factorio.data.core.lualib: __1__... + +[hotpatch-warning] +contains-comments=mod code contains comments! +contains-comments-no-lf=mod contains comments and no linefeed! +contains-comments-console=comments from console will comment out the entire code! +reset-events-not-running=tried to reset events for mod that isn't running! +remote-interface-exists=remote interface __1__ already exists, removing... +command-exists=command __1__ already exists, removing... +already-loaded=mod already loaded +reinitializing=reinitializing... +already-exists=mod already exists +reinstalling=reinstalling mod in-place + +[hotpatch-error] +invalid-API-access=Invalid API access: __1__ +not-installed=mod not installed, cannot install file for mod that does not exist +compilation-failed=compilation failed for mod +execution-failed=execution failed for mod diff --git a/lib/scenarios/Hotpatch/locale/eo/freeplay.cfg b/lib/scenarios/Hotpatch/locale/eo/freeplay.cfg new file mode 100644 index 00000000..8cafc148 --- /dev/null +++ b/lib/scenarios/Hotpatch/locale/eo/freeplay.cfg @@ -0,0 +1,3 @@ +msg-intro=Tio estas la Factorio Libera Ludado. Via tasko estas ĵeti raketon en spacon. Fari tiun per konstruanta Raketo Silo kaj ĵeti raketon kun satelito. Vi bezonos esplori altnivelaj teknologioj por malŝlosi la Raketo Silo. Start malgranda, labori vian vojon supren per aŭtomatigo kaj ne forgesu protekti vin mem de la indiĝenoj. + + diff --git a/lib/scenarios/Hotpatch/locale/es-ES/freeplay.cfg b/lib/scenarios/Hotpatch/locale/es-ES/freeplay.cfg new file mode 100644 index 00000000..56ddef04 --- /dev/null +++ b/lib/scenarios/Hotpatch/locale/es-ES/freeplay.cfg @@ -0,0 +1,3 @@ +msg-intro=Este es el modo de juego libre de Factorio. Tu tarea es la de lanzar un cohete al espacio. Logra esto construyendo un Silo de Cohetes y lanzando un cohete con un satélite. Necesitarás investigar tecnologías avanzadas para poder desbloquear el Silo de Cohetes. Comienza poco a poco, trabaja en tu sistema de automatización y no te olvides de protegerte de los nativos. + + diff --git a/lib/scenarios/Hotpatch/locale/et/freeplay.cfg b/lib/scenarios/Hotpatch/locale/et/freeplay.cfg new file mode 100644 index 00000000..c029a340 --- /dev/null +++ b/lib/scenarios/Hotpatch/locale/et/freeplay.cfg @@ -0,0 +1,3 @@ +msg-intro=See on Factorio vabamäng. Sinu eesmärgiks on saata rakett avakosmosesse. Seda saad teha ehitades Raketi Silotorni ja lennutades Raketti koos satelliidiga. Selleks pead õppima selgeks keerukad tehnoloogiaharud, et Raketi Silotorni lahti lukustada. Alusta väikselt ning liigu edasi automatiseerimise abil ja ära unusta ennast kaitsta kohalike eluvormide eest. + + diff --git a/lib/scenarios/Hotpatch/locale/fi/freeplay.cfg b/lib/scenarios/Hotpatch/locale/fi/freeplay.cfg new file mode 100644 index 00000000..07881fa2 --- /dev/null +++ b/lib/scenarios/Hotpatch/locale/fi/freeplay.cfg @@ -0,0 +1,3 @@ +msg-intro=Tämä on Factorion vapaapelimuoto. Tavoitteesi on lähettää satelliitti ulkoavaruuteen rakentamalla rakettisiilo ja laukaisemalla raketti. Rakettisiilon voit rakentaa vasta, kun olet kehittänyt siihen tarvittavat huipputeknologiat. Aloita pienestä ja automatisoi kaikki tarvittava, äläkä unohda suojella itseäsi alkuasukkailta. + + diff --git a/lib/scenarios/Hotpatch/locale/fr/freeplay.cfg b/lib/scenarios/Hotpatch/locale/fr/freeplay.cfg new file mode 100644 index 00000000..b60de722 --- /dev/null +++ b/lib/scenarios/Hotpatch/locale/fr/freeplay.cfg @@ -0,0 +1,3 @@ +msg-intro=Il s'agit du mode de jeu libre de Factorio. Votre tâche consiste à lancer une fusée dans l'espace. Ceci en construisant un silo à fusée, et en lançant un satellite avec la fusée. Vous aurez besoin de rechercher des technologies de pointe afin de débloquer le silo. Commencez petit, évoluez en automatisant et n'oubliez pas de vous protéger contre les autochtones. + + diff --git a/lib/scenarios/Hotpatch/locale/he/freeplay.cfg b/lib/scenarios/Hotpatch/locale/he/freeplay.cfg new file mode 100644 index 00000000..664c2751 --- /dev/null +++ b/lib/scenarios/Hotpatch/locale/he/freeplay.cfg @@ -0,0 +1,3 @@ +msg-intro=.םיימוקמה םירוציה ינפמ םכמצע לע ןגהל וחכשת לאו ,יטמוטוא ןוכימ תועצמאב םכלש הישעתה תא וביחרה ,ןטקב וליחתה .ןקתמה תיינב תא רשפאל ידכ תומדקתמ תויגולונכט רוקחל םכילע היהי .ןייוול ליכמה ליט ותועצמאב רגשלו םיליט רוגיש ןקתמ תונבל םכילע .ללחל ליט רגשל איה םכתמישמ .ויִרֹוטְקַפ לש ישפוחה שיחרתה והז + + diff --git a/lib/scenarios/Hotpatch/locale/hu/freeplay.cfg b/lib/scenarios/Hotpatch/locale/hu/freeplay.cfg new file mode 100644 index 00000000..cfe12672 --- /dev/null +++ b/lib/scenarios/Hotpatch/locale/hu/freeplay.cfg @@ -0,0 +1,3 @@ +msg-intro=Ez a Factorio szabad játék módja. A feladatod az, hogy kilőj egy rakétát az űr felderítésére. Ehhez egy Rakétasilót kell építened, melyből aztán egy műholddal felszerelt rakétát kell kilőnöd. A Rakétasiló kifejlesztéséhez különféle technológiákat kell fejlesztened. Kezdj kicsiben és küzdd fel magad az automatizálásban. Ne felejts el védekezni a honos fajok ellen. + + diff --git a/lib/scenarios/Hotpatch/locale/it/freeplay.cfg b/lib/scenarios/Hotpatch/locale/it/freeplay.cfg new file mode 100644 index 00000000..8906d682 --- /dev/null +++ b/lib/scenarios/Hotpatch/locale/it/freeplay.cfg @@ -0,0 +1,3 @@ +msg-intro=Questa è la modalità libera di Factorio. Il tuo obiettivo è quello di lanciare il razzo nello spazio. Completalo costruendo un Silo per Razzi e lanciando il razzo con il satellite. Avrai bisogno di ricercare tecnologie avanzate per riuscire a sbloccare il Silo per Razzi. Inizia in piccolo, espandi con l'automazione e non dimenticare di proteggerti dai nativi. + + diff --git a/lib/scenarios/Hotpatch/locale/ja/freeplay.cfg b/lib/scenarios/Hotpatch/locale/ja/freeplay.cfg new file mode 100644 index 00000000..83994d04 --- /dev/null +++ b/lib/scenarios/Hotpatch/locale/ja/freeplay.cfg @@ -0,0 +1,3 @@ +msg-intro=これは、Factorioのフリープレイです。ロケットサイロを建造し、衛星を載せたロケットを宇宙へ打ち上げるのがあなたの任務です。ロケットサイロをアンロックするためには高度な技術研究をする必要があります。小規模に始め、自動化しつつ進み、原住生物から身を守ることも忘れないでください。 + + diff --git a/lib/scenarios/Hotpatch/locale/ko/freeplay.cfg b/lib/scenarios/Hotpatch/locale/ko/freeplay.cfg new file mode 100644 index 00000000..cfd96b36 --- /dev/null +++ b/lib/scenarios/Hotpatch/locale/ko/freeplay.cfg @@ -0,0 +1,3 @@ +msg-intro=Factorio 자유플레이입니다. 당신의 목표는 우주로 로켓을 발사하는 것입니다. 로켓 격납고를 건설하고 인공위성을 탑재한 로켓을 발사하여 이 목표를 달성하세요. 로켓 격납고의 잠금을 해제하기 위해 고급 기술들을 연구할 필요가 있습니다. 작은 것부터 시작해 자동화로 차근차근 발전하고, 토착 생물로부터 자신을 보호하는 것도 잊지 마세요. + + diff --git a/lib/scenarios/Hotpatch/locale/lt/freeplay.cfg b/lib/scenarios/Hotpatch/locale/lt/freeplay.cfg new file mode 100644 index 00000000..3e3b01fe --- /dev/null +++ b/lib/scenarios/Hotpatch/locale/lt/freeplay.cfg @@ -0,0 +1,3 @@ +msg-intro=Tai Factorio laisvas žaidimas. Jūsų užduotis - paleisti raketą į kosmosą. Tam reikia pastatyti Raketos šachtą ir paleisti raketą su palydovu. Reikės atrasti pažangias technologijas kad atrakintumėte Raketos šachtą. Pradėkite palengva, tobulinkite automatiką ir neužmirškite apsisaugoti nuo vietinių. + + diff --git a/lib/scenarios/Hotpatch/locale/lv/freeplay.cfg b/lib/scenarios/Hotpatch/locale/lv/freeplay.cfg new file mode 100644 index 00000000..581e4b52 --- /dev/null +++ b/lib/scenarios/Hotpatch/locale/lv/freeplay.cfg @@ -0,0 +1,2 @@ +msg-intro=Šis ir Factorio brīvais režīms. Tavs uzdevums ir palaist raķeti kosmosā. Lai to izdarītu, tev vajadzēs uzbūvēt palaišanas platformu un palaist raķeti ar satelītu. Tev būs jāizpēta sarežģītas tehnoloģijas, lai atbloķētu palaišanas platformu. Sāc no mazumiņa, strādā un neaizmirsti pasargāt sevi no šīs planētas iemītniekiem... + diff --git a/lib/scenarios/Hotpatch/locale/nl/freeplay.cfg b/lib/scenarios/Hotpatch/locale/nl/freeplay.cfg new file mode 100644 index 00000000..3086e01f --- /dev/null +++ b/lib/scenarios/Hotpatch/locale/nl/freeplay.cfg @@ -0,0 +1,3 @@ +msg-intro=Dit is Factorio vrij spel. Je taak is om een raket te lanceren in de ruimte. Bouw hiervoor een raket-silo en lanceer een raket met een satelliet. Je zult geavanceerde technologieën moeten onderzoeken om de raket-silo vrij te spelen. Begin op kleine schaal, breidt uit en automatiseer je fabriek en vergeet niet jezelf te beschermen tegen de inheemse bewoners. + + diff --git a/lib/scenarios/Hotpatch/locale/no/freeplay.cfg b/lib/scenarios/Hotpatch/locale/no/freeplay.cfg new file mode 100644 index 00000000..5fb97c8f --- /dev/null +++ b/lib/scenarios/Hotpatch/locale/no/freeplay.cfg @@ -0,0 +1,3 @@ +msg-intro=Dette er Factorio frispilling. Din oppgave er å sende en rakett til verdensrommet. Bygg en rakettsilo og send opp en rakett med en satellitt. Du blir nødt til å utforske avanserte teknologier for å få tilgang til rakettsiloen. Begynn i det små, jobb deg oppover med hjelp av automatisering, men ikke glem å beskytte deg mot de innfødte. + + diff --git a/lib/scenarios/Hotpatch/locale/pl/freeplay.cfg b/lib/scenarios/Hotpatch/locale/pl/freeplay.cfg new file mode 100644 index 00000000..323dfb81 --- /dev/null +++ b/lib/scenarios/Hotpatch/locale/pl/freeplay.cfg @@ -0,0 +1,3 @@ +msg-intro=To jest swobodna rozgrywka Factorio. Twoim zadaniem jest wysłanie rakiety w kosmos. Skonstruuj silos rakietowy i wystrzel z niego rakietę z satelitą. Żeby odblokować silos rakietowy będziesz musiał odkryć zaawansowane technologie. Rozpocznij od podstaw, rozwijaj się wykorzystując automatyzację i nie zapomnij o obronie przed rodzimymi mieszkańcami planety. + + diff --git a/lib/scenarios/Hotpatch/locale/pt-BR/freeplay.cfg b/lib/scenarios/Hotpatch/locale/pt-BR/freeplay.cfg new file mode 100644 index 00000000..cdb5557d --- /dev/null +++ b/lib/scenarios/Hotpatch/locale/pt-BR/freeplay.cfg @@ -0,0 +1,3 @@ +msg-intro=Este é o modo livre do Factorio. Sua missão é lançar um foguete no espaço. Para fazer isso construa um centro de lançamento de foguetes e lance o foguete com um satélite. Você vai precisar pesquisar tecnologias avançadas para conseguir desbloquear um Centro de lançamento. Comece do começo, continue na automação e não se esqueça de se proteger dos nativos. + + diff --git a/lib/scenarios/Hotpatch/locale/pt-PT/freeplay.cfg b/lib/scenarios/Hotpatch/locale/pt-PT/freeplay.cfg new file mode 100644 index 00000000..030b54ce --- /dev/null +++ b/lib/scenarios/Hotpatch/locale/pt-PT/freeplay.cfg @@ -0,0 +1,3 @@ +msg-intro=Este é o jogo livre Factorio. A tua tarefa é lançar um foguetão para explorar o espaço. Faça isto, construindo um Silo de Foguetão e lançar um foguetão com um satélite. Tu vais precisar de pesquisar tecnologias avançadas para poderes desbloquear o Silo de Foguetão. Começa simples, trabalha e evolui com a automação e não te esqueças de proteger-te dos nativos. + + diff --git a/lib/scenarios/Hotpatch/locale/ro/freeplay.cfg b/lib/scenarios/Hotpatch/locale/ro/freeplay.cfg new file mode 100644 index 00000000..134ce290 --- /dev/null +++ b/lib/scenarios/Hotpatch/locale/ro/freeplay.cfg @@ -0,0 +1,3 @@ +msg-intro=Acesta este modul Factorio freeplay. Sarcina ta este de a lansa o rachetă în spațiu. Fă acest lucru prin construirea unui siloz de rachete și lansarea unei rachete cu satelit. Va trebui să cercetezi tehnologiile avansate pentru a debloca silozul de rachete. Începe încet, avansează cu automatizarea și nu uita să te protejezi de inamici. + + diff --git a/lib/scenarios/Hotpatch/locale/ru/freeplay.cfg b/lib/scenarios/Hotpatch/locale/ru/freeplay.cfg new file mode 100644 index 00000000..0cc1b040 --- /dev/null +++ b/lib/scenarios/Hotpatch/locale/ru/freeplay.cfg @@ -0,0 +1,3 @@ +msg-intro=Это режим свободной игры Factorio. Ваша задача запустить спутник в космос. Для его запуска потребуется создать ракету и ракетную шахту. Начните с малого, работайте над автоматизацией производства, исследуйте новые технологии и не забывайте о защите от местных форм жизни. + + diff --git a/lib/scenarios/Hotpatch/locale/sk/freeplay.cfg b/lib/scenarios/Hotpatch/locale/sk/freeplay.cfg new file mode 100644 index 00000000..ea4bb174 --- /dev/null +++ b/lib/scenarios/Hotpatch/locale/sk/freeplay.cfg @@ -0,0 +1,3 @@ +msg-intro=Toto je voľná hra Factorio. Tvojou úlohou je vypustiť raketu do vesmíru. Postav raketové silo a vypusti raketu so satelitom. Pre odomknutie raketového sila budeš musieť vyskúmať pokročilé technológie. Začni v malom, postupuj s automatizáciou a nezabnudni sa chrániť pred domácimi. + + diff --git a/lib/scenarios/Hotpatch/locale/sl/freeplay.cfg b/lib/scenarios/Hotpatch/locale/sl/freeplay.cfg new file mode 100644 index 00000000..b919dd8a --- /dev/null +++ b/lib/scenarios/Hotpatch/locale/sl/freeplay.cfg @@ -0,0 +1,3 @@ +msg-intro=To je Factorio freeplay. Vaša naloga je poslati raketo v vesolje. To storite tako, da zgradite silos za raketo in jo skupaj s satelitom izstrelite v vesolje. Raziskati bo potrebno napredne tehnologije, da bi lahko odklenili Raketni silos. Začnete z majhnim, nato nadgradite vse s pomočjo avtomatizacije in ne pozabite, zaščitite se pred domorodci. + + diff --git a/lib/scenarios/Hotpatch/locale/sr/freeplay.cfg b/lib/scenarios/Hotpatch/locale/sr/freeplay.cfg new file mode 100644 index 00000000..3a449cd4 --- /dev/null +++ b/lib/scenarios/Hotpatch/locale/sr/freeplay.cfg @@ -0,0 +1,2 @@ +msg-intro=Ово је Factorio freeplay. Твој задатак је да лансираш ракету са сателитом у свемир. Уради ово грађом ракетног силоса. Треба да истражиш пуно напредних технологија да би откључао ракетни силос. Почни мало, а онда напредуј са аутоматизацијом и не заборави да се заштитиш од ванземаљаца. + diff --git a/lib/scenarios/Hotpatch/locale/sv-SE/freeplay.cfg b/lib/scenarios/Hotpatch/locale/sv-SE/freeplay.cfg new file mode 100644 index 00000000..72fabb9b --- /dev/null +++ b/lib/scenarios/Hotpatch/locale/sv-SE/freeplay.cfg @@ -0,0 +1,3 @@ +msg-intro=Det här är Factorio frispel. Ditt uppdrag är att avfyra en raket ut i rymden. Gör det genom att tillverka en raket silo och avfyra en raket med en satellit. Du kommer att behöva forska fram avancerade teknologier för att kunna låsa upp raket silon. Börja smått, jobba dig uppåt med hjälp av automatisering och glöm inte att försvara dig själv mot infödingarna. + + diff --git a/lib/scenarios/Hotpatch/locale/th/freeplay.cfg b/lib/scenarios/Hotpatch/locale/th/freeplay.cfg new file mode 100644 index 00000000..067594bd --- /dev/null +++ b/lib/scenarios/Hotpatch/locale/th/freeplay.cfg @@ -0,0 +1,3 @@ +msg-intro=นี้คือโหมดอิสระของ Factorio ภารกิจของคุณคือส่งจรวดสู่อวกาศ ซึ่งทำได้โดยการสร้างไซโลจรวดและส่งจรวดขึ้นไปพร้อมกับดาวเทียม คุณต้องวิจัยค้นคว้าเทคโนโลยีขั้นสูงเพื่อปลดล็อคไซโลจรวด เริ่มจากเล็กๆไปเรื่อยๆด้วยระบบอัตโนมัติและอย่าลืมป้องกันตัวเองจากคนพื้นเมืองด้วย + + diff --git a/lib/scenarios/Hotpatch/locale/tr/freeplay.cfg b/lib/scenarios/Hotpatch/locale/tr/freeplay.cfg new file mode 100644 index 00000000..321f0ea9 --- /dev/null +++ b/lib/scenarios/Hotpatch/locale/tr/freeplay.cfg @@ -0,0 +1,3 @@ +msg-intro=Bu Factorio'nun serbest oynanışı. Görevin roketleri uzaya yollamak. Bunu Roket Silosu yaparak ve uyduyla roket yollayarak yapacaksın. Roket Silo'sunu açmak için gelişmiş teknolojileri sırayla açmalısın. Yavaş başla, otomatikleş ve yerlilerden kendini korumayı untuma. + + diff --git a/lib/scenarios/Hotpatch/locale/uk/freeplay.cfg b/lib/scenarios/Hotpatch/locale/uk/freeplay.cfg new file mode 100644 index 00000000..1c494050 --- /dev/null +++ b/lib/scenarios/Hotpatch/locale/uk/freeplay.cfg @@ -0,0 +1,3 @@ +msg-intro=Це один з типів гри в Factorio - "Вільна гра". Ваше завдання - запустити ракету у космічний простір. Зробіть це, побудувавши ракетну шахту та запустивши ракету зі супутником. Вам потрібно дослідити передові технології, щоб мати змогу побудувати ракетну шахту. Починайте з малого, розвивайтеся за допомогою автоматизації, але не забувайте і про оборону! + + diff --git a/lib/scenarios/Hotpatch/locale/vi/freeplay.cfg b/lib/scenarios/Hotpatch/locale/vi/freeplay.cfg new file mode 100644 index 00000000..a158815c --- /dev/null +++ b/lib/scenarios/Hotpatch/locale/vi/freeplay.cfg @@ -0,0 +1,3 @@ +msg-intro=Đầy là chế độ freeplay của Factorio. Nhiệm cụ của bạn là phóng một chiếc tên lửa lên vũ trụ. Hãy xây dựng một bệ phóng tên lửa để phóng một chiếc tên lửa kèm theo vệ tinh. Bạn sẽ cần phải nghiên cứu nhiều công nghệ tiên tiến để có thể xây dựng bệ phóng tên lửa. Bắt đầu từ quy mô nhỏ, phát triển dần dần bằng tự động hóa, và đừng quên bảo vệ bản thân mình khỏi những sinh vật sinh sống ở đây. + + diff --git a/lib/scenarios/Hotpatch/locale/zh-CN/freeplay.cfg b/lib/scenarios/Hotpatch/locale/zh-CN/freeplay.cfg new file mode 100644 index 00000000..122bf6fc --- /dev/null +++ b/lib/scenarios/Hotpatch/locale/zh-CN/freeplay.cfg @@ -0,0 +1,3 @@ +msg-intro=欢迎来到异星工厂标准模式,你的目标是要把火箭发射到太空。为此你需要研发一系列高等科技,解锁并修建火箭发射井,直到把携带有卫星的火箭发射出去。从小作坊生产起步,逐渐建立起属于自己的自动化大工厂吧!同时,别忘了防范异星虫子的骚扰 ... + + diff --git a/lib/scenarios/Hotpatch/locale/zh-TW/freeplay.cfg b/lib/scenarios/Hotpatch/locale/zh-TW/freeplay.cfg new file mode 100644 index 00000000..e4e9d1cb --- /dev/null +++ b/lib/scenarios/Hotpatch/locale/zh-TW/freeplay.cfg @@ -0,0 +1,3 @@ +msg-intro=這是異星工廠的自由模式。你的任務是發射火箭,探索外太空。請建造一個火箭發射井,並發射攜帶著衛星的火箭。你會需要研究大量的進階科技以解鎖火箭發射井。從頭開始,靠著自動化科技的協助向目標邁進時,也別忘記從外星住民手的中保護你自己啊! + + diff --git a/master.js b/master.js index ae3ec2a4..c030f57c 100644 --- a/master.js +++ b/master.js @@ -10,6 +10,12 @@ combinator signals. node master.js */ +// Attempt updating +// const updater = require("./updater.js"); +// updater.update().then(console.log); + +(async ()=>{ + // Set the process title, shows up as the title of the CMD window on windows // and as the process name in ps/top on linux. process.title = "clusterioMaster"; @@ -20,32 +26,41 @@ process.on('unhandledRejection', r => console.log(r)); // configgy stuff debug = false; +// reduces loadtime from 1700 ms to 1200 ms on my i3 + SSD +// tested by logging Date.now() here and when the webserver started listening +require('cache-require-paths'); + +// argument parsing +const args = require('minimist')(process.argv.slice(2)); + +// Library for create folder recursively if it does not exist +const mkdirp = require("mkdirp"); +const averagedTimeSeries = require("averaged-timeseries"); +const deepmerge = require("deepmerge"); +const path = require("path"); +const fs = require("fs-extra"); +const jwt = require('jsonwebtoken'); +const bcrypt = require('bcryptjs'); +const crypto = require('crypto'); +const base64url = require('base64url'); +const moment = require("moment"); +const request = require("request") + // constants -const masterModFolder = "./database/masterMods/"; -var config = require('./config'); +console.log(`Requiring config from ${args.config || './config'}`) +const config = require(args.config || './config'); +config.databaseDirectory = args.databaseDirectory || config.databaseDirectory || "./database"; +const masterModFolder = path.join(config.databaseDirectory, "/masterMods/"); +mkdirp.sync(config.databaseDirectory); +mkdirp.sync(masterModFolder); // homebrew modules const getFactorioLocale = require("./lib/getFactorioLocale"); const stringUtils = require("./lib/stringUtils"); +const fileOps = require("_app/fileOps"); // homemade express middleware for token auth -const authenticate = require("./lib/authenticate"); - -// Library for create folder recursively if it does not exist -const mkdirp = require("mkdirp"); -mkdirp.sync("./database"); -mkdirp.sync(masterModFolder); -const averagedTimeSeries = require("averaged-timeseries"); -const deepmerge = require("deepmerge"); -const path = require("path"); -const fs = require("fs"); -const nedb = require("nedb"); - -const jwt = require('jsonwebtoken'); -const bcrypt = require('bcryptjs'); - -var crypto = require('crypto'); -var base64url = require('base64url'); +const authenticate = require("./lib/authenticate")(config); /** Sync */ function randomStringAsBase64Url(size) { @@ -54,6 +69,11 @@ function randomStringAsBase64Url(size) { if (!fs.existsSync("secret-api-token.txt")) { config.masterAuthSecret = randomStringAsBase64Url(256); fs.writeFileSync("config.json",JSON.stringify(config, null, 4)); + fs.writeFileSync("secret-api-token.txt", jwt.sign({ id: "api" }, config.masterAuthSecret, { + expiresIn: 86400*365 // expires in 1 year + })); + console.log("Generated new master authentication private key!"); + process.exit(0); } // write an auth token to file fs.writeFileSync("secret-api-token.txt", jwt.sign({ id: "api" }, config.masterAuthSecret, { @@ -62,12 +82,14 @@ fs.writeFileSync("secret-api-token.txt", jwt.sign({ id: "api" }, config.masterAu const express = require("express"); const compression = require('compression'); +const cookieParser = require('cookie-parser'); const ejs = require("ejs"); // Required for express post requests const bodyParser = require("body-parser"); const fileUpload = require('express-fileupload'); var app = express(); +app.use(cookieParser()); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ parameterLimit: 100000, @@ -82,10 +104,17 @@ app.set('views', path.join(__dirname, 'static')); app.set('view engine', 'html'); app.engine('html', ejs.renderFile); -var routes = require("./routes.js"); -routes(app); - +// give ejs access to some interesting information +app.use(function(req, res, next){ + res.locals.res = res; + res.locals.req = req; + res.locals.masterPlugins = masterPlugins; + res.locals.slaves = slaves; + res.locals.moment = moment; + next(); +}); +require("./routes.js")(app); // Set folder to serve static content from (the website) app.use(express.static('static')); // mod downloads @@ -94,14 +123,19 @@ app.use(express.static(masterModFolder)); // set up logging software const prometheusPrefix = "clusterio_"; const Prometheus = require('prom-client'); +const expressPrometheus = require('express-prometheus-request-metrics'); Prometheus.collectDefaultMetrics({ timeout: 10000 }); // collects RAM usage etc every 10 s -const httpRequestDurationMilliseconds = new Prometheus.Histogram({ - name: prometheusPrefix+'http_request_duration_ms', - help: 'Duration of HTTP requests in ms', - labelNames: ['route'], - // buckets for response time from 0.1ms to 500ms - buckets: [0.10, 5, 15, 50, 100, 200, 300, 400, 500] -}); +if(!config.disablePrometheusPushgateway){ + const pushgateway = new Prometheus.Pushgateway('http://hme.danielv.no:9091'); + setInterval(() => { + registerMoreMetrics(); + pushgateway.push({ jobName: 'clusterio', groupings: {instance: config.publicIP + ":" + config.masterPort, owner: config.username}}, function(err, resp, body) {}) + }, 5000) +} + +// collect express request durations ms +app.use(expressPrometheus(Prometheus)); + const endpointHitCounter = new Prometheus.Gauge({ name: prometheusPrefix+'endpoint_hit_gauge', help: "How many requests a particular endpoint has gotten", @@ -116,11 +150,6 @@ const prometheusWsUsageCounter = new Prometheus.Counter({ help: 'Websocket traffic', labelNames: ["connectionType", "instanceID"], }); -const prometheusProductionGauge = new Prometheus.Gauge({ - name: prometheusPrefix+'production_gauge', - help: 'Items produced by instance', - labelNames: ["instanceID", "itemName"], -}); const prometheusExportGauge = new Prometheus.Gauge({ name: prometheusPrefix+'export_gauge', help: 'Items exported by instance', @@ -141,18 +170,24 @@ const prometheusPlayerCountGauge = new Prometheus.Gauge({ help: 'Amount of players connected to this cluster', labelNames: ["instanceID", "instanceName"], }); +const prometheusUPSGauge = new Prometheus.Gauge({ + name: prometheusPrefix+'UPS_gauge', + help: 'UPS of the current server', + labelNames: ["instanceID", "instanceName"], +}); +const prometheusTickGauge = new Prometheus.Gauge({ + name: prometheusPrefix+'tick_gauge', + help: 'Servers current gametick', + labelNames: ["instanceID", "instanceName"], +}); const prometheusMasterInventoryGauge = new Prometheus.Gauge({ name: prometheusPrefix+'master_inventory_gauge', help: 'Amount of items stored on master', labelNames: ["itemName"], }); setInterval(()=>{ - let numberOfActiveSlaves = 0; - for(let instance in slaves){ - if(Date.now() - Number(slaves[instance].time) < 1000 * 30) numberOfActiveSlaves++; - } - prometheusConnectedInstancesCounter.set(numberOfActiveSlaves); -},10000); + fs.writeFileSync("database/items.json", JSON.stringify(db.items)); +},config.autosaveInterval || 60000); /** GET Prometheus metrics endpoint. Returns performance and usage metrics in a prometheus readable format. @memberof clusterioMaster @@ -161,53 +196,54 @@ GET Prometheus metrics endpoint. Returns performance and usage metrics in a prom */ app.get('/metrics', (req, res) => { endpointHitCounter.labels(req.route.path).inc(); - let reqStartTime = Date.now(); res.set('Content-Type', Prometheus.register.contentType); /// gather some static metrics - // playercount - for(let instanceID in slaves){try{ - prometheusPlayerCountGauge.labels(instanceID, slaves[instanceID].instanceName).set(Number(slaves[instanceID].playerCount) || 0); - }catch(e){}} + registerMoreMetrics(); + res.end(Prometheus.register.metrics()); +}); + +function registerMoreMetrics(){ + for(let instanceID in slaves){ + // playercount + try{ + prometheusPlayerCountGauge.labels(instanceID, slaves[instanceID].instanceName).set(Number(slaves[instanceID].playerCount) || 0); + }catch(e){} + // UPS + try{ + prometheusUPSGauge.labels(instanceID, slaves[instanceID].instanceName).set(Number(slaves[instanceID].meta.UPS) || 60); + if(slaves[instanceID].meta.tick && typeof slaves[instanceID].meta.tick === "number") prometheusUPSGauge.labels(instanceID, slaves[instanceID].instanceName).set(Number(slaves[instanceID].meta.tick) || 0); + }catch(e){} + } // inventory for(let key in db.items){ if(typeof db.items[key] == "number" || typeof db.items[key] == "string"){ prometheusMasterInventoryGauge.labels(key).set(Number(db.items[key]) || 0); } } - - res.end(Prometheus.register.metrics()); - httpRequestDurationMilliseconds.labels(req.route.path).observe(Date.now()-reqStartTime); -}); + // Slave count + let numberOfActiveSlaves = 0; + for(let instance in slaves){ + if(Date.now() - Number(slaves[instance].time) < 1000 * 30) numberOfActiveSlaves++; + } + prometheusConnectedInstancesCounter.set(numberOfActiveSlaves); +} // set up database -var Datastore = require('nedb'); -db = {}; -var LinvoDB = require("linvodb3"); -LinvoDB.dbPath = "./database/linvodb/"; -// database for items in system -// db.items = new Datastore({ filename: 'database/items.db', autoload: true }); - -// in memory database for combinator signals -db.signals = new Datastore({ filename: 'database/signals.db', autoload: true, inMemoryOnly: true}); -db.signals.ensureIndex({ fieldName: 'time', expireAfterSeconds: 3600 }, function (err) {}); - -// production chart database -db.flows = new LinvoDB("flows", {}, {}); - +const db = {}; (function(){ try{ - let x = fs.statSync("database/slaves.json"); - console.log("loading slaves from database/slaves.json"); - slaves = JSON.parse(fs.readFileSync("database/slaves.json")); + let x = fs.statSync(path.resolve(config.databaseDirectory, "/slaves.json")); + console.log(`loading slaves from path.resolve(config.databaseDirectory, "slaves.json")`); + slaves = JSON.parse(fs.readFileSync(path.resolve(config.databaseDirectory, "slaves.json"))); } catch (e){ slaves = {}; } try{ - x = fs.statSync("database/items.json"); - console.log("loading items from database/items.json"); - db.items = JSON.parse(fs.readFileSync("database/items.json")); + x = fs.statSync(path.resolve(config.databaseDirectory, "items.json")); + console.log(`loading items from ${path.resolve(config.databaseDirectory, "items.json")}`); + db.items = JSON.parse(fs.readFileSync(path.resolve(config.databaseDirectory, "items.json"))); } catch (e){ db.items = {}; } @@ -241,23 +277,35 @@ db.items.removeItem = function(object) { } // store slaves and inventory in a .json full of JSON data -process.on('SIGINT', function () { +process.on('SIGINT', shutdown); // ctrl + c +process.on('SIGHUP', shutdown); // terminal closed +async function shutdown() { console.log('Ctrl-C...'); + let exitStartTime = Date.now(); // set insane limit to slave length, if its longer than this we are probably being ddosed or something if(slaves && Object.keys(slaves).length < 2000000){ console.log("saving to slaves.json"); - fs.writeFileSync("database/slaves.json", JSON.stringify(slaves)); + fs.writeFileSync(path.resolve(config.databaseDirectory, "slaves.json"), JSON.stringify(slaves)); } else if(slaves) { console.log("Slave database too large, not saving ("+Object.keys(slaves).length+")"); } if(db.items && Object.keys(db.items).length < 50000){ console.log("saving to items.json"); - fs.writeFileSync("database/items.json", JSON.stringify(db.items)); + fs.writeFileSync(path.resolve(config.databaseDirectory, "items.json"), JSON.stringify(db.items)); } else if(slaves) { console.log("Item database too large, not saving ("+Object.keys(slaves).length+")"); } - process.exit(2); -}); + for(let i in masterPlugins){ + let plugin = masterPlugins[i]; + if(plugin.main && plugin.main.onExit && typeof plugin.main.onExit == "function"){ + let startTime = Date.now(); + await plugin.main.onExit(); + console.log("Plugin "+plugin.pluginConfig.name+" exited in "+(Date.now()-startTime)+"ms"); + } + } + console.log("Clusterio cleanly exited in "+(Date.now()-exitStartTime)+"ms"); + process.exit(0); +} /** POST World ID management. Slaves post here to tell the server they exist @@ -267,7 +315,6 @@ POST World ID management. Slaves post here to tell the server they exist */ app.post("/api/getID", authenticate.middleware, function(req,res) { endpointHitCounter.labels(req.route.path).inc(); - let reqStartTime = Date.now(); // request.body should be an object // {rconPort, rconPassword, serverPort, mac, time} // time us a unix timestamp we can use to check for how long the server has been unresponsive @@ -284,9 +331,8 @@ app.post("/api/getID", authenticate.middleware, function(req,res) { slaves[req.body.unique][k] = req.body[k]; } } - console.log("Slave registered: " + slaves[req.body.unique].mac + " : " + slaves[req.body.unique].serverPort+" at " + slaves[req.body.unique].publicIP + " with name " + slaves[req.body.unique].instanceName); + // console.log("Slave registered: " + slaves[req.body.unique].mac + " : " + slaves[req.body.unique].serverPort+" at " + slaves[req.body.unique].publicIP + " with name " + slaves[req.body.unique].instanceName); } - httpRequestDurationMilliseconds.labels(req.route.path).observe(Date.now()-reqStartTime); }); /** POST Allows you to add metadata related to slaves for other tools, like owner data, descriptions or statistics. @@ -296,7 +342,6 @@ POST Allows you to add metadata related to slaves for other tools, like owner da */ app.post("/api/editSlaveMeta", authenticate.middleware, function(req,res) { endpointHitCounter.labels(req.route.path).inc(); - let reqStartTime = Date.now(); // request.body should be an object // {instanceID, pass, meta:{x,y,z}} @@ -309,12 +354,11 @@ app.post("/api/editSlaveMeta", authenticate.middleware, function(req,res) { slaves[req.body.instanceID].meta = deepmerge(slaves[req.body.instanceID].meta, req.body.meta, {clone:true}); let metaPortion = JSON.stringify(req.body.meta); if(metaPortion.length > 50) metaPortion = metaPortion.substring(0,20) + "..."; - console.log("Updating slave "+slaves[req.body.instanceID].instanceName+": " + slaves[req.body.instanceID].mac + " : " + slaves[req.body.instanceID].serverPort+" at " + slaves[req.body.instanceID].publicIP, metaPortion); + // console.log("Updating slave "+slaves[req.body.instanceID].instanceName+": " + slaves[req.body.instanceID].mac + " : " + slaves[req.body.instanceID].serverPort+" at " + slaves[req.body.instanceID].publicIP, metaPortion); } else { res.send("ERROR: Invalid instanceID or password") } } - httpRequestDurationMilliseconds.labels(req.route.path).observe(Date.now()-reqStartTime); }); /** @@ -325,7 +369,6 @@ POST Get metadata from a single slave. For whenever you find the data returned b */ app.post("/api/getSlaveMeta", function (req, res) { endpointHitCounter.labels(req.route.path).inc(); - let reqStartTime = Date.now(); console.log("body", req.body); if(req.body && req.body.instanceID && req.body.password){ console.log("returning meta for ", req.body.instanceID); @@ -334,7 +377,6 @@ app.post("/api/getSlaveMeta", function (req, res) { res.status(400); res.send('{"INVALID REQUEST":1}'); } - httpRequestDurationMilliseconds.labels(req.route.path).observe(Date.now()-reqStartTime); }); // mod management // should handle uploading and checking if mods are uploaded @@ -346,7 +388,6 @@ POST Check if a mod has been uploaded to the master before. Only checks against */ app.post("/api/checkMod", function(req,res) { endpointHitCounter.labels(req.route.path).inc(); - let reqStartTime = Date.now(); let files = fs.readdirSync(masterModFolder); let found = false; files.forEach(file => { @@ -361,7 +402,6 @@ app.post("/api/checkMod", function(req,res) { res.send("found"); } res.end(); - httpRequestDurationMilliseconds.labels(req.route.path).observe(Date.now()-reqStartTime); }); /** POST endpoint for uploading mods to the master server. Required for automatic mod downloads with factorioClusterioClient (electron app, see seperate repo) @@ -371,22 +411,20 @@ POST endpoint for uploading mods to the master server. Required for automatic mo */ app.post("/api/uploadMod", authenticate.middleware, function(req,res) { endpointHitCounter.labels(req.route.path).inc(); - let reqStartTime = Date.now(); - if (!req.files) { - res.send('No files were uploaded.'); - return; - } else { - console.log(req.files.file); - req.files.file.mv('./database/masterMods/'+req.files.file.name, function(err) { + if (req.files && req.files.file) { + // console.log(req.files.file); + req.files.file.mv(path.resolve(config.databaseDirectory, "masterMods", req.files.file.name), function(err) { if (err) { res.status(500).send(err); } else { res.send('File uploaded!'); console.log("Uploaded mod: " + req.files.file.name); } - httpRequestDurationMilliseconds.labels(req.route.path).observe(Date.now()-reqStartTime); }); - } + } else { + res.send('No files were uploaded.'); + return; + } }); /** GET endpoint for getting information about all our slaves @@ -399,7 +437,6 @@ let slaveCache = { }; app.get("/api/slaves", function(req, res) { endpointHitCounter.labels(req.route.path).inc(); - let reqStartTime = Date.now(); res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); if(!slaveCache.cache || Date.now() - slaveCache.timestamp > 5000){ @@ -413,7 +450,6 @@ app.get("/api/slaves", function(req, res) { } res.send(slaveCache.cache); - httpRequestDurationMilliseconds.labels(req.route.path).observe(Date.now()-reqStartTime); }); var recievedItemStatisticsBySlaveID = {}; var sentItemStatisticsBySlaveID = {}; @@ -436,7 +472,6 @@ POST endpoint for storing items in master's inventory. */ app.post("/api/place", authenticate.middleware, function(req, res) { endpointHitCounter.labels(req.route.path).inc(); - let reqStartTime = Date.now(); res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); let x; @@ -456,10 +491,12 @@ app.post("/api/place", authenticate.middleware, function(req, res) { } }); } - if( x.instanceID - && x.instanceName - && !isNaN(Number(x.count))// This is in no way a proper authentication or anything, its just to make sure everybody are registered as slaves before modifying the cluster (or not, to maintain backwards compat) - /*&& stringUtils.hashCode(slaves[x.unique].rconPassword) == x.unique*/){ + if(x.instanceID + && x.instanceName + && !isNaN(Number(x.count))// This is in no way a proper authentication or anything, its just to make sure everybody are registered as slaves before modifying the cluster (or not, to maintain backwards compat) + /*&& stringUtils.hashCode(slaves[x.unique].rconPassword) == x.unique*/ + && x.name + && typeof x.name == "string"){ if(config.logItemTransfers){ console.log("added: " + req.body.name + " " + req.body.count+" from "+x.instanceName+" ("+x.instanceID+")"); } @@ -489,7 +526,6 @@ app.post("/api/place", authenticate.middleware, function(req, res) { } else { res.send("failure"); } - httpRequestDurationMilliseconds.labels(req.route.path).observe(Date.now()-reqStartTime); }); /** POST endpoint to remove items from DB when client orders items. @@ -505,7 +541,6 @@ POST endpoint to remove items from DB when client orders items. _doleDivisionFactor = {}; //If the server regularly can't fulfill requests, this number grows until it can. Then it slowly shrinks back down. app.post("/api/remove", authenticate.middleware, function(req, res) { endpointHitCounter.labels(req.route.path).inc(); - let reqStartTime = Date.now(); const doleDivisionRetardation = 10; //lower rates will equal more dramatic swings const maxDoleDivision = 250; //a higher cap will divide the store more ways, but will take longer to recover as supplies increase res.header("Access-Control-Allow-Origin", "*"); @@ -521,7 +556,11 @@ app.post("/api/remove", authenticate.middleware, function(req, res) { if(slaves[object.instanceID]) object.instanceName = slaves[object.instanceID].instanceName; let item = db.items[object.name] // console.dir(doc); - if (!item) { + if(!item + ||(( + config.disableImportsOfEverythingExceptElectricity === true || config.disableImportsOfEverythingExceptElectricity === "true" ) + && object.name != "electricity") + ){ if(config.logItemTransfers){ console.log('failure could not find ' + object.name); } @@ -559,58 +598,15 @@ app.post("/api/remove", authenticate.middleware, function(req, res) { prometheusDoleFactorGauge.labels(object.name).set(_doleDivisionFactor[object.name] || 0); prometheusImportGauge.labels(object.instanceID, object.name).inc(Number(object.count) || 0); res.send({count: object.count, name: object.name}); - httpRequestDurationMilliseconds.labels(req.route.path).observe(Date.now()-reqStartTime); } else { // if we didn't have enough, attempt giving out a smaller amount next time _doleDivisionFactor[object.name] = Math.min(maxDoleDivision, Math.max((_doleDivisionFactor[object.name]||0)||1, 1) * 2); res.send({name:object.name, count:0}); - httpRequestDurationMilliseconds.labels(req.route.path).observe(Date.now()-reqStartTime); //console.log('failure out of ' + object.name + " | " + object.count + " from " + object.instanceID + " ("+object.instanceName+")"); } } }); -// circuit stuff -/** -POST endpoint to send and store circuit frames on the master. -Gives no response - -@memberof clusterioMaster -@instance -@alias api/setSignal -@param {object} circuitFrame -@param {number} circuitFrame.time -*/ -app.post("/api/setSignal", authenticate.middleware, function(req,res) { - endpointHitCounter.labels(req.route.path).inc(); - let reqStartTime = Date.now(); - if(typeof req.body == "object" && req.body.time){ - db.signals.insert(req.body); - httpRequestDurationMilliseconds.labels(req.route.path).observe(Date.now()-reqStartTime); - // console.log("signal set"); - } -}); -/** -POST endpoint to read database of circuit signals sent to master - -@memberof clusterioMaster -@instance -@alias api/readSignal -@returns {object} circuitFrame -*/ -app.post("/api/readSignal", function(req,res) { - endpointHitCounter.labels(req.route.path).inc(); - let reqStartTime = Date.now(); - // request.body should be an object - // {since:UNIXTIMESTAMP,} - // we should send an array of all signals since then - db.signals.find({time:{$gte: req.body.since}}, function (err, docs) { - // $gte means greater than or equal to, meaning we only get entries newer than the timestamp - res.send(docs); - httpRequestDurationMilliseconds.labels(req.route.path).observe(Date.now()-reqStartTime); - // console.log(docs); - }); -}); /** GET endpoint to read the masters current inventory of items. @@ -622,7 +618,6 @@ GET endpoint to read the masters current inventory of items. // endpoint for getting an inventory of what we got app.get("/api/inventory", function(req, res) { endpointHitCounter.labels(req.route.path).inc(); - let reqStartTime = Date.now(); res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); // Check it and send it @@ -633,7 +628,6 @@ app.get("/api/inventory", function(req, res) { } } res.send(JSON.stringify(inventory)); - httpRequestDurationMilliseconds.labels(req.route.path).observe(Date.now()-reqStartTime); }); /** GET endpoint to read the masters inventory as an object with key:value pairs @@ -645,185 +639,39 @@ GET endpoint to read the masters inventory as an object with key:value pairs */ app.get("/api/inventoryAsObject", function(req, res) { endpointHitCounter.labels(req.route.path).inc(); - let reqStartTime = Date.now(); res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); // Check it and send it res.send(JSON.stringify(db.items)); - httpRequestDurationMilliseconds.labels(req.route.path).observe(Date.now()-reqStartTime); -}); - -// post flowstats here for production graphs -// {timestamp: Date, instanceID: string, data: {"item":number}} -/** -POST endpoint to log production graph statistics. Should contain a timestamp -gathered from Date.now(), a instanceID (also reffered to as "unique") and of -course the timeSeries data. - -@memberof clusterioMaster -@instance -@alias api/logStats -@param {object} JSON {timestamp: Date.now(), instanceID: "string", data: {"item": number}} -@returns {string} failure -*/ -app.post("/api/logStats", authenticate.middleware, function(req,res) { - endpointHitCounter.labels(req.route.path).inc(); - let reqStartTime = Date.now(); - if(typeof req.body == "object" && req.body.instanceID && req.body.timestamp && req.body.data) { - if(Number(req.body.timestamp) != NaN){ - req.body.timestamp = Number(req.body.timestamp); - db.flows.insert({ - instanceID: req.body.instanceID, - timestamp: req.body.timestamp, - data: req.body.data, - }); - try{ - Object.keys(req.body.data).forEach(itemName => { - prometheusProductionGauge.labels(req.body.instanceID, itemName).inc(Number(req.body.data[itemName]) || 0); - }); - }catch(e){console.log(e)}; - console.log("inserted: " + req.body.instanceID + " | " + req.body.timestamp); - } else { - console.log("error invalid timestamp " + req.body.timestamp); - res.send("failure"); - } - } else { - res.send("failure"); - } - httpRequestDurationMilliseconds.labels(req.route.path).observe(Date.now()-reqStartTime); -}); -// {instanceID: string, fromTime: Date, toTime, Date} -/** -POST endpoint to get timeSeries statistics stored on the master. Can give production -graphs and other IO statistics. - -@memberof clusterioMaster -@instance -@alias api/getStats -@param {object} JSON -@returns {object} - with statistics -*/ -app.post("/api/getStats", function(req,res) { - endpointHitCounter.labels(req.route.path).inc(); - let reqStartTime = Date.now(); - if(typeof req.body == "string"){ - req.body = JSON.parse(req.body); - } - // console.log(req.body); - if(typeof req.body == "object" && req.body.instanceID && req.body.statistic === undefined) { - // if not specified, get stats for last 24 hours - if(!req.body.fromTime) { - req.body.fromTime = Date.now() - 86400000 // 24 hours in MS - } - if(!req.body.toTime) { - req.body.toTime = Date.now(); - } - console.log("Looking... " + JSON.stringify(req.body)); - db.flows.find({ - instanceID: req.body.instanceID, - }, function(err, docs) { - let entries = docs.filter(function (el) { - return el.timestamp <= req.body.toTime && el.timestamp >= req.body.fromTime; - }); - // console.log(entries); - res.send(entries); - }); - } else if(typeof req.body == "object" && req.body.instanceID && typeof req.body.instanceID == "string" && req.body.itemName){ - if(req.body.statistic == "place"){ - console.log(`sending place data for instanceID ${req.body.instanceID} ${req.body.itemName}`); - // Gather data - //console.log(recievedItemStatisticsBySlaveID) - let itemStats = recievedItemStatisticsBySlaveID[req.body.instanceID]; - if(itemStats === undefined){ - res.send({statusForDebugging:"no data available"}); - return false; - } - let data = itemStats.get(config.itemStats.maxEntries, req.body.itemName); - - res.send({ - maxEntries:config.itemStats.maxEntries, - entriesPerSecond: config.itemStats.entriesPerSecond, - data: data, - }); - } else if(req.body.statistic == "remove"){ - console.log(`sending remove data for instanceID ${req.body.instanceID} ${req.body.itemName}`); - // Gather data - // console.log(sentItemStatisticsBySlaveID) - let itemStats = sentItemStatisticsBySlaveID[req.body.instanceID]; - console.log(itemStats) - if(typeof itemStats == "object"){ - let data = itemStats.get(config.itemStats.maxEntries, req.body.itemName); - //console.log(itemStats.get(config.itemStats.maxEntries, req.body.itemName)); - res.send({ - maxEntries:config.itemStats.maxEntries, - entriesPerSecond: config.itemStats.entriesPerSecond, - data: data, - }); - } else { - res.send({statusForDebugging:"no data available"}); - } - } - } - httpRequestDurationMilliseconds.labels(req.route.path).observe(Date.now()-reqStartTime); }); /** -POST endpoint. Modified version of api/getStats which feeds the web interface production graphs -at /nodes. It is like getStats but it does not report items which were not -produced at the moment of recording. (This is to save space, the 0-items were -making up about 92% of the response body weight.) + GET endpoint to read the masters inventory as an object with key:value pairs -@memberof clusterioMaster -@instance -@alias api/getTimelineStats -@param {object} JSON {instanceID:1941029, fromTime: ???, toTime: ???} -@returns {object[]} timeseries where each entry is a set point in time. -*/ -app.post("/api/getTimelineStats", function(req,res) { + @memberof clusterioMaster + @instance + @alias api/modData + @returns {object} JSON + */ + +// wrap a request in an promise + async function downloadPage(url) { + return new Promise((resolve, reject) => { + request(url, (error, response, body) => { + resolve(body); + }); + }); + } + +app.get("/api/modmeta", async function(req, res) { endpointHitCounter.labels(req.route.path).inc(); - let reqStartTime = Date.now(); - console.log(req.body); - if(typeof req.body == "object" && req.body.instanceID) { - // if not specified, get stats for last 24 hours - if(!req.body.fromTime) { - req.body.fromTime = Date.now() - 86400000 // 24 hours in MS - } - if(!req.body.toTime) { - req.body.toTime = Date.now(); - } - console.log("Looking... " + JSON.stringify(req.body)); - db.flows.find({ - instanceID: req.body.instanceID, - }, function(err, docs) { - if(err) { - console.error(err); - return; - } - - //Filter out all the entries outside the time range. - docs = docs.filter(function (el) { - return el.timestamp <= req.body.toTime && el.timestamp >= req.body.fromTime; - }); - - //Filter out all the elements that weren't produced. - docs.forEach(el => { - for(let key in el.data) { - if(el.data[key] === '0') { - // Set value to undefined instead of using the delete keyword. - // This is because the delete keyword is super duper slow. - // https://stackoverflow.com/questions/208105/how-do-i-remove-a-property-from-a-javascript-object - - //delete el.data[key]; - el.data[key] = undefined; - } - } - }); - - res.send(docs); - httpRequestDurationMilliseconds.labels(req.route.path).observe(Date.now()-reqStartTime); - }); - } + res.header("Access-Control-Allow-Origin", "*"); + res.setHeader('Content-Type', 'application/json'); + let modData = await downloadPage("https://mods.factorio.com/api/mods/" + req.query.modname); + res.send(modData); }); + + /** POST endpoint for running commands on slaves. Requires x-access-token header to be set. Find you api token in secret-api-token.txt on the master (after running it once) @@ -836,7 +684,6 @@ Requires x-access-token header to be set. Find you api token in secret-api-token */ app.post("/api/runCommand", (req,res) => { endpointHitCounter.labels(req.route.path).inc(); - let reqStartTime = Date.now(); var token = req.headers['x-access-token']; if(!token) return res.status(401).send({ auth: false, message: 'No token provided.' }); @@ -859,7 +706,6 @@ app.post("/api/runCommand", (req,res) => { wsSlaves[instanceID].runCommand(body.command); } res.status(200).send({auth: true, message: "success", response: "Cluster wide messaging initiated"}); - httpRequestDurationMilliseconds.labels(req.route.path).observe(Date.now()-reqStartTime); } else if ( body.instanceID && wsSlaves[body.instanceID] @@ -870,11 +716,9 @@ app.post("/api/runCommand", (req,res) => { // execute command wsSlaves[body.instanceID].runCommand(body.command, data => { res.status(200).send({auth: true, message: "success", data}); - httpRequestDurationMilliseconds.labels(req.route.path).observe(Date.now()-reqStartTime); }); } else { res.status(400).send({auth: true, message: "Error: invalid request.body"}); - httpRequestDurationMilliseconds.labels(req.route.path).observe(Date.now()-reqStartTime); } }); }); @@ -888,16 +732,30 @@ GET endpoint. Returns factorio's base locale as a JSON object. */ app.get("/api/getFactorioLocale", function(req,res){ endpointHitCounter.labels(req.route.path).inc(); - let reqStartTime = Date.now(); getFactorioLocale.asObject(config.factorioDirectory, "en", (err, factorioLocale) => { res.send(factorioLocale); - httpRequestDurationMilliseconds.labels(req.route.path).observe(Date.now()-reqStartTime); }); }); -var server = require("http").Server(app); -server.listen(config.masterPort || 8080, function () { - console.log("Listening on port %s...", server.address().port); -}); +if(args.sslPort || config.sslPort){ + try{ + var certificate = fs.readFileSync(path.resolve(config.databaseDirectory, 'certificates/cert.crt')); + var privateKey = fs.readFileSync(path.resolve(config.databaseDirectory, 'certificates/cert.key')); + + httpsServer = require("https").createServer({ + key: privateKey, + cert: certificate + }, app).listen(args.sslPort || config.sslPort); + } catch(e){ + console.error(e) + } +} +if(args.masterPort || config.masterPort){ + server = require("http").Server(app); + server.listen(args.masterPort || config.masterPort || 8080, function () { + console.log("Listening on port %s...", server.address().port); + }); +} + /* Websockets for remoteMap */ var io = require("socket.io")(server); var slaveMappers = {}; @@ -1024,7 +882,7 @@ class wsSlave { runCommand(command, callback){ let commandID = Math.random().toString(); this.socket.emit("runCommand", {command, commandID}); - this.commandsWaitingForReturn[commandID] = {callback, timestamp: Date.now()}; + if(commandID) this.commandsWaitingForReturn[commandID] = {callback, timestamp: Date.now()}; } } io.on('connection', function (socket) { @@ -1043,7 +901,7 @@ io.on('connection', function (socket) { if(terminatedConnections > 0) console.log("SOCKET | There are currently "+currentConnections+" websocket connections, deleting "+terminatedConnections+" on timeout"); // tell our friend that we are listening - socket.emit('hello', { hello: 'world' }); + setTimeout(()=>socket.emit('hello', { hello: 'world' }), 5000); /* initial processing for remoteMap */ socket.on('registerSlaveMapper', function (data) { @@ -1080,4 +938,49 @@ io.on('connection', function (socket) { }); }); +// handle plugins on the master +pluginManagement(); +masterPlugins = []; +async function pluginManagement(){ + let startPluginLoad = Date.now(); + masterPlugins = await getPlugins(); + console.log("All plugins loaded in "+(Date.now() - startPluginLoad)+"ms"); + return; +} + +async function getPlugins(){ + const pluginManager = require("./lib/manager/pluginManager.js")(config); + let plugins = []; + let pluginsToLoad = await pluginManager.getPlugins(); + for(let i = 0; i < pluginsToLoad.length; i++){ + let pluginStartedLoading = Date.now(); // just for logging + let stats = await fs.stat(pluginsToLoad[i].pluginPath); + if(stats.isDirectory()){ + let pluginConfig = pluginsToLoad[i]; + if(pluginConfig.masterPlugin && pluginConfig.enabled){ + let masterPlugin = require(path.resolve(pluginConfig.pluginPath, pluginConfig.masterPlugin)); + plugins.push({ + main:new masterPlugin({ + config, + pluginConfig, + path: pluginConfig.pluginPath, + socketio: io, + express: app, + Prometheus, + }), + pluginConfig, + }); + + console.log("Loaded plugin "+pluginConfig.name+" in "+(Date.now() - pluginStartedLoading)+"ms"); + } + } + } + for(let i in plugins){ + let plugin = plugins[i]; + if(plugin.main.onLoadFinish && typeof plugin.main.onLoadFinish == "function") await plugin.main.onLoadFinish({plugins}); + } + return plugins; +} + module.exports = app; +})(); diff --git a/node_modules/_app/clusterTools/checkHotpatchInstallation.js b/node_modules/_app/clusterTools/checkHotpatchInstallation.js new file mode 100644 index 00000000..4ffb825f --- /dev/null +++ b/node_modules/_app/clusterTools/checkHotpatchInstallation.js @@ -0,0 +1,9 @@ +module.exports = async function checkHotpatchInstallation(messageInterface){ + let yn = await messageInterface("/silent-command if remote.interfaces['hotpatch'] then rcon.print('true') else rcon.print('false') end"); + yn = yn.replace(/(\r\n\t|\n|\r\t)/gm, ""); + if(yn == "true"){ + return true; + } else if(yn == "false"){ + return false; + } +} diff --git a/node_modules/_app/clusterTools/getCommand.js b/node_modules/_app/clusterTools/getCommand.js new file mode 100644 index 00000000..8d5afc11 --- /dev/null +++ b/node_modules/_app/clusterTools/getCommand.js @@ -0,0 +1,16 @@ +const fs = require("fs-extra"); + +const getLua = require("./getLua.js"); + +let commandCache; +module.exports = async function getCommand(file, COMPRESS = true){ + commandCache = commandCache || {}; + if(!commandCache[file]){ + commandCache[file] = await getLua(file); + return commandCache[file]; + } else if(typeof commandCache[file] == "string"){ + return commandCache[file]; + } else { + throw new Error("Command not found"); + } +} diff --git a/node_modules/_app/clusterTools/getInstanceName.js b/node_modules/_app/clusterTools/getInstanceName.js new file mode 100644 index 00000000..018242a7 --- /dev/null +++ b/node_modules/_app/clusterTools/getInstanceName.js @@ -0,0 +1,40 @@ +const needle = require("needle"); + +module.exports = function(config){ + const instances = []; + + if(config){ + return function getInstanceName(instanceID){ + return new Promise((resolve, reject) => { + let instance = instances[instanceID]; + if(!instance){ + needle.get(config.masterIP+":"+config.masterPort+ '/api/slaves', (err, response) => { + if(err || response.statusCode != 200) { + console.log("Unable to get JSON master/api/slaves, master might be unaccessible"); + } else if (response && response.body) { + if(Buffer.isBuffer(response.body)) {console.log(response.body.toString("utf-8")); reject(new Error())} + try { + for (let index in response.body) + instances[index] = response.body[index].instanceName; + } catch (e){ + console.log(e); + return null; + } + instance = instances[instanceID] + if (!instance) instance = instanceID; //somehow the master doesn't know the instance + resolve(instance); + } + }); + } else { + resolve(instance); + } + }); + } + } else { + return async function thisAintGonnaWork(){ + let err = new Error("getInstanceName from _app/clusterTools needs to be called with {config} to work"); + console.log(err); + throw err; + } + } +} diff --git a/node_modules/_app/clusterTools/getLua.js b/node_modules/_app/clusterTools/getLua.js new file mode 100644 index 00000000..a54c8dac --- /dev/null +++ b/node_modules/_app/clusterTools/getLua.js @@ -0,0 +1,40 @@ +const fs = require("fs-extra"); + +module.exports = function getLua(filePath, COMPRESS_LUA = true){ + return new Promise((resolve, reject) => { + fs.readFile(filePath, "utf8", (err, contents) => { + if(err){ + reject(err); + } else { + // split content into lines + contents = contents.split(/\r?\n/); + + // join those lines after making them safe again + contents = contents.reduce((acc, val) => { + val = val.replace(/\\/g ,'\\\\'); + // remove leading and trailing spaces + val = val.trim(); + // escape single quotes + val = val.replace(/'/g ,'\\\''); + + // remove single line comments + let singleLineCommentPosition = val.indexOf("--"); + let multiLineCommentPosition = val.indexOf("--[["); + + if(multiLineCommentPosition === -1 && singleLineCommentPosition !== -1) { + val = val.substr(0, singleLineCommentPosition); + } + + return acc + val + '\\n'; + }, ""); // need the "" or it will not process the first row, potentially leaving a single line comment in that disables the whole code + + // console.log(contents); + + // this takes about 46 ms to minify train_stop_tracking.lua in my tests on an i3 6100u + if(COMPRESS_LUA) contents = require("luamin").minify(contents); + + resolve(contents); + } + }); + }); +} diff --git a/node_modules/_app/clusterTools/index.js b/node_modules/_app/clusterTools/index.js new file mode 100644 index 00000000..40475890 --- /dev/null +++ b/node_modules/_app/clusterTools/index.js @@ -0,0 +1,41 @@ +/** + +Functions frequently needed from different places in the cluster. Ex: + * Loading Lua scripts + * Checking if hotpatch is installed + * Getting an instance's displayname from their ID + +*/ + +module.exports = ({config} = {}) => ({ + /** + async function to get Lua from a file, remove comments, minify and return. + + arg1 is the file path + arg2 is whether or not to compress the Lua. Makes debugging harder, but reduces bandwidth when many players are online. Recommended for polling commands. Defaults to true. + Promise resolves with a string of Lua. + */ + getLua: require("./getLua.js"), + /** + async function to check whether hotpatch is installed in a server. + + First argument is the servers async messageInterface for rcon return values + Promise resolves with boolean. + */ + checkHotpatchInstallation: require("./checkHotpatchInstallation.js"), + /** + async function to get an instances displayname from their instanceID + + arg1 is the instanceID + Promise resolves with a string, either the displayName or instanceID if not found. + */ + getInstanceName: require("./getInstanceName.js")(config), + /** + async function same as getLua, but using an in memory cache to lower FS operations. + + arg1 is the file path + arg2 is whether or not to compress, default true + Promise resolves with a string of Lua. + */ + getCommand: require("./getCommand.js"), +}) \ No newline at end of file diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 079ff096..00000000 --- a/package-lock.json +++ /dev/null @@ -1,9638 +0,0 @@ -{ - "name": "clusterio", - "version": "1.7.1", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "JSONStream": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz", - "integrity": "sha1-cH92HgHa6eFvG8+TcDt4xwlmV5o=", - "dev": true, - "requires": { - "jsonparse": "1.3.1", - "through": "2.3.8" - } - }, - "abbrev": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", - "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=", - "dev": true - }, - "abstract-leveldown": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-2.6.3.tgz", - "integrity": "sha512-2++wDf/DYqkPR3o5tbfdhF96EfMApo1GpPfzOsR/ZYXdkSmELlvOOEAl9iKkRsktMPHdGjO4rtkBpf2I7TiTeA==", - "requires": { - "xtend": "4.0.1" - } - }, - "accepts": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz", - "integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=", - "requires": { - "mime-types": "2.1.17", - "negotiator": "0.6.1" - } - }, - "acorn": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-1.2.2.tgz", - "integrity": "sha1-yM4n3grMdtiW0rH6099YjZ6C8BQ=" - }, - "after": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", - "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=" - }, - "ajv": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.2.3.tgz", - "integrity": "sha1-wG9Zh3jETGsWGrr+NGa4GtGBTtI=", - "requires": { - "co": "4.6.0", - "fast-deep-equal": "1.0.0", - "json-schema-traverse": "0.3.1", - "json-stable-stringify": "1.0.1" - } - }, - "align-text": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", - "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", - "dev": true, - "requires": { - "kind-of": "3.2.2", - "longest": "1.0.1", - "repeat-string": "1.6.1" - } - }, - "amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" - }, - "ansi": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/ansi/-/ansi-0.3.1.tgz", - "integrity": "sha1-DELU+xcWDVqa8eSEus4cZpIsGyE=" - }, - "ansi-align": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", - "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", - "requires": { - "string-width": "2.1.1" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "requires": { - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "requires": { - "ansi-regex": "3.0.0" - } - } - } - }, - "ansi-escapes": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", - "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=" - }, - "ansi-html": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", - "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", - "dev": true - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" - }, - "anymatch": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", - "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", - "dev": true, - "requires": { - "micromatch": "2.3.11", - "normalize-path": "2.1.1" - }, - "dependencies": { - "micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", - "dev": true, - "requires": { - "arr-diff": "2.0.0", - "array-unique": "0.2.1", - "braces": "1.8.5", - "expand-brackets": "0.1.5", - "extglob": "0.3.2", - "filename-regex": "2.0.1", - "is-extglob": "1.0.0", - "is-glob": "2.0.1", - "kind-of": "3.2.2", - "normalize-path": "2.1.1", - "object.omit": "2.0.1", - "parse-glob": "3.0.4", - "regex-cache": "0.4.4" - } - } - } - }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" - }, - "are-we-there-yet": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", - "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", - "requires": { - "delegates": "1.0.0", - "readable-stream": "2.3.3" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "readable-stream": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", - "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" - } - }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "requires": { - "safe-buffer": "5.1.1" - } - } - } - }, - "argparse": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", - "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", - "requires": { - "sprintf-js": "1.0.3" - } - }, - "argv": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/argv/-/argv-0.0.2.tgz", - "integrity": "sha1-7L0W+JSbFXGDcRsb2jNPN4QBhas=" - }, - "arr-diff": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", - "dev": true, - "requires": { - "arr-flatten": "1.1.0" - } - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true - }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" - }, - "array-iterate": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-iterate/-/array-iterate-1.1.1.tgz", - "integrity": "sha1-hlv3+K851rCYLGCQKRSsdrwBCPY=", - "dev": true - }, - "array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", - "dev": true - }, - "arraybuffer.slice": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz", - "integrity": "sha1-8zshWfBTKj8xB6JywMz70a0peco=" - }, - "as-table": { - "version": "1.0.31", - "resolved": "https://registry.npmjs.org/as-table/-/as-table-1.0.31.tgz", - "integrity": "sha1-0AGAAk7LttGnRxUN91HTcWrqgWY=", - "requires": { - "printable-characters": "1.0.38" - } - }, - "asap": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.5.tgz", - "integrity": "sha1-UidltQw1EEkOUtfc/ghe+bqWlY8=" - }, - "asn1": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" - }, - "assert": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", - "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", - "requires": { - "util": "github:ivshti/node-util#97b6beed8e0e478cd4184965ac94bf316f94e5f5" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - }, - "assertion-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.2.tgz", - "integrity": "sha1-E8pRXYYgbaC6xm6DTdOX2HWBCUw=", - "dev": true - }, - "ast-types": { - "version": "0.8.15", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.8.15.tgz", - "integrity": "sha1-ju8IJ/BN/w7IhXupJavj/qYZTlI=" - }, - "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" - }, - "async-each": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", - "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", - "dev": true - }, - "async-limiter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "atob": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.0.3.tgz", - "integrity": "sha1-GcenYEc3dEaPILLS0DNyrX1Mv10=", - "dev": true - }, - "averaged-timeseries": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/averaged-timeseries/-/averaged-timeseries-1.4.0.tgz", - "integrity": "sha512-1WrcZHh/f9g3z36Ckd83n5axXUScLwhfvWFdAiZK29YS48UzPhiYyPWLv0iJGYt9w0iJ0ubTTponL+fUebIkbQ==", - "requires": { - "assert": "1.4.1" - } - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" - }, - "aws4": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", - "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=" - }, - "axios": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.16.2.tgz", - "integrity": "sha1-uk+S8XFn37q0CYN4VFS5rBScPG0=", - "requires": { - "follow-redirects": "1.2.5", - "is-buffer": "1.1.5" - } - }, - "babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", - "dev": true, - "requires": { - "chalk": "1.1.3", - "esutils": "2.0.2", - "js-tokens": "3.0.2" - } - }, - "babel-core": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.0.tgz", - "integrity": "sha1-rzL3izGm/O8RnIew/Y2XU/A6C7g=", - "dev": true, - "requires": { - "babel-code-frame": "6.26.0", - "babel-generator": "6.26.0", - "babel-helpers": "6.24.1", - "babel-messages": "6.23.0", - "babel-register": "6.26.0", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "convert-source-map": "1.5.0", - "debug": "2.6.9", - "json5": "0.5.1", - "lodash": "4.17.4", - "minimatch": "3.0.4", - "path-is-absolute": "1.0.1", - "private": "0.1.7", - "slash": "1.0.0", - "source-map": "0.5.7" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "babel-generator": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.0.tgz", - "integrity": "sha1-rBriAHC3n248odMmlhMFN3TyDcU=", - "dev": true, - "requires": { - "babel-messages": "6.23.0", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "detect-indent": "4.0.0", - "jsesc": "1.3.0", - "lodash": "4.17.4", - "source-map": "0.5.7", - "trim-right": "1.0.1" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "babel-helper-bindify-decorators": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-bindify-decorators/-/babel-helper-bindify-decorators-6.24.1.tgz", - "integrity": "sha1-FMGeXxQte0fxmlJDHlKxzLxAozA=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-helper-builder-binary-assignment-operator-visitor": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz", - "integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=", - "dev": true, - "requires": { - "babel-helper-explode-assignable-expression": "6.24.1", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-helper-builder-react-jsx": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-helper-builder-react-jsx/-/babel-helper-builder-react-jsx-6.26.0.tgz", - "integrity": "sha1-Of+DE7dci2Xc7/HzHTg+D/KkCKA=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "esutils": "2.0.2" - } - }, - "babel-helper-call-delegate": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", - "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", - "dev": true, - "requires": { - "babel-helper-hoist-variables": "6.24.1", - "babel-runtime": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-helper-define-map": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz", - "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=", - "dev": true, - "requires": { - "babel-helper-function-name": "6.24.1", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "lodash": "4.17.4" - } - }, - "babel-helper-explode-assignable-expression": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz", - "integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-helper-explode-class": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-explode-class/-/babel-helper-explode-class-6.24.1.tgz", - "integrity": "sha1-fcKjkQ3uAHBW4eMdZAztPVTqqes=", - "dev": true, - "requires": { - "babel-helper-bindify-decorators": "6.24.1", - "babel-runtime": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-helper-function-name": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", - "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", - "dev": true, - "requires": { - "babel-helper-get-function-arity": "6.24.1", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-helper-get-function-arity": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", - "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-helper-hoist-variables": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", - "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-helper-optimise-call-expression": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", - "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-helper-regex": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz", - "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "lodash": "4.17.4" - } - }, - "babel-helper-remap-async-to-generator": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz", - "integrity": "sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=", - "dev": true, - "requires": { - "babel-helper-function-name": "6.24.1", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-helper-replace-supers": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", - "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", - "dev": true, - "requires": { - "babel-helper-optimise-call-expression": "6.24.1", - "babel-messages": "6.23.0", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-helpers": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", - "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" - } - }, - "babel-messages": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", - "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-check-es2015-constants": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", - "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-syntax-async-functions": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", - "integrity": "sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU=", - "dev": true - }, - "babel-plugin-syntax-async-generators": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.13.0.tgz", - "integrity": "sha1-a8lj67FuzLrmuStZbrfzXDQqi5o=", - "dev": true - }, - "babel-plugin-syntax-class-constructor-call": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-constructor-call/-/babel-plugin-syntax-class-constructor-call-6.18.0.tgz", - "integrity": "sha1-nLnTn+Q8hgC+yBRkVt3L1OGnZBY=", - "dev": true - }, - "babel-plugin-syntax-class-properties": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz", - "integrity": "sha1-1+sjt5oxf4VDlixQW4J8fWysJ94=", - "dev": true - }, - "babel-plugin-syntax-decorators": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-decorators/-/babel-plugin-syntax-decorators-6.13.0.tgz", - "integrity": "sha1-MSVjtNvePMgGzuPkFszurd0RrAs=", - "dev": true - }, - "babel-plugin-syntax-do-expressions": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-do-expressions/-/babel-plugin-syntax-do-expressions-6.13.0.tgz", - "integrity": "sha1-V0d1YTmqJtOQ0JQQsDdEugfkeW0=", - "dev": true - }, - "babel-plugin-syntax-dynamic-import": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz", - "integrity": "sha1-jWomIpyDdFqZgqRBBRVyyqF5sdo=", - "dev": true - }, - "babel-plugin-syntax-exponentiation-operator": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz", - "integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=", - "dev": true - }, - "babel-plugin-syntax-export-extensions": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-export-extensions/-/babel-plugin-syntax-export-extensions-6.13.0.tgz", - "integrity": "sha1-cKFITw+QiaToStRLrDU8lbmxJyE=", - "dev": true - }, - "babel-plugin-syntax-flow": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz", - "integrity": "sha1-TDqyCiryaqIM0lmVw5jE63AxDI0=", - "dev": true - }, - "babel-plugin-syntax-function-bind": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-function-bind/-/babel-plugin-syntax-function-bind-6.13.0.tgz", - "integrity": "sha1-SMSV8Xe98xqYHnMvVa3AvdJgH0Y=", - "dev": true - }, - "babel-plugin-syntax-jsx": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz", - "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=", - "dev": true - }, - "babel-plugin-syntax-object-rest-spread": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", - "integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U=", - "dev": true - }, - "babel-plugin-syntax-trailing-function-commas": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz", - "integrity": "sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM=", - "dev": true - }, - "babel-plugin-system-import-transformer": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/babel-plugin-system-import-transformer/-/babel-plugin-system-import-transformer-3.1.0.tgz", - "integrity": "sha1-038Mro5h7zkGAggzHZMbXmMNfF8=", - "dev": true, - "requires": { - "babel-plugin-syntax-dynamic-import": "6.18.0" - } - }, - "babel-plugin-transform-async-generator-functions": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-generator-functions/-/babel-plugin-transform-async-generator-functions-6.24.1.tgz", - "integrity": "sha1-8FiQAUX9PpkHpt3yjaWfIVJYpds=", - "dev": true, - "requires": { - "babel-helper-remap-async-to-generator": "6.24.1", - "babel-plugin-syntax-async-generators": "6.13.0", - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-async-to-generator": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz", - "integrity": "sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=", - "dev": true, - "requires": { - "babel-helper-remap-async-to-generator": "6.24.1", - "babel-plugin-syntax-async-functions": "6.13.0", - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-class-constructor-call": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-class-constructor-call/-/babel-plugin-transform-class-constructor-call-6.24.1.tgz", - "integrity": "sha1-gNwoVQWsBn3LjWxl4vbxGrd2Xvk=", - "dev": true, - "requires": { - "babel-plugin-syntax-class-constructor-call": "6.18.0", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" - } - }, - "babel-plugin-transform-class-properties": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz", - "integrity": "sha1-anl2PqYdM9NvN7YRqp3vgagbRqw=", - "dev": true, - "requires": { - "babel-helper-function-name": "6.24.1", - "babel-plugin-syntax-class-properties": "6.13.0", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" - } - }, - "babel-plugin-transform-decorators": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-decorators/-/babel-plugin-transform-decorators-6.24.1.tgz", - "integrity": "sha1-eIAT2PjGtSIr33s0Q5Df13Vp4k0=", - "dev": true, - "requires": { - "babel-helper-explode-class": "6.24.1", - "babel-plugin-syntax-decorators": "6.13.0", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-plugin-transform-decorators-legacy": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-decorators-legacy/-/babel-plugin-transform-decorators-legacy-1.3.4.tgz", - "integrity": "sha1-dBtY9sW86eYCfgiC2cmU8E82aSU=", - "dev": true, - "requires": { - "babel-plugin-syntax-decorators": "6.13.0", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" - } - }, - "babel-plugin-transform-do-expressions": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-do-expressions/-/babel-plugin-transform-do-expressions-6.22.0.tgz", - "integrity": "sha1-KMyvkoEtlJws0SgfaQyP3EaK6bs=", - "dev": true, - "requires": { - "babel-plugin-syntax-do-expressions": "6.13.0", - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-es2015-arrow-functions": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", - "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-es2015-block-scoped-functions": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", - "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-es2015-block-scoping": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz", - "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "lodash": "4.17.4" - } - }, - "babel-plugin-transform-es2015-classes": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", - "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", - "dev": true, - "requires": { - "babel-helper-define-map": "6.26.0", - "babel-helper-function-name": "6.24.1", - "babel-helper-optimise-call-expression": "6.24.1", - "babel-helper-replace-supers": "6.24.1", - "babel-messages": "6.23.0", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-plugin-transform-es2015-computed-properties": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", - "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" - } - }, - "babel-plugin-transform-es2015-destructuring": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", - "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-es2015-duplicate-keys": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz", - "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-plugin-transform-es2015-for-of": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", - "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-es2015-function-name": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", - "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", - "dev": true, - "requires": { - "babel-helper-function-name": "6.24.1", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-plugin-transform-es2015-literals": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", - "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-es2015-modules-amd": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", - "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", - "dev": true, - "requires": { - "babel-plugin-transform-es2015-modules-commonjs": "6.26.0", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" - } - }, - "babel-plugin-transform-es2015-modules-commonjs": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.0.tgz", - "integrity": "sha1-DYOUApt9xqvhqX7xgeAHWN0uXYo=", - "dev": true, - "requires": { - "babel-plugin-transform-strict-mode": "6.24.1", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-plugin-transform-es2015-modules-systemjs": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", - "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", - "dev": true, - "requires": { - "babel-helper-hoist-variables": "6.24.1", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" - } - }, - "babel-plugin-transform-es2015-modules-umd": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz", - "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=", - "dev": true, - "requires": { - "babel-plugin-transform-es2015-modules-amd": "6.24.1", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" - } - }, - "babel-plugin-transform-es2015-object-super": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz", - "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", - "dev": true, - "requires": { - "babel-helper-replace-supers": "6.24.1", - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-es2015-parameters": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", - "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", - "dev": true, - "requires": { - "babel-helper-call-delegate": "6.24.1", - "babel-helper-get-function-arity": "6.24.1", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-plugin-transform-es2015-shorthand-properties": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", - "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-plugin-transform-es2015-spread": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", - "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-es2015-sticky-regex": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", - "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", - "dev": true, - "requires": { - "babel-helper-regex": "6.26.0", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-plugin-transform-es2015-template-literals": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", - "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-es2015-typeof-symbol": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", - "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-es2015-unicode-regex": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", - "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", - "dev": true, - "requires": { - "babel-helper-regex": "6.26.0", - "babel-runtime": "6.26.0", - "regexpu-core": "2.0.0" - } - }, - "babel-plugin-transform-exponentiation-operator": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz", - "integrity": "sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=", - "dev": true, - "requires": { - "babel-helper-builder-binary-assignment-operator-visitor": "6.24.1", - "babel-plugin-syntax-exponentiation-operator": "6.13.0", - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-export-extensions": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-export-extensions/-/babel-plugin-transform-export-extensions-6.22.0.tgz", - "integrity": "sha1-U3OLR+deghhYnuqUbLvTkQm75lM=", - "dev": true, - "requires": { - "babel-plugin-syntax-export-extensions": "6.13.0", - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-flow-strip-types": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.22.0.tgz", - "integrity": "sha1-hMtnKTXUNxT9wyvOhFaNh0Qc988=", - "dev": true, - "requires": { - "babel-plugin-syntax-flow": "6.18.0", - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-function-bind": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-function-bind/-/babel-plugin-transform-function-bind-6.22.0.tgz", - "integrity": "sha1-xvuOlqwpajELjPjqQBRiQH3fapc=", - "dev": true, - "requires": { - "babel-plugin-syntax-function-bind": "6.13.0", - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-object-rest-spread": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz", - "integrity": "sha1-DzZpLVD+9rfi1LOsFHgTepY7ewY=", - "dev": true, - "requires": { - "babel-plugin-syntax-object-rest-spread": "6.13.0", - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-react-display-name": { - "version": "6.25.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-display-name/-/babel-plugin-transform-react-display-name-6.25.0.tgz", - "integrity": "sha1-Z+K/Hx6ck6sI25Z5LgU5K/LMKNE=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-react-jsx": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx/-/babel-plugin-transform-react-jsx-6.24.1.tgz", - "integrity": "sha1-hAoCjn30YN/DotKfDA2R9jduZqM=", - "dev": true, - "requires": { - "babel-helper-builder-react-jsx": "6.26.0", - "babel-plugin-syntax-jsx": "6.18.0", - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-react-jsx-self": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx-self/-/babel-plugin-transform-react-jsx-self-6.22.0.tgz", - "integrity": "sha1-322AqdomEqEh5t3XVYvL7PBuY24=", - "dev": true, - "requires": { - "babel-plugin-syntax-jsx": "6.18.0", - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-react-jsx-source": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx-source/-/babel-plugin-transform-react-jsx-source-6.22.0.tgz", - "integrity": "sha1-ZqwSFT9c0tF7PBkmj0vwGX9E7NY=", - "dev": true, - "requires": { - "babel-plugin-syntax-jsx": "6.18.0", - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-regenerator": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", - "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", - "dev": true, - "requires": { - "regenerator-transform": "0.10.1" - } - }, - "babel-plugin-transform-strict-mode": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", - "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-preset-env": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.6.1.tgz", - "integrity": "sha512-W6VIyA6Ch9ePMI7VptNn2wBM6dbG0eSz25HEiL40nQXCsXGTGZSTZu1Iap+cj3Q0S5a7T9+529l/5Bkvd+afNA==", - "dev": true, - "requires": { - "babel-plugin-check-es2015-constants": "6.22.0", - "babel-plugin-syntax-trailing-function-commas": "6.22.0", - "babel-plugin-transform-async-to-generator": "6.24.1", - "babel-plugin-transform-es2015-arrow-functions": "6.22.0", - "babel-plugin-transform-es2015-block-scoped-functions": "6.22.0", - "babel-plugin-transform-es2015-block-scoping": "6.26.0", - "babel-plugin-transform-es2015-classes": "6.24.1", - "babel-plugin-transform-es2015-computed-properties": "6.24.1", - "babel-plugin-transform-es2015-destructuring": "6.23.0", - "babel-plugin-transform-es2015-duplicate-keys": "6.24.1", - "babel-plugin-transform-es2015-for-of": "6.23.0", - "babel-plugin-transform-es2015-function-name": "6.24.1", - "babel-plugin-transform-es2015-literals": "6.22.0", - "babel-plugin-transform-es2015-modules-amd": "6.24.1", - "babel-plugin-transform-es2015-modules-commonjs": "6.26.0", - "babel-plugin-transform-es2015-modules-systemjs": "6.24.1", - "babel-plugin-transform-es2015-modules-umd": "6.24.1", - "babel-plugin-transform-es2015-object-super": "6.24.1", - "babel-plugin-transform-es2015-parameters": "6.24.1", - "babel-plugin-transform-es2015-shorthand-properties": "6.24.1", - "babel-plugin-transform-es2015-spread": "6.22.0", - "babel-plugin-transform-es2015-sticky-regex": "6.24.1", - "babel-plugin-transform-es2015-template-literals": "6.22.0", - "babel-plugin-transform-es2015-typeof-symbol": "6.23.0", - "babel-plugin-transform-es2015-unicode-regex": "6.24.1", - "babel-plugin-transform-exponentiation-operator": "6.24.1", - "babel-plugin-transform-regenerator": "6.26.0", - "browserslist": "2.9.0", - "invariant": "2.2.2", - "semver": "5.4.1" - } - }, - "babel-preset-flow": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-preset-flow/-/babel-preset-flow-6.23.0.tgz", - "integrity": "sha1-5xIYiHCFrpoktb5Baa/7WZgWxJ0=", - "dev": true, - "requires": { - "babel-plugin-transform-flow-strip-types": "6.22.0" - } - }, - "babel-preset-react": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-preset-react/-/babel-preset-react-6.24.1.tgz", - "integrity": "sha1-umnfrqRfw+xjm2pOzqbhdwLJE4A=", - "dev": true, - "requires": { - "babel-plugin-syntax-jsx": "6.18.0", - "babel-plugin-transform-react-display-name": "6.25.0", - "babel-plugin-transform-react-jsx": "6.24.1", - "babel-plugin-transform-react-jsx-self": "6.22.0", - "babel-plugin-transform-react-jsx-source": "6.22.0", - "babel-preset-flow": "6.23.0" - } - }, - "babel-preset-stage-0": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-preset-stage-0/-/babel-preset-stage-0-6.24.1.tgz", - "integrity": "sha1-VkLRUEL5E4TX5a+LyIsduVsDnmo=", - "dev": true, - "requires": { - "babel-plugin-transform-do-expressions": "6.22.0", - "babel-plugin-transform-function-bind": "6.22.0", - "babel-preset-stage-1": "6.24.1" - } - }, - "babel-preset-stage-1": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-preset-stage-1/-/babel-preset-stage-1-6.24.1.tgz", - "integrity": "sha1-dpLNfc1oSZB+auSgqFWJz7niv7A=", - "dev": true, - "requires": { - "babel-plugin-transform-class-constructor-call": "6.24.1", - "babel-plugin-transform-export-extensions": "6.22.0", - "babel-preset-stage-2": "6.24.1" - } - }, - "babel-preset-stage-2": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-preset-stage-2/-/babel-preset-stage-2-6.24.1.tgz", - "integrity": "sha1-2eKWD7PXEYfw5k7sYrwHdnIZvcE=", - "dev": true, - "requires": { - "babel-plugin-syntax-dynamic-import": "6.18.0", - "babel-plugin-transform-class-properties": "6.24.1", - "babel-plugin-transform-decorators": "6.24.1", - "babel-preset-stage-3": "6.24.1" - } - }, - "babel-preset-stage-3": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-preset-stage-3/-/babel-preset-stage-3-6.24.1.tgz", - "integrity": "sha1-g2raCp56f6N8sTj7kyb4eTSkg5U=", - "dev": true, - "requires": { - "babel-plugin-syntax-trailing-function-commas": "6.22.0", - "babel-plugin-transform-async-generator-functions": "6.24.1", - "babel-plugin-transform-async-to-generator": "6.24.1", - "babel-plugin-transform-exponentiation-operator": "6.24.1", - "babel-plugin-transform-object-rest-spread": "6.26.0" - } - }, - "babel-register": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", - "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", - "dev": true, - "requires": { - "babel-core": "6.26.0", - "babel-runtime": "6.26.0", - "core-js": "2.5.1", - "home-or-tmp": "2.0.0", - "lodash": "4.17.4", - "mkdirp": "0.5.1", - "source-map-support": "0.4.18" - }, - "dependencies": { - "core-js": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz", - "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs=", - "dev": true - } - } - }, - "babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", - "dev": true, - "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" - }, - "dependencies": { - "core-js": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz", - "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs=", - "dev": true - } - } - }, - "babel-template": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", - "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "lodash": "4.17.4" - } - }, - "babel-traverse": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", - "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", - "dev": true, - "requires": { - "babel-code-frame": "6.26.0", - "babel-messages": "6.23.0", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "debug": "2.6.9", - "globals": "9.18.0", - "invariant": "2.2.2", - "lodash": "4.17.4" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, - "babel-types": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", - "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "esutils": "2.0.2", - "lodash": "4.17.4", - "to-fast-properties": "1.0.3" - } - }, - "babelify": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/babelify/-/babelify-7.3.0.tgz", - "integrity": "sha1-qlau3nBn/XvVSWZu4W3ChQh+iOU=", - "dev": true, - "requires": { - "babel-core": "6.26.0", - "object-assign": "4.1.1" - } - }, - "babylon": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", - "dev": true - }, - "backo2": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", - "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" - }, - "bagpipe": { - "version": "github:Ivshti/bagpipe#328cc576d682539d635af3a8c360696af353e44a", - "requires": { - "inherits": "github:Ivshti/inherits#0c5b02ddc4172bf2e55460916401ac37cfe6f9d3" - }, - "dependencies": { - "inherits": { - "version": "github:Ivshti/inherits#0c5b02ddc4172bf2e55460916401ac37cfe6f9d3" - } - } - }, - "bail": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.2.tgz", - "integrity": "sha1-99bBcxYwqfnw1NNe0fli4gdKF2Q=", - "dev": true - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, - "requires": { - "cache-base": "1.0.1", - "class-utils": "0.3.5", - "component-emitter": "1.2.1", - "define-property": "1.0.0", - "isobject": "3.0.1", - "mixin-deep": "1.2.0", - "pascalcase": "0.1.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "base62": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/base62/-/base62-0.1.1.tgz", - "integrity": "sha1-e0F0wvlESXU7EcJlHAg9qEGnsIQ=" - }, - "base64-arraybuffer": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", - "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=" - }, - "base64id": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz", - "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=" - }, - "bcrypt-pbkdf": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", - "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", - "optional": true, - "requires": { - "tweetnacl": "0.14.5" - } - }, - "better-assert": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", - "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", - "requires": { - "callsite": "1.0.0" - } - }, - "binary-extensions": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.10.0.tgz", - "integrity": "sha1-muuabF6IY4qtFx4Wf1kAq+JINdA=", - "dev": true - }, - "binary-search-tree": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/binary-search-tree/-/binary-search-tree-0.2.5.tgz", - "integrity": "sha1-fbs7IQ/coIJFDa0jNMMErzm9x4Q=", - "requires": { - "underscore": "1.4.4" - }, - "dependencies": { - "underscore": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz", - "integrity": "sha1-YaajIBBiKvoHljvzJSA88SI51gQ=" - } - } - }, - "bindings": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.3.0.tgz", - "integrity": "sha512-DpLh5EzMR2kzvX1KIlVC0VkC3iZtHKTgdtZ0a3pglBZdaQFjt5S9g9xd1lE+YvXyfd6mtCeRnrUfOLYiTMlNSw==" - }, - "bl": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.1.tgz", - "integrity": "sha1-ysMo977kVzDUBLaSID/LWQ4XLV4=", - "requires": { - "readable-stream": "2.3.3" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "readable-stream": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", - "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" - } - }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "requires": { - "safe-buffer": "5.1.1" - } - } - } - }, - "blob": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.4.tgz", - "integrity": "sha1-vPEwUspURj8w+fx+lbmkdjCpSSE=" - }, - "bluebird": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", - "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" - }, - "body": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/body/-/body-5.1.0.tgz", - "integrity": "sha1-5LoM5BCkaTYyM2dgnstOZVMSUGk=", - "dev": true, - "requires": { - "continuable-cache": "0.3.1", - "error": "7.0.2", - "raw-body": "1.1.7", - "safe-json-parse": "1.0.1" - }, - "dependencies": { - "bytes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz", - "integrity": "sha1-NWnt6Lo0MV+rmcPpLLBMciDeH6g=", - "dev": true - }, - "raw-body": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-1.1.7.tgz", - "integrity": "sha1-HQJ8K/oRasxmI7yo8AAWVyqH1CU=", - "dev": true, - "requires": { - "bytes": "1.0.0", - "string_decoder": "0.10.31" - } - } - } - }, - "body-parser": { - "version": "1.18.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", - "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", - "requires": { - "bytes": "3.0.0", - "content-type": "1.0.4", - "debug": "2.6.9", - "depd": "1.1.1", - "http-errors": "1.6.2", - "iconv-lite": "0.4.19", - "on-finished": "2.3.0", - "qs": "6.5.1", - "raw-body": "2.3.2", - "type-is": "1.6.15" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "iconv-lite": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", - "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" - }, - "qs": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" - } - } - }, - "boom": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", - "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", - "requires": { - "hoek": "4.2.0" - } - }, - "boxen": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.2.2.tgz", - "integrity": "sha1-Px1AMsMP/qnUsCwyLq8up0HcvOU=", - "requires": { - "ansi-align": "2.0.0", - "camelcase": "4.1.0", - "chalk": "2.3.0", - "cli-boxes": "1.0.0", - "string-width": "2.1.1", - "term-size": "1.2.0", - "widest-line": "1.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" - }, - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "requires": { - "color-convert": "1.9.0" - } - }, - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" - }, - "chalk": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", - "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", - "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "4.5.0" - } - }, - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=" - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "requires": { - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "requires": { - "ansi-regex": "3.0.0" - } - }, - "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", - "requires": { - "has-flag": "2.0.0" - } - } - } - }, - "brace-expansion": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", - "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", - "requires": { - "balanced-match": "1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", - "dev": true, - "requires": { - "expand-range": "1.8.2", - "preserve": "0.2.0", - "repeat-element": "1.1.2" - } - }, - "browser-resolve": { - "version": "1.11.2", - "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.2.tgz", - "integrity": "sha1-j/CbCixCFxihBRwmCzLkj0QpOM4=", - "dev": true, - "requires": { - "resolve": "1.1.7" - } - }, - "browser-stdout": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", - "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=", - "dev": true - }, - "browserslist": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-2.9.0.tgz", - "integrity": "sha512-vJEBcDTANoDhSHL46NeOEW5hvQw7It9uCqzeFPQhpawXfnOwnpvW5C97vn1eGJ7iCkSg8wWU0nYObE7d/N95Iw==", - "dev": true, - "requires": { - "caniuse-lite": "1.0.30000764", - "electron-to-chromium": "1.3.27" - } - }, - "buffer-shims": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", - "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=", - "dev": true - }, - "builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", - "dev": true - }, - "busboy": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", - "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=", - "requires": { - "dicer": "0.2.5", - "readable-stream": "1.1.14" - } - }, - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" - }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, - "requires": { - "collection-visit": "1.0.0", - "component-emitter": "1.2.1", - "get-value": "2.0.6", - "has-value": "1.0.0", - "isobject": "3.0.1", - "set-value": "2.0.0", - "to-object-path": "0.3.0", - "union-value": "1.0.0", - "unset-value": "1.0.0" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "callsite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", - "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=" - }, - "camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", - "dev": true, - "optional": true - }, - "caniuse-lite": { - "version": "1.0.30000764", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000764.tgz", - "integrity": "sha1-l+p0cvnT5pHu3jTyGYPPwhmseEI=", - "dev": true - }, - "canvasjs": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/canvasjs/-/canvasjs-1.8.1.tgz", - "integrity": "sha1-aYpOAC4zbOg+8vkQGmvSdrp2V6M=" - }, - "caporal": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/caporal/-/caporal-0.7.0.tgz", - "integrity": "sha512-8F1WjLnwN9BbGKz9h91m58nkoOFPbCSJAzY9cr5BWp/TOsNdC0XRiHgN7U/f1sC0peuVPKrMXwd22gZWHyQSJw==", - "requires": { - "bluebird": "3.5.1", - "chalk": "1.1.3", - "cli-table2": "0.2.0", - "fast-levenshtein": "2.0.6", - "lodash.camelcase": "4.3.0", - "micromist": "1.0.2", - "prettyjson": "1.2.1", - "tabtab": "2.2.2", - "winston": "2.4.0" - } - }, - "capture-stack-trace": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz", - "integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0=" - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" - }, - "ccount": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.0.2.tgz", - "integrity": "sha1-U7ai+BW7d7nChx97mnLDol8djok=", - "dev": true - }, - "center-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", - "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", - "dev": true, - "optional": true, - "requires": { - "align-text": "0.1.4", - "lazy-cache": "1.0.4" - } - }, - "chai": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-3.5.0.tgz", - "integrity": "sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc=", - "dev": true, - "requires": { - "assertion-error": "1.0.2", - "deep-eql": "0.1.3", - "type-detect": "1.0.0" - } - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - }, - "dependencies": { - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" - } - } - }, - "character-entities": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.1.tgz", - "integrity": "sha1-92hxvl72bdt/j440eOzDdMJ9bco=", - "dev": true - }, - "character-entities-html4": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-1.1.1.tgz", - "integrity": "sha1-NZoqSg9+KdPcKsmb2+Ie45Q46lA=", - "dev": true - }, - "character-entities-legacy": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.1.tgz", - "integrity": "sha1-9Ad53xoQGHK7UQo9KV4fzPFHIC8=", - "dev": true - }, - "character-reference-invalid": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.1.tgz", - "integrity": "sha1-lCg191Dk7GGjCOYMLvjMEBEgLvw=", - "dev": true - }, - "child_process": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/child_process/-/child_process-1.0.2.tgz", - "integrity": "sha1-sffn/HPSXn/R1FWtyU4UODAYK1o=" - }, - "chokidar": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", - "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", - "dev": true, - "requires": { - "anymatch": "1.3.2", - "async-each": "1.0.1", - "glob-parent": "2.0.0", - "inherits": "2.0.3", - "is-binary-path": "1.0.1", - "is-glob": "2.0.1", - "path-is-absolute": "1.0.1", - "readdirp": "2.1.0" - } - }, - "chownr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz", - "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=" - }, - "class-utils": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.5.tgz", - "integrity": "sha1-F+eTEDdQ+WJ7IXbqNM/RtWWQPIA=", - "dev": true, - "requires": { - "arr-union": "3.1.0", - "define-property": "0.2.5", - "isobject": "3.0.1", - "lazy-cache": "2.0.2", - "static-extend": "0.1.2" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "0.1.6" - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - }, - "lazy-cache": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-2.0.2.tgz", - "integrity": "sha1-uRkKT5EzVGlIQIWfio9whNiCImQ=", - "dev": true, - "requires": { - "set-getter": "0.1.0" - } - } - } - }, - "cli-boxes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", - "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=" - }, - "cli-cursor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", - "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", - "requires": { - "restore-cursor": "1.0.1" - } - }, - "cli-spinners": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-1.1.0.tgz", - "integrity": "sha1-8YR7FohE2RemceudFH499JfJDQY=" - }, - "cli-table2": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/cli-table2/-/cli-table2-0.2.0.tgz", - "integrity": "sha1-LR738hig54biFFQFYtS9F3/jLZc=", - "requires": { - "colors": "1.1.2", - "lodash": "3.10.1", - "string-width": "1.0.2" - }, - "dependencies": { - "lodash": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", - "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=" - } - } - }, - "cli-width": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", - "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=" - }, - "cliui": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", - "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", - "dev": true, - "optional": true, - "requires": { - "center-align": "0.1.3", - "right-align": "0.1.3", - "wordwrap": "0.0.2" - }, - "dependencies": { - "wordwrap": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", - "dev": true, - "optional": true - } - } - }, - "clone": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.1.tgz", - "integrity": "sha1-0hfR6WERjjrJpLi7oyhVU79kfNs=", - "dev": true - }, - "clone-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", - "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=", - "dev": true - }, - "clone-stats": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", - "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", - "dev": true - }, - "cloneable-readable": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.0.0.tgz", - "integrity": "sha1-pikNQT8hemEjL5XkWP84QYz7ARc=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "process-nextick-args": "1.0.7", - "through2": "2.0.3" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "readable-stream": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", - "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", - "dev": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" - } - }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } - }, - "through2": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", - "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", - "dev": true, - "requires": { - "readable-stream": "2.3.3", - "xtend": "4.0.1" - } - } - } - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" - }, - "codecov": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/codecov/-/codecov-2.3.1.tgz", - "integrity": "sha1-fdqUXNWKH2CBAltbA+4Bou8g+G4=", - "requires": { - "argv": "0.0.2", - "request": "2.77.0", - "urlgrey": "0.4.4" - }, - "dependencies": { - "assert-plus": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", - "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=" - }, - "aws-sign2": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", - "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=" - }, - "boom": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", - "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", - "requires": { - "hoek": "2.16.3" - } - }, - "caseless": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", - "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=" - }, - "cryptiles": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", - "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", - "requires": { - "boom": "2.10.1" - } - }, - "form-data": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", - "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", - "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.5", - "mime-types": "2.1.17" - } - }, - "har-validator": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz", - "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=", - "requires": { - "chalk": "1.1.3", - "commander": "2.9.0", - "is-my-json-valid": "2.16.1", - "pinkie-promise": "2.0.1" - } - }, - "hawk": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", - "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", - "requires": { - "boom": "2.10.1", - "cryptiles": "2.0.5", - "hoek": "2.16.3", - "sntp": "1.0.9" - } - }, - "hoek": { - "version": "2.16.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", - "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=" - }, - "http-signature": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", - "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", - "requires": { - "assert-plus": "0.2.0", - "jsprim": "1.4.1", - "sshpk": "1.13.1" - } - }, - "node-uuid": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", - "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=" - }, - "qs": { - "version": "6.3.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.3.2.tgz", - "integrity": "sha1-51vV9uJoEioqDgvaYwslUMFmUCw=" - }, - "request": { - "version": "2.77.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.77.0.tgz", - "integrity": "sha1-KwDYIDDt7cyXCJ/6XYgQqcKqMUs=", - "requires": { - "aws-sign2": "0.6.0", - "aws4": "1.6.0", - "caseless": "0.11.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.1.4", - "har-validator": "2.0.6", - "hawk": "3.1.3", - "http-signature": "1.1.1", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.17", - "node-uuid": "1.4.8", - "oauth-sign": "0.8.2", - "qs": "6.3.2", - "stringstream": "0.0.5", - "tough-cookie": "2.3.3", - "tunnel-agent": "0.4.3" - } - }, - "sntp": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", - "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", - "requires": { - "hoek": "2.16.3" - } - }, - "tunnel-agent": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", - "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=" - } - } - }, - "collapse-white-space": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.3.tgz", - "integrity": "sha1-S5BvZw5aljqHt2sOFolkM0G2Ajw=", - "dev": true - }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "dev": true, - "requires": { - "map-visit": "1.0.0", - "object-visit": "1.0.1" - } - }, - "color-convert": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz", - "integrity": "sha1-Gsz5fdc5uYO/mU1W/sj5WFNkG3o=", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "colors": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", - "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=" - }, - "combined-stream": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", - "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", - "requires": { - "delayed-stream": "1.0.0" - } - }, - "comma-separated-tokens": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.4.tgz", - "integrity": "sha1-cgg+WNSkYvAYZvZhf02Yo807ikY=", - "dev": true, - "requires": { - "trim": "0.0.1" - } - }, - "command-exists": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.2.tgz", - "integrity": "sha1-EoGcZPr5VEbsCuB/5sr7brNwiyI=" - }, - "commander": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", - "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", - "requires": { - "graceful-readlink": "1.0.1" - } - }, - "component-bind": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", - "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=" - }, - "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" - }, - "component-inherit": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", - "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=" - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "concat-stream": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", - "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", - "requires": { - "inherits": "2.0.3", - "readable-stream": "2.2.11", - "typedarray": "0.0.6" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "readable-stream": { - "version": "2.2.11", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.11.tgz", - "integrity": "sha512-h+8+r3MKEhkiVrwdKL8aWs1oc1VvBu33ueshOvS26RsZQ3Amhx/oO3TKe4lApSV9ueY6as8EAh7mtuFjdlhg9Q==", - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "safe-buffer": "5.0.1", - "string_decoder": "1.0.2", - "util-deprecate": "1.0.2" - } - }, - "safe-buffer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz", - "integrity": "sha1-0mPKVGls2KMGtcplUekt5XkY++c=" - }, - "string_decoder": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.2.tgz", - "integrity": "sha1-sp4fThEl+pehA4K4pTNze3SR4Xk=", - "requires": { - "safe-buffer": "5.0.1" - } - } - } - }, - "configstore": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.1.tgz", - "integrity": "sha512-5oNkD/L++l0O6xGXxb1EWS7SivtjfGQlRyxJsYgE0Z495/L81e2h4/d3r969hoPXuFItzNOKMtsXgYG4c7dYvw==", - "requires": { - "dot-prop": "4.2.0", - "graceful-fs": "4.1.11", - "make-dir": "1.1.0", - "unique-string": "1.0.0", - "write-file-atomic": "2.3.0", - "xdg-basedir": "3.0.0" - } - }, - "connect-busboy": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/connect-busboy/-/connect-busboy-0.0.2.tgz", - "integrity": "sha1-rFyclmchcYheV2xmsr/ZXTuxEJc=", - "requires": { - "busboy": "0.2.14" - } - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" - }, - "content-disposition": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", - "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" - }, - "continuable-cache": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/continuable-cache/-/continuable-cache-0.3.1.tgz", - "integrity": "sha1-vXJ6f67XfnH/OYWskzUakSczrQ8=", - "dev": true - }, - "convert-source-map": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.0.tgz", - "integrity": "sha1-ms1whRxtXf3ZPZKC5e35SgP/RrU=", - "dev": true - }, - "cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "cookiejar": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.1.tgz", - "integrity": "sha1-Qa1XsbVVlR7BcUEqgZQrHoIA00o=", - "dev": true - }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true - }, - "core-js": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.3.0.tgz", - "integrity": "sha1-+rg/uwstjchfpjbEudNMdUIMbWU=" - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "create-error-class": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", - "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", - "requires": { - "capture-stack-trace": "1.0.0" - } - }, - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "requires": { - "lru-cache": "4.1.1", - "shebang-command": "1.2.0", - "which": "1.2.14" - } - }, - "cryptiles": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", - "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", - "requires": { - "boom": "5.2.0" - }, - "dependencies": { - "boom": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", - "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", - "requires": { - "hoek": "4.2.0" - } - } - } - }, - "crypto-random-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", - "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=" - }, - "cycle": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", - "integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI=" - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "requires": { - "assert-plus": "1.0.0" - } - }, - "debug": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.7.tgz", - "integrity": "sha1-krrR9tBbu2u6Isyoi80OyJTChh4=", - "requires": { - "ms": "2.0.0" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "dev": true - }, - "deep-eql": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", - "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=", - "dev": true, - "requires": { - "type-detect": "0.1.1" - }, - "dependencies": { - "type-detect": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz", - "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=", - "dev": true - } - } - }, - "deep-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", - "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", - "dev": true - }, - "deep-extend": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", - "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=" - }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, - "deepmerge": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-1.5.2.tgz", - "integrity": "sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ==" - }, - "deferred-leveldown": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-1.2.2.tgz", - "integrity": "sha512-uukrWD2bguRtXilKt6cAWKyoXrTSMo5m7crUdLfWQmu8kIm88w3QZoUL+6nhpfKVmhHANER6Re3sKoNoZ3IKMA==", - "requires": { - "abstract-leveldown": "2.6.3" - } - }, - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "1.0.1" - } - }, - "defined": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", - "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", - "dev": true - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" - }, - "depd": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", - "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=" - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" - }, - "detab": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/detab/-/detab-2.0.1.tgz", - "integrity": "sha512-/hhdqdQc5thGrqzjyO/pz76lDZ5GSuAs6goxOaKTsvPk7HNnzAyFN5lyHgqpX4/s1i66K8qMGj+VhA9504x7DQ==", - "dev": true, - "requires": { - "repeat-string": "1.6.1" - } - }, - "detect-indent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", - "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", - "dev": true, - "requires": { - "repeating": "2.0.1" - } - }, - "detective": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/detective/-/detective-4.5.0.tgz", - "integrity": "sha1-blqMaybmx6JUsca210kNmOyR7dE=", - "dev": true, - "requires": { - "acorn": "4.0.13", - "defined": "1.0.0" - }, - "dependencies": { - "acorn": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", - "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", - "dev": true - } - } - }, - "dicer": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", - "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=", - "requires": { - "readable-stream": "1.1.14", - "streamsearch": "0.1.2" - } - }, - "diff": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.2.0.tgz", - "integrity": "sha1-yc45Okt8vQsFinJck98pkCeGj/k=", - "dev": true - }, - "disparity": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/disparity/-/disparity-2.0.0.tgz", - "integrity": "sha1-V92stHMkrl9Y0swNqIbbTOnutxg=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "diff": "1.4.0" - }, - "dependencies": { - "diff": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-1.4.0.tgz", - "integrity": "sha1-fyjS657nsVqX79ic5j3P2qPMur8=", - "dev": true - } - } - }, - "doctrine-temporary-fork": { - "version": "2.0.0-alpha-allowarrayindex", - "resolved": "https://registry.npmjs.org/doctrine-temporary-fork/-/doctrine-temporary-fork-2.0.0-alpha-allowarrayindex.tgz", - "integrity": "sha1-QAFahn6yfnWybIKLcVJPE3+J+fA=", - "dev": true, - "requires": { - "esutils": "2.0.2", - "isarray": "1.0.0" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - } - } - }, - "documentation": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/documentation/-/documentation-5.3.3.tgz", - "integrity": "sha512-Avfo2uXeavwN/iiQ0djoJbSHAlzIU2MuZeebmDoFs2HmcAf+OHqokWSWZSD/wSuQXuhH7yiIsbM6dWGXb1gruw==", - "dev": true, - "requires": { - "ansi-html": "0.0.7", - "babel-core": "6.26.0", - "babel-generator": "6.26.0", - "babel-plugin-system-import-transformer": "3.1.0", - "babel-plugin-transform-decorators-legacy": "1.3.4", - "babel-preset-env": "1.6.1", - "babel-preset-react": "6.24.1", - "babel-preset-stage-0": "6.24.1", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "babelify": "7.3.0", - "babylon": "6.18.0", - "chalk": "2.3.0", - "chokidar": "1.7.0", - "concat-stream": "1.6.0", - "disparity": "2.0.0", - "doctrine-temporary-fork": "2.0.0-alpha-allowarrayindex", - "get-port": "3.1.0", - "git-url-parse": "6.2.2", - "github-slugger": "1.2.0", - "glob": "7.1.2", - "globals-docs": "2.3.0", - "highlight.js": "9.12.0", - "js-yaml": "3.8.4", - "lodash": "4.17.4", - "mdast-util-inject": "1.1.0", - "micromatch": "3.1.4", - "mime": "1.4.1", - "module-deps-sortable": "4.0.6", - "parse-filepath": "1.0.1", - "pify": "3.0.0", - "read-pkg-up": "2.0.0", - "remark": "8.0.0", - "remark-html": "6.0.1", - "remark-toc": "4.0.1", - "remote-origin-url": "0.4.0", - "shelljs": "0.7.8", - "stream-array": "1.1.2", - "strip-json-comments": "2.0.1", - "tiny-lr": "1.0.5", - "unist-builder": "1.0.2", - "unist-util-visit": "1.1.3", - "vfile": "2.2.0", - "vfile-reporter": "4.0.0", - "vfile-sort": "2.1.0", - "vinyl": "2.1.0", - "vinyl-fs": "2.4.4", - "yargs": "9.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "dev": true, - "requires": { - "color-convert": "1.9.0" - } - }, - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - }, - "chalk": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", - "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", - "dev": true, - "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "4.5.0" - } - }, - "cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", - "dev": true, - "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wrap-ansi": "2.1.0" - }, - "dependencies": { - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - } - } - } - }, - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "4.0.0" - }, - "dependencies": { - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "3.0.0" - } - } - } - }, - "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", - "dev": true, - "requires": { - "has-flag": "2.0.0" - } - }, - "yargs": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-9.0.1.tgz", - "integrity": "sha1-UqzCP+7Kw0BCB47njAwAf1CF20w=", - "dev": true, - "requires": { - "camelcase": "4.1.0", - "cliui": "3.2.0", - "decamelize": "1.2.0", - "get-caller-file": "1.0.2", - "os-locale": "2.1.0", - "read-pkg-up": "2.0.0", - "require-directory": "2.1.1", - "require-main-filename": "1.0.1", - "set-blocking": "2.0.0", - "string-width": "2.1.1", - "which-module": "2.0.0", - "y18n": "3.2.1", - "yargs-parser": "7.0.0" - } - } - } - }, - "dot-prop": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", - "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", - "requires": { - "is-obj": "1.0.1" - } - }, - "duplexer2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", - "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", - "dev": true, - "requires": { - "readable-stream": "2.3.3" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "readable-stream": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", - "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", - "dev": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" - } - }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } - } - } - }, - "duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" - }, - "duplexify": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.5.1.tgz", - "integrity": "sha512-j5goxHTwVED1Fpe5hh3q9R93Kip0Bg2KVAt4f8CEYM3UEwYcPSvWbXaUQOzdX/HtiNomipv+gU7ASQPDbV7pGQ==", - "dev": true, - "requires": { - "end-of-stream": "1.4.0", - "inherits": "2.0.3", - "readable-stream": "2.3.3", - "stream-shift": "1.0.0" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "readable-stream": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", - "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", - "dev": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" - } - }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } - } - } - }, - "eachr": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/eachr/-/eachr-3.2.0.tgz", - "integrity": "sha1-LDXkPqCGUW95l8+At6pk1VpKRIQ=", - "requires": { - "editions": "1.3.3", - "typechecker": "4.4.1" - } - }, - "ecc-jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", - "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", - "optional": true, - "requires": { - "jsbn": "0.1.1" - } - }, - "editions": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/editions/-/editions-1.3.3.tgz", - "integrity": "sha1-CQcQG92iD6w8vjNMJ8vQaI3Jmls=" - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" - }, - "ejs": { - "version": "2.5.7", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.5.7.tgz", - "integrity": "sha1-zIcsFoiArjxxiXYv1f/ACJbJUYo=" - }, - "electron-to-chromium": { - "version": "1.3.27", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.27.tgz", - "integrity": "sha1-eOy4o5kGYYe7N07t412ccFZagD0=", - "dev": true - }, - "emoji-regex": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.1.1.tgz", - "integrity": "sha1-xs0OwbBkLio8Z6ETfvxeeW2k+I4=", - "dev": true - }, - "encodeurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", - "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=" - }, - "end-of-stream": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.0.tgz", - "integrity": "sha1-epDYM+/abPpurA9JSduw+tOmMgY=", - "requires": { - "once": "1.4.0" - } - }, - "engine.io": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.1.4.tgz", - "integrity": "sha1-PQIRtwpVLOhB/8fahiezAamkFi4=", - "requires": { - "accepts": "1.3.3", - "base64id": "1.0.0", - "cookie": "0.3.1", - "debug": "2.6.9", - "engine.io-parser": "2.1.1", - "uws": "0.14.5", - "ws": "3.3.1" - }, - "dependencies": { - "accepts": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", - "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo=", - "requires": { - "mime-types": "2.1.17", - "negotiator": "0.6.1" - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - } - } - }, - "engine.io-client": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.1.4.tgz", - "integrity": "sha1-T88TcLRxY70s6b4nM5ckMDUNTqE=", - "requires": { - "component-emitter": "1.2.1", - "component-inherit": "0.0.3", - "debug": "2.6.9", - "engine.io-parser": "2.1.1", - "has-cors": "1.1.0", - "indexof": "0.0.1", - "parseqs": "0.0.5", - "parseuri": "0.0.5", - "ws": "3.3.1", - "xmlhttprequest-ssl": "1.5.4", - "yeast": "0.1.2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - } - } - }, - "engine.io-parser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.1.tgz", - "integrity": "sha1-4Ps/DgRi9/WLt3waUun1p+JuRmg=", - "requires": { - "after": "0.8.2", - "arraybuffer.slice": "0.0.6", - "base64-arraybuffer": "0.1.5", - "blob": "0.0.4", - "has-binary2": "1.0.2" - } - }, - "errno": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.4.tgz", - "integrity": "sha1-uJbiOp5ei6M4cfyZar02NfyaHH0=", - "requires": { - "prr": "0.0.0" - }, - "dependencies": { - "prr": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/prr/-/prr-0.0.0.tgz", - "integrity": "sha1-GoS4WQgyVQFBGFPQCB7j+obikmo=" - } - } - }, - "error": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/error/-/error-7.0.2.tgz", - "integrity": "sha1-pfdf/02ZJhJt2sDqXcOOaJFTywI=", - "dev": true, - "requires": { - "string-template": "0.2.1", - "xtend": "4.0.1" - } - }, - "error-ex": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", - "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", - "dev": true, - "requires": { - "is-arrayish": "0.2.1" - } - }, - "es3ify": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/es3ify/-/es3ify-0.1.4.tgz", - "integrity": "sha1-rZ+l3xrjTz8x4SEbWBiy1RB439E=", - "requires": { - "esprima-fb": "3001.1.0-dev-harmony-fb", - "jstransform": "3.0.0", - "through": "2.3.8" - }, - "dependencies": { - "esprima-fb": { - "version": "3001.1.0-dev-harmony-fb", - "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-3001.0001.0000-dev-harmony-fb.tgz", - "integrity": "sha1-t303q8046gt3Qmu4vCkizmtCZBE=" - } - } - }, - "es6-promise": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.0.2.tgz", - "integrity": "sha1-AQ1YWEI6XxGJeWZfRkhqlcbuK7Y=" - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "escodegen": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", - "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=", - "dev": true, - "requires": { - "esprima": "2.7.3", - "estraverse": "1.9.3", - "esutils": "2.0.2", - "optionator": "0.8.2", - "source-map": "0.2.0" - }, - "dependencies": { - "esprima": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", - "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", - "dev": true - }, - "source-map": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", - "integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=", - "dev": true, - "optional": true, - "requires": { - "amdefine": "1.0.1" - } - } - } - }, - "esmangle-evaluator": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/esmangle-evaluator/-/esmangle-evaluator-1.0.1.tgz", - "integrity": "sha1-Yg2GbvSGGzMR91dm1SqFcrs8YzY=" - }, - "esprima": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=" - }, - "estraverse": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", - "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=", - "dev": true - }, - "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", - "dev": true - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" - }, - "execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", - "requires": { - "cross-spawn": "5.1.0", - "get-stream": "3.0.0", - "is-stream": "1.1.0", - "npm-run-path": "2.0.2", - "p-finally": "1.0.0", - "signal-exit": "3.0.2", - "strip-eof": "1.0.0" - } - }, - "exit-hook": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", - "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=" - }, - "expand-brackets": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", - "dev": true, - "requires": { - "is-posix-bracket": "0.1.1" - } - }, - "expand-range": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", - "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", - "dev": true, - "requires": { - "fill-range": "2.2.3" - } - }, - "expand-template": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-1.1.0.tgz", - "integrity": "sha512-kkjwkMqj0h4w/sb32ERCDxCQkREMCAgS39DscDnSwDsbxnwwM1BTZySdC3Bn1lhY7vL08n9GoO/fVTynjDgRyQ==" - }, - "express": { - "version": "4.16.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.16.2.tgz", - "integrity": "sha1-41xt/i1kt9ygpc1PIXgb4ymeB2w=", - "requires": { - "accepts": "1.3.4", - "array-flatten": "1.1.1", - "body-parser": "1.18.2", - "content-disposition": "0.5.2", - "content-type": "1.0.4", - "cookie": "0.3.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "1.1.1", - "encodeurl": "1.0.1", - "escape-html": "1.0.3", - "etag": "1.8.1", - "finalhandler": "1.1.0", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "1.1.2", - "on-finished": "2.3.0", - "parseurl": "1.3.2", - "path-to-regexp": "0.1.7", - "proxy-addr": "2.0.2", - "qs": "6.5.1", - "range-parser": "1.2.0", - "safe-buffer": "5.1.1", - "send": "0.16.1", - "serve-static": "1.13.1", - "setprototypeof": "1.1.0", - "statuses": "1.3.1", - "type-is": "1.6.15", - "utils-merge": "1.0.1", - "vary": "1.1.2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "qs": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" - }, - "setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" - } - } - }, - "express-fileupload": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/express-fileupload/-/express-fileupload-0.0.6.tgz", - "integrity": "sha1-bCtAXtEV4LGgfIKF2UCWLfLfmeA=", - "requires": { - "connect-busboy": "0.0.2", - "fs-extra": "0.22.1", - "streamifier": "0.1.1" - }, - "dependencies": { - "fs-extra": { - "version": "0.22.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.22.1.tgz", - "integrity": "sha1-X9b4BJ3JdsoZ6yNV1lgXPKvM4FY=", - "requires": { - "graceful-fs": "4.1.11", - "jsonfile": "2.4.0", - "rimraf": "2.6.1" - } - } - } - }, - "extend": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "0.1.1" - } - }, - "external-editor": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-1.1.1.tgz", - "integrity": "sha1-Etew24UPf/fnCBuvQAVwAGDEYAs=", - "requires": { - "extend": "3.0.1", - "spawn-sync": "1.0.15", - "tmp": "0.0.29" - } - }, - "extglob": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", - "dev": true, - "requires": { - "is-extglob": "1.0.0" - } - }, - "extract-opts": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/extract-opts/-/extract-opts-3.3.1.tgz", - "integrity": "sha1-WrvtyYwNUgLjJ4cn+Rktfghsa+E=", - "requires": { - "eachr": "3.2.0", - "editions": "1.3.3", - "typechecker": "4.4.1" - } - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" - }, - "eyes": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", - "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=" - }, - "falafel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/falafel/-/falafel-1.2.0.tgz", - "integrity": "sha1-wY0k71CRF0pJfzGM0ksCaiXN2rQ=", - "requires": { - "acorn": "1.2.2", - "foreach": "2.0.5", - "isarray": "0.0.1", - "object-keys": "1.0.11" - } - }, - "fast-deep-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", - "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=" - }, - "fast-future": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/fast-future/-/fast-future-1.0.2.tgz", - "integrity": "sha1-hDWpqqAteSSNF9cE52JZMB2ZKAo=" - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" - }, - "faye-websocket": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", - "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", - "dev": true, - "requires": { - "websocket-driver": "0.7.0" - } - }, - "figures": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", - "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", - "requires": { - "escape-string-regexp": "1.0.5", - "object-assign": "4.1.1" - } - }, - "filename-regex": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", - "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", - "dev": true - }, - "fill-range": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", - "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", - "dev": true, - "requires": { - "is-number": "2.1.0", - "isobject": "2.1.0", - "randomatic": "1.1.7", - "repeat-element": "1.1.2", - "repeat-string": "1.6.1" - } - }, - "finalhandler": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", - "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=", - "requires": { - "debug": "2.6.9", - "encodeurl": "1.0.1", - "escape-html": "1.0.3", - "on-finished": "2.3.0", - "parseurl": "1.3.2", - "statuses": "1.3.1", - "unpipe": "1.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - } - } - }, - "find-remove": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/find-remove/-/find-remove-1.0.1.tgz", - "integrity": "sha1-cqLeYnzeotSocjvl+7JKv7c/m1I=", - "requires": { - "fmerge": "1.2.0", - "rimraf": "2.6.1" - } - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "2.0.0" - } - }, - "first-chunk-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz", - "integrity": "sha1-Wb+1DNkF9g18OUzT2ayqtOatk04=", - "dev": true - }, - "fmerge": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fmerge/-/fmerge-1.2.0.tgz", - "integrity": "sha1-NumdKuJV4+4a9ma033gFU2cc9pI=" - }, - "follow-redirects": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.2.5.tgz", - "integrity": "sha512-lMhwQTryFbG+wYsAIEKC1Kf5IGDlVNnONRogIBllh7LLoV7pNIxW0z9fhjRar9NBql+hd2Y49KboVVNxf6GEfg==", - "requires": { - "debug": "2.6.9" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - } - } - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true - }, - "for-own": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", - "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", - "dev": true, - "requires": { - "for-in": "1.0.2" - } - }, - "foreach": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", - "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" - }, - "form-data": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.1.tgz", - "integrity": "sha1-b7lPvXGIUwbXPRXMSX/kzE7NRL8=", - "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.5", - "mime-types": "2.1.17" - } - }, - "formatio": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.2.0.tgz", - "integrity": "sha1-87IWfZBoxGmKjVH092CjmlTYGOs=", - "dev": true, - "requires": { - "samsam": "1.3.0" - } - }, - "formidable": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.1.1.tgz", - "integrity": "sha1-lriIb3w8NQi5Mta9cMTTqI818ak=", - "dev": true - }, - "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" - }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "dev": true, - "requires": { - "map-cache": "0.2.2" - } - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" - }, - "fs": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.2.tgz", - "integrity": "sha1-4fJE7zkzwbKmS9R5kTYGDQ9ZFPg=" - }, - "fs-extra": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-1.0.0.tgz", - "integrity": "sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA=", - "requires": { - "graceful-fs": "4.1.11", - "jsonfile": "2.4.0", - "klaw": "1.3.1" - } - }, - "fs-jetpack": { - "version": "0.13.3", - "resolved": "https://registry.npmjs.org/fs-jetpack/-/fs-jetpack-0.13.3.tgz", - "integrity": "sha1-nmxHEY8Ls9iAzIj5+KfSWGP0+xU=", - "requires": { - "minimatch": "3.0.4" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "requires": { - "aproba": "1.2.0", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "4.1.1", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.2" - } - }, - "generate-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", - "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=" - }, - "generate-object-property": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", - "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", - "requires": { - "is-property": "1.0.2" - } - }, - "get-caller-file": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", - "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=", - "dev": true - }, - "get-port": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.1.0.tgz", - "integrity": "sha1-7wGxioTKZIaXD/meVERhQac//T4=" - }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" - }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true - }, - "getmac": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/getmac/-/getmac-1.2.1.tgz", - "integrity": "sha1-DQlf0GJ4UAQ+rB3PoLEgu9wUJtE=", - "requires": { - "extract-opts": "3.3.1" - } - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "requires": { - "assert-plus": "1.0.0" - } - }, - "git-up": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/git-up/-/git-up-2.0.9.tgz", - "integrity": "sha1-IZv9J8gtrurYSVvrOG3Bjq5jY20=", - "dev": true, - "requires": { - "is-ssh": "1.3.0", - "parse-url": "1.3.11" - } - }, - "git-url-parse": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-6.2.2.tgz", - "integrity": "sha1-vkkCThS4SHVTQ2tFcri0OVMvqHE=", - "dev": true, - "requires": { - "git-up": "2.0.9" - } - }, - "github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=" - }, - "github-slugger": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.2.0.tgz", - "integrity": "sha512-wIaa75k1vZhyPm9yWrD08A5Xnx/V+RmzGrpjQuLemGKSb77Qukiaei58Bogrl/LZSADDfPzKJX8jhLs4CRTl7Q==", - "dev": true, - "requires": { - "emoji-regex": "6.1.1" - } - }, - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "glob-all": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-all/-/glob-all-3.1.0.tgz", - "integrity": "sha1-iRPd+17hrHgSZWJBsD1SF8ZLAqs=", - "requires": { - "glob": "7.1.2", - "yargs": "1.2.6" - } - }, - "glob-base": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", - "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", - "dev": true, - "requires": { - "glob-parent": "2.0.0", - "is-glob": "2.0.1" - } - }, - "glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", - "dev": true, - "requires": { - "is-glob": "2.0.1" - } - }, - "glob-stream": { - "version": "5.3.5", - "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-5.3.5.tgz", - "integrity": "sha1-pVZlqajM3EGRWofHAeMtTgFvrSI=", - "dev": true, - "requires": { - "extend": "3.0.1", - "glob": "5.0.15", - "glob-parent": "3.1.0", - "micromatch": "2.3.11", - "ordered-read-streams": "0.3.0", - "through2": "0.6.5", - "to-absolute-glob": "0.1.1", - "unique-stream": "2.2.1" - }, - "dependencies": { - "glob": { - "version": "5.0.15", - "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", - "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", - "dev": true, - "requires": { - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "requires": { - "is-glob": "3.1.0", - "path-dirname": "1.0.2" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "2.1.1" - } - }, - "micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", - "dev": true, - "requires": { - "arr-diff": "2.0.0", - "array-unique": "0.2.1", - "braces": "1.8.5", - "expand-brackets": "0.1.5", - "extglob": "0.3.2", - "filename-regex": "2.0.1", - "is-extglob": "1.0.0", - "is-glob": "2.0.1", - "kind-of": "3.2.2", - "normalize-path": "2.1.1", - "object.omit": "2.0.1", - "parse-glob": "3.0.4", - "regex-cache": "0.4.4" - }, - "dependencies": { - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true, - "requires": { - "is-extglob": "1.0.0" - } - } - } - } - } - }, - "global-dirs": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.0.tgz", - "integrity": "sha1-ENNAOeDfBCcuJizyQiT3IJQ0308=", - "requires": { - "ini": "1.3.4" - } - }, - "globals": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", - "dev": true - }, - "globals-docs": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/globals-docs/-/globals-docs-2.3.0.tgz", - "integrity": "sha1-3KQIivGW94APTrp4Pq7/M1y2dZw=", - "dev": true - }, - "got": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", - "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", - "requires": { - "create-error-class": "3.0.2", - "duplexer3": "0.1.4", - "get-stream": "3.0.0", - "is-redirect": "1.0.0", - "is-retry-allowed": "1.1.0", - "is-stream": "1.1.0", - "lowercase-keys": "1.0.0", - "safe-buffer": "5.1.1", - "timed-out": "4.0.1", - "unzip-response": "2.0.1", - "url-parse-lax": "1.0.0" - }, - "dependencies": { - "unzip-response": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz", - "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=" - } - } - }, - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" - }, - "graceful-readlink": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", - "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=" - }, - "growl": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz", - "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=", - "dev": true - }, - "gulp-sourcemaps": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-1.6.0.tgz", - "integrity": "sha1-uG/zSdgBzrVuHZ59x7vLS33uYAw=", - "dev": true, - "requires": { - "convert-source-map": "1.5.0", - "graceful-fs": "4.1.11", - "strip-bom": "2.0.0", - "through2": "2.0.3", - "vinyl": "1.2.0" - }, - "dependencies": { - "clone": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.3.tgz", - "integrity": "sha1-KY1+IjFmD0DAA8LtMUDezz9TCF8=", - "dev": true - }, - "clone-stats": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", - "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=", - "dev": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "readable-stream": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", - "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", - "dev": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" - } - }, - "replace-ext": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", - "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=", - "dev": true - }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } - }, - "strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "dev": true, - "requires": { - "is-utf8": "0.2.1" - } - }, - "through2": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", - "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", - "dev": true, - "requires": { - "readable-stream": "2.3.3", - "xtend": "4.0.1" - } - }, - "vinyl": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", - "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", - "dev": true, - "requires": { - "clone": "1.0.3", - "clone-stats": "0.0.1", - "replace-ext": "0.0.1" - } - } - } - }, - "handlebars": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.10.tgz", - "integrity": "sha1-PTDHGLCaPZbyPqTMH0A8TTup/08=", - "dev": true, - "requires": { - "async": "1.5.2", - "optimist": "0.6.1", - "source-map": "0.4.4", - "uglify-js": "2.8.29" - }, - "dependencies": { - "source-map": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", - "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", - "dev": true, - "requires": { - "amdefine": "1.0.1" - } - } - } - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" - }, - "har-validator": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", - "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", - "requires": { - "ajv": "5.2.3", - "har-schema": "2.0.0" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "requires": { - "ansi-regex": "2.1.1" - } - }, - "has-binary2": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.2.tgz", - "integrity": "sha1-6D26SfC5vk0CbSc2U1DZ8D9Uvpg=", - "requires": { - "isarray": "2.0.1" - }, - "dependencies": { - "isarray": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", - "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" - } - } - }, - "has-cors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", - "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=" - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" - }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "dev": true, - "requires": { - "get-value": "2.0.6", - "has-values": "1.0.0", - "isobject": "3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "dev": true, - "requires": { - "is-number": "3.0.0", - "kind-of": "4.0.0" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.5" - } - } - } - }, - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "1.1.5" - } - } - } - }, - "hash-files": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/hash-files/-/hash-files-1.1.1.tgz", - "integrity": "sha1-X4nGTvIezmnIJhJUt2Wb1A6R2N4=", - "requires": { - "async": "1.5.2", - "glob-all": "3.1.0", - "opter": "1.1.0", - "read-files": "0.1.0", - "underscore": "1.8.3" - } - }, - "hast-util-is-element": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-1.0.0.tgz", - "integrity": "sha1-P3IWl4sq4U2YdJh4eCZ18zvjzgA=", - "dev": true - }, - "hast-util-sanitize": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/hast-util-sanitize/-/hast-util-sanitize-1.1.2.tgz", - "integrity": "sha1-0QvWdXoh5ZwTq8iuNTDdO219Z54=", - "dev": true, - "requires": { - "xtend": "4.0.1" - } - }, - "hast-util-to-html": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-3.1.0.tgz", - "integrity": "sha1-iCyZhJ5AEw6ZHAQuRW1FPZXDbP8=", - "dev": true, - "requires": { - "ccount": "1.0.2", - "comma-separated-tokens": "1.0.4", - "hast-util-is-element": "1.0.0", - "hast-util-whitespace": "1.0.0", - "html-void-elements": "1.0.2", - "kebab-case": "1.0.0", - "property-information": "3.2.0", - "space-separated-tokens": "1.1.1", - "stringify-entities": "1.3.1", - "unist-util-is": "2.1.1", - "xtend": "4.0.1" - } - }, - "hast-util-whitespace": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-1.0.0.tgz", - "integrity": "sha1-vQlpGWJdKTbh/xe8Tff9cn8X7Ok=", - "dev": true - }, - "hat": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/hat/-/hat-0.0.3.tgz", - "integrity": "sha1-uwFKnmSzeIrtgAWRdBPU/z1QLYo=" - }, - "hawk": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", - "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", - "requires": { - "boom": "4.3.1", - "cryptiles": "3.1.2", - "hoek": "4.2.0", - "sntp": "2.0.2" - } - }, - "he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", - "dev": true - }, - "highlight.js": { - "version": "9.12.0", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.12.0.tgz", - "integrity": "sha1-5tnb5Xy+/mB1HwKvM2GVhwyQwB4=", - "dev": true - }, - "hoek": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz", - "integrity": "sha512-v0XCLxICi9nPfYrS9RL8HbYnXi9obYAeLbSP00BmnZwCK9+Ih9WOjoZ8YoHCoav2csqn4FOz4Orldsy2dmDwmQ==" - }, - "home-or-tmp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", - "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", - "dev": true, - "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" - } - }, - "hosted-git-info": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz", - "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==", - "dev": true - }, - "html-void-elements": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-1.0.2.tgz", - "integrity": "sha1-nSLgyjKsyVs/RbjVtPb73AWv/VU=", - "dev": true - }, - "html5-validator": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/html5-validator/-/html5-validator-1.0.6.tgz", - "integrity": "sha512-um8JTV50o+9JUz35jfyvcluM7D6YHCUUHDNmb/wnWRUYFeL/PG27JmcQrI96tNkFKXuxc9iP0nryhH2OJoWIjg==", - "requires": { - "axios": "0.16.2", - "caporal": "0.7.0", - "chalk": "2.3.0", - "log-symbols": "2.1.0", - "normalize-url": "1.9.1", - "ora": "1.3.0", - "update-notifier": "2.3.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "requires": { - "color-convert": "1.9.0" - } - }, - "chalk": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", - "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", - "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "4.5.0" - } - }, - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=" - }, - "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", - "requires": { - "has-flag": "2.0.0" - } - } - } - }, - "http-basic": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/http-basic/-/http-basic-2.5.1.tgz", - "integrity": "sha1-jORHvbW2xXf4pj4/p4BW7Eu02/s=", - "requires": { - "caseless": "0.11.0", - "concat-stream": "1.6.0", - "http-response-object": "1.1.0" - }, - "dependencies": { - "caseless": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", - "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=" - } - } - }, - "http-errors": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", - "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", - "requires": { - "depd": "1.1.1", - "inherits": "2.0.3", - "setprototypeof": "1.0.3", - "statuses": "1.3.1" - } - }, - "http-parser-js": { - "version": "0.4.9", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.9.tgz", - "integrity": "sha1-6hoE+2St/wJC6ZdPKX3Uw8rSceE=", - "dev": true - }, - "http-response-object": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-1.1.0.tgz", - "integrity": "sha1-p8TnWq6C87tJBOT0P2FWc7TVGMM=" - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "requires": { - "assert-plus": "1.0.0", - "jsprim": "1.4.1", - "sshpk": "1.13.1" - } - }, - "iconv-lite": { - "version": "0.4.15", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.15.tgz", - "integrity": "sha1-/iZaIYrGpXz+hUkn6dBMGYJe3es=" - }, - "immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=" - }, - "import-lazy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", - "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=" - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" - }, - "indexof": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=" - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "ini": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz", - "integrity": "sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4=" - }, - "inline-process-browser": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/inline-process-browser/-/inline-process-browser-1.0.0.tgz", - "integrity": "sha1-RqYbFT3TybFiSxoAYm7bT39BTyI=", - "requires": { - "falafel": "1.2.0", - "through2": "0.6.5" - } - }, - "inquirer": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-1.2.3.tgz", - "integrity": "sha1-TexvMvN+97sLLtPx0aXD9UUHSRg=", - "requires": { - "ansi-escapes": "1.4.0", - "chalk": "1.1.3", - "cli-cursor": "1.0.2", - "cli-width": "2.2.0", - "external-editor": "1.1.1", - "figures": "1.7.0", - "lodash": "4.17.4", - "mute-stream": "0.0.6", - "pinkie-promise": "2.0.1", - "run-async": "2.3.0", - "rx": "4.1.0", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "through": "2.3.8" - } - }, - "interpret": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.0.4.tgz", - "integrity": "sha1-ggzdWIuGj/sZGoCVBtbJyPISsbA=", - "dev": true - }, - "invariant": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", - "integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=", - "dev": true, - "requires": { - "loose-envify": "1.3.1" - } - }, - "invert-kv": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", - "dev": true - }, - "ipaddr.js": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.5.2.tgz", - "integrity": "sha1-1LUFvemUaYfM8PxY2QEP+WB+P6A=" - }, - "is-absolute": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-0.2.6.tgz", - "integrity": "sha1-IN5p89uULvLYe5wto28XIjWxtes=", - "dev": true, - "requires": { - "is-relative": "0.2.1", - "is-windows": "0.2.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - } - }, - "is-alphabetical": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.1.tgz", - "integrity": "sha1-x3B5zJHU76x3W+EDS/LSQ/lebwg=", - "dev": true - }, - "is-alphanumeric": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-alphanumeric/-/is-alphanumeric-1.0.0.tgz", - "integrity": "sha1-Spzvcdr0wAHB2B1j0UDPU/1oifQ=", - "dev": true - }, - "is-alphanumerical": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.1.tgz", - "integrity": "sha1-37SqTRCF4zvbYcLe6cgOnGwZ9Ts=", - "dev": true, - "requires": { - "is-alphabetical": "1.0.1", - "is-decimal": "1.0.1" - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", - "dev": true, - "requires": { - "binary-extensions": "1.10.0" - } - }, - "is-buffer": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz", - "integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=" - }, - "is-builtin-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", - "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", - "dev": true, - "requires": { - "builtin-modules": "1.1.1" - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - } - }, - "is-decimal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.1.tgz", - "integrity": "sha1-9ftqlJlq2ejjdh+/vQkfH8qMToI=", - "dev": true - }, - "is-descriptor": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.1.tgz", - "integrity": "sha512-G3fFVFTqfaqu7r4YuSBHKBAuOaLz8Sy7ekklUpFEliaLMP1Y2ZjoN9jS62YWCAPQrQpMUQSitRlrzibbuCZjdA==", - "dev": true, - "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "is-dotfile": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", - "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", - "dev": true - }, - "is-equal-shallow": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", - "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", - "dev": true, - "requires": { - "is-primitive": "2.0.0" - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true - }, - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true - }, - "is-finite": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", - "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", - "dev": true, - "requires": { - "number-is-nan": "1.0.1" - } - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "requires": { - "number-is-nan": "1.0.1" - } - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true, - "requires": { - "is-extglob": "1.0.0" - } - }, - "is-hexadecimal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.1.tgz", - "integrity": "sha1-bghLvJIGH7sJcexYts5tQE4k2mk=", - "dev": true - }, - "is-installed-globally": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", - "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", - "requires": { - "global-dirs": "0.1.0", - "is-path-inside": "1.0.0" - } - }, - "is-my-json-valid": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.16.1.tgz", - "integrity": "sha512-ochPsqWS1WXj8ZnMIV0vnNXooaMhp7cyL4FMSIPKTtnV0Ha/T19G2b9kkhcNsabV9bxYkze7/aLZJb/bYuFduQ==", - "requires": { - "generate-function": "2.0.0", - "generate-object-property": "1.2.0", - "jsonpointer": "4.0.1", - "xtend": "4.0.1" - } - }, - "is-npm": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", - "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=" - }, - "is-number": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - } - }, - "is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=" - }, - "is-odd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-1.0.0.tgz", - "integrity": "sha1-O4qTLrAos3dcObsJ6RdnrM22kIg=", - "dev": true, - "requires": { - "is-number": "3.0.0" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - } - } - } - }, - "is-path-inside": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.0.tgz", - "integrity": "sha1-/AbloWg/vaE95mev9xe7wQpI838=", - "requires": { - "path-is-inside": "1.0.2" - } - }, - "is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=" - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "requires": { - "isobject": "3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "is-posix-bracket": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", - "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", - "dev": true - }, - "is-primitive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", - "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", - "dev": true - }, - "is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" - }, - "is-property": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", - "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=" - }, - "is-redirect": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", - "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=" - }, - "is-relative": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-0.2.1.tgz", - "integrity": "sha1-0n9MfVFtF1+2ENuEu+7yPDvJeqU=", - "dev": true, - "requires": { - "is-unc-path": "0.1.2" - } - }, - "is-retry-allowed": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", - "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=" - }, - "is-ssh": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.3.0.tgz", - "integrity": "sha1-6+oRaaJhTaOSpjdANmw84EnY3/Y=", - "dev": true, - "requires": { - "protocols": "1.4.6" - } - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "is-unc-path": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-0.1.2.tgz", - "integrity": "sha1-arBTpyVzwQJQ/0FqOBTDUXivObk=", - "dev": true, - "requires": { - "unc-path-regex": "0.1.2" - } - }, - "is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", - "dev": true - }, - "is-valid-glob": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-0.3.0.tgz", - "integrity": "sha1-1LVcafUYhvm2XHDWwmItN+KfSP4=", - "dev": true - }, - "is-whitespace-character": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.1.tgz", - "integrity": "sha1-muAXbzKCtlRXoZks2whPil+DPjs=", - "dev": true - }, - "is-windows": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-0.2.0.tgz", - "integrity": "sha1-3hqm1j6indJIc3tp8f+LgALSEIw=", - "dev": true - }, - "is-word-character": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-word-character/-/is-word-character-1.0.1.tgz", - "integrity": "sha1-WgP6HqkazopusMfNdw64bWXIvvs=", - "dev": true - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" - }, - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - } - } - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" - }, - "istanbul": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/istanbul/-/istanbul-0.4.5.tgz", - "integrity": "sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs=", - "dev": true, - "requires": { - "abbrev": "1.0.9", - "async": "1.5.2", - "escodegen": "1.8.1", - "esprima": "2.7.3", - "glob": "5.0.15", - "handlebars": "4.0.10", - "js-yaml": "3.8.4", - "mkdirp": "0.5.1", - "nopt": "3.0.6", - "once": "1.4.0", - "resolve": "1.1.7", - "supports-color": "3.2.3", - "which": "1.2.14", - "wordwrap": "1.0.0" - }, - "dependencies": { - "esprima": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", - "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", - "dev": true - }, - "glob": { - "version": "5.0.15", - "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", - "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", - "dev": true, - "requires": { - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - } - } - }, - "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", - "dev": true - }, - "js-yaml": { - "version": "3.8.4", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.8.4.tgz", - "integrity": "sha1-UgtFZPhlc7qWZir4Woyvp7S1pvY=", - "requires": { - "argparse": "1.0.9", - "esprima": "3.1.3" - } - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "optional": true - }, - "jsesc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", - "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", - "dev": true - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" - }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" - }, - "json-stable-stringify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", - "requires": { - "jsonify": "0.0.0" - } - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" - }, - "json3": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", - "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", - "dev": true - }, - "json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", - "dev": true - }, - "jsonfile": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", - "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", - "requires": { - "graceful-fs": "4.1.11" - } - }, - "jsonify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" - }, - "jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", - "dev": true - }, - "jsonpointer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", - "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=" - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "jstransform": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/jstransform/-/jstransform-3.0.0.tgz", - "integrity": "sha1-olkats7o2XvzvoMNv6IxO4fNZAs=", - "requires": { - "base62": "0.1.1", - "esprima-fb": "3001.1.0-dev-harmony-fb", - "source-map": "0.1.31" - }, - "dependencies": { - "esprima-fb": { - "version": "3001.1.0-dev-harmony-fb", - "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-3001.0001.0000-dev-harmony-fb.tgz", - "integrity": "sha1-t303q8046gt3Qmu4vCkizmtCZBE=" - } - } - }, - "jszip": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.1.4.tgz", - "integrity": "sha512-z6w8iYIxZ/fcgul0j/OerkYnkomH8BZigvzbxVmr2h5HkZUrPtk2kjYtLkqR9wwQxEP6ecKNoKLsbhd18jfnGA==", - "requires": { - "core-js": "2.3.0", - "es6-promise": "3.0.2", - "lie": "3.1.1", - "pako": "1.0.6", - "readable-stream": "2.0.6" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "lie": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", - "integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=", - "requires": { - "immediate": "3.0.6" - } - }, - "readable-stream": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", - "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "string_decoder": "0.10.31", - "util-deprecate": "1.0.2" - } - } - } - }, - "kebab-case": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/kebab-case/-/kebab-case-1.0.0.tgz", - "integrity": "sha1-P55JkK3K0MaGwOcB92RYaPdfkes=", - "dev": true - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.5" - } - }, - "klaw": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", - "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", - "requires": { - "graceful-fs": "4.1.11" - } - }, - "latest-version": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", - "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", - "requires": { - "package-json": "4.0.1" - } - }, - "lazy-cache": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", - "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", - "dev": true, - "optional": true - }, - "lazystream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", - "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", - "dev": true, - "requires": { - "readable-stream": "2.3.3" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "readable-stream": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", - "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", - "dev": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" - } - }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } - } - } - }, - "lcid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", - "dev": true, - "requires": { - "invert-kv": "1.0.0" - } - }, - "level-codec": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/level-codec/-/level-codec-7.0.1.tgz", - "integrity": "sha512-Ua/R9B9r3RasXdRmOtd+t9TCOEIIlts+TN/7XTT2unhDaL6sJn83S3rUyljbr6lVtw49N3/yA0HHjpV6Kzb2aQ==" - }, - "level-errors": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/level-errors/-/level-errors-1.0.5.tgz", - "integrity": "sha512-/cLUpQduF6bNrWuAC4pwtUKA5t669pCsCi2XbmojG2tFeOr9j6ShtdDCtFFQO1DRt+EVZhx9gPzP9G2bUaG4ig==", - "requires": { - "errno": "0.1.4" - } - }, - "level-iterator-stream": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/level-iterator-stream/-/level-iterator-stream-1.3.1.tgz", - "integrity": "sha1-5Dt4sagUPm+pek9IXrjqUwNS8u0=", - "requires": { - "inherits": "2.0.3", - "level-errors": "1.0.5", - "readable-stream": "1.1.14", - "xtend": "4.0.1" - } - }, - "leveldown": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/leveldown/-/leveldown-1.9.0.tgz", - "integrity": "sha512-3MwcrnCUIuFiKp/jSrG1UqDTV4k1yH8f5HH6T9dpqCKG+lRxcfo2KwAqbzTT+TTKfCbaATeHMy9mm1y6sI3ZvA==", - "requires": { - "abstract-leveldown": "2.7.2", - "bindings": "1.3.0", - "fast-future": "1.0.2", - "nan": "2.7.0", - "prebuild-install": "2.3.0" - }, - "dependencies": { - "abstract-leveldown": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-2.7.2.tgz", - "integrity": "sha512-+OVvxH2rHVEhWLdbudP6p0+dNMXu8JA1CbhP19T8paTYAcX7oJ4OVjT+ZUVpv7mITxXHqDMej+GdqXBmXkw09w==", - "requires": { - "xtend": "4.0.1" - } - } - } - }, - "levelup": { - "version": "1.3.9", - "resolved": "https://registry.npmjs.org/levelup/-/levelup-1.3.9.tgz", - "integrity": "sha512-VVGHfKIlmw8w1XqpGOAGwq6sZm2WwWLmlDcULkKWQXEA5EopA8OBNJ2Ck2v6bdk8HeEZSbCSEgzXadyQFm76sQ==", - "requires": { - "deferred-leveldown": "1.2.2", - "level-codec": "7.0.1", - "level-errors": "1.0.5", - "level-iterator-stream": "1.3.1", - "prr": "1.0.1", - "semver": "5.4.1", - "xtend": "4.0.1" - }, - "dependencies": { - "semver": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==" - } - } - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "requires": { - "prelude-ls": "1.1.2", - "type-check": "0.3.2" - } - }, - "lie": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.0.2.tgz", - "integrity": "sha1-/9oh17uibzd8rYZdNkmy/Izjn+o=", - "requires": { - "es3ify": "0.1.4", - "immediate": "3.0.6", - "inline-process-browser": "1.0.0", - "unreachable-branch-transform": "0.3.0" - } - }, - "linvodb": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/linvodb/-/linvodb-0.0.0.tgz", - "integrity": "sha1-5NPZm/gYu+5R3O/a6CjophedEqg=" - }, - "linvodb3": { - "version": "3.25.0", - "resolved": "https://registry.npmjs.org/linvodb3/-/linvodb3-3.25.0.tgz", - "integrity": "sha1-q7FlHuufoiZ9cwdVkcPMHDzB+yo=", - "requires": { - "async": "1.5.2", - "bagpipe": "github:Ivshti/bagpipe#328cc576d682539d635af3a8c360696af353e44a", - "binary-search-tree": "0.2.6", - "hat": "0.0.3", - "levelup": "1.3.9", - "path": "0.12.7", - "underscore": "1.8.3", - "util": "github:Ivshti/node-util#97b6beed8e0e478cd4184965ac94bf316f94e5f5" - }, - "dependencies": { - "binary-search-tree": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/binary-search-tree/-/binary-search-tree-0.2.6.tgz", - "integrity": "sha1-xtKRlOKGgn/P/geQEOa/d97xDOM=", - "requires": { - "underscore": "1.4.4" - }, - "dependencies": { - "underscore": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz", - "integrity": "sha1-YaajIBBiKvoHljvzJSA88SI51gQ=" - } - } - }, - "inherits": { - "version": "github:Ivshti/inherits#0c5b02ddc4172bf2e55460916401ac37cfe6f9d3" - }, - "util": { - "version": "github:Ivshti/node-util#97b6beed8e0e478cd4184965ac94bf316f94e5f5", - "requires": { - "inherits": "github:Ivshti/inherits#0c5b02ddc4172bf2e55460916401ac37cfe6f9d3" - } - } - } - }, - "livereload-js": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-2.2.2.tgz", - "integrity": "sha1-bIclfmSKtHW8JOoldFftzB+NC8I=", - "dev": true - }, - "load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "parse-json": "2.2.0", - "pify": "2.3.0", - "strip-bom": "3.0.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } - } - }, - "localforage": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.5.0.tgz", - "integrity": "sha1-a5lOGbVmEfqF3zmS3zl6xKtm6BU=", - "requires": { - "lie": "3.0.2" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "2.0.0", - "path-exists": "3.0.0" - } - }, - "lodash": { - "version": "4.17.4", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", - "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" - }, - "lodash._baseassign": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz", - "integrity": "sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4=", - "dev": true, - "requires": { - "lodash._basecopy": "3.0.1", - "lodash.keys": "3.1.2" - } - }, - "lodash._basecopy": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", - "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=", - "dev": true - }, - "lodash._basecreate": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz", - "integrity": "sha1-G8ZhYU2qf8MRt9A78WgGoCE8+CE=", - "dev": true - }, - "lodash._getnative": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", - "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=", - "dev": true - }, - "lodash._isiterateecall": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", - "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=", - "dev": true - }, - "lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=" - }, - "lodash.create": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lodash.create/-/lodash.create-3.1.1.tgz", - "integrity": "sha1-1/KEnw29p+BGgruM1yqwIkYd6+c=", - "dev": true, - "requires": { - "lodash._baseassign": "3.2.0", - "lodash._basecreate": "3.0.3", - "lodash._isiterateecall": "3.0.9" - } - }, - "lodash.difference": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", - "integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw=" - }, - "lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" - }, - "lodash.isarguments": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", - "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=", - "dev": true - }, - "lodash.isarray": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", - "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", - "dev": true - }, - "lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" - }, - "lodash.keys": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", - "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", - "dev": true, - "requires": { - "lodash._getnative": "3.9.1", - "lodash.isarguments": "3.1.0", - "lodash.isarray": "3.0.4" - } - }, - "lodash.pad": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/lodash.pad/-/lodash.pad-4.5.1.tgz", - "integrity": "sha1-QzCUmoM6fI2iLMIPaibE1Z3runA=" - }, - "lodash.padend": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz", - "integrity": "sha1-U8y6BH0G4VjTEfRdpiX05J5vFm4=" - }, - "lodash.padstart": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.padstart/-/lodash.padstart-4.6.1.tgz", - "integrity": "sha1-0uPuv/DZ05rVD1y9G1KnvOa7YRs=" - }, - "lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" - }, - "log-symbols": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.1.0.tgz", - "integrity": "sha512-zLeLrzMA1A2vRF1e/0Mo+LNINzi6jzBylHj5WqvQ/WK/5WCZt8si9SyN4p9llr/HRYvVR1AoXHRHl4WTHyQAzQ==", - "requires": { - "chalk": "2.3.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "requires": { - "color-convert": "1.9.0" - } - }, - "chalk": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", - "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", - "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "4.5.0" - } - }, - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=" - }, - "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", - "requires": { - "has-flag": "2.0.0" - } - } - } - }, - "lolex": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-1.6.0.tgz", - "integrity": "sha1-OpoCg0UqR9dDnnJzG54H1zhuSfY=", - "dev": true - }, - "longest": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", - "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", - "dev": true - }, - "longest-streak": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-2.0.2.tgz", - "integrity": "sha512-TmYTeEYxiAmSVdpbnQDXGtvYOIRsCMg89CVZzwzc2o7GFL1CjoiRPjH5ec0NFAVlAx3fVof9dX/t6KKRAo2OWA==", - "dev": true - }, - "loose-envify": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", - "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", - "dev": true, - "requires": { - "js-tokens": "3.0.2" - } - }, - "lowercase-keys": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz", - "integrity": "sha1-TjNms55/VFfjXxMkvfb4jQv8cwY=" - }, - "lru-cache": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", - "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", - "requires": { - "pseudomap": "1.0.2", - "yallist": "2.1.2" - } - }, - "luamin": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/luamin/-/luamin-1.0.4.tgz", - "integrity": "sha1-lEUptY/G+k0x6s4uA1PUEhDw49M=", - "requires": { - "luaparse": "0.2.1" - } - }, - "luaparse": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/luaparse/-/luaparse-0.2.1.tgz", - "integrity": "sha1-qo9WEysN6X0388mRqd9C4OF/ZWw=" - }, - "make-dir": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.1.0.tgz", - "integrity": "sha512-0Pkui4wLJ7rxvmfUvs87skoEaxmu0hCUApF8nonzpl7q//FWp9zu8W61Scz4sd/kUiqDxvUhtoam2efDyiBzcA==", - "requires": { - "pify": "3.0.0" - } - }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "dev": true, - "requires": { - "object-visit": "1.0.1" - } - }, - "markdown-escapes": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/markdown-escapes/-/markdown-escapes-1.0.1.tgz", - "integrity": "sha1-GZTfLTr0gR3lmmcUk0wrIpJzRRg=", - "dev": true - }, - "markdown-table": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-1.1.1.tgz", - "integrity": "sha1-Sz3ToTPRUYuO8NvHCb8qG0gkvIw=", - "dev": true - }, - "mdast-util-compact": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-compact/-/mdast-util-compact-1.0.1.tgz", - "integrity": "sha1-zbX4TitqLTEU3zO9BdnLMuPECDo=", - "dev": true, - "requires": { - "unist-util-modify-children": "1.1.1", - "unist-util-visit": "1.1.3" - } - }, - "mdast-util-definitions": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-1.2.2.tgz", - "integrity": "sha512-9NloPSwaB9f1PKcGqaScfqRf6zKOEjTIXVIbPOmgWI/JKxznlgVXC5C+8qgl3AjYg2vJBRgLYfLICaNiac89iA==", - "dev": true, - "requires": { - "unist-util-visit": "1.1.3" - } - }, - "mdast-util-inject": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-inject/-/mdast-util-inject-1.1.0.tgz", - "integrity": "sha1-2wa4tYW+lZotzS+H9HK6m3VvNnU=", - "dev": true, - "requires": { - "mdast-util-to-string": "1.0.4" - } - }, - "mdast-util-to-hast": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-2.5.0.tgz", - "integrity": "sha1-8IeETSVcdUDzaQbaMLoQbA7l7i8=", - "dev": true, - "requires": { - "collapse-white-space": "1.0.3", - "detab": "2.0.1", - "mdast-util-definitions": "1.2.2", - "mdurl": "1.0.1", - "trim": "0.0.1", - "trim-lines": "1.1.0", - "unist-builder": "1.0.2", - "unist-util-generated": "1.1.1", - "unist-util-position": "3.0.0", - "unist-util-visit": "1.1.3", - "xtend": "4.0.1" - } - }, - "mdast-util-to-string": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-1.0.4.tgz", - "integrity": "sha1-XEVch4yTVfDB5/PotxnPWDaRrPs=", - "dev": true - }, - "mdast-util-toc": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-toc/-/mdast-util-toc-2.0.1.tgz", - "integrity": "sha1-sdLLI7+wH4Evp7Vb/+iwqL7fbyE=", - "dev": true, - "requires": { - "github-slugger": "1.2.0", - "mdast-util-to-string": "1.0.4", - "unist-util-visit": "1.1.3" - } - }, - "mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=", - "dev": true - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" - }, - "mem": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", - "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", - "dev": true, - "requires": { - "mimic-fn": "1.1.0" - } - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" - }, - "merge-stream": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz", - "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=", - "dev": true, - "requires": { - "readable-stream": "2.3.3" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "readable-stream": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", - "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", - "dev": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" - } - }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } - } - } - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" - }, - "micromatch": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.4.tgz", - "integrity": "sha512-kFRtviKYoAJT+t7HggMl0tBFGNAKLw/S7N+CO9qfEQyisob1Oy4pao+geRbkyeEd+V9aOkvZ4mhuyPvI/q9Sfg==", - "dev": true, - "requires": { - "arr-diff": "4.0.0", - "array-unique": "0.3.2", - "braces": "2.3.0", - "define-property": "1.0.0", - "extend-shallow": "2.0.1", - "extglob": "2.0.2", - "fragment-cache": "0.2.1", - "kind-of": "6.0.0", - "nanomatch": "1.2.5", - "object.pick": "1.3.0", - "regex-not": "1.0.0", - "snapdragon": "0.8.1", - "to-regex": "3.0.1" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.0.tgz", - "integrity": "sha512-P4O8UQRdGiMLWSizsApmXVQDBS6KCt7dSexgLKBmH5Hr1CZq7vsnscFh8oR1sP1ab1Zj0uCHCEzZeV6SfUf3rA==", - "dev": true, - "requires": { - "arr-flatten": "1.1.0", - "array-unique": "0.3.2", - "define-property": "1.0.0", - "extend-shallow": "2.0.1", - "fill-range": "4.0.0", - "isobject": "3.0.1", - "repeat-element": "1.1.2", - "snapdragon": "0.8.1", - "snapdragon-node": "2.1.1", - "split-string": "3.0.2", - "to-regex": "3.0.1" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "2.6.7", - "define-property": "0.2.5", - "extend-shallow": "2.0.1", - "posix-character-classes": "0.1.1", - "regex-not": "1.0.0", - "snapdragon": "0.8.1", - "to-regex": "3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "0.1.6" - } - } - } - }, - "extglob": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.2.tgz", - "integrity": "sha512-I0+eZBH+jFGL8F5BnIz2ON2nKCjTS3AS3H/5PeSmCp7UVC70Ym8IhdRiQly2juKYQ//f7z1aj1BRpQniFJoU1w==", - "dev": true, - "requires": { - "array-unique": "0.3.2", - "define-property": "1.0.0", - "expand-brackets": "2.1.4", - "extend-shallow": "2.0.1", - "fragment-cache": "0.2.1", - "regex-not": "1.0.0", - "snapdragon": "0.8.1", - "to-regex": "3.0.1" - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "2.0.1", - "is-number": "3.0.0", - "repeat-string": "1.6.1", - "to-regex-range": "2.1.1" - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.5" - } - } - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.0.tgz", - "integrity": "sha512-sUd5AnFyOPh+RW+ZIHd1FHuwM4OFvhKCPVxxhamLxWLpmv1xQ394lzRMmhLQOiMpXvnB64YRLezWaJi5xGk7Dg==", - "dev": true - } - } - }, - "micromist": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/micromist/-/micromist-1.0.2.tgz", - "integrity": "sha1-QfhJSaBMMM3GCjlNDLBqqgi4Y2Q=", - "requires": { - "lodash.camelcase": "4.3.0" - } - }, - "mime": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", - "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" - }, - "mime-db": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", - "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=" - }, - "mime-types": { - "version": "2.1.17", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", - "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", - "requires": { - "mime-db": "1.30.0" - } - }, - "mimic-fn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.1.0.tgz", - "integrity": "sha1-5md4PZLonb00KBi1IwudYqZyrRg=" - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "1.1.8" - } - }, - "minimist": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.1.0.tgz", - "integrity": "sha1-md9lelJXTCHJBXSX33QnkLK0wN4=" - }, - "mixin-deep": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.2.0.tgz", - "integrity": "sha1-0CuMb4ttS49ZgtP9AJxJGYUcP+I=", - "dev": true, - "requires": { - "for-in": "1.0.2", - "is-extendable": "0.1.1" - } - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "requires": { - "minimist": "0.0.8" - }, - "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" - } - } - }, - "mkdirp-promise": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz", - "integrity": "sha1-6bj2jlUsaKnBcTuEiD96HdA5uKE=", - "requires": { - "mkdirp": "0.5.1" - } - }, - "mocha": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-3.5.3.tgz", - "integrity": "sha512-/6na001MJWEtYxHOV1WLfsmR4YIynkUEhBwzsb+fk2qmQ3iqsi258l/Q2MWHJMImAcNpZ8DEdYAK72NHoIQ9Eg==", - "dev": true, - "requires": { - "browser-stdout": "1.3.0", - "commander": "2.9.0", - "debug": "2.6.8", - "diff": "3.2.0", - "escape-string-regexp": "1.0.5", - "glob": "7.1.1", - "growl": "1.9.2", - "he": "1.1.1", - "json3": "3.3.2", - "lodash.create": "3.1.1", - "mkdirp": "0.5.1", - "supports-color": "3.1.2" - }, - "dependencies": { - "debug": { - "version": "2.6.8", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", - "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "glob": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", - "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=", - "dev": true, - "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "supports-color": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.1.2.tgz", - "integrity": "sha1-cqJiiU2dQIuVbKBf83su2KbiotU=", - "dev": true, - "requires": { - "has-flag": "1.0.0" - } - } - } - }, - "mocha-sinon": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mocha-sinon/-/mocha-sinon-2.0.0.tgz", - "integrity": "sha1-cjqTEOfXN9e3fHpmghI3QlsDLUg=", - "dev": true - }, - "mocha.parallel": { - "version": "0.15.3", - "resolved": "https://registry.npmjs.org/mocha.parallel/-/mocha.parallel-0.15.3.tgz", - "integrity": "sha512-3UqVrq5XtAtR2eMHl/N6URoSmizwaCcc84iIETtytEMbP0cIo6KacpCJFmF1id090Ig+HmbAuakC7RM4msfC2Q==", - "dev": true, - "requires": { - "bluebird": "2.11.0", - "semaphore": "1.1.0" - }, - "dependencies": { - "bluebird": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", - "integrity": "sha1-U0uQM8AiyVecVro7Plpcqvu2UOE=", - "dev": true - } - } - }, - "mock-fs": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-4.4.2.tgz", - "integrity": "sha512-dF+yxZSojSiI8AXGoxj5qdFWpucndc54Ug+TwlpHFaV7j22MGG+OML2+FVa6xAZtjb/OFFQhOC37Jegx2GbEwA==", - "dev": true - }, - "module-deps-sortable": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/module-deps-sortable/-/module-deps-sortable-4.0.6.tgz", - "integrity": "sha1-ElGkuixEqS32mJvQKdoSGk8hCbA=", - "dev": true, - "requires": { - "JSONStream": "1.3.1", - "browser-resolve": "1.11.2", - "concat-stream": "1.5.2", - "defined": "1.0.0", - "detective": "4.5.0", - "duplexer2": "0.1.4", - "inherits": "2.0.3", - "parents": "1.0.1", - "readable-stream": "2.3.3", - "resolve": "1.1.7", - "stream-combiner2": "1.1.1", - "subarg": "1.0.0", - "through2": "2.0.3", - "xtend": "4.0.1" - }, - "dependencies": { - "concat-stream": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.2.tgz", - "integrity": "sha1-cIl4Yk2FavQaWnQd790mHadSwmY=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "readable-stream": "2.0.6", - "typedarray": "0.0.6" - }, - "dependencies": { - "readable-stream": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", - "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", - "dev": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "string_decoder": "0.10.31", - "util-deprecate": "1.0.2" - } - } - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "readable-stream": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", - "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", - "dev": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" - }, - "dependencies": { - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } - } - } - }, - "through2": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", - "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", - "dev": true, - "requires": { - "readable-stream": "2.3.3", - "xtend": "4.0.1" - } - } - } - }, - "moment": { - "version": "2.19.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.19.1.tgz", - "integrity": "sha1-VtoaLRy/AdOLfhr8McELz6GSkWc=" - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "mute-stream": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.6.tgz", - "integrity": "sha1-SJYrGeFp/R38JAs/HnMXYnu8R9s=" - }, - "nan": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.7.0.tgz", - "integrity": "sha1-2Vv3IeyHfgjbJ27T/G63j5CDrUY=" - }, - "nanomatch": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.5.tgz", - "integrity": "sha512-ZHJamn1utzcUvW8Bais+Kk7pobp6dKmUEKOSQ/HI2glGwOMA/GvjRRKlLyORBUrdRXnwTU/6LIBcW7jYSlutgg==", - "dev": true, - "requires": { - "arr-diff": "4.0.0", - "array-unique": "0.3.2", - "define-property": "1.0.0", - "extend-shallow": "2.0.1", - "fragment-cache": "0.2.1", - "is-odd": "1.0.0", - "kind-of": "5.1.0", - "object.pick": "1.3.0", - "regex-not": "1.0.0", - "snapdragon": "0.8.1", - "to-regex": "3.0.1" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "native-promise-only": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/native-promise-only/-/native-promise-only-0.8.1.tgz", - "integrity": "sha1-IKMYwwy0X3H+et+/eyHJnBRy7xE=", - "dev": true - }, - "ncp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", - "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=" - }, - "nedb": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/nedb/-/nedb-1.8.0.tgz", - "integrity": "sha1-DjUCzYLABNU1WkPJ5VV3vXvZHYg=", - "requires": { - "async": "0.2.10", - "binary-search-tree": "0.2.5", - "localforage": "1.5.0", - "mkdirp": "0.5.1", - "underscore": "1.4.4" - }, - "dependencies": { - "async": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", - "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=" - }, - "underscore": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz", - "integrity": "sha1-YaajIBBiKvoHljvzJSA88SI51gQ=" - } - } - }, - "needle": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/needle/-/needle-1.6.0.tgz", - "integrity": "sha1-9SpYWJchIWGOAC+OY4TK2sItYk8=", - "requires": { - "debug": "2.6.7", - "iconv-lite": "0.4.15" - } - }, - "negotiator": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", - "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" - }, - "nock": { - "version": "9.0.27", - "resolved": "https://registry.npmjs.org/nock/-/nock-9.0.27.tgz", - "integrity": "sha512-UCdl4VFGBrnNLgk4Rx2NKjPbzVVk6BSsG4xjDhPcKwWwHWVxYT/TcuCT/9ObG46ez5d/jg7+4EFbPuF3XjDRtQ==", - "dev": true, - "requires": { - "chai": "3.5.0", - "debug": "2.6.7", - "deep-equal": "1.0.1", - "json-stringify-safe": "5.0.1", - "lodash": "4.17.4", - "mkdirp": "0.5.1", - "propagate": "0.4.0", - "qs": "6.4.0", - "semver": "5.4.1" - } - }, - "node-abi": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.1.1.tgz", - "integrity": "sha512-6oxV13poCOv7TfGvhsSz6XZWpXeKkdGVh72++cs33OfMh3KAX8lN84dCvmqSETyDXAFcUHtV7eJrgFBoOqZbNQ==" - }, - "node-factorio-api": { - "version": "0.1.28", - "resolved": "https://registry.npmjs.org/node-factorio-api/-/node-factorio-api-0.1.28.tgz", - "integrity": "sha512-e09FBa90LM2BI+YfCBZjR5ZjeOkvBYOzcbhBIuhndrg/a9Va8TjLIII+8W5vjrD4cKkL5AOHAijamDjRZNg5Xg==", - "requires": { - "fs-jetpack": "0.13.3", - "jszip": "3.1.4", - "request": "2.83.0", - "request-progress": "3.0.0", - "request-promise": "4.2.2", - "semver": "5.4.1" - } - }, - "noop-logger": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz", - "integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI=" - }, - "nopt": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", - "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", - "dev": true, - "requires": { - "abbrev": "1.0.9" - } - }, - "normalize-package-data": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", - "dev": true, - "requires": { - "hosted-git-info": "2.5.0", - "is-builtin-module": "1.0.0", - "semver": "5.4.1", - "validate-npm-package-license": "3.0.1" - } - }, - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "1.1.0" - } - }, - "normalize-url": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", - "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", - "requires": { - "object-assign": "4.1.1", - "prepend-http": "1.0.4", - "query-string": "4.3.4", - "sort-keys": "1.1.2" - } - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "requires": { - "path-key": "2.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "requires": { - "are-we-there-yet": "1.1.4", - "console-control-strings": "1.1.0", - "gauge": "2.7.4", - "set-blocking": "2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" - }, - "oauth-sign": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", - "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "object-component": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", - "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=" - }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dev": true, - "requires": { - "copy-descriptor": "0.1.1", - "define-property": "0.2.5", - "kind-of": "3.2.2" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "0.1.6" - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - } - } - }, - "object-keys": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", - "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=" - }, - "object-path": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/object-path/-/object-path-0.11.4.tgz", - "integrity": "sha1-NwrnUvvzfePqcKhhwju6iRVpGUk=" - }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "dev": true, - "requires": { - "isobject": "3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "object.omit": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", - "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", - "dev": true, - "requires": { - "for-own": "0.1.5", - "is-extendable": "0.1.1" - } - }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "dev": true, - "requires": { - "isobject": "3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "requires": { - "ee-first": "1.1.1" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1.0.2" - } - }, - "onetime": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", - "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=" - }, - "opter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/opter/-/opter-1.1.0.tgz", - "integrity": "sha1-JZiuu2Cz8acyKvEJcIb055jISnY=", - "requires": { - "commander": "2.9.0", - "js-yaml": "3.8.4", - "object-path": "0.11.4", - "underscore": "1.8.3", - "z-schema": "3.18.2", - "z-schema-errors": "0.0.1" - } - }, - "optimist": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", - "dev": true, - "requires": { - "minimist": "0.0.10", - "wordwrap": "0.0.3" - }, - "dependencies": { - "minimist": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", - "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", - "dev": true - }, - "wordwrap": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", - "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", - "dev": true - } - } - }, - "optionator": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", - "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", - "dev": true, - "requires": { - "deep-is": "0.1.3", - "fast-levenshtein": "2.0.6", - "levn": "0.3.0", - "prelude-ls": "1.1.2", - "type-check": "0.3.2", - "wordwrap": "1.0.0" - } - }, - "ora": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-1.3.0.tgz", - "integrity": "sha1-gAeN0rkqk0r2ajrXKluRBpTt5Ro=", - "requires": { - "chalk": "1.1.3", - "cli-cursor": "2.1.0", - "cli-spinners": "1.1.0", - "log-symbols": "1.0.2" - }, - "dependencies": { - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", - "requires": { - "restore-cursor": "2.0.0" - } - }, - "log-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", - "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=", - "requires": { - "chalk": "1.1.3" - } - }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "requires": { - "mimic-fn": "1.1.0" - } - }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "requires": { - "onetime": "2.0.1", - "signal-exit": "3.0.2" - } - } - } - }, - "ordered-read-streams": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-0.3.0.tgz", - "integrity": "sha1-cTfmmzKYuzQiR6G77jiByA4v14s=", - "dev": true, - "requires": { - "is-stream": "1.1.0", - "readable-stream": "2.3.3" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "readable-stream": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", - "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", - "dev": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" - } - }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } - } - } - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" - }, - "os-locale": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", - "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", - "dev": true, - "requires": { - "execa": "0.7.0", - "lcid": "1.0.0", - "mem": "1.1.0" - } - }, - "os-shim": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/os-shim/-/os-shim-0.1.3.tgz", - "integrity": "sha1-a2LDeRz3kJ6jXtRuF2WLtBfLORc=" - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" - }, - "p-limit": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.1.0.tgz", - "integrity": "sha1-sH/y2aXYi+yAYDWJWiurZqJ5iLw=", - "dev": true - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "1.1.0" - } - }, - "package-json": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", - "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", - "requires": { - "got": "6.7.1", - "registry-auth-token": "3.3.1", - "registry-url": "3.1.0", - "semver": "5.4.1" - } - }, - "pako": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", - "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==" - }, - "parents": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz", - "integrity": "sha1-/t1NK/GTp3dF/nHjcdc8MwfZx1E=", - "dev": true, - "requires": { - "path-platform": "0.11.15" - } - }, - "parse-entities": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-1.1.1.tgz", - "integrity": "sha1-gRLYhHExnyerrk1klksSL+ThuJA=", - "dev": true, - "requires": { - "character-entities": "1.2.1", - "character-entities-legacy": "1.1.1", - "character-reference-invalid": "1.1.1", - "is-alphanumerical": "1.0.1", - "is-decimal": "1.0.1", - "is-hexadecimal": "1.0.1" - } - }, - "parse-filepath": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.1.tgz", - "integrity": "sha1-FZ1hVdQ5BNFsEO9piRHaHpGWm3M=", - "dev": true, - "requires": { - "is-absolute": "0.2.6", - "map-cache": "0.2.2", - "path-root": "0.1.1" - } - }, - "parse-git-config": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/parse-git-config/-/parse-git-config-0.2.0.tgz", - "integrity": "sha1-Jygz/dFf6hRvt10zbSNrljtv9wY=", - "dev": true, - "requires": { - "ini": "1.3.4" - } - }, - "parse-glob": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", - "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", - "dev": true, - "requires": { - "glob-base": "0.3.0", - "is-dotfile": "1.0.3", - "is-extglob": "1.0.0", - "is-glob": "2.0.1" - } - }, - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true, - "requires": { - "error-ex": "1.3.1" - } - }, - "parse-url": { - "version": "1.3.11", - "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-1.3.11.tgz", - "integrity": "sha1-V8FUKKuKiSsfQ4aWRccR0OFEtVQ=", - "dev": true, - "requires": { - "is-ssh": "1.3.0", - "protocols": "1.4.6" - } - }, - "parseqs": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", - "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", - "requires": { - "better-assert": "1.0.2" - } - }, - "parseuri": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", - "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", - "requires": { - "better-assert": "1.0.2" - } - }, - "parseurl": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", - "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" - }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true - }, - "path": { - "version": "0.12.7", - "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", - "integrity": "sha1-1NwqUGxM4hl+tIHr/NWzbAFAsQ8=", - "requires": { - "process": "0.11.10", - "util": "github:ivshti/node-util#97b6beed8e0e478cd4184965ac94bf316f94e5f5" - } - }, - "path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" - }, - "path-platform": { - "version": "0.11.15", - "resolved": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz", - "integrity": "sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I=", - "dev": true - }, - "path-root": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", - "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", - "dev": true, - "requires": { - "path-root-regex": "0.1.2" - } - }, - "path-root-regex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", - "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=", - "dev": true - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" - }, - "path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "dev": true, - "requires": { - "pify": "2.3.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } - } - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "requires": { - "pinkie": "2.0.4" - } - }, - "portfinder": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.13.tgz", - "integrity": "sha1-uzLs2HwnEErm7kS1o8y/Drsa7ek=", - "requires": { - "async": "1.5.2", - "debug": "2.6.7", - "mkdirp": "0.5.1" - } - }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true - }, - "prebuild-install": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-2.3.0.tgz", - "integrity": "sha512-gzjq2oHB8oMbzJSsSh9MQ64zrXZGt092/uT4TLZlz2qnrPxpWqp4vYB7LZrDxnlxf5RfbCjkgDI/z0EIVuYzAw==", - "requires": { - "expand-template": "1.1.0", - "github-from-package": "0.0.0", - "minimist": "1.2.0", - "mkdirp": "0.5.1", - "node-abi": "2.1.1", - "noop-logger": "0.1.1", - "npmlog": "4.1.2", - "os-homedir": "1.0.2", - "pump": "1.0.2", - "rc": "1.2.1", - "simple-get": "1.4.3", - "tar-fs": "1.16.0", - "tunnel-agent": "0.6.0", - "xtend": "4.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" - } - } - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true - }, - "prepend-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=" - }, - "preserve": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", - "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", - "dev": true - }, - "prettyjson": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prettyjson/-/prettyjson-1.2.1.tgz", - "integrity": "sha1-/P+rQdGcq0365eV15kJGYZsS0ok=", - "requires": { - "colors": "1.1.2", - "minimist": "1.2.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" - } - } - }, - "printable-characters": { - "version": "1.0.38", - "resolved": "https://registry.npmjs.org/printable-characters/-/printable-characters-1.0.38.tgz", - "integrity": "sha1-du+ErM/X+DZvtxOPuSRmqRbVmbw=" - }, - "private": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/private/-/private-0.1.7.tgz", - "integrity": "sha1-aM5eih7woju1cMwoU3tTMqumPvE=" - }, - "process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" - }, - "process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" - }, - "promise": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.1.1.tgz", - "integrity": "sha1-SJZUxpJha4qlWwck+oCbt9tJxb8=", - "requires": { - "asap": "2.0.5" - } - }, - "propagate": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/propagate/-/propagate-0.4.0.tgz", - "integrity": "sha1-8/zKCm/gZzanulcpZgaWF8EwtIE=", - "dev": true - }, - "property-information": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-3.2.0.tgz", - "integrity": "sha1-/RSDyPusYYCPX+NZ52k6H0ilgzE=", - "dev": true - }, - "protocols": { - "version": "1.4.6", - "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.6.tgz", - "integrity": "sha1-+LsmPqG1/Xp2BNJri+Ob13Z4v4o=", - "dev": true - }, - "proxy-addr": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.2.tgz", - "integrity": "sha1-ZXFQT0e7mI7IGAJT+F3X4UlSvew=", - "requires": { - "forwarded": "0.1.2", - "ipaddr.js": "1.5.2" - } - }, - "prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" - }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" - }, - "pump": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.2.tgz", - "integrity": "sha1-Oz7mUS+U8OV1U4wXmV+fFpkKXVE=", - "requires": { - "end-of-stream": "1.4.0", - "once": "1.4.0" - } - }, - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" - }, - "qs": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", - "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=" - }, - "query-string": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", - "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", - "requires": { - "object-assign": "4.1.1", - "strict-uri-encode": "1.1.0" - } - }, - "randomatic": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", - "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==", - "dev": true, - "requires": { - "is-number": "3.0.0", - "kind-of": "4.0.0" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.5" - } - } - } - }, - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "1.1.5" - } - } - } - }, - "range-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", - "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" - }, - "raw-body": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", - "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", - "requires": { - "bytes": "3.0.0", - "http-errors": "1.6.2", - "iconv-lite": "0.4.19", - "unpipe": "1.0.0" - }, - "dependencies": { - "iconv-lite": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", - "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" - } - } - }, - "rc": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.1.tgz", - "integrity": "sha1-LgPo5C7kULjLPc5lvhv4l04d/ZU=", - "requires": { - "deep-extend": "0.4.2", - "ini": "1.3.4", - "minimist": "1.2.0", - "strip-json-comments": "2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" - } - } - }, - "read-files": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/read-files/-/read-files-0.1.0.tgz", - "integrity": "sha1-YGu3oBd5Utai+YPVmAlglDFNMh4=" - }, - "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "dev": true, - "requires": { - "load-json-file": "2.0.0", - "normalize-package-data": "2.4.0", - "path-type": "2.0.0" - } - }, - "read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", - "dev": true, - "requires": { - "find-up": "2.1.0", - "read-pkg": "2.0.0" - } - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "0.0.1", - "string_decoder": "0.10.31" - } - }, - "readdirp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", - "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "minimatch": "3.0.4", - "readable-stream": "2.3.3", - "set-immediate-shim": "1.0.1" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "readable-stream": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", - "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", - "dev": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" - } - }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } - } - } - }, - "recast": { - "version": "0.10.43", - "resolved": "https://registry.npmjs.org/recast/-/recast-0.10.43.tgz", - "integrity": "sha1-uV1Q9tYHYaX2JS4V2AZ4FoSRzn8=", - "requires": { - "ast-types": "0.8.15", - "esprima-fb": "15001.1001.0-dev-harmony-fb", - "private": "0.1.7", - "source-map": "0.5.6" - }, - "dependencies": { - "esprima-fb": { - "version": "15001.1001.0-dev-harmony-fb", - "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-15001.1001.0-dev-harmony-fb.tgz", - "integrity": "sha1-Q761fsJujPI3092LM+QlM1d/Jlk=" - }, - "source-map": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", - "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=" - } - } - }, - "rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", - "dev": true, - "requires": { - "resolve": "1.1.7" - } - }, - "regenerate": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.3.tgz", - "integrity": "sha512-jVpo1GadrDAK59t/0jRx5VxYWQEDkkEKi6+HjE3joFVLfDOh9Xrdh0dF1eSq+BI/SwvTQ44gSscJ8N5zYL61sg==", - "dev": true - }, - "regenerator-runtime": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz", - "integrity": "sha512-/aA0kLeRb5N9K0d4fw7ooEbI+xDe+DKD499EQqygGqeS8N3xto15p09uY2xj7ixP81sNPXvRLnAQIqdVStgb1A==", - "dev": true - }, - "regenerator-transform": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz", - "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "private": "0.1.7" - } - }, - "regex-cache": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", - "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", - "dev": true, - "requires": { - "is-equal-shallow": "0.1.3" - } - }, - "regex-not": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.0.tgz", - "integrity": "sha1-Qvg+OXcWIt+CawKvF2Ul1qXxV/k=", - "dev": true, - "requires": { - "extend-shallow": "2.0.1" - } - }, - "regexpu-core": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", - "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", - "dev": true, - "requires": { - "regenerate": "1.3.3", - "regjsgen": "0.2.0", - "regjsparser": "0.1.5" - } - }, - "registry-auth-token": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.1.tgz", - "integrity": "sha1-+w0yie4Nmtosu1KvXf5mywcNMAY=", - "requires": { - "rc": "1.2.1", - "safe-buffer": "5.1.1" - } - }, - "registry-url": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", - "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", - "requires": { - "rc": "1.2.1" - } - }, - "regjsgen": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", - "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", - "dev": true - }, - "regjsparser": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", - "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", - "dev": true, - "requires": { - "jsesc": "0.5.0" - }, - "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true - } - } - }, - "remark": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/remark/-/remark-8.0.0.tgz", - "integrity": "sha512-K0PTsaZvJlXTl9DN6qYlvjTkqSZBFELhROZMrblm2rB+085flN84nz4g/BscKRMqDvhzlK1oQ/xnWQumdeNZYw==", - "dev": true, - "requires": { - "remark-parse": "4.0.0", - "remark-stringify": "4.0.0", - "unified": "6.1.5" - } - }, - "remark-html": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/remark-html/-/remark-html-6.0.1.tgz", - "integrity": "sha1-UJTSxx95Qf2yroZbrHZid1fOCcE=", - "dev": true, - "requires": { - "hast-util-sanitize": "1.1.2", - "hast-util-to-html": "3.1.0", - "mdast-util-to-hast": "2.5.0", - "xtend": "4.0.1" - } - }, - "remark-parse": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-4.0.0.tgz", - "integrity": "sha512-XZgICP2gJ1MHU7+vQaRM+VA9HEL3X253uwUM/BGgx3iv6TH2B3bF3B8q00DKcyP9YrJV+/7WOWEWBFF/u8cIsw==", - "dev": true, - "requires": { - "collapse-white-space": "1.0.3", - "is-alphabetical": "1.0.1", - "is-decimal": "1.0.1", - "is-whitespace-character": "1.0.1", - "is-word-character": "1.0.1", - "markdown-escapes": "1.0.1", - "parse-entities": "1.1.1", - "repeat-string": "1.6.1", - "state-toggle": "1.0.0", - "trim": "0.0.1", - "trim-trailing-lines": "1.1.0", - "unherit": "1.1.0", - "unist-util-remove-position": "1.1.1", - "vfile-location": "2.0.2", - "xtend": "4.0.1" - } - }, - "remark-slug": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/remark-slug/-/remark-slug-4.2.3.tgz", - "integrity": "sha1-jZh9Dl5j1KSeo3uQ/pmaPc/IG3I=", - "dev": true, - "requires": { - "github-slugger": "1.2.0", - "mdast-util-to-string": "1.0.4", - "unist-util-visit": "1.1.3" - } - }, - "remark-stringify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-4.0.0.tgz", - "integrity": "sha512-xLuyKTnuQer3ke9hkU38SUYLiTmS078QOnoFavztmbt/pAJtNSkNtFgR0U//uCcmG0qnyxao+PDuatQav46F1w==", - "dev": true, - "requires": { - "ccount": "1.0.2", - "is-alphanumeric": "1.0.0", - "is-decimal": "1.0.1", - "is-whitespace-character": "1.0.1", - "longest-streak": "2.0.2", - "markdown-escapes": "1.0.1", - "markdown-table": "1.1.1", - "mdast-util-compact": "1.0.1", - "parse-entities": "1.1.1", - "repeat-string": "1.6.1", - "state-toggle": "1.0.0", - "stringify-entities": "1.3.1", - "unherit": "1.1.0", - "xtend": "4.0.1" - } - }, - "remark-toc": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/remark-toc/-/remark-toc-4.0.1.tgz", - "integrity": "sha1-/zb/beVOoH3Vnj9TNKSjqsHpMYU=", - "dev": true, - "requires": { - "mdast-util-toc": "2.0.1", - "remark-slug": "4.2.3" - } - }, - "remote-origin-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/remote-origin-url/-/remote-origin-url-0.4.0.tgz", - "integrity": "sha1-TT4pAvNOLTfRwmPYdxC3frQIajA=", - "dev": true, - "requires": { - "parse-git-config": "0.2.0" - } - }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", - "dev": true - }, - "repeat-element": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", - "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", - "dev": true - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true - }, - "repeating": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", - "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", - "dev": true, - "requires": { - "is-finite": "1.0.2" - } - }, - "replace-ext": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", - "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", - "dev": true - }, - "request": { - "version": "2.83.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz", - "integrity": "sha512-lR3gD69osqm6EYLk9wB/G1W/laGWjzH90t1vEa2xuxHD5KUrSzp9pUSfTm+YC5Nxt2T8nMPEvKlhbQayU7bgFw==", - "requires": { - "aws-sign2": "0.7.0", - "aws4": "1.6.0", - "caseless": "0.12.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.3.1", - "har-validator": "5.0.3", - "hawk": "6.0.2", - "http-signature": "1.2.0", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.17", - "oauth-sign": "0.8.2", - "performance-now": "2.1.0", - "qs": "6.5.1", - "safe-buffer": "5.1.1", - "stringstream": "0.0.5", - "tough-cookie": "2.3.3", - "tunnel-agent": "0.6.0", - "uuid": "3.1.0" - }, - "dependencies": { - "qs": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" - } - } - }, - "request-progress": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", - "integrity": "sha1-TKdUCBx/7GP1BeT6qCWqBs1mnb4=", - "requires": { - "throttleit": "1.0.0" - } - }, - "request-promise": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.2.tgz", - "integrity": "sha1-0epG1lSm7k+O5qT+oQGMIpEZBLQ=", - "requires": { - "bluebird": "3.5.1", - "request-promise-core": "1.1.1", - "stealthy-require": "1.1.1", - "tough-cookie": "2.3.3" - } - }, - "request-promise-core": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz", - "integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=", - "requires": { - "lodash": "4.17.4" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", - "dev": true - }, - "resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", - "dev": true - }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true - }, - "restore-cursor": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", - "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", - "requires": { - "exit-hook": "1.1.1", - "onetime": "1.1.0" - } - }, - "right-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", - "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", - "dev": true, - "optional": true, - "requires": { - "align-text": "0.1.4" - } - }, - "right-pad": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/right-pad/-/right-pad-1.0.1.tgz", - "integrity": "sha1-jKCMLLtbVedNr6lr9/0aJ9VoyNA=" - }, - "rimraf": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", - "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=", - "requires": { - "glob": "7.1.2" - } - }, - "rmdir-sync": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/rmdir-sync/-/rmdir-sync-1.0.1.tgz", - "integrity": "sha1-8thlTaNcOGzC/zoPE7L6m+vwCQU=" - }, - "run-async": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", - "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", - "requires": { - "is-promise": "2.1.0" - } - }, - "rx": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/rx/-/rx-4.1.0.tgz", - "integrity": "sha1-pfE/957zt0D+MKqAP7CfmIBdR4I=" - }, - "safe-buffer": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" - }, - "safe-json-parse": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/safe-json-parse/-/safe-json-parse-1.0.1.tgz", - "integrity": "sha1-PnZyPjjf3aE8mx0poeB//uSzC1c=", - "dev": true - }, - "samsam": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.3.0.tgz", - "integrity": "sha512-1HwIYD/8UlOtFS3QO3w7ey+SdSDFE4HRNLZoZRYVQefrOY3l17epswImeB1ijgJFQJodIaHcwkp3r/myBjFVbg==", - "dev": true - }, - "semaphore": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/semaphore/-/semaphore-1.1.0.tgz", - "integrity": "sha512-O4OZEaNtkMd/K0i6js9SL+gqy0ZCBMgUvlSqHKi4IBdjhe7wB8pwztUk1BbZ1fmrvpwFrPbHzqd2w5pTcJH6LA==", - "dev": true - }, - "semver": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==" - }, - "semver-diff": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", - "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", - "requires": { - "semver": "5.4.1" - } - }, - "send": { - "version": "0.16.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.16.1.tgz", - "integrity": "sha512-ElCLJdJIKPk6ux/Hocwhk7NFHpI3pVm/IZOYWqUmoxcgeyM+MpxHHKhb8QmlJDX1pU6WrgaHBkVNm73Sv7uc2A==", - "requires": { - "debug": "2.6.9", - "depd": "1.1.1", - "destroy": "1.0.4", - "encodeurl": "1.0.1", - "escape-html": "1.0.3", - "etag": "1.8.1", - "fresh": "0.5.2", - "http-errors": "1.6.2", - "mime": "1.4.1", - "ms": "2.0.0", - "on-finished": "2.3.0", - "range-parser": "1.2.0", - "statuses": "1.3.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - } - } - }, - "serve-static": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.1.tgz", - "integrity": "sha512-hSMUZrsPa/I09VYFJwa627JJkNs0NrfL1Uzuup+GqHfToR2KcsXFymXSV90hoyw3M+msjFuQly+YzIH/q0MGlQ==", - "requires": { - "encodeurl": "1.0.1", - "escape-html": "1.0.3", - "parseurl": "1.3.2", - "send": "0.16.1" - } - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, - "set-getter": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/set-getter/-/set-getter-0.1.0.tgz", - "integrity": "sha1-12nBgsnVpR9AkUXy+6guXoboA3Y=", - "dev": true, - "requires": { - "to-object-path": "0.3.0" - } - }, - "set-immediate-shim": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", - "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", - "dev": true - }, - "set-value": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", - "dev": true, - "requires": { - "extend-shallow": "2.0.1", - "is-extendable": "0.1.1", - "is-plain-object": "2.0.4", - "split-string": "3.0.2" - } - }, - "setprototypeof": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", - "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "requires": { - "shebang-regex": "1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" - }, - "shelljs": { - "version": "0.7.8", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.8.tgz", - "integrity": "sha1-3svPh0sNHl+3LhSxZKloMEjprLM=", - "dev": true, - "requires": { - "glob": "7.1.2", - "interpret": "1.0.4", - "rechoir": "0.6.2" - } - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" - }, - "simple-get": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-1.4.3.tgz", - "integrity": "sha1-6XVe2kB+ltpAxeUVjJ6jezO+y+s=", - "requires": { - "once": "1.4.0", - "unzip-response": "1.0.2", - "xtend": "4.0.1" - } - }, - "simple-rcon": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/simple-rcon/-/simple-rcon-0.3.1.tgz", - "integrity": "sha1-IwPbzDE5yJPscApyeLukP4yxBDs=" - }, - "sinon": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-2.4.1.tgz", - "integrity": "sha512-vFTrO9Wt0ECffDYIPSP/E5bBugt0UjcBQOfQUMh66xzkyPEnhl/vM2LRZi2ajuTdkH07sA6DzrM6KvdvGIH8xw==", - "dev": true, - "requires": { - "diff": "3.2.0", - "formatio": "1.2.0", - "lolex": "1.6.0", - "native-promise-only": "0.8.1", - "path-to-regexp": "1.7.0", - "samsam": "1.3.0", - "text-encoding": "0.6.4", - "type-detect": "4.0.3" - }, - "dependencies": { - "path-to-regexp": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", - "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", - "dev": true, - "requires": { - "isarray": "0.0.1" - } - }, - "type-detect": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.3.tgz", - "integrity": "sha1-Dj8mcLRAmbC0bChNE2p+9Jx0wuo=", - "dev": true - } - } - }, - "slash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", - "dev": true - }, - "snapdragon": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.1.tgz", - "integrity": "sha1-4StUh/re0+PeoKyR6UAL91tAE3A=", - "dev": true, - "requires": { - "base": "0.11.2", - "debug": "2.6.7", - "define-property": "0.2.5", - "extend-shallow": "2.0.1", - "map-cache": "0.2.2", - "source-map": "0.5.7", - "source-map-resolve": "0.5.1", - "use": "2.0.2" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "0.1.6" - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dev": true, - "requires": { - "define-property": "1.0.0", - "isobject": "3.0.1", - "snapdragon-util": "3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "dev": true, - "requires": { - "kind-of": "3.2.2" - } - }, - "sntp": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.0.2.tgz", - "integrity": "sha1-UGQRDwr4X3z9t9a2ekACjOUrSys=", - "requires": { - "hoek": "4.2.0" - } - }, - "socket.io": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.0.4.tgz", - "integrity": "sha1-waRZDO/4fs8TxyZS8Eb3FrKeYBQ=", - "requires": { - "debug": "2.6.7", - "engine.io": "3.1.4", - "socket.io-adapter": "1.1.1", - "socket.io-client": "2.0.4", - "socket.io-parser": "3.1.2" - } - }, - "socket.io-adapter": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz", - "integrity": "sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs=" - }, - "socket.io-client": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.0.4.tgz", - "integrity": "sha1-CRilUkBtxeVAs4Dc2Xr8SmQzL44=", - "requires": { - "backo2": "1.0.2", - "base64-arraybuffer": "0.1.5", - "component-bind": "1.0.0", - "component-emitter": "1.2.1", - "debug": "2.6.7", - "engine.io-client": "3.1.4", - "has-cors": "1.1.0", - "indexof": "0.0.1", - "object-component": "0.0.3", - "parseqs": "0.0.5", - "parseuri": "0.0.5", - "socket.io-parser": "3.1.2", - "to-array": "0.1.4" - } - }, - "socket.io-parser": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.1.2.tgz", - "integrity": "sha1-28IoIVH8T6675Aru3Ady66YZ9/I=", - "requires": { - "component-emitter": "1.2.1", - "debug": "2.6.7", - "has-binary2": "1.0.2", - "isarray": "2.0.1" - }, - "dependencies": { - "isarray": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", - "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" - } - } - }, - "sort-keys": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", - "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", - "requires": { - "is-plain-obj": "1.1.0" - } - }, - "source-map": { - "version": "0.1.31", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.31.tgz", - "integrity": "sha1-n3BNDWnZ4TioG63267T94z0VHGE=", - "requires": { - "amdefine": "1.0.1" - } - }, - "source-map-resolve": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.1.tgz", - "integrity": "sha512-0KW2wvzfxm8NCTb30z0LMNyPqWCdDGE2viwzUaucqJdkTRXtZiSY3I+2A6nVAjmdOy0I4gU8DwnVVGsk9jvP2A==", - "dev": true, - "requires": { - "atob": "2.0.3", - "decode-uri-component": "0.2.0", - "resolve-url": "0.2.1", - "source-map-url": "0.4.0", - "urix": "0.1.0" - } - }, - "source-map-support": { - "version": "0.4.18", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", - "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", - "dev": true, - "requires": { - "source-map": "0.5.7" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", - "dev": true - }, - "space-separated-tokens": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.1.tgz", - "integrity": "sha1-lpW5355lrsGBHUw/nOUlILwvfk0=", - "dev": true, - "requires": { - "trim": "0.0.1" - } - }, - "spawn-sync": { - "version": "1.0.15", - "resolved": "https://registry.npmjs.org/spawn-sync/-/spawn-sync-1.0.15.tgz", - "integrity": "sha1-sAeZVX63+wyDdsKdROih6mfldHY=", - "requires": { - "concat-stream": "1.6.0", - "os-shim": "0.1.3" - } - }, - "spdx-correct": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz", - "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=", - "dev": true, - "requires": { - "spdx-license-ids": "1.2.2" - } - }, - "spdx-expression-parse": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz", - "integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw=", - "dev": true - }, - "spdx-license-ids": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz", - "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=", - "dev": true - }, - "split-string": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.0.2.tgz", - "integrity": "sha512-d6myUSfwmBz1izkY4r7r7I0PL41rh21qUDYK1OgclmGHeoqQoujduGxMbzw6BlF3HKmJR4sMpbWVo7/Xzg4YBQ==", - "dev": true, - "requires": { - "extend-shallow": "2.0.1" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" - }, - "sshpk": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", - "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", - "requires": { - "asn1": "0.2.3", - "assert-plus": "1.0.0", - "bcrypt-pbkdf": "1.0.1", - "dashdash": "1.14.1", - "ecc-jsbn": "0.1.1", - "getpass": "0.1.7", - "jsbn": "0.1.1", - "tweetnacl": "0.14.5" - } - }, - "stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" - }, - "state-toggle": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.0.tgz", - "integrity": "sha1-0g+aYWu08MO5i5GSLSW2QKorxCU=", - "dev": true - }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "dev": true, - "requires": { - "define-property": "0.2.5", - "object-copy": "0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "0.1.6" - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "statuses": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", - "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=" - }, - "stealthy-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", - "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=" - }, - "stream-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/stream-array/-/stream-array-1.1.2.tgz", - "integrity": "sha1-nl9zRfITfDDuO0mLkRToC1K7frU=", - "dev": true, - "requires": { - "readable-stream": "2.1.5" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "readable-stream": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.1.5.tgz", - "integrity": "sha1-ZvqLcg4UOLNkaB8q0aY8YYRIydA=", - "dev": true, - "requires": { - "buffer-shims": "1.0.0", - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "string_decoder": "0.10.31", - "util-deprecate": "1.0.2" - } - } - } - }, - "stream-combiner2": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", - "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=", - "dev": true, - "requires": { - "duplexer2": "0.1.4", - "readable-stream": "2.3.3" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "readable-stream": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", - "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", - "dev": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" - } - }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } - } - } - }, - "stream-shift": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", - "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", - "dev": true - }, - "streamifier": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/streamifier/-/streamifier-0.1.1.tgz", - "integrity": "sha1-l+mNj6TRBdYqJpHR3AfoINuN/E8=" - }, - "streamsearch": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", - "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" - }, - "strict-uri-encode": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", - "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=" - }, - "string-template": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/string-template/-/string-template-0.2.1.tgz", - "integrity": "sha1-QpMuWYo1LQH8IuwzZ9nYTuxsmt0=", - "dev": true - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - }, - "stringify-entities": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-1.3.1.tgz", - "integrity": "sha1-sVDsLXKsTBtfMktR+2soyc3/BYw=", - "dev": true, - "requires": { - "character-entities-html4": "1.1.1", - "character-entities-legacy": "1.1.1", - "is-alphanumerical": "1.0.1", - "is-hexadecimal": "1.0.1" - } - }, - "stringstream": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", - "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=" - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "2.1.1" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - }, - "strip-bom-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-1.0.0.tgz", - "integrity": "sha1-5xRDmFd9Uaa+0PoZlPoF9D/ZiO4=", - "dev": true, - "requires": { - "first-chunk-stream": "1.0.0", - "strip-bom": "2.0.0" - }, - "dependencies": { - "strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "dev": true, - "requires": { - "is-utf8": "0.2.1" - } - } - } - }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" - }, - "subarg": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", - "integrity": "sha1-9izxdYHplrSPyWVpn1TAauJouNI=", - "dev": true, - "requires": { - "minimist": "1.2.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - }, - "superagent": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.7.0.tgz", - "integrity": "sha512-/8trxO6NbLx4YXb7IeeFTSmsQ35pQBiTBsLNvobZx7qBzBeHYvKCyIIhW2gNcWbLzYxPAjdgFbiepd8ypwC0Gw==", - "dev": true, - "requires": { - "component-emitter": "1.2.1", - "cookiejar": "2.1.1", - "debug": "3.1.0", - "extend": "3.0.1", - "form-data": "2.3.1", - "formidable": "1.1.1", - "methods": "1.1.2", - "mime": "1.4.1", - "qs": "6.5.1", - "readable-stream": "2.3.3" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "qs": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", - "dev": true - }, - "readable-stream": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", - "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", - "dev": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" - } - }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } - } - } - }, - "supertest": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-3.0.0.tgz", - "integrity": "sha1-jUu2j9GDDuBwM7HFpamkAhyWUpY=", - "dev": true, - "requires": { - "methods": "1.1.2", - "superagent": "3.7.0" - } - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "1.0.0" - } - }, - "sync-request": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/sync-request/-/sync-request-4.1.0.tgz", - "integrity": "sha512-iFbOBWYaznBNbheIKaMkj+3EabpEsXbuwcTVuYkRjoav+Om5L8VXXLIXms0cHxkouXMRCQaSfhfau9/HyIbM2Q==", - "requires": { - "command-exists": "1.2.2", - "concat-stream": "1.6.0", - "get-port": "3.1.0", - "http-response-object": "1.1.0", - "then-request": "2.2.0" - } - }, - "tabtab": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/tabtab/-/tabtab-2.2.2.tgz", - "integrity": "sha1-egR/FDsBC0y9MfhX6ClhUSy/ThQ=", - "requires": { - "debug": "2.6.7", - "inquirer": "1.2.3", - "lodash.difference": "4.5.0", - "lodash.uniq": "4.5.0", - "minimist": "1.2.0", - "mkdirp": "0.5.1", - "npmlog": "2.0.4", - "object-assign": "4.1.1" - }, - "dependencies": { - "gauge": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-1.2.7.tgz", - "integrity": "sha1-6c7FSD09TuDvRLYKfZnkk14TbZM=", - "requires": { - "ansi": "0.3.1", - "has-unicode": "2.0.1", - "lodash.pad": "4.5.1", - "lodash.padend": "4.6.1", - "lodash.padstart": "4.6.1" - } - }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" - }, - "npmlog": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-2.0.4.tgz", - "integrity": "sha1-mLUlMPJRTKkNCexbIsiEZyI3VpI=", - "requires": { - "ansi": "0.3.1", - "are-we-there-yet": "1.1.4", - "gauge": "1.2.7" - } - } - } - }, - "tar-fs": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-1.16.0.tgz", - "integrity": "sha512-I9rb6v7mjWLtOfCau9eH5L7sLJyU2BnxtEZRQ5Mt+eRKmf1F0ohXmT/Jc3fr52kDvjJ/HV5MH3soQfPL5bQ0Yg==", - "requires": { - "chownr": "1.0.1", - "mkdirp": "0.5.1", - "pump": "1.0.2", - "tar-stream": "1.5.4" - } - }, - "tar-stream": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.5.4.tgz", - "integrity": "sha1-NlSc8E7RrumyowwBQyUiONr5QBY=", - "requires": { - "bl": "1.2.1", - "end-of-stream": "1.4.0", - "readable-stream": "2.3.3", - "xtend": "4.0.1" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "readable-stream": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", - "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" - } - }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "requires": { - "safe-buffer": "5.1.1" - } - } - } - }, - "term-size": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", - "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", - "requires": { - "execa": "0.7.0" - } - }, - "text-encoding": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz", - "integrity": "sha1-45mpgiV6J22uQou5KEXLcb3CbRk=", - "dev": true - }, - "then-request": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/then-request/-/then-request-2.2.0.tgz", - "integrity": "sha1-ZnizL6DKIY/laZgbvYhxtZQGDYE=", - "requires": { - "caseless": "0.11.0", - "concat-stream": "1.6.0", - "http-basic": "2.5.1", - "http-response-object": "1.1.0", - "promise": "7.1.1", - "qs": "6.4.0" - }, - "dependencies": { - "caseless": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", - "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=" - } - } - }, - "throttleit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz", - "integrity": "sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw=" - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" - }, - "through2": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", - "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", - "requires": { - "readable-stream": "1.0.34", - "xtend": "4.0.1" - }, - "dependencies": { - "readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "0.0.1", - "string_decoder": "0.10.31" - } - } - } - }, - "through2-filter": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-2.0.0.tgz", - "integrity": "sha1-YLxVoNrLdghdsfna6Zq0P4PWIuw=", - "dev": true, - "requires": { - "through2": "2.0.3", - "xtend": "4.0.1" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "readable-stream": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", - "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", - "dev": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" - } - }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } - }, - "through2": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", - "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", - "dev": true, - "requires": { - "readable-stream": "2.3.3", - "xtend": "4.0.1" - } - } - } - }, - "timed-out": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", - "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=" - }, - "tiny-lr": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-1.0.5.tgz", - "integrity": "sha512-YrxUSiMgOVh3PnAqtdAUQuUVEVRnqcRCxJ3BHrl/aaWV2fplKKB60oClM0GH2Gio2hcXvkxMUxsC/vXZrQePlg==", - "dev": true, - "requires": { - "body": "5.1.0", - "debug": "2.6.7", - "faye-websocket": "0.10.0", - "livereload-js": "2.2.2", - "object-assign": "4.1.1", - "qs": "6.4.0" - } - }, - "tmp": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.29.tgz", - "integrity": "sha1-8lEl/w3Z2jzLDC3Tce4SiLuRKMA=", - "requires": { - "os-tmpdir": "1.0.2" - } - }, - "to-absolute-glob": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-0.1.1.tgz", - "integrity": "sha1-HN+kcqnvUMI57maZm2YsoOs5k38=", - "dev": true, - "requires": { - "extend-shallow": "2.0.1" - } - }, - "to-array": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", - "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=" - }, - "to-fast-properties": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", - "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", - "dev": true - }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - } - }, - "to-regex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.1.tgz", - "integrity": "sha1-FTWL7kosg712N3uh3ASdDxiDeq4=", - "dev": true, - "requires": { - "define-property": "0.2.5", - "extend-shallow": "2.0.1", - "regex-not": "1.0.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "0.1.6" - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "requires": { - "is-number": "3.0.0", - "repeat-string": "1.6.1" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - } - } - } - }, - "tough-cookie": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", - "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", - "requires": { - "punycode": "1.4.1" - } - }, - "trim": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", - "integrity": "sha1-WFhUf2spB1fulczMZm+1AITEYN0=", - "dev": true - }, - "trim-lines": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-1.1.0.tgz", - "integrity": "sha1-mSbQPt4Tuhj31CIiYx+wTHn/Jv4=", - "dev": true - }, - "trim-right": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", - "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", - "dev": true - }, - "trim-trailing-lines": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.0.tgz", - "integrity": "sha1-eu+7eAjfnWafbaLkOMrIxGradoQ=", - "dev": true - }, - "trough": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.1.tgz", - "integrity": "sha1-qf2LA5Swro//guBjOgo2zK1bX4Y=", - "dev": true - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "requires": { - "safe-buffer": "5.1.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "optional": true - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "requires": { - "prelude-ls": "1.1.2" - } - }, - "type-detect": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-1.0.0.tgz", - "integrity": "sha1-diIXzAbbJY7EiQihKY6LlRIejqI=", - "dev": true - }, - "type-is": { - "version": "1.6.15", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", - "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", - "requires": { - "media-typer": "0.3.0", - "mime-types": "2.1.17" - } - }, - "typechecker": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/typechecker/-/typechecker-4.4.1.tgz", - "integrity": "sha1-+XuV9RsDhBchLWd9RaNz7nvO1+Y=", - "requires": { - "editions": "1.3.3" - } - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" - }, - "uglify-js": { - "version": "2.8.29", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", - "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", - "dev": true, - "optional": true, - "requires": { - "source-map": "0.5.6", - "uglify-to-browserify": "1.0.2", - "yargs": "3.10.0" - }, - "dependencies": { - "source-map": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", - "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=", - "dev": true, - "optional": true - }, - "yargs": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", - "dev": true, - "optional": true, - "requires": { - "camelcase": "1.2.1", - "cliui": "2.1.0", - "decamelize": "1.2.0", - "window-size": "0.1.0" - } - } - } - }, - "uglify-to-browserify": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", - "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", - "dev": true, - "optional": true - }, - "ultron": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.0.tgz", - "integrity": "sha1-sHoualQagV/Go0zNRTO67DB8qGQ=" - }, - "unc-path-regex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", - "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=", - "dev": true - }, - "underscore": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", - "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" - }, - "unherit": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.0.tgz", - "integrity": "sha1-a5qu379z3xdWrZ4xbdmBiFhAzX0=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "xtend": "4.0.1" - } - }, - "unified": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/unified/-/unified-6.1.5.tgz", - "integrity": "sha1-cWk3hyYhpjE15iztLzrGoGPG+4c=", - "dev": true, - "requires": { - "bail": "1.0.2", - "extend": "3.0.1", - "is-plain-obj": "1.1.0", - "trough": "1.0.1", - "vfile": "2.2.0", - "x-is-function": "1.0.4", - "x-is-string": "0.1.0" - } - }, - "union-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", - "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", - "dev": true, - "requires": { - "arr-union": "3.1.0", - "get-value": "2.0.6", - "is-extendable": "0.1.1", - "set-value": "0.4.3" - }, - "dependencies": { - "set-value": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", - "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", - "dev": true, - "requires": { - "extend-shallow": "2.0.1", - "is-extendable": "0.1.1", - "is-plain-object": "2.0.4", - "to-object-path": "0.3.0" - } - } - } - }, - "unique-stream": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.2.1.tgz", - "integrity": "sha1-WqADz76Uxf+GbE59ZouxxNuts2k=", - "dev": true, - "requires": { - "json-stable-stringify": "1.0.1", - "through2-filter": "2.0.0" - } - }, - "unique-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", - "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", - "requires": { - "crypto-random-string": "1.0.0" - } - }, - "unist-builder": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unist-builder/-/unist-builder-1.0.2.tgz", - "integrity": "sha1-jDuZA+9kvPsRfdfPal2Y/Bs7J7Y=", - "dev": true, - "requires": { - "object-assign": "4.1.1" - } - }, - "unist-util-generated": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-1.1.1.tgz", - "integrity": "sha1-mfFseJWayFTe58YVwpGSTIv03n8=", - "dev": true - }, - "unist-util-is": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-2.1.1.tgz", - "integrity": "sha1-DDEmKeP5YMZukx6BLT2A53AQlHs=", - "dev": true - }, - "unist-util-modify-children": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unist-util-modify-children/-/unist-util-modify-children-1.1.1.tgz", - "integrity": "sha1-ZtfmpEnm9nIguXarPLi166w55R0=", - "dev": true, - "requires": { - "array-iterate": "1.1.1" - } - }, - "unist-util-position": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-3.0.0.tgz", - "integrity": "sha1-5uHgPu64HF4a/lU+jUrfvXwNj4I=", - "dev": true - }, - "unist-util-remove-position": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-1.1.1.tgz", - "integrity": "sha1-WoXBVV/BugwQG4ZwfRXlD6TIcbs=", - "dev": true, - "requires": { - "unist-util-visit": "1.1.3" - } - }, - "unist-util-stringify-position": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-1.1.1.tgz", - "integrity": "sha1-PMvcU2ee7W7PN3fdf14yKcG2qjw=", - "dev": true - }, - "unist-util-visit": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.1.3.tgz", - "integrity": "sha1-7CaOcxudJ3p5pbWqBkOZDkBdYAs=", - "dev": true - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" - }, - "unreachable-branch-transform": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/unreachable-branch-transform/-/unreachable-branch-transform-0.3.0.tgz", - "integrity": "sha1-2ZzExudG0mSSiEW2EdtUsPNHTKo=", - "requires": { - "esmangle-evaluator": "1.0.1", - "recast": "0.10.43", - "through2": "0.6.5" - } - }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dev": true, - "requires": { - "has-value": "0.3.1", - "isobject": "3.0.1" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dev": true, - "requires": { - "get-value": "2.0.6", - "has-values": "0.1.4", - "isobject": "2.1.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "unzip-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-1.0.2.tgz", - "integrity": "sha1-uYTwh3/AqJwsdzzB73tbIytbBv4=" - }, - "update-notifier": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.3.0.tgz", - "integrity": "sha1-TognpruRUUCrCTVZ1wFOPruDdFE=", - "requires": { - "boxen": "1.2.2", - "chalk": "2.3.0", - "configstore": "3.1.1", - "import-lazy": "2.1.0", - "is-installed-globally": "0.1.0", - "is-npm": "1.0.0", - "latest-version": "3.1.0", - "semver-diff": "2.1.0", - "xdg-basedir": "3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "requires": { - "color-convert": "1.9.0" - } - }, - "chalk": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", - "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", - "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "4.5.0" - } - }, - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=" - }, - "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", - "requires": { - "has-flag": "2.0.0" - } - } - } - }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true - }, - "url-parse-lax": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", - "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", - "requires": { - "prepend-http": "1.0.4" - } - }, - "urlgrey": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/urlgrey/-/urlgrey-0.4.4.tgz", - "integrity": "sha1-iS/pWWCAXoVRnxzUOJ8stMu3ZS8=" - }, - "use": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/use/-/use-2.0.2.tgz", - "integrity": "sha1-riig1y+TvyJCKhii43mZMRLeyOg=", - "dev": true, - "requires": { - "define-property": "0.2.5", - "isobject": "3.0.1", - "lazy-cache": "2.0.2" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "0.1.6" - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - }, - "lazy-cache": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-2.0.2.tgz", - "integrity": "sha1-uRkKT5EzVGlIQIWfio9whNiCImQ=", - "dev": true, - "requires": { - "set-getter": "0.1.0" - } - } - } - }, - "util": { - "version": "github:ivshti/node-util#97b6beed8e0e478cd4184965ac94bf316f94e5f5", - "requires": { - "inherits": "github:Ivshti/inherits#0c5b02ddc4172bf2e55460916401ac37cfe6f9d3" - }, - "dependencies": { - "inherits": { - "version": "github:Ivshti/inherits#0c5b02ddc4172bf2e55460916401ac37cfe6f9d3" - } - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" - }, - "uuid": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", - "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==" - }, - "uws": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/uws/-/uws-0.14.5.tgz", - "integrity": "sha1-Z6rzPEaypYel9mZtAPdpEyjxSdw=", - "optional": true - }, - "vali-date": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/vali-date/-/vali-date-1.0.0.tgz", - "integrity": "sha1-G5BKWWCfsyjvB4E4Qgk09rhnCaY=", - "dev": true - }, - "validate-npm-package-license": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz", - "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=", - "dev": true, - "requires": { - "spdx-correct": "1.0.2", - "spdx-expression-parse": "1.0.4" - } - }, - "validator": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-6.3.0.tgz", - "integrity": "sha1-R84j7Y1Ord+p1LjvAHG2zxB418g=" - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "requires": { - "assert-plus": "1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "1.3.0" - } - }, - "vfile": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-2.2.0.tgz", - "integrity": "sha1-zkek+zNZIrIz5TXbD32BIdj87U4=", - "dev": true, - "requires": { - "is-buffer": "1.1.5", - "replace-ext": "1.0.0", - "unist-util-stringify-position": "1.1.1" - } - }, - "vfile-location": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-2.0.2.tgz", - "integrity": "sha1-02dcWch3SY5JK0dW/2Xkrxp1IlU=", - "dev": true - }, - "vfile-reporter": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/vfile-reporter/-/vfile-reporter-4.0.0.tgz", - "integrity": "sha1-6m8K4TQvSEFXOYXgX5QXNvJ96do=", - "dev": true, - "requires": { - "repeat-string": "1.6.1", - "string-width": "1.0.2", - "supports-color": "4.5.0", - "unist-util-stringify-position": "1.1.1", - "vfile-statistics": "1.1.0" - }, - "dependencies": { - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true - }, - "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", - "dev": true, - "requires": { - "has-flag": "2.0.0" - } - } - } - }, - "vfile-sort": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/vfile-sort/-/vfile-sort-2.1.0.tgz", - "integrity": "sha1-SVAcnou+Wt/y6bOnZx7hseIMUhA=", - "dev": true - }, - "vfile-statistics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/vfile-statistics/-/vfile-statistics-1.1.0.tgz", - "integrity": "sha1-AhBMYP3u0dEbH3OtZTMLdjSz2JU=", - "dev": true - }, - "vinyl": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.1.0.tgz", - "integrity": "sha1-Ah+cLPlR1rk5lDyJ617lrdT9kkw=", - "dev": true, - "requires": { - "clone": "2.1.1", - "clone-buffer": "1.0.0", - "clone-stats": "1.0.0", - "cloneable-readable": "1.0.0", - "remove-trailing-separator": "1.1.0", - "replace-ext": "1.0.0" - } - }, - "vinyl-fs": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-2.4.4.tgz", - "integrity": "sha1-vm/zJwy1Xf19MGNkDegfJddTIjk=", - "dev": true, - "requires": { - "duplexify": "3.5.1", - "glob-stream": "5.3.5", - "graceful-fs": "4.1.11", - "gulp-sourcemaps": "1.6.0", - "is-valid-glob": "0.3.0", - "lazystream": "1.0.0", - "lodash.isequal": "4.5.0", - "merge-stream": "1.0.1", - "mkdirp": "0.5.1", - "object-assign": "4.1.1", - "readable-stream": "2.3.3", - "strip-bom": "2.0.0", - "strip-bom-stream": "1.0.0", - "through2": "2.0.3", - "through2-filter": "2.0.0", - "vali-date": "1.0.0", - "vinyl": "1.2.0" - }, - "dependencies": { - "clone": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.3.tgz", - "integrity": "sha1-KY1+IjFmD0DAA8LtMUDezz9TCF8=", - "dev": true - }, - "clone-stats": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", - "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=", - "dev": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "readable-stream": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", - "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", - "dev": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" - } - }, - "replace-ext": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", - "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=", - "dev": true - }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } - }, - "strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "dev": true, - "requires": { - "is-utf8": "0.2.1" - } - }, - "through2": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", - "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", - "dev": true, - "requires": { - "readable-stream": "2.3.3", - "xtend": "4.0.1" - } - }, - "vinyl": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", - "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", - "dev": true, - "requires": { - "clone": "1.0.3", - "clone-stats": "0.0.1", - "replace-ext": "0.0.1" - } - } - } - }, - "websocket-driver": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz", - "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", - "dev": true, - "requires": { - "http-parser-js": "0.4.9", - "websocket-extensions": "0.1.3" - } - }, - "websocket-extensions": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", - "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==", - "dev": true - }, - "which": { - "version": "1.2.14", - "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz", - "integrity": "sha1-mofEN48D6CfOyvGs31bHNsAcFOU=", - "requires": { - "isexe": "2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "wide-align": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz", - "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", - "requires": { - "string-width": "1.0.2" - } - }, - "widest-line": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-1.0.0.tgz", - "integrity": "sha1-DAnIXCqUaD0Nfq+O4JfVZL8OEFw=", - "requires": { - "string-width": "1.0.2" - } - }, - "window-size": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", - "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", - "dev": true, - "optional": true - }, - "winston": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.0.tgz", - "integrity": "sha1-gIBQuT1SZh7Z+2wms/DIJnCLCu4=", - "requires": { - "async": "1.0.0", - "colors": "1.0.3", - "cycle": "1.0.3", - "eyes": "0.1.8", - "isstream": "0.1.2", - "stack-trace": "0.0.10" - }, - "dependencies": { - "async": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", - "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=" - }, - "colors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", - "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=" - } - } - }, - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true - }, - "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "dev": true, - "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "write-file-atomic": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.3.0.tgz", - "integrity": "sha512-xuPeK4OdjWqtfi59ylvVL0Yn35SF3zgcAcv7rBPFHVaEapaDr4GdGgm3j7ckTwH9wHL7fGmgfAnb0+THrHb8tA==", - "requires": { - "graceful-fs": "4.1.11", - "imurmurhash": "0.1.4", - "signal-exit": "3.0.2" - } - }, - "ws": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.1.tgz", - "integrity": "sha512-8A/uRMnQy8KCQsmep1m7Bk+z/+LIkeF7w+TDMLtX1iZm5Hq9HsUDmgFGaW1ACW5Cj0b2Qo7wCvRhYN2ErUVp/A==", - "requires": { - "async-limiter": "1.0.0", - "safe-buffer": "5.1.1", - "ultron": "1.1.0" - } - }, - "x-is-function": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/x-is-function/-/x-is-function-1.0.4.tgz", - "integrity": "sha1-XSlNw9Joy90GJYDgxd93o5HR+h4=", - "dev": true - }, - "x-is-string": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/x-is-string/-/x-is-string-0.1.0.tgz", - "integrity": "sha1-R0tQhlrzpJqcRlfwWs0UVFj3fYI=", - "dev": true - }, - "xdg-basedir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", - "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=" - }, - "xmlhttprequest-ssl": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.4.tgz", - "integrity": "sha1-BPVgkVcks4kIhxXMDteBPpZ3v1c=" - }, - "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" - }, - "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", - "dev": true - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" - }, - "yargs": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-1.2.6.tgz", - "integrity": "sha1-nHtKgv1dWVsr8Xq23MQxNUMv40s=", - "requires": { - "minimist": "0.1.0" - } - }, - "yargs-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz", - "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", - "dev": true, - "requires": { - "camelcase": "4.1.0" - }, - "dependencies": { - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - } - } - }, - "yeast": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", - "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=" - }, - "z-schema": { - "version": "3.18.2", - "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-3.18.2.tgz", - "integrity": "sha1-5CIZa17+YLRq3vPD8q7y3qqREWE=", - "requires": { - "commander": "2.9.0", - "lodash.get": "4.4.2", - "lodash.isequal": "4.5.0", - "validator": "6.3.0" - } - }, - "z-schema-errors": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/z-schema-errors/-/z-schema-errors-0.0.1.tgz", - "integrity": "sha1-4GJwpMpDklcp8ldkeJyp9Gv/D30=", - "requires": { - "xtend": "4.0.1" - } - } - } -} diff --git a/package.json b/package.json index 98fdc942..3a39e4f9 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,8 @@ "cover": "./node_modules/.bin/istanbul cover ./node_modules/mocha/bin/_mocha -- \"./{{,node_modules/_app/**/},!(node_modules)/**/}*.spec.js\" -R spec || echo . && codecov -t f7b38085-36eb-4d72-84e6-64721910171e", "docs": "documentation build index.js --config documentation.yml -f html -o docs", "livedocs": "documentation serve --watch *.js lib/** --config documentation.yml", - "install": "node lib/npmPostinstall.js" + "install": "node lib/npmPostinstall.js", + "bundle-dependencies": "bundle-dependencies" }, "keywords": [ "factorio" @@ -22,30 +23,34 @@ "as-table": "^1.0.31", "averaged-timeseries": "^1.4.0", "base64url": "^3.0.0", + "bcrypt": "^2.0.1", + "bcrypt-promise": "^2.0.0", "bcryptjs": "^2.4.3", "body-parser": "^1.18.2", + "cache-require-paths": "^0.3.0", + "cli-interact": "^0.1.9", "codecov": "^3.0.1", "compression": "^1.7.2", + "cookie-parser": "^1.4.3", "deepmerge": "^1.5.2", "ejs": "^2.5.7", "express": "^4.16.2", "express-fileupload": "0.0.6", - "follow-redirects": "^1.4.1", - "fs-extra": "^1.0.0", + "express-prometheus-request-metrics": "github:burnash/express-prometheus-request-metrics", + "follow-redirects": "^1.5.0", + "fs-extra": "^6.0.1", "getmac": "^1.4.0", "hash-files": "^1.1.1", "html5-validator": "^1.0.7", "ini": "^1.3.5", "jsonwebtoken": "^8.2.1", - "leveldown": "^1.9.0", - "linvodb3": "^3.25.1", "luamin": "^1.0.4", + "minimist": "^1.2.0", "mkdirp": "^0.5.1", "mkdirp-promise": "^5.0.1", - "moment": "^2.20.1", + "moment": "^2.22.2", "ncp": "^2.0.0", - "nedb": "^1.8.0", - "needle": "^1.1.0", + "needle": "^2.2.4", "node-factorio-api": "^0.3.1-alpha", "node-forge": "^0.7.1", "prom-client": "^10.2.3", @@ -53,6 +58,8 @@ "request": "^2.83.0", "right-pad": "^1.0.1", "rmdir-sync": "^1.0.1", + "sanitizer": "^0.1.3", + "simple-git": "^1.96.0", "single-line-log": "^1.1.2", "socket.io": "^2.0.4", "socket.io-client": "^2.0.4", @@ -68,5 +75,6 @@ "nock": "^9.1.6", "sinon": "^2.4.1", "supertest": "^3.0.0" - } + }, + "snyk": true } diff --git a/sharedPlugins/UPSdisplay/index.js b/sharedPlugins/UPSdisplay/index.js index 5618783e..e8928abb 100644 --- a/sharedPlugins/UPSdisplay/index.js +++ b/sharedPlugins/UPSdisplay/index.js @@ -3,12 +3,20 @@ const needle = require("needle"); class UPSdisplay { - constructor(slaveConfig, msgInterface){ + constructor(slaveConfig, msgInterface, extras){ this.config = slaveConfig; this.messageInterface = msgInterface; + this.socket = extras.socket; + this.historicalTicks = []; setInterval(() => { - this.messageInterface("/silent-command game.write_file('UPSdisplay.txt', game.tick, true, 0)"); + this.messageInterface("/silent-command rcon.print(game.tick)", data => { + this.tick = Number(data); + this.historicalTicks[this.historicalTicks.length] = {tick:Number(data), timestamp:Date.now()}; + if(this.historicalTicks.length > 30){ + this.historicalTicks.shift(); // delete last element in array (position 0); + } + }); },1000); setInterval(() => { if(this.historicalTicks && this.historicalTicks[0]){ @@ -17,24 +25,15 @@ class UPSdisplay { let UPS = Math.round(ticksInPeriod / (timePeriod/1000)); // console.log("UPS: " + UPS); try{ - needle.post(this.config.masterIP+':'+this.config.masterPort+'/api/editSlaveMeta', {instanceID: this.config.unique, password: this.config.clientPassword, meta: {UPS:UPS}}, {headers: {'x-access-token': this.config.masterAuthToken}}, function(err, resp) { + needle.post(this.config.masterIP+':'+this.config.masterPort+'/api/editSlaveMeta', {instanceID: this.config.unique, password: this.config.clientPassword, meta: {UPS:UPS, tick:this.tick}}, {headers: {'x-access-token': this.config.masterAuthToken}}, function(err, resp) { // success? }); } catch (err){ console.log(err); } } - }, 10000); + }, 5000); this.messageInterface("/silent-command game.print('UPSdisplay enabled')"); } - scriptOutput(data){ - if (data !== null && !isNaN(data)){ - // console.log("Tick: " + data); - this.historicalTicks[this.historicalTicks.length] = {tick:Number(data), timestamp:Date.now()}; - if(this.historicalTicks.length > 30){ - this.historicalTicks.shift(); // delete last element in array (position 0); - } - } - } } module.exports = UPSdisplay; \ No newline at end of file diff --git a/sharedPlugins/clusterioMod/config.js b/sharedPlugins/clusterioMod/config.js new file mode 100644 index 00000000..01c46abf --- /dev/null +++ b/sharedPlugins/clusterioMod/config.js @@ -0,0 +1,11 @@ +/* + Clusterio plugin to allow for chat between instances. +*/ +module.exports = { + // Name of package. For display somewhere I guess. + name: "clusterioMod", + version: "1.0.0", + binary: "nodePackage", + description: "Server side handling of functionality in the clusterio lua mod", + masterPlugin: "masterPlugin.js", +} diff --git a/sharedPlugins/clusterioMod/index.js b/sharedPlugins/clusterioMod/index.js new file mode 100644 index 00000000..4377c842 --- /dev/null +++ b/sharedPlugins/clusterioMod/index.js @@ -0,0 +1,395 @@ +const needle = require("needle"); +const fs = require("fs-extra"); + +const objectOps = require("./../../lib/objectOps.js"); +const fileOps = require("_app/fileOps"); +const stringUtils = require("./../../lib/stringUtils.js"); + +const pluginConfig = require("./config"); +const COMPRESS_LUA = false; + +module.exports = class remoteCommands { + constructor(mergedConfig, messageInterface, extras){ + this.messageInterface = messageInterface; + this.config = mergedConfig; + this.socket = extras.socket; + this.socket.on("hello", () => { + + }); + const that = this; + const instance = process.argv[3]; + const instancedirectory = this.config.instanceDirectory + '/' + instance; + + const needleOptionsWithTokenAuthHeader = { + headers: { + 'x-access-token': this.config.masterAuthToken + }, + }; + + (async ()=>{ + let hotpatchInstallStatus = await this.checkHotpatchInstallation(); + this.messageInterface("Hotpach installation status: "+hotpatchInstallStatus); + if(hotpatchInstallStatus){ + // let mainCode = await this.getSafeLua("sharedPlugins/playerManager/lua/playerTracking.lua"); + // if(mainCode) var returnValue = await messageInterface("/silent-command remote.call('hotpatch', 'update', '"+pluginConfig.name+"', '"+pluginConfig.version+"', '"+mainCode+"')"); + } + })().catch(e => console.log(e)); + + // flow/production statistics ------------------------------------------------------------ + var oldFlowStats = false; + var oldTimestamp; + var oldFlowStats; + setInterval(function(){ + fs.readFile(instancedirectory + "/script-output/flows.txt", {encoding: "utf8"}, function(err, data) { + if(!err && data) { + let timestamp = Date.now(); + data = data.split("\n"); + let flowStats = []; + for(let i = 0; i < data.length; i++) { + // try catch to remove any invalid json + try{ + flowStats[flowStats.length] = JSON.parse(data[i]); + } catch (e) { + // console.log(" invalid json: " + i); + // some lines of JSON are invalid but don't worry, we just filter em out + } + } + // fluids + let flowStat1 = flowStats[flowStats.length-1].flows.player.input_counts + // items + let flowStat2 = flowStats[flowStats.length-2].flows.player.input_counts + // merge fluid and item flows + let totalFlows = {}; + for(let key in flowStat1) totalFlows[key] = flowStat1[key]; + for(let key in flowStat2) totalFlows[key] = flowStat2[key]; + if(oldFlowStats && totalFlows && oldTimestamp) { + let payload = objectOps.deepclone(totalFlows); + // change from total reported to per time unit + for(let key in oldFlowStats) { + // get production per minute + payload[key] = Math.floor((payload[key] - oldFlowStats[key])/(timestamp - oldTimestamp)*60000); + if(payload[key] < 0) { + payload[key] = 0; + } + } + for(let key in payload) { + if(payload[key] == '0') { + delete payload[key]; + } + } + console.log("Recorded flows, copper plate since last time: " + payload["copper-plate"]); + needle.post(that.config.masterIP + ":" + that.config.masterPort + '/api/logStats', {timestamp: timestamp, instanceID: that.config.unique, data: payload}, needleOptionsWithTokenAuthHeader, function (err, response, body) { + // we did it, keep going + }); + } + oldTimestamp = timestamp; + oldFlowStats = totalFlows; + fs.writeFileSync(instancedirectory + "/script-output/flows.txt", ""); + } + }); + // we don't need to update stats quickly as that could be expensive + }, 60000*5); + + // provide items -------------------------------------------------------------- + // trigger when something happens to output.txt + fs.watch(instancedirectory + "/script-output/output.txt", function (eventType, filename) { + // get array of lines in file + let items = fs.readFileSync(instancedirectory + "/script-output/output.txt", "utf8").split("\n"); + // if you found anything, reset the file + if (items[0]) { + fs.writeFileSync(instancedirectory + "/script-output/output.txt", ""); + } + for (let i = 0; i < items.length; i++) { + if (items[i]) { + let g = items[i].split(" "); + g[0] = g[0].replace("\u0000", ""); + // console.log("exporting " + JSON.stringify(g)); + // send our entity and count to the master for him to keep track of + needle.post(that.config.masterIP + ":" + that.config.masterPort + '/api/place', { + name: g[0], + count: g[1], + instanceName: instance, // name of instance + instanceID: that.config.unique, // a hash computed from the randomly generated rcon password + }, needleOptionsWithTokenAuthHeader, function (err, resp, body) { + if(body == "failure") console.error("#### Export failed! Lost: "+g[1]+" "+g[0]); + if(that.config.logItemTransfers){ + if(body == "success") console.log(`Exported ${g[1]} ${g[0]} to master`); + } + }); + } + } + }); + + // request items -------------------------------------------------------------- + setInterval(function () { + // get array of lines in file + let items = fs.readFileSync(instancedirectory + "/script-output/orders.txt", "utf8").split("\n"); + // if we actually got anything from the file, proceed and reset file + if (items[0]) { + fs.writeFileSync(instancedirectory + "/script-output/orders.txt", ""); + // prepare a package of all our requested items in a more transfer friendly format + var preparedPackage = {}; + for (let i = 0; i < items.length; i++) { + (function (i) { + if (items[i]) { + items[i] = items[i].split(" "); + items[i][0] = items[i][0].replace("\u0000", ""); + items[i][0] = items[i][0].replace(",", ""); + if (preparedPackage[items[i][0]]) { + // if we have buffered some already, sum the new items + if (typeof Number(preparedPackage[items[i][0]].count) == "number" && typeof Number(items[i][1]) == "number") { + preparedPackage[items[i][0]] = { + "name": items[i][0], + "count": Number(preparedPackage[items[i][0]].count) + Number(items[i][1]), + "instanceName":instance, + "instanceID":that.config.unique, + }; + // else just add em in without summing + } else if (typeof Number(items[i][1]) == "number") { + preparedPackage[items[i][0]] = { + "name": items[i][0], + "count": Number(items[i][1]), + "instanceName":instance, + "instanceID":that.config.unique, + }; + } + // this condition will NEVER be triggered but we know how that goes + } else if (typeof Number(items[i][1]) == "number") { + preparedPackage[items[i][0]] = { + "name": items[i][0], + "count": Number(items[i][1]), + "instanceName":instance, + "instanceID":that.config.unique, + }; + } + } + })(i); + } + // request our items, one item at a time + for (let i = 0; i < Object.keys(preparedPackage).length; i++) { + // console.log(preparedPackage[Object.keys(preparedPackage)[i]]); + needle.post(that.config.masterIP + ":" + that.config.masterPort + '/api/remove', preparedPackage[Object.keys(preparedPackage)[i]], needleOptionsWithTokenAuthHeader, function (err, response, body) { + if (response && response.body && typeof response.body == "object") { + // buffer confirmed orders + confirmedOrders[confirmedOrders.length] = {name:response.body.name,count:response.body.count} + if(that.config.logItemTransfers){ + console.log(`Imported ${response.body.count} ${response.body.name} from master`); + } + } + }); + } + // if we got some confirmed orders + // console.log("Importing " + confirmedOrders.length + " items! " + JSON.stringify(confirmedOrders)); + //if (!(confirmedOrders.length>0)){return;} + let cmd="local t={"; + for(let i=0;i320) // Factorio max packet size is 508 + { + that.messageInterface("/silent-command remote.call('clusterio', 'runcode', '"+cmd.slice(0, -1)+"}"+ " for k, item in pairs(t) do GiveItemsToStorage(k, item) end')"); + cmd="local t={"; + } + } + if (!(cmd==="local t={")){ + that.messageInterface("/silent-command remote.call('clusterio', 'runcode', '"+cmd.slice(0, -1)+"}"+ " for k, item in pairs(t) do GiveItemsToStorage(k, item) end')"); + } + confirmedOrders=[]; + } + }, 1000); + // COMBINATOR SIGNALS --------------------------------------------------------- + // get inventory from Master and RCON it to our slave + setInterval(() => { + needle.get(that.config.masterIP + ":" + that.config.masterPort + '/api/inventory', function (err, response, body) { + if(err){ + console.error("Unable to get JSON master/api/inventory, master might be unaccessible"); + } else if (response && response.body) { + // Take the inventory we (hopefully) got and turn it into the format LUA accepts + if(Buffer.isBuffer(response.body)) {console.log(response.body.toString("utf-8")); throw new Error();} // We are probably contacting the wrong webserver + try { + var inventory = JSON.parse(response.body); + var inventoryFrame = {}; + for (let i = 0; i < inventory.length; i++) { + inventoryFrame[inventory[i].name] = Number(inventory[i].count); + if(inventoryFrame[inventory[i].name] >= Math.pow(2, 31)){ + inventoryFrame[inventory[i].name] = Math.pow(2, 30); // set it waaay lower, 31 -1 would probably suffice + } + } + inventoryFrame["signal-unixtime"] = Math.floor(Date.now()/1000); + // console.log("RCONing inventory! " + JSON.stringify(inventoryFrame)); + let first = true; + let cmd="local s={"; + for (let key in inventoryFrame) + { + cmd+='["'+key+'"]='+inventoryFrame[key]+","; + if(first && cmd.length>300 || !first && cmd.length > 320) // Factorio max packet size is 508 + { + that.messageInterface("/silent-command remote.call('clusterio', 'runcode', '"+(first ? 'global.ticksSinceMasterPinged=0 ':'')+cmd.slice(0, -1)+"}"+ " for name,count in pairs(s) do global.invdata[name]=count end')"); + cmd="local s={"; + first = false; + } + } + if (!(cmd==="local s={")){ + that.messageInterface("/silent-command remote.call('clusterio', 'runcode', '"+(first ? 'global.ticksSinceMasterPinged=0 ':'')+cmd.slice(0, -1)+"}"+ " for name,count in pairs(s) do global.invdata[name]=count end')"); + } + that.messageInterface("/silent-command remote.call('clusterio', 'runcode', 'UpdateInvCombinators()')"); + } catch (e){ + console.log(e); + } + } + }); + }, 1550); + // Make sure world has its worldID + setTimeout(function(){ + messageInterface("/silent-command remote.call('clusterio','setWorldID',"+that.config.unique+")") + }, 20000); + /* REMOTE SIGNALLING + * send any signals the slave has been told to send + * Fetch combinator signals from the server + */ + this.socket.on("processCombinatorSignal", circuitFrameWithMeta => { + if(circuitFrameWithMeta && typeof circuitFrameWithMeta == "object" && circuitFrameWithMeta.frame && Array.isArray(circuitFrameWithMeta.frame)){ + messageInterface("/silent-command remote.call('clusterio', 'receiveFrame', '"+JSON.stringify(circuitFrameWithMeta.frame)+"')"); + } + }); + // get outbound frames from file and send to master + // get array of lines in file, each line should correspond to a JSON encoded frame + let signals = fs.readFileSync(instancedirectory + "/script-output/txbuffer.txt", "utf8").split("\n"); + // if we actually got anything from the file, proceed and reset file + let readingTxBufferSoon = false; + let txBufferClearCounter = 0; + fs.watch(instancedirectory + "/script-output/txbuffer.txt", "utf-8", (eventType, filename) => { + if(!readingTxBufferSoon){ // use a 100ms delay to avoid messing with rapid sequential writes from factorio (I think that might be a problem maybe?) + readingTxBufferSoon = true; + setTimeout(()=>{ + txBufferClearCounter++; + fs.readFile(instancedirectory + "/script-output/txbuffer.txt", "utf-8", (err, signals) => { + signals = signals.split("\n"); + if (signals[0]) { + //if(txBufferClearCounter > 500){ + fs.writeFileSync(instancedirectory + "/script-output/txbuffer.txt", ""); + // txBufferClearCounter = 0; + //} + + // loop through all our frames + for (let i = 0; i < signals.length; i++) { + if (signals[i] && objectOps.isJSON(signals[i])) { + // signals[i] is a JSON array called a "frame" of signals. We timestamp it for storage on master + // then we unpack and RCON in this.frame to the game later. + let framepart = JSON.parse(signals[i]); + let doneframe = { + time: Date.now(), + frame: framepart, // thats our array of objects(single signals); + } + // send to master using socket.io, opened at the top of instanceManagement() + this.socket.emit("combinatorSignal", doneframe); + } else { + // console.log("Invalid jsony: "+typeof signals[i]) + } + } + } + }); + readingTxBufferSoon = false; + },100); + } + }); + } + async getCommand(file){ + this.commandCache = this.commandCache || {}; + if(!this.commandCache[file]){ + try{ + let command = (await fs.readFile(file)).toString(); + this.commandCache[file] = command.replace(/(\r\n\t|\n|\r\t)/gm, " "); // remove newlines + return this.commandCache[file]; + } catch(e){ + console.log("Unable to get command from file!"); + console.log(e) + } + } else if(typeof this.commandCache[file] == "string"){ + return this.commandCache[file]; + } else { + throw new Error("Command not found"); + } + } + async factorioOutput(data){ + + } + getInstanceName(instanceID){ + return new Promise((resolve, reject) => { + let instance = this.instances[instanceID]; + if(!instance){ + needle.get(this.config.masterIP+":"+this.config.masterPort+ '/api/slaves', (err, response) => { + if(err || response.statusCode != 200) { + console.log("Unable to get JSON master/api/slaves, master might be unaccessible"); + } else if (response && response.body) { + if(Buffer.isBuffer(response.body)) {console.log(response.body.toString("utf-8")); throw new Error();} + try { + for (let index in response.body) + this.instances[index] = response.body[index].instanceName; + } catch (e){ + console.log(e); + return null; + } + instance = this.instances[instanceID] + if (!instance) instance = instanceID; //somehow the master doesn't know the instance + resolve(instance); + } + }); + } else { + resolve(instance); + } + }); + } + async getSafeLua(filePath){ + return new Promise((resolve, reject) => { + fs.readFile(filePath, "utf8", (err, contents) => { + if(err){ + reject(err); + } else { + // split content into lines + contents = contents.split(/\r?\n/); + + // join those lines after making them safe again + contents = contents.reduce((acc, val) => { + val = val.replace(/\\/g ,'\\\\'); + // remove leading and trailing spaces + val = val.trim(); + // escape single quotes + val = val.replace(/'/g ,'\\\''); + + // remove single line comments + let singleLineCommentPosition = val.indexOf("--"); + let multiLineCommentPosition = val.indexOf("--[["); + + if(multiLineCommentPosition === -1 && singleLineCommentPosition !== -1) { + val = val.substr(0, singleLineCommentPosition); + } + + return acc + val + '\\n'; + }, ""); // need the "" or it will not process the first row, potentially leaving a single line comment in that disables the whole code + + // console.log(contents); + + // this takes about 46 ms to minify train_stop_tracking.lua in my tests on an i3 + if(COMPRESS_LUA) contents = require("luamin").minify(contents); + + resolve(contents); + } + }); + }); + } + async checkHotpatchInstallation(){ + let yn = await this.messageInterface("/silent-command if remote.interfaces['hotpatch'] then rcon.print('true') else rcon.print('false') end"); + yn = yn.replace(/(\r\n\t|\n|\r\t)/gm, ""); + if(yn == "true"){ + return true; + } else if(yn == "false"){ + return false; + } + } +} +async function sleep(s){ + return new Promise((resolve, reject) => { + setTimeout(resolve, s*1000); + }); +} diff --git a/sharedPlugins/clusterioMod/masterPlugin.js b/sharedPlugins/clusterioMod/masterPlugin.js new file mode 100644 index 00000000..e02ca4b0 --- /dev/null +++ b/sharedPlugins/clusterioMod/masterPlugin.js @@ -0,0 +1,83 @@ +const fs = require("fs-extra"); +const path = require("path"); +const Express = require("express"); +const ejs = require("ejs"); + +class masterPlugin { + constructor({config, pluginConfig, pluginPath, socketio, express}){ + this.config = config; + this.pluginConfig = pluginConfig; + this.pluginPath = pluginPath; + this.io = socketio; + this.app = express; + + // load databases + this.items = getDatabaseSync("database/items.json"); + + this.clients = {}; + this.slaves = {}; + + // initialize web API + // require("./js/api-endpoints.js")(this); + + // expose UI elements embedded in the master + // this.ui = require("./js/ui.js").ui; + + this.io.on("connection", socket => { + + }); + } + async broadcastCommand(command){ + let returnValues = []; + for(let instanceID in this.slaves){ + let slave = this.slaves[instanceID]; + slave.emit("runCommand", { + // commandID:Math.random(), + command, + }); + }; + return returnValues; + } + findInArray(key, value, array){ + let indexes = []; + for(let i in array){ + if(array[i][key] && array[i][key] === value) indexes.push(i); + } + return indexes; + } + async onExit(){ + // await saveDatabase("database/items.json", this.items); + return; + } +} +module.exports = masterPlugin; + +function arrayRemoveDuplicates(array){ + let newArray = []; + array.forEach(value => { + if(!newArray.includes(value)) newArray.push(value); + }); + return newArray; +} +function getDatabaseSync(path){ + let db; + try { + db = JSON.parse(fs.readFileSync(path, "utf8")); + } catch(e){ + db = {}; + } + return db; +} +async function saveDatabase(path, database){ + if(!path){ + throw new Error("No path provided!"); + } else if(!database){ + throw new Error("No database provided!"); + } else { + try { + await fs.writeFile(path, JSON.stringify(database, null, 4)); + } catch(e){ + throw new Error("Unable to write to database! "+path); + } + } +} diff --git a/sharedPlugins/globalChat/index.js b/sharedPlugins/globalChat/index.js index 52de1c11..65fe7738 100644 --- a/sharedPlugins/globalChat/index.js +++ b/sharedPlugins/globalChat/index.js @@ -5,14 +5,19 @@ module.exports = class remoteCommands { this.messageInterface = messageInterface; this.config = mergedConfig; this.socket = extras.socket; - console.log(mergedConfig) this.instances = {}; this.socket.on("hello", () => this.socket.emit("registerChatReciever")); this.socket.on("gameChat", async data => { if(data.instanceID && data.instanceID != this.config.unique.toString()){ - if(data.data.includes("[CHAT]") && (data.data.toLowerCase().includes("/shout") || data.data.toLowerCase().includes("!shout"))){ + if(data.data.includes("[CHAT]") + // if this isn't used as an info command or something (reduce spam) + && !data.data.includes("!info") + // check if we are allowed to cross chat at all, default to true + && (mergedConfig.enableCrossServerShout == undefined || mergedConfig.enableCrossServerShout) + // check if we are supposed to relay this message + && (mergedConfig.mirrorAllChat || data.data.toLowerCase().includes("/shout") || data.data.toLowerCase().includes("!shout"))){ let words = data.data.trim().split(" "); words.shift(); words.shift(); diff --git a/sharedPlugins/inventoryImports/index.js b/sharedPlugins/inventoryImports/index.js index ebec81df..40dd929e 100644 --- a/sharedPlugins/inventoryImports/index.js +++ b/sharedPlugins/inventoryImports/index.js @@ -12,7 +12,7 @@ class inventoryImports { this.messageInterface = messageInterface; messageInterface("/silent-command game.print('InventoryImports enabled')"); setInterval(function(){ - messageInterface("Polling inventories\n") + // messageInterface("Polling inventories\n") messageInterface(functions.pollInventories(pluginConfig.scriptOutputFileSubscription)); }, 10000); } diff --git a/sharedPlugins/inventoryImports/interfacingFunctions.js b/sharedPlugins/inventoryImports/interfacingFunctions.js index 2b4aaca0..de9b6d54 100644 --- a/sharedPlugins/inventoryImports/interfacingFunctions.js +++ b/sharedPlugins/inventoryImports/interfacingFunctions.js @@ -141,7 +141,7 @@ function pollInventories(outputFile){ //console.log("/silent-command "+'local a=game.players["Danielv123"].get_inventory(defines.inventory.player_main).get_contents()local b=game.players["Danielv123"].get_quickbar().get_contents()local c={}for d=1,game.players["Danielv123"].force.character_logistic_slot_count do c[d]=game.players["Danielv123"].character.get_request_slot(d)end;local e="{inventory:{"for f,g in pairs(a)do e=e.."['+"'"+'"..f.."'+"'"+']:"..g..","end;for f,g in pairs(b)do e=e.."['+"'"+'"..f.."'+"'"+']:"..g..","end;e=e.."},requestSlots:{"for f,g in pairs(c)do e=e.."['+"'"+'"..g["name"].."'+"'"+']:"..g["count"]..", "end;e=e.."}}"game.write_file("'+pluginConfig.scriptOutputFileSubscription+'",e)') // console.log("/silent-command "+'local a="{"for b,c in pairs(game.players)do local d=game.players[b].get_inventory(defines.inventory.player_main).get_contents()local e=game.players[b].get_quickbar().get_contents()local f={}for g=1,game.players[b].force.character_logistic_slot_count do f[g]=game.players[b].character.get_request_slot(g)end;a=a..b..":{inventory:{"for h,i in pairs(d)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;for h,i in pairs(e)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;a=a.."},requestSlots:{"for h,i in pairs(f)do a=a.."['+"'"+'"..i["name"].."'+"'"+']:"..i["count"]..", "end;a=a.."}},"end;a=a.."}"game.write_file("'+pluginConfig.scriptOutputFileSubscription+'",a)') //return "/silent-command "+'local a="{"for b,c in pairs(game.players)do if c.connected then local d=game.players[b].get_inventory(defines.inventory.player_main).get_contents()local e=game.players[b].get_quickbar().get_contents()local f={}for g=1,game.players[b].force.character_logistic_slot_count do f[g]=game.players[b].character.get_request_slot(g)end;a=a..b..":{inventory:{"for h,i in pairs(d)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;for h,i in pairs(e)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;a=a.."},requestSlots:{"for h,i in pairs(f)do a=a.."['+"'"+'"..i["name"].."'+"'"+']:"..i["count"]..", "end;a=a.."}},"end end;a=a.."}"game.write_file("'+outputFile+'",a)' - return '/silent-command '+'local a='+"'"+'{"players":{'+"'"+'local b=false;for c,d in pairs(game.players)do if d.connected and d.character then if b then a=a..'+"'"+','+"'"+'else b=true end;local e=game.players[c].get_inventory(defines.inventory.player_main).get_contents()local f=game.players[c].get_quickbar().get_contents()local g={}for h=1,game.players[c].force.character_logistic_slot_count do g[h]=game.players[c].character.get_request_slot(h)end;a=a..'+"'"+'"'+"'"+'..c..'+"'"+'":{"inventory":{'+"'"+'local i=false;for j,k in pairs(e)do if i then a=a..'+"'"+','+"'"+'else i=true end;a=a..'+"'"+'"'+"'"+'..j..'+"'"+'":'+"'"+'..k end;for j,k in pairs(f)do if i then a=a..'+"'"+','+"'"+'else i=true end;a=a..'+"'"+'"'+"'"+'..j..'+"'"+'":'+"'"+'..k end;a=a..'+"'"+'},"requestSlots":{'+"'"+'i=false;for j,k in pairs(g)do if i then a=a..'+"'"+','+"'"+'else i=true end;a=a..'+"'"+'"'+"'"+'..k['+"'"+'name'+"'"+']..'+"'"+'":'+"'"+'..k['+"'"+'count'+"'"+']end;a=a..'+"'"+'}}'+"'"+'end end;a=a.."}}"game.write_file("'+outputFile+'",a,true,0)' + return '/silent-command '+'local a='+"'"+'{"players":{'+"'"+'local b=false;for c,d in pairs(game.players)do if d.connected and d.character then if b then a=a..'+"'"+','+"'"+'else b=true end;local e=game.players[c].get_inventory(defines.inventory.player_main).get_contents()local f=game.players[c].get_quickbar().get_contents()local g={}local z={}for j,k in pairs(e)do if z[j] ~= nil then z[j] = z[j] + k else z[j] = k end end for j,k in pairs(f)do if z[j] ~= nil then z[j] = z[j] + k else z[j] = k end end for h=1,game.players[c].force.character_logistic_slot_count do g[h]=game.players[c].character.get_request_slot(h)end;a=a..'+"'"+'"'+"'"+'..c..'+"'"+'":{"inventory":{'+"'"+'local i=false;for j,k in pairs(z)do if i then a=a..'+"'"+','+"'"+'else i=true end;a=a..'+"'"+'"'+"'"+'..j..'+"'"+'":'+"'"+'..k end;a=a..'+"'"+'},"requestSlots":{'+"'"+'i=false;for j,k in pairs(g)do if i then a=a..'+"'"+','+"'"+'else i=true end;a=a..'+"'"+'"'+"'"+'..k['+"'"+'name'+"'"+']..'+"'"+'":'+"'"+'..k['+"'"+'count'+"'"+']end;a=a..'+"'"+'}}'+"'"+'end end;a=a.."}}"game.write_file("'+outputFile+'",a,true,0)' /* needle.post(config.masterIP+':'+config.masterPort+'/api/editSlaveMeta', {instanceID: config.unique, password: config.clientPassword, meta: {UPS:UPS}}, function(err, resp) { // success? diff --git a/sharedPlugins/remoteCommands/index.js b/sharedPlugins/remoteCommands/index.js index 98dc11a6..62a7df20 100644 --- a/sharedPlugins/remoteCommands/index.js +++ b/sharedPlugins/remoteCommands/index.js @@ -22,4 +22,4 @@ module.exports = class remoteCommands { scriptOutput(data){ // we don't return anything yet } -} \ No newline at end of file +} diff --git a/sharedPlugins/remoteMap/DISABLED b/sharedPlugins/remoteMap/DISABLED new file mode 100644 index 00000000..e69de29b diff --git a/sharedPlugins/remoteMap/chunkStore.js b/sharedPlugins/remoteMap/chunkStore.js index 7146f75b..330ab601 100644 --- a/sharedPlugins/remoteMap/chunkStore.js +++ b/sharedPlugins/remoteMap/chunkStore.js @@ -2,11 +2,12 @@ var mkdirp = require("mkdirp-promise"); var mkdirpSync = require("mkdirp").sync; var fs = require("fs"); var events = require("events"); +var path = require("path"); var objectOps = require("./../../lib/objectOps"); var LinvoDB = require("linvodb3"); -LinvoDB.dbPath = "./database/linvodb/"; +// LinvoDB.dbPath = "./database/linvodb/"; let allowLogging = true; @@ -20,6 +21,7 @@ module.exports = class chunkStore { this.chunkSize = chunkSize; this.path = (path[path.length] == "/") ? path : path+"/"; mkdirpSync(path+name); + LinvoDB.dbPath = this.path; this.db = new LinvoDB(name, {}, {}); diff --git a/sharedPlugins/scenario_loader/config.js b/sharedPlugins/scenario_loader/config.js new file mode 100644 index 00000000..98acf05e --- /dev/null +++ b/sharedPlugins/scenario_loader/config.js @@ -0,0 +1,10 @@ +/* + Clusterio plugin for loading softmod scenarios using Hotpatch + To load a scenario, simply drop it inside sharedPlugins/scenario_loader/scenarios/ +*/ +module.exports = { + name: "scenario_loader", + version: "1.0.0", + binary: "nodePackage", + description: "Simple way to load scenarios using Hotpatch" +} diff --git a/sharedPlugins/scenario_loader/index.js b/sharedPlugins/scenario_loader/index.js new file mode 100644 index 00000000..d1542eb6 --- /dev/null +++ b/sharedPlugins/scenario_loader/index.js @@ -0,0 +1,97 @@ +const fs = require("fs-extra"); +const path = require("path"); +const clusterTools = require("_app/clusterTools")(); + +module.exports = class remoteCommands { + constructor(mergedConfig, messageInterface, extras){ + this.messageInterface = messageInterface; + this.config = mergedConfig; + + (async () => { + let hotpatchInstallStatus = await this.checkHotpatchInstallation(); + messageInterface("Hotpach installation status: "+hotpatchInstallStatus); + if(hotpatchInstallStatus){ + let scenarioDir = "scenarios"; + await fs.ensureDir(scenarioDir) + let scenarios = await fs.readdir(scenarioDir); + for(let i = 0; i < scenarios.length; i++){ + let startTime = Date.now(); + let scenPath = path.join(scenarioDir, scenarios[i]); + let stat = await fs.stat(scenPath); + if(stat.isDirectory){ + async function loadFiles(dir, rootdir){ + let returnFiles = []; + let entries = await fs.readdir(path.join(rootdir, dir)); + for(let o = 0; o < entries.length; o++){ + let stat = await fs.stat(path.join(rootdir, dir, entries[o])); + if(stat.isDirectory()){ + // recurse + let subFolder = await loadFiles(path.join(dir, entries[o]), rootdir); + returnFiles = returnFiles.concat(subFolder); + } else if(entries[o].split(".")[entries[o].split(".").length-1].toLowerCase() == "lua"){ + // if it is a Lua file, read its contents + // let data = await fs.readFile(path.join(rootdir, dir, entries[o])); + let data = await clusterTools.getLua(path.join(rootdir, dir, entries[o]), false); + returnFiles.push({ + name: path.join(dir, entries[o].split(".")[0]), + data, + }); + } + } + return returnFiles; + } + let fileMap = await loadFiles("", scenPath); + let files = {}; + fileMap.forEach(map => { + files[map.name] = map.data; + console.log(map.name); + }); + + let fileImportString = `\{`; + for(let k in files){ + let name = k.replace(/\\/g, '/'); + name = name.replace(/ /g, ''); + // make sure *not* to include control.lua as it is provided as a seperate argument + if(name != "control") fileImportString += `["${name}"] = '${files[k]}', `; + } + fileImportString += `\}`; + if(files.control) var returnValue = await messageInterface(`/silent-command remote.call('hotpatch', 'update', '${scenarios[i]}', '1.0.0', '${files.control}', ${fileImportString})`); + if(returnValue) messageInterface(returnValue); + messageInterface(`Loaded scenario ${scenarios[i]} in ${Math.floor(Date.now()-startTime)}ms`); + } + } + } + })(); + } + async checkHotpatchInstallation(){ + let yn = await this.messageInterface("/silent-command if remote.interfaces['hotpatch'] then rcon.print('true') else rcon.print('false') end"); + yn = yn.replace(/(\r\n\t|\n|\r\t)/gm, ""); + if(yn == "true"){ + return true; + } else if(yn == "false"){ + return false; + } + } +} + +// this function is bad in terms of scope since it relies on a global for return values, should be refactored. +var filesCollection = []; +const directoriesToSkip = ['bower_components', 'node_modules', 'www', 'platforms', '.git']; + +function readDirectorySynchronously(directory) { + var currentDirectorypath = /*path.join(__dirname + */directory//); + + var currentDirectory = fs.readdirSync(currentDirectorypath, 'utf8'); + + currentDirectory.forEach(file => { + var fileShouldBeSkipped = directoriesToSkip.indexOf(file) > -1; + var pathOfCurrentItem = path.join(/*__dirname + */directory + '/' + file); + if (!fileShouldBeSkipped && fs.statSync(pathOfCurrentItem).isFile()) { + filesCollection.push(pathOfCurrentItem); + } + else if (!fileShouldBeSkipped) { + var directorypath = path.join(directory, file); + readDirectorySynchronously(directorypath); + } + }); +} diff --git a/sharedPlugins/serverManager/config.js b/sharedPlugins/serverManager/config.js new file mode 100644 index 00000000..9b1f8277 --- /dev/null +++ b/sharedPlugins/serverManager/config.js @@ -0,0 +1,13 @@ +/* + Clusterio plugin for remote server management +*/ +module.exports = { + name: "serverManager", + version: "1.0.0", + binary: "nodePackage", + description: "Server management tools for remote config edits and web based server management", + masterPlugin: "masterPlugin.js", + dependencies: [ + "playerManager", + ] +} diff --git a/sharedPlugins/serverManager/index.js b/sharedPlugins/serverManager/index.js new file mode 100644 index 00000000..7b519b3d --- /dev/null +++ b/sharedPlugins/serverManager/index.js @@ -0,0 +1,43 @@ +const needle = require("needle"); +const fs = require("fs-extra"); + +const pluginConfig = require("./config"); +const COMPRESS_LUA = false; + +module.exports = class remoteCommands { + constructor(mergedConfig, messageInterface, extras){ + this.messageInterface = messageInterface; + this.config = mergedConfig; + this.socket = extras.socket; + const pluginManager = require("./../../lib/manager/pluginManager.js")(this.config); + const configManager = require("./../../lib/manager/configManager.js"); + // const modManager = require("./../../lib/manager/modManager.js")(this.config); + this.socket.on("hello", () => { + this.socket.emit("registerServerManager"); + }); + this.socket.on("serverManagerGetStatus", async data => { + + }); + this.socket.on("serverManagerGetMods", async (data, callback) => { + // TODO: Implement this function separately from listMods (which console.logs it) + // let mods = modManager.getMods() + // callback(mods); + }); + this.socket.on("serverManagerGetConfig", async (data, callback) => { + let config = await configManager.getConfig(data.instance); + callback(config); + }); + this.socket.on("serverManagerEditConfig", async (data, callback) => { + callback(configManager.editConfig(data.instance, data.entry, data.value)); + }); + this.socket.on("serverManagerGetPlugins", async (data, callback) => { + callback(await pluginManager.getPlugins()); + }); + this.socket.on("serverManagerEnablePlugin", async (data, callback) => { + callback(await pluginManager.enablePlugin(data.name, data.instanceID /* Currently ignored */)); + }); + this.socket.on("serverManagerDisablePlugin", async (data, callback) => { + callback(await pluginManager.disablePlugin(data.name, data.instanceID /* Currently ignored */)); + }); + } +} diff --git a/sharedPlugins/serverManager/js/api-endpoints.js b/sharedPlugins/serverManager/js/api-endpoints.js new file mode 100644 index 00000000..6c3301ef --- /dev/null +++ b/sharedPlugins/serverManager/js/api-endpoints.js @@ -0,0 +1,127 @@ +module.exports = that => { + /** + - Enable a plugin by deleting the DISABLED file + - Use socket.io to update masters list of plugins for this server + + Give standard JSON object response + */ + that.app.post("/api/serverManager/enablePlugin", async (req, res) => { + if((req.body.token || req.cookies.token) + && req.body.instanceID + && typeof req.body.instanceID == "string" + && req.body.pluginName + && typeof req.body.pluginName == "string" + && that.playerManager){ + let perms = await that.playerManager.main.getPermissions((req.body.token || req.cookies.token)); + if(perms.cluster.includes("enablePlugin") + || (perms.instance[req.body.instanceID] + && perms.instance[req.body.instanceID].includes("enablePlugin"))){ + that.serverSockets[req.body.instanceID].socket.emit("serverManagerEnablePlugin", { + name: req.body.pluginName, + instanceID: req.body.instanceID, + }, async resp => { + if(resp.ok){ + await updateData("plugins", req.body.instanceID); + } + res.send(resp); + }); + } else { + res.send({ + ok:false, + msg:"Insufficient permissions", + }); + } + } else { + res.send({ + ok:false, + msg:"Invalid request body", + }); + } + }); + /** + - Disable a plugin by adding an empty file named DISABLED to the plugins install pathname + - Use socket.io to update masters lost of plugins for this server + + Give standard JSON object response + */ + that.app.post("/api/serverManager/disablePlugin", async (req, res) => { + if((req.body.token || req.cookies.token) + && req.body.instanceID + && typeof req.body.instanceID == "string" + && req.body.pluginName + && typeof req.body.pluginName == "string" + && that.playerManager){ + let perms = await that.playerManager.main.getPermissions((req.body.token || req.cookies.token)); + if(perms.cluster.includes("disablePlugin") + || (perms.instance[req.body.instanceID] + && perms.instance[req.body.instanceID].includes("disablePlugin"))){ + that.serverSockets[req.body.instanceID].socket.emit("serverManagerDisablePlugin", { + name: req.body.pluginName, + instanceID: req.body.instanceID, + }, async resp => { + if(resp.ok){ + await updateData("plugins", req.body.instanceID); + } + res.send(resp); + }); + } else { + res.send({ + ok:false, + msg:"Insufficient permissions", + }); + } + } else { + res.send({ + ok:false, + msg:"Invalid request body", + }); + } + }); + that.app.post("/api/serverManager/editConfig", async (req,res) => { + if((req.body.token || req.cookies.token) + && req.body.instanceID + && typeof req.body.instanceID == "string" + && req.body.entry + && typeof req.body.entry == "string" + && req.body.value !== undefined + && that.playerManager){ + let perms = await that.playerManager.main.getPermissions((req.body.token || req.cookies.token)); + if(perms.cluster.includes("writeConfig") + || (perms.instance[req.body.instanceID] + && perms.instance[req.body.instanceID].includes("writeConfig"))){ + that.serverSockets[req.body.instanceID].socket.emit("serverManagerEditConfig", { + entry: req.body.entry, + value: req.body.value, + }, async resp => { + if(resp.ok){ + // Retrieve the latest config from the server and store it + await updateData("config", req.body.instanceID); + } + res.send(resp); + }); + } else { + res.send({ + ok:false, + msg:"Insufficient permissions", + }); + } + } else { + res.send({ + ok:false, + msg:"Invalid request body", + }); + } + }); + async function updateData(type, instanceID){ + // Retrieve the latest ${type} from the server and store it + if(type == "config"){ + that.serverSockets[instanceID].socket.emit("serverManagerGetConfig", {}, data => { + that.serverSockets[instanceID].config = data; + }); + } else if(type == "plugins"){ + that.serverSockets[instanceID].socket.emit("serverManagerGetPlugins", {}, data => { + that.serverSockets[instanceID].plugins = data; + }); + } + } +} diff --git a/sharedPlugins/serverManager/js/ui.js b/sharedPlugins/serverManager/js/ui.js new file mode 100644 index 00000000..a7d23289 --- /dev/null +++ b/sharedPlugins/serverManager/js/ui.js @@ -0,0 +1,19 @@ +let masterPlugin; + +module.exports = { + injectThis: function(x){masterPlugin = x; return this}, + ui: { + slaveListing: [ + { + name:"ServerMangerButton", + getHtml: ({slave}) => { + let html = ""; + if(masterPlugin.serverSockets[slave.unique]){ + let socket = masterPlugin.serverSockets[slave.unique]; + return `Server manager` + } + }, + } + ], + } +} diff --git a/sharedPlugins/serverManager/masterPlugin.js b/sharedPlugins/serverManager/masterPlugin.js new file mode 100644 index 00000000..cfe7e2a3 --- /dev/null +++ b/sharedPlugins/serverManager/masterPlugin.js @@ -0,0 +1,158 @@ +const fs = require("fs-extra"); +const path = require("path"); +const Express = require("express"); +const ejs = require("ejs"); +const bcrypt = require("bcrypt-promise"); +const crypto = require('crypto'); +const base64url = require('base64url'); +const sanitizer = require('sanitizer'); + + +class masterPlugin { + constructor({config, pluginConfig, pluginPath, socketio, express}){ + this.config = config; + this.pluginConfig = pluginConfig; + this.pluginPath = pluginPath; + this.io = socketio; + this.app = express; + + // load databases + // const database = getDatabaseSync("database/playerManager.json"); + // this.whitelist = getDatabaseSync("database/whitelist.json").whitelist || []; + // this.banlist = getDatabaseSync("database/banlist.json").banlist || []; + + // initialize web API + require("./js/api-endpoints.js")(this); + + // expose UI elements embedded in the master + this.ui = require("./js/ui.js").injectThis(this).ui; + + this.serverSockets = {}; + + this.io.on("connection", socket => { + let instanceID = "unknown"; + let isServerManager = false; + socket.on("registerSlave", data => { + if(data.instanceID && !isNaN(Number(data.instanceID))){ + instanceID = data.instanceID; + if(isServerManager){ + this.serverSockets[instanceID] = {socket}; + validServerManager({server: this.serverSockets[instanceID]}); + } + } + }); + socket.on("registerServerManager", () => { + isServerManager = true; + if(instanceID !== "unknown"){ + this.serverSockets[instanceID] = {socket}; + validServerManager({server: this.serverSockets[instanceID]}); + } + console.log("Registered serverManager socket") + + socket.on("disconnect", () => { + if(this.serverSockets[instanceID]) delete this.serverSockets[instanceID]; + console.log(`serverManager ${instanceID} disconnected, ${(Object.keys(this.serverSockets).length)} left`); + }); + }); + socket.on("gameChat", async data => { + // let chatLine = data.data.replace(/(\r\n\t|\n|\r\t)/gm, "").replace("\r", ""); + // if(typeof chatLine == "string") this.handleChatLine(chatLine, instanceID); + }); + }); + let validServerManager = ({server}) => { + server.socket.emit("serverManagerGetPlugins", {}, data => { + server.plugins = data; + }); + server.socket.emit("serverManagerGetConfig", {}, data => { + server.config = data; + }); + } + + // I can't seem to get express static pages + ejs rendering to work properly, so I write my own thing. + let pages = [ + { + addr: "/serverManager", + path: path.join(__dirname,"static/index.html"), + render: ejs + }, + ] + pages.forEach(page => { + this.app.get(page.addr, async (req,res) => { + if(page.render){ + if((req.query.token || req.cookies.token) && this.playerManager){ + var permissions = await this.playerManager.main.getPermissions((req.query.token || req.cookies.token)); + } else { + var permissions = {}; + } + page.render.renderFile(page.path, { + permissions, + serverManager: this, + req: req, + }, async (err, prom) => { + if(err) console.log(err); + let str = await prom; + res.send(str); + }); + } else { + res.send(await fs.readFile(page.path)); + } + }); + }); + this.app.use('/serverManager', Express.static(path.join(__dirname, 'static'))); + } + findInArray(key, value, array){ + let indexes = []; + for(let i in array){ + if(array[i][key] && array[i][key] === value) indexes.push(i); + } + return indexes; + } + async onExit(){ + // await saveDatabase("database/banlist.json", {banlist: this.banlist}); + return; + } + async onLoadFinish({plugins}){ + this.masterPlugins = plugins; + plugins.forEach(plugin => { + if(plugin.pluginConfig.name == "playerManager"){ + this.playerManager = plugin; + } + }); + } + async onPlayerManagerGetPermissions({permissions, user}){ + if(user + && user.admin){ + permissions.cluster.push("addPlugin"); + permissions.cluster.push("removePlugin"); + permissions.cluster.push("enablePlugin"); + permissions.cluster.push("disablePlugin"); + permissions.cluster.push("readConfig"); + permissions.cluster.push("writeConfig"); + } + return permissions; + } +} +module.exports = masterPlugin; + +function getDatabaseSync(path){ + let db; + try { + db = JSON.parse(fs.readFileSync(path, "utf8")); + } catch(e){ + db = {}; + } + return db; +} +async function saveDatabase(path, database){ + if(!path){ + throw new Error("No path provided!"); + } else if(!database){ + throw new Error("No database provided!"); + } else { + try { + await fs.writeFile(path, JSON.stringify(database, null, 4)); + } catch(e){ + throw new Error("Unable to write to database! "+path); + } + } +} diff --git a/sharedPlugins/serverManager/static/index.html b/sharedPlugins/serverManager/static/index.html new file mode 100644 index 00000000..45d4af12 --- /dev/null +++ b/sharedPlugins/serverManager/static/index.html @@ -0,0 +1,54 @@ + + + <%- include("./../../../static/head.html") %> + + + + + + <%- include("./../../../static/sidebar.html") %> +
+
+ <% + html = ""; + if(req.query + && req.query.instanceID + && slaves[req.query.instanceID] + && slaves[req.query.instanceID].mods + && serverManager.serverSockets[req.query.instanceID]){ + let slave = slaves[req.query.instanceID]; + html += ` +
+ ${slave.mods.map(mod => mod.name).reduce((acc, modname) => acc + "
  • " + modname + "
  • ")} +
    +
      ` + let server = serverManager.serverSockets[req.query.instanceID]; + if(server.plugins) server.plugins.forEach(plugin => { + html += `
    • ${plugin.name}

      ${plugin.description}

    • ` + }); + html += `
    ` + if(permissions && permissions.cluster && permissions.cluster.includes("readConfig")){ + if(server.config){ + html += `
      `; + server.config.forEach(setting => { + if(setting.entry[1] !== "_" && setting.entry !== "masterAuthSecret"){ + html += `
    • +

      ${setting.entry}

      + +
    • ` + } + }); + } + } + html += `
    + +
    ` + } + %> + <%- html %> +
    +
    + + diff --git a/sharedPlugins/serverManager/static/index.js b/sharedPlugins/serverManager/static/index.js new file mode 100644 index 00000000..8c893dbf --- /dev/null +++ b/sharedPlugins/serverManager/static/index.js @@ -0,0 +1,60 @@ +// Handle config editing +originalSettings = {}; +let settings = document.querySelectorAll(".settingInput"); +settings.forEach(setting => { + let entry = setting.children[0].innerText + originalSettings[setting.children[0].innerText] = { + entry: setting.children[0].innerText, + value: setting.children[1].value, + } + setting.children[1].addEventListener("input", ()=>{ + let newValue = setting.children[1].value; + if(newValue != originalSettings[entry].value){ + setting.children[1].style.backgroundColor = "lightgreen"; + } else { + setting.children[1].style.backgroundColor = ""; + } + }); +}); +let submitSettings = document.querySelector("#submitSettings"); +submitSettings.addEventListener("click", async ()=>{ + let settings = document.querySelectorAll(".settingInput"); + for(let i = 0; i < settings.length; i++){ + let setting = settings[i]; + let entry = setting.children[0].innerText + if(setting.children[1].value != originalSettings[entry].value){ + // upload new changes, display results somehow + let status = await postJSON("/api/serverManager/editConfig", { + entry, + value: setting.children[1].value, + instanceID: getParameterByName("instanceID"), + }); + if(status.ok){ + setting.children[1].style.backgroundColor = ""; + } + } + } +}); +// Handle plugin management +let pluginStatuses = document.querySelectorAll(".pluginStatus"); +pluginStatuses.forEach(pluginStatus => { + pluginStatus.addEventListener("click", async ()=>{ + if(pluginStatus.style.backgroundColor == "green"){ + let status = await postJSON("/api/serverManager/disablePlugin", { + instanceID: getParameterByName("instanceID"), + pluginName: pluginStatus.parentElement.children[1].children[0].innerText, + }); + if(status.ok){ + pluginStatus.style.backgroundColor = "red"; + } + } else { + let status = await postJSON("/api/serverManager/enablePlugin", { + instanceID: getParameterByName("instanceID"), + pluginName: pluginStatus.parentElement.children[1].children[0].innerText, + }); + if(status.ok){ + pluginStatus.style.backgroundColor = "green"; + } + } + }); +}); \ No newline at end of file diff --git a/sharedPlugins/serverManager/static/serverManager.css b/sharedPlugins/serverManager/static/serverManager.css new file mode 100644 index 00000000..7a610d99 --- /dev/null +++ b/sharedPlugins/serverManager/static/serverManager.css @@ -0,0 +1,23 @@ +#mods { + width: calc(( 100% ) / 4); + float:left; +} +#plugins { + width:calc((( 100% ) - (( 100% ) / 4))/2); + float:left; +} +#config { + width:calc((( 100% ) - (( 100% ) / 4))/2); + float:left; +} +ul { + list-style: none; + padding:0px; +} +#plugins > ul > li > div { + float:left; +} +#plugins .pluginStatus { + width:40px; + height: 40px; +} \ No newline at end of file diff --git a/sharedPlugins/statisticsExporter/Lua/exportStatistics.lua b/sharedPlugins/statisticsExporter/Lua/exportStatistics.lua new file mode 100644 index 00000000..c50196b5 --- /dev/null +++ b/sharedPlugins/statisticsExporter/Lua/exportStatistics.lua @@ -0,0 +1,19 @@ +local statisticsData = "[" +for _, force in pairs(game.forces) do + statisticsData = statisticsData .. '{"forceName":"'..force.name..'","data":{' + local statistics = {"item_production_statistics", "fluid_production_statistics", "kill_count_statistics", "entity_build_count_statistics"} + for key, stat in pairs(statistics) do + statisticsData = statisticsData..'"'..stat..'":{"input":{' + for name, value in pairs(force[stat].input_counts) do + statisticsData = statisticsData..'"'..name..'":'..value..',' + end + statisticsData = statisticsData..'},"output":{' + for name, value in pairs(force[stat].output_counts) do + statisticsData = statisticsData..'"'..name..'":'..value..',' + end + statisticsData = statisticsData .. '}},' + end + statisticsData = statisticsData..'}},' +end +statisticsData = statisticsData..']' +rcon.print(statisticsData) diff --git a/sharedPlugins/statisticsExporter/config.js b/sharedPlugins/statisticsExporter/config.js new file mode 100644 index 00000000..bfa220de --- /dev/null +++ b/sharedPlugins/statisticsExporter/config.js @@ -0,0 +1,10 @@ +/* + Clusterio plugin for gathering ingame statistics +*/ +module.exports = { + name: "statisticsExporter", + version: "1.0.0", + binary: "nodePackage", + description: "Gathers ingame stats such as production, consumption, kills and buildings placed", + masterPlugin: "masterPlugin.js" +} diff --git a/sharedPlugins/statisticsExporter/index.js b/sharedPlugins/statisticsExporter/index.js new file mode 100644 index 00000000..bc2579fb --- /dev/null +++ b/sharedPlugins/statisticsExporter/index.js @@ -0,0 +1,40 @@ +const needle = require("needle"); +const fs = require("fs-extra"); +const path = require("path"); + +const clusterTools = require("_app/clusterTools")(); +const pluginConfig = require("./config"); + +module.exports = class remoteCommands { + constructor(mergedConfig, messageInterface, extras){ + this.messageInterface = messageInterface; + this.config = mergedConfig; + + const needleOptionsWithTokenAuthHeader = { + headers: { + 'x-access-token': this.config.masterAuthToken + }, + }; + + // Gather and submit statistics every 15 seconds + setInterval(async () => { + let stats = await this.getStats(); + stats.forEach(force => { + needle.post(`${mergedConfig.masterIP}:${mergedConfig.masterPort}/api/logStats`, { + instanceID: this.config.unique, + force: force.forceName, + data: force.data, + }, needleOptionsWithTokenAuthHeader, (err, resp) => { + if(!err && resp.body && resp.body.ok == false) console.log(resp.body); + }); + }); + }, 15000); + } + async getStats(){ + let command = fs.readFileSync(path.join(__dirname, "Lua/exportStatistics.lua"), "utf8")//clusterTools.getLua(path.join(__dirname, "Lua/exportStatistics.lua"), false); + let string = await this.messageInterface(`/silent-command ${command}`); + var data; + eval(`data = ${string}`); + return data; + } +} diff --git a/sharedPlugins/statisticsExporter/masterPlugin.js b/sharedPlugins/statisticsExporter/masterPlugin.js new file mode 100644 index 00000000..89b1a97b --- /dev/null +++ b/sharedPlugins/statisticsExporter/masterPlugin.js @@ -0,0 +1,58 @@ +const fs = require("fs-extra"); +const path = require("path"); + +class masterPlugin { + constructor({config, socketio, express, Prometheus}){ + const authenticate = require("./../../lib/authenticate")(config); + + this.config = config; + this.io = socketio; + const app = express; + + const prometheusStatisticsGauge = new Prometheus.Gauge({ + name: 'clusterio_statistics_gauge', + help: 'Items produced/built/killed by a force', + labelNames: ["instanceID", "force", "statistic", "direction", "itemName"], + }); + + /** --- TODO: Replace this with a simpler Prometheus exporter. Requires changes to the mod as well. + POST endpoint to log production graph statistics. Should contain an instanceID (also reffered to as "unique") + and of course the timeSeries data organized by category. + + @memberof clusterioMaster + @instance + @alias api/logStats + @param {object} JSON {instanceID: "string", force: "string" data: {"item_production_statistics": {"input":{"iron-plate":150}, "output":{}}, ...}} + @returns {string} failure + */ + app.post("/api/logStats", authenticate.middleware, function(req,res) { + // endpointHitCounter.labels(req.route.path).inc(); + if(typeof req.body == "object" + && req.body.instanceID + && typeof req.body.instanceID == "string" + && req.body.instanceID.length < 100 + && req.body.data + && typeof req.body.data == "object" + && !Array.isArray(req.body.data)){ + try{ + Object.keys(req.body.data).forEach(statisticName => { + Object.keys(req.body.data[statisticName]).forEach(direction => { + let statHash = req.body.data[statisticName][direction]; + Object.keys(statHash).forEach(item => { + let value = Number(statHash[item]); + if(value) prometheusStatisticsGauge.labels(req.body.instanceID, req.body.force, statisticName, direction, item).set(value); + }); + }); + }); + res.send({ok:true, msg:`Statistics successfully reported at ${Date.now()}`}); + }catch(e){ + res.send({ok:false, msg:"Statistics reporter crashed, see sharedPlugins/statisticsExporter/masterPlugin.js"}); + console.log(e) + }; + } else { + res.send("failure"); + } + }); + } +} +module.exports = masterPlugin; diff --git a/static/bootstrap.min.css b/static/bootstrap.min.css new file mode 100644 index 00000000..304d4da3 --- /dev/null +++ b/static/bootstrap.min.css @@ -0,0 +1,12 @@ +/*! + * Bootswatch v4.1.3 + * Homepage: https://bootswatch.com + * Copyright 2012-2018 Thomas Park + * Licensed under MIT + * Based on Bootstrap +*//*! + * Bootstrap v4.1.3 (https://getbootstrap.com/) + * Copyright 2011-2018 The Bootstrap Authors + * Copyright 2011-2018 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#ee5f5b;--orange:#fd7e14;--yellow:#f89406;--green:#62c462;--teal:#20c997;--cyan:#5bc0de;--white:#fff;--gray:#7A8288;--gray-dark:#3A3F44;--primary:#3A3F44;--secondary:#7A8288;--success:#62c462;--info:#5bc0de;--warning:#f89406;--danger:#ee5f5b;--light:#e9ecef;--dark:#272B30;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-family-monospace:SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace}*,*::before,*::after{-webkit-box-sizing:border-box;box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:transparent}@-ms-viewport{width:device-width}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";font-size:0.9375rem;font-weight:400;line-height:1.5;color:#aaa;text-align:left;background-color:#272B30}[tabindex="-1"]:focus{outline:0 !important}hr{-webkit-box-sizing:content-box;box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:0.5rem}p{margin-top:0;margin-bottom:1rem}abbr[title],abbr[data-original-title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul,dl{margin-top:0;margin-bottom:1rem}ol ol,ul ul,ol ul,ul ol{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}dfn{font-style:italic}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#fff;text-decoration:none;background-color:transparent;-webkit-text-decoration-skip:objects}a:hover{color:#d9d9d9;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):hover,a:not([href]):not([tabindex]):focus{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}pre,code,kbd,samp{font-family:SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:0.75rem;padding-bottom:0.75rem;color:#7A8288;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:0.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}input,button,select,optgroup,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}button,html [type="button"],[type="reset"],[type="submit"]{-webkit-appearance:button}button::-moz-focus-inner,[type="button"]::-moz-focus-inner,[type="reset"]::-moz-focus-inner,[type="submit"]::-moz-focus-inner{padding:0;border-style:none}input[type="radio"],input[type="checkbox"]{-webkit-box-sizing:border-box;box-sizing:border-box;padding:0}input[type="date"],input[type="time"],input[type="datetime-local"],input[type="month"]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button{height:auto}[type="search"]{outline-offset:-2px;-webkit-appearance:none}[type="search"]::-webkit-search-cancel-button,[type="search"]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none !important}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{margin-bottom:0.5rem;font-family:inherit;font-weight:500;line-height:1.2;color:inherit}h1,.h1{font-size:2.34375rem}h2,.h2{font-size:1.875rem}h3,.h3{font-size:1.640625rem}h4,.h4{font-size:1.40625rem}h5,.h5{font-size:1.171875rem}h6,.h6{font-size:0.9375rem}.lead{font-size:1.171875rem;font-weight:300}.display-1{font-size:6rem;font-weight:300;line-height:1.2}.display-2{font-size:5.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4.5rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,0.1)}small,.small{font-size:80%;font-weight:400}mark,.mark{padding:0.2em;background-color:#fcf8e3}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:0.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.171875rem}.blockquote-footer{display:block;font-size:80%;color:#7A8288}.blockquote-footer::before{content:"\2014 \00A0"}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:0.25rem;background-color:#272B30;border:1px solid #dee2e6;border-radius:0.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:0.5rem;line-height:1}.figure-caption{font-size:90%;color:#7A8288}code{font-size:87.5%;color:#e83e8c;word-break:break-word}a>code{color:inherit}kbd{padding:0.2rem 0.4rem;font-size:87.5%;color:#fff;background-color:#272B30;border-radius:0.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:inherit}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width: 576px){.container{max-width:540px}}@media (min-width: 768px){.container{max-width:720px}}@media (min-width: 992px){.container{max-width:960px}}@media (min-width: 1200px){.container{max-width:1140px}}.container-fluid{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*="col-"]{padding-right:0;padding-left:0}.col-1,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-10,.col-11,.col-12,.col,.col-auto,.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm,.col-sm-auto,.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12,.col-md,.col-md-auto,.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg,.col-lg-auto,.col-xl-1,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl,.col-xl-auto{position:relative;width:100%;min-height:1px;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-1{-webkit-box-flex:0;-ms-flex:0 0 8.3333333333%;flex:0 0 8.3333333333%;max-width:8.3333333333%}.col-2{-webkit-box-flex:0;-ms-flex:0 0 16.6666666667%;flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-webkit-box-flex:0;-ms-flex:0 0 33.3333333333%;flex:0 0 33.3333333333%;max-width:33.3333333333%}.col-5{-webkit-box-flex:0;-ms-flex:0 0 41.6666666667%;flex:0 0 41.6666666667%;max-width:41.6666666667%}.col-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-webkit-box-flex:0;-ms-flex:0 0 58.3333333333%;flex:0 0 58.3333333333%;max-width:58.3333333333%}.col-8{-webkit-box-flex:0;-ms-flex:0 0 66.6666666667%;flex:0 0 66.6666666667%;max-width:66.6666666667%}.col-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-webkit-box-flex:0;-ms-flex:0 0 83.3333333333%;flex:0 0 83.3333333333%;max-width:83.3333333333%}.col-11{-webkit-box-flex:0;-ms-flex:0 0 91.6666666667%;flex:0 0 91.6666666667%;max-width:91.6666666667%}.col-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-last{-webkit-box-ordinal-group:14;-ms-flex-order:13;order:13}.order-0{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.order-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-1{margin-left:8.3333333333%}.offset-2{margin-left:16.6666666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.3333333333%}.offset-5{margin-left:41.6666666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.3333333333%}.offset-8{margin-left:66.6666666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.3333333333%}.offset-11{margin-left:91.6666666667%}@media (min-width: 576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-sm-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-sm-1{-webkit-box-flex:0;-ms-flex:0 0 8.3333333333%;flex:0 0 8.3333333333%;max-width:8.3333333333%}.col-sm-2{-webkit-box-flex:0;-ms-flex:0 0 16.6666666667%;flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-sm-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-webkit-box-flex:0;-ms-flex:0 0 33.3333333333%;flex:0 0 33.3333333333%;max-width:33.3333333333%}.col-sm-5{-webkit-box-flex:0;-ms-flex:0 0 41.6666666667%;flex:0 0 41.6666666667%;max-width:41.6666666667%}.col-sm-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-webkit-box-flex:0;-ms-flex:0 0 58.3333333333%;flex:0 0 58.3333333333%;max-width:58.3333333333%}.col-sm-8{-webkit-box-flex:0;-ms-flex:0 0 66.6666666667%;flex:0 0 66.6666666667%;max-width:66.6666666667%}.col-sm-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-webkit-box-flex:0;-ms-flex:0 0 83.3333333333%;flex:0 0 83.3333333333%;max-width:83.3333333333%}.col-sm-11{-webkit-box-flex:0;-ms-flex:0 0 91.6666666667%;flex:0 0 91.6666666667%;max-width:91.6666666667%}.col-sm-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-sm-last{-webkit-box-ordinal-group:14;-ms-flex-order:13;order:13}.order-sm-0{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.order-sm-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-sm-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-sm-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-sm-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-sm-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-sm-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-sm-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-sm-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-sm-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-sm-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-sm-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-sm-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.3333333333%}.offset-sm-2{margin-left:16.6666666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.3333333333%}.offset-sm-5{margin-left:41.6666666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.3333333333%}.offset-sm-8{margin-left:66.6666666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.3333333333%}.offset-sm-11{margin-left:91.6666666667%}}@media (min-width: 768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-md-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-md-1{-webkit-box-flex:0;-ms-flex:0 0 8.3333333333%;flex:0 0 8.3333333333%;max-width:8.3333333333%}.col-md-2{-webkit-box-flex:0;-ms-flex:0 0 16.6666666667%;flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-md-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-webkit-box-flex:0;-ms-flex:0 0 33.3333333333%;flex:0 0 33.3333333333%;max-width:33.3333333333%}.col-md-5{-webkit-box-flex:0;-ms-flex:0 0 41.6666666667%;flex:0 0 41.6666666667%;max-width:41.6666666667%}.col-md-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-webkit-box-flex:0;-ms-flex:0 0 58.3333333333%;flex:0 0 58.3333333333%;max-width:58.3333333333%}.col-md-8{-webkit-box-flex:0;-ms-flex:0 0 66.6666666667%;flex:0 0 66.6666666667%;max-width:66.6666666667%}.col-md-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-webkit-box-flex:0;-ms-flex:0 0 83.3333333333%;flex:0 0 83.3333333333%;max-width:83.3333333333%}.col-md-11{-webkit-box-flex:0;-ms-flex:0 0 91.6666666667%;flex:0 0 91.6666666667%;max-width:91.6666666667%}.col-md-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-md-last{-webkit-box-ordinal-group:14;-ms-flex-order:13;order:13}.order-md-0{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.order-md-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-md-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-md-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-md-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-md-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-md-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-md-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-md-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-md-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-md-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-md-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-md-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.3333333333%}.offset-md-2{margin-left:16.6666666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.3333333333%}.offset-md-5{margin-left:41.6666666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.3333333333%}.offset-md-8{margin-left:66.6666666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.3333333333%}.offset-md-11{margin-left:91.6666666667%}}@media (min-width: 992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-lg-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-lg-1{-webkit-box-flex:0;-ms-flex:0 0 8.3333333333%;flex:0 0 8.3333333333%;max-width:8.3333333333%}.col-lg-2{-webkit-box-flex:0;-ms-flex:0 0 16.6666666667%;flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-lg-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-webkit-box-flex:0;-ms-flex:0 0 33.3333333333%;flex:0 0 33.3333333333%;max-width:33.3333333333%}.col-lg-5{-webkit-box-flex:0;-ms-flex:0 0 41.6666666667%;flex:0 0 41.6666666667%;max-width:41.6666666667%}.col-lg-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-webkit-box-flex:0;-ms-flex:0 0 58.3333333333%;flex:0 0 58.3333333333%;max-width:58.3333333333%}.col-lg-8{-webkit-box-flex:0;-ms-flex:0 0 66.6666666667%;flex:0 0 66.6666666667%;max-width:66.6666666667%}.col-lg-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-webkit-box-flex:0;-ms-flex:0 0 83.3333333333%;flex:0 0 83.3333333333%;max-width:83.3333333333%}.col-lg-11{-webkit-box-flex:0;-ms-flex:0 0 91.6666666667%;flex:0 0 91.6666666667%;max-width:91.6666666667%}.col-lg-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-lg-last{-webkit-box-ordinal-group:14;-ms-flex-order:13;order:13}.order-lg-0{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.order-lg-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-lg-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-lg-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-lg-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-lg-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-lg-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-lg-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-lg-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-lg-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-lg-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-lg-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-lg-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.3333333333%}.offset-lg-2{margin-left:16.6666666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.3333333333%}.offset-lg-5{margin-left:41.6666666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.3333333333%}.offset-lg-8{margin-left:66.6666666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.3333333333%}.offset-lg-11{margin-left:91.6666666667%}}@media (min-width: 1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-xl-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-xl-1{-webkit-box-flex:0;-ms-flex:0 0 8.3333333333%;flex:0 0 8.3333333333%;max-width:8.3333333333%}.col-xl-2{-webkit-box-flex:0;-ms-flex:0 0 16.6666666667%;flex:0 0 16.6666666667%;max-width:16.6666666667%}.col-xl-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-webkit-box-flex:0;-ms-flex:0 0 33.3333333333%;flex:0 0 33.3333333333%;max-width:33.3333333333%}.col-xl-5{-webkit-box-flex:0;-ms-flex:0 0 41.6666666667%;flex:0 0 41.6666666667%;max-width:41.6666666667%}.col-xl-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-webkit-box-flex:0;-ms-flex:0 0 58.3333333333%;flex:0 0 58.3333333333%;max-width:58.3333333333%}.col-xl-8{-webkit-box-flex:0;-ms-flex:0 0 66.6666666667%;flex:0 0 66.6666666667%;max-width:66.6666666667%}.col-xl-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-webkit-box-flex:0;-ms-flex:0 0 83.3333333333%;flex:0 0 83.3333333333%;max-width:83.3333333333%}.col-xl-11{-webkit-box-flex:0;-ms-flex:0 0 91.6666666667%;flex:0 0 91.6666666667%;max-width:91.6666666667%}.col-xl-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-xl-last{-webkit-box-ordinal-group:14;-ms-flex-order:13;order:13}.order-xl-0{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.order-xl-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-xl-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-xl-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-xl-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-xl-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-xl-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-xl-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-xl-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-xl-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-xl-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-xl-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-xl-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.3333333333%}.offset-xl-2{margin-left:16.6666666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.3333333333%}.offset-xl-5{margin-left:41.6666666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.3333333333%}.offset-xl-8{margin-left:66.6666666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.3333333333%}.offset-xl-11{margin-left:91.6666666667%}}.table{width:100%;margin-bottom:1rem;background-color:transparent}.table th,.table td{padding:0.75rem;vertical-align:top;border-top:1px solid rgba(0,0,0,0.6)}.table thead th{vertical-align:bottom;border-bottom:2px solid rgba(0,0,0,0.6)}.table tbody+tbody{border-top:2px solid rgba(0,0,0,0.6)}.table .table{background-color:#272B30}.table-sm th,.table-sm td{padding:0.3rem}.table-bordered{border:1px solid rgba(0,0,0,0.6)}.table-bordered th,.table-bordered td{border:1px solid rgba(0,0,0,0.6)}.table-bordered thead th,.table-bordered thead td{border-bottom-width:2px}.table-borderless th,.table-borderless td,.table-borderless thead th,.table-borderless tbody+tbody{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,0.05)}.table-hover tbody tr:hover{background-color:rgba(255,255,255,0.075)}.table-primary,.table-primary>th,.table-primary>td{background-color:#c8c9cb}.table-hover .table-primary:hover{background-color:#bbbcbf}.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#bbbcbf}.table-secondary,.table-secondary>th,.table-secondary>td{background-color:#dadcde}.table-hover .table-secondary:hover{background-color:#cdcfd2}.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#cdcfd2}.table-success,.table-success>th,.table-success>td{background-color:#d3eed3}.table-hover .table-success:hover{background-color:#c1e7c1}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#c1e7c1}.table-info,.table-info>th,.table-info>td{background-color:#d1edf6}.table-hover .table-info:hover{background-color:#bce5f2}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#bce5f2}.table-warning,.table-warning>th,.table-warning>td{background-color:#fde1b9}.table-hover .table-warning:hover{background-color:#fcd6a0}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#fcd6a0}.table-danger,.table-danger>th,.table-danger>td{background-color:#fad2d1}.table-hover .table-danger:hover{background-color:#f8bcba}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f8bcba}.table-light,.table-light>th,.table-light>td{background-color:#f9fafb}.table-hover .table-light:hover{background-color:#eaedf1}.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#eaedf1}.table-dark,.table-dark>th,.table-dark>td{background-color:#c3c4c5}.table-hover .table-dark:hover{background-color:#b6b7b8}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b6b7b8}.table-active,.table-active>th,.table-active>td{background-color:rgba(255,255,255,0.075)}.table-hover .table-active:hover{background-color:rgba(242,242,242,0.075)}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(242,242,242,0.075)}.table .thead-dark th{color:#fff;background-color:#272B30;border-color:rgba(0,0,0,0.6)}.table .thead-light th{color:#52575C;background-color:#e9ecef;border-color:rgba(0,0,0,0.6)}.table-dark{color:#fff;background-color:#272B30}.table-dark th,.table-dark td,.table-dark thead th{border-color:rgba(0,0,0,0.6)}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,0.05)}.table-dark.table-hover tbody tr:hover{background-color:rgba(255,255,255,0.075)}@media (max-width: 575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-sm>.table-bordered{border:0}}@media (max-width: 767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-md>.table-bordered{border:0}}@media (max-width: 991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-lg>.table-bordered{border:0}}@media (max-width: 1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(2.90625rem + 2px);padding:0.75rem 1rem;font-size:0.9375rem;line-height:1.5;color:#52575C;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:0.25rem;-webkit-transition:border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;transition:border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out}@media screen and (prefers-reduced-motion: reduce){.form-control{-webkit-transition:none;transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:focus{color:#52575C;background-color:#fff;border-color:#757f89;outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(58,63,68,0.25);box-shadow:0 0 0 0.2rem rgba(58,63,68,0.25)}.form-control::-webkit-input-placeholder{color:#7A8288;opacity:1}.form-control:-ms-input-placeholder{color:#7A8288;opacity:1}.form-control::-ms-input-placeholder{color:#7A8288;opacity:1}.form-control::placeholder{color:#7A8288;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#ccc;opacity:1}select.form-control:focus::-ms-value{color:#52575C;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(0.75rem + 1px);padding-bottom:calc(0.75rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(0.5rem + 1px);padding-bottom:calc(0.5rem + 1px);font-size:1.171875rem;line-height:1.5}.col-form-label-sm{padding-top:calc(0.25rem + 1px);padding-bottom:calc(0.25rem + 1px);font-size:0.8203125rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding-top:0.75rem;padding-bottom:0.75rem;margin-bottom:0;line-height:1.5;color:#aaa;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-sm,.form-control-plaintext.form-control-lg{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.73046875rem + 2px);padding:0.25rem 0.5rem;font-size:0.8203125rem;line-height:1.5;border-radius:0.2rem}.form-control-lg{height:calc(2.7578125rem + 2px);padding:0.5rem 1rem;font-size:1.171875rem;line-height:1.5;border-radius:0.3rem}select.form-control[size],select.form-control[multiple]{height:auto}textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:0.25rem}.form-row{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*="col-"]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:0.3rem;margin-left:-1.25rem}.form-check-input:disabled ~ .form-check-label{color:#7A8288}.form-check-label{margin-bottom:0}.form-check-inline{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding-left:0;margin-right:0.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:0.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:0.25rem;font-size:80%;color:#62c462}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:0.25rem 0.5rem;margin-top:.1rem;font-size:0.8203125rem;line-height:1.5;color:#272B30;background-color:rgba(98,196,98,0.9);border-radius:0.25rem}.was-validated .form-control:valid,.form-control.is-valid,.was-validated .custom-select:valid,.custom-select.is-valid{border-color:#62c462}.was-validated .form-control:valid:focus,.form-control.is-valid:focus,.was-validated .custom-select:valid:focus,.custom-select.is-valid:focus{border-color:#62c462;-webkit-box-shadow:0 0 0 0.2rem rgba(98,196,98,0.25);box-shadow:0 0 0 0.2rem rgba(98,196,98,0.25)}.was-validated .form-control:valid ~ .valid-feedback,.was-validated .form-control:valid ~ .valid-tooltip,.form-control.is-valid ~ .valid-feedback,.form-control.is-valid ~ .valid-tooltip,.was-validated .custom-select:valid ~ .valid-feedback,.was-validated .custom-select:valid ~ .valid-tooltip,.custom-select.is-valid ~ .valid-feedback,.custom-select.is-valid ~ .valid-tooltip{display:block}.was-validated .form-control-file:valid ~ .valid-feedback,.was-validated .form-control-file:valid ~ .valid-tooltip,.form-control-file.is-valid ~ .valid-feedback,.form-control-file.is-valid ~ .valid-tooltip{display:block}.was-validated .form-check-input:valid ~ .form-check-label,.form-check-input.is-valid ~ .form-check-label{color:#62c462}.was-validated .form-check-input:valid ~ .valid-feedback,.was-validated .form-check-input:valid ~ .valid-tooltip,.form-check-input.is-valid ~ .valid-feedback,.form-check-input.is-valid ~ .valid-tooltip{display:block}.was-validated .custom-control-input:valid ~ .custom-control-label,.custom-control-input.is-valid ~ .custom-control-label{color:#62c462}.was-validated .custom-control-input:valid ~ .custom-control-label::before,.custom-control-input.is-valid ~ .custom-control-label::before{background-color:#bfe7bf}.was-validated .custom-control-input:valid ~ .valid-feedback,.was-validated .custom-control-input:valid ~ .valid-tooltip,.custom-control-input.is-valid ~ .valid-feedback,.custom-control-input.is-valid ~ .valid-tooltip{display:block}.was-validated .custom-control-input:valid:checked ~ .custom-control-label::before,.custom-control-input.is-valid:checked ~ .custom-control-label::before{background-color:#87d287}.was-validated .custom-control-input:valid:focus ~ .custom-control-label::before,.custom-control-input.is-valid:focus ~ .custom-control-label::before{-webkit-box-shadow:0 0 0 1px #272B30,0 0 0 0.2rem rgba(98,196,98,0.25);box-shadow:0 0 0 1px #272B30,0 0 0 0.2rem rgba(98,196,98,0.25)}.was-validated .custom-file-input:valid ~ .custom-file-label,.custom-file-input.is-valid ~ .custom-file-label{border-color:#62c462}.was-validated .custom-file-input:valid ~ .custom-file-label::after,.custom-file-input.is-valid ~ .custom-file-label::after{border-color:inherit}.was-validated .custom-file-input:valid ~ .valid-feedback,.was-validated .custom-file-input:valid ~ .valid-tooltip,.custom-file-input.is-valid ~ .valid-feedback,.custom-file-input.is-valid ~ .valid-tooltip{display:block}.was-validated .custom-file-input:valid:focus ~ .custom-file-label,.custom-file-input.is-valid:focus ~ .custom-file-label{-webkit-box-shadow:0 0 0 0.2rem rgba(98,196,98,0.25);box-shadow:0 0 0 0.2rem rgba(98,196,98,0.25)}.invalid-feedback{display:none;width:100%;margin-top:0.25rem;font-size:80%;color:#ee5f5b}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:0.25rem 0.5rem;margin-top:.1rem;font-size:0.8203125rem;line-height:1.5;color:#fff;background-color:rgba(238,95,91,0.9);border-radius:0.25rem}.was-validated .form-control:invalid,.form-control.is-invalid,.was-validated .custom-select:invalid,.custom-select.is-invalid{border-color:#ee5f5b}.was-validated .form-control:invalid:focus,.form-control.is-invalid:focus,.was-validated .custom-select:invalid:focus,.custom-select.is-invalid:focus{border-color:#ee5f5b;-webkit-box-shadow:0 0 0 0.2rem rgba(238,95,91,0.25);box-shadow:0 0 0 0.2rem rgba(238,95,91,0.25)}.was-validated .form-control:invalid ~ .invalid-feedback,.was-validated .form-control:invalid ~ .invalid-tooltip,.form-control.is-invalid ~ .invalid-feedback,.form-control.is-invalid ~ .invalid-tooltip,.was-validated .custom-select:invalid ~ .invalid-feedback,.was-validated .custom-select:invalid ~ .invalid-tooltip,.custom-select.is-invalid ~ .invalid-feedback,.custom-select.is-invalid ~ .invalid-tooltip{display:block}.was-validated .form-control-file:invalid ~ .invalid-feedback,.was-validated .form-control-file:invalid ~ .invalid-tooltip,.form-control-file.is-invalid ~ .invalid-feedback,.form-control-file.is-invalid ~ .invalid-tooltip{display:block}.was-validated .form-check-input:invalid ~ .form-check-label,.form-check-input.is-invalid ~ .form-check-label{color:#ee5f5b}.was-validated .form-check-input:invalid ~ .invalid-feedback,.was-validated .form-check-input:invalid ~ .invalid-tooltip,.form-check-input.is-invalid ~ .invalid-feedback,.form-check-input.is-invalid ~ .invalid-tooltip{display:block}.was-validated .custom-control-input:invalid ~ .custom-control-label,.custom-control-input.is-invalid ~ .custom-control-label{color:#ee5f5b}.was-validated .custom-control-input:invalid ~ .custom-control-label::before,.custom-control-input.is-invalid ~ .custom-control-label::before{background-color:#fad0cf}.was-validated .custom-control-input:invalid ~ .invalid-feedback,.was-validated .custom-control-input:invalid ~ .invalid-tooltip,.custom-control-input.is-invalid ~ .invalid-feedback,.custom-control-input.is-invalid ~ .invalid-tooltip{display:block}.was-validated .custom-control-input:invalid:checked ~ .custom-control-label::before,.custom-control-input.is-invalid:checked ~ .custom-control-label::before{background-color:#f38c89}.was-validated .custom-control-input:invalid:focus ~ .custom-control-label::before,.custom-control-input.is-invalid:focus ~ .custom-control-label::before{-webkit-box-shadow:0 0 0 1px #272B30,0 0 0 0.2rem rgba(238,95,91,0.25);box-shadow:0 0 0 1px #272B30,0 0 0 0.2rem rgba(238,95,91,0.25)}.was-validated .custom-file-input:invalid ~ .custom-file-label,.custom-file-input.is-invalid ~ .custom-file-label{border-color:#ee5f5b}.was-validated .custom-file-input:invalid ~ .custom-file-label::after,.custom-file-input.is-invalid ~ .custom-file-label::after{border-color:inherit}.was-validated .custom-file-input:invalid ~ .invalid-feedback,.was-validated .custom-file-input:invalid ~ .invalid-tooltip,.custom-file-input.is-invalid ~ .invalid-feedback,.custom-file-input.is-invalid ~ .invalid-tooltip{display:block}.was-validated .custom-file-input:invalid:focus ~ .custom-file-label,.custom-file-input.is-invalid:focus ~ .custom-file-label{-webkit-box-shadow:0 0 0 0.2rem rgba(238,95,91,0.25);box-shadow:0 0 0 0.2rem rgba(238,95,91,0.25)}.form-inline{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width: 576px){.form-inline label{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;margin-bottom:0}.form-inline .form-group{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin-bottom:0}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .input-group,.form-inline .custom-select{width:auto}.form-inline .form-check{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;margin-top:0;margin-right:0.25rem;margin-left:0}.form-inline .custom-control{-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;text-align:center;white-space:nowrap;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border:1px solid transparent;padding:0.75rem 1rem;font-size:0.9375rem;line-height:1.5;border-radius:0.25rem;-webkit-transition:color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;transition:color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out}@media screen and (prefers-reduced-motion: reduce){.btn{-webkit-transition:none;transition:none}}.btn:hover,.btn:focus{text-decoration:none}.btn:focus,.btn.focus{outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(58,63,68,0.25);box-shadow:0 0 0 0.2rem rgba(58,63,68,0.25)}.btn.disabled,.btn:disabled{opacity:0.65}.btn:not(:disabled):not(.disabled){cursor:pointer}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#3A3F44;border-color:#3A3F44}.btn-primary:hover{color:#fff;background-color:#282c2f;border-color:#232628}.btn-primary:focus,.btn-primary.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(58,63,68,0.5);box-shadow:0 0 0 0.2rem rgba(58,63,68,0.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#3A3F44;border-color:#3A3F44}.btn-primary:not(:disabled):not(.disabled):active,.btn-primary:not(:disabled):not(.disabled).active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#232628;border-color:#1d1f22}.btn-primary:not(:disabled):not(.disabled):active:focus,.btn-primary:not(:disabled):not(.disabled).active:focus,.show>.btn-primary.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(58,63,68,0.5);box-shadow:0 0 0 0.2rem rgba(58,63,68,0.5)}.btn-secondary{color:#fff;background-color:#7A8288;border-color:#7A8288}.btn-secondary:hover{color:#fff;background-color:#686f74;border-color:#62686d}.btn-secondary:focus,.btn-secondary.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(122,130,136,0.5);box-shadow:0 0 0 0.2rem rgba(122,130,136,0.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#7A8288;border-color:#7A8288}.btn-secondary:not(:disabled):not(.disabled):active,.btn-secondary:not(:disabled):not(.disabled).active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#62686d;border-color:#5c6267}.btn-secondary:not(:disabled):not(.disabled):active:focus,.btn-secondary:not(:disabled):not(.disabled).active:focus,.show>.btn-secondary.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(122,130,136,0.5);box-shadow:0 0 0 0.2rem rgba(122,130,136,0.5)}.btn-success{color:#272B30;background-color:#62c462;border-color:#62c462}.btn-success:hover{color:#fff;background-color:#46ba46;border-color:#42b142}.btn-success:focus,.btn-success.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(98,196,98,0.5);box-shadow:0 0 0 0.2rem rgba(98,196,98,0.5)}.btn-success.disabled,.btn-success:disabled{color:#272B30;background-color:#62c462;border-color:#62c462}.btn-success:not(:disabled):not(.disabled):active,.btn-success:not(:disabled):not(.disabled).active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#42b142;border-color:#3fa73f}.btn-success:not(:disabled):not(.disabled):active:focus,.btn-success:not(:disabled):not(.disabled).active:focus,.show>.btn-success.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(98,196,98,0.5);box-shadow:0 0 0 0.2rem rgba(98,196,98,0.5)}.btn-info{color:#272B30;background-color:#5bc0de;border-color:#5bc0de}.btn-info:hover{color:#fff;background-color:#3bb4d8;border-color:#31b0d5}.btn-info:focus,.btn-info.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(91,192,222,0.5);box-shadow:0 0 0 0.2rem rgba(91,192,222,0.5)}.btn-info.disabled,.btn-info:disabled{color:#272B30;background-color:#5bc0de;border-color:#5bc0de}.btn-info:not(:disabled):not(.disabled):active,.btn-info:not(:disabled):not(.disabled).active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#31b0d5;border-color:#2aaacf}.btn-info:not(:disabled):not(.disabled):active:focus,.btn-info:not(:disabled):not(.disabled).active:focus,.show>.btn-info.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(91,192,222,0.5);box-shadow:0 0 0 0.2rem rgba(91,192,222,0.5)}.btn-warning{color:#272B30;background-color:#f89406;border-color:#f89406}.btn-warning:hover{color:#fff;background-color:#d37e05;border-color:#c67605}.btn-warning:focus,.btn-warning.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(248,148,6,0.5);box-shadow:0 0 0 0.2rem rgba(248,148,6,0.5)}.btn-warning.disabled,.btn-warning:disabled{color:#272B30;background-color:#f89406;border-color:#f89406}.btn-warning:not(:disabled):not(.disabled):active,.btn-warning:not(:disabled):not(.disabled).active,.show>.btn-warning.dropdown-toggle{color:#fff;background-color:#c67605;border-color:#ba6f04}.btn-warning:not(:disabled):not(.disabled):active:focus,.btn-warning:not(:disabled):not(.disabled).active:focus,.show>.btn-warning.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(248,148,6,0.5);box-shadow:0 0 0 0.2rem rgba(248,148,6,0.5)}.btn-danger{color:#fff;background-color:#ee5f5b;border-color:#ee5f5b}.btn-danger:hover{color:#fff;background-color:#ea3d38;border-color:#e9322d}.btn-danger:focus,.btn-danger.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(238,95,91,0.5);box-shadow:0 0 0 0.2rem rgba(238,95,91,0.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#ee5f5b;border-color:#ee5f5b}.btn-danger:not(:disabled):not(.disabled):active,.btn-danger:not(:disabled):not(.disabled).active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#e9322d;border-color:#e82721}.btn-danger:not(:disabled):not(.disabled):active:focus,.btn-danger:not(:disabled):not(.disabled).active:focus,.show>.btn-danger.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(238,95,91,0.5);box-shadow:0 0 0 0.2rem rgba(238,95,91,0.5)}.btn-light{color:#272B30;background-color:#e9ecef;border-color:#e9ecef}.btn-light:hover{color:#272B30;background-color:#d3d9df;border-color:#cbd3da}.btn-light:focus,.btn-light.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(233,236,239,0.5);box-shadow:0 0 0 0.2rem rgba(233,236,239,0.5)}.btn-light.disabled,.btn-light:disabled{color:#272B30;background-color:#e9ecef;border-color:#e9ecef}.btn-light:not(:disabled):not(.disabled):active,.btn-light:not(:disabled):not(.disabled).active,.show>.btn-light.dropdown-toggle{color:#272B30;background-color:#cbd3da;border-color:#c4ccd4}.btn-light:not(:disabled):not(.disabled):active:focus,.btn-light:not(:disabled):not(.disabled).active:focus,.show>.btn-light.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(233,236,239,0.5);box-shadow:0 0 0 0.2rem rgba(233,236,239,0.5)}.btn-dark{color:#fff;background-color:#272B30;border-color:#272B30}.btn-dark:hover{color:#fff;background-color:#16181b;border-color:#101214}.btn-dark:focus,.btn-dark.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(39,43,48,0.5);box-shadow:0 0 0 0.2rem rgba(39,43,48,0.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#272B30;border-color:#272B30}.btn-dark:not(:disabled):not(.disabled):active,.btn-dark:not(:disabled):not(.disabled).active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#101214;border-color:#0a0b0d}.btn-dark:not(:disabled):not(.disabled):active:focus,.btn-dark:not(:disabled):not(.disabled).active:focus,.show>.btn-dark.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(39,43,48,0.5);box-shadow:0 0 0 0.2rem rgba(39,43,48,0.5)}.btn-outline-primary{color:#3A3F44;background-color:transparent;background-image:none;border-color:#3A3F44}.btn-outline-primary:hover{color:#fff;background-color:#3A3F44;border-color:#3A3F44}.btn-outline-primary:focus,.btn-outline-primary.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(58,63,68,0.5);box-shadow:0 0 0 0.2rem rgba(58,63,68,0.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#3A3F44;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled):active,.btn-outline-primary:not(:disabled):not(.disabled).active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#3A3F44;border-color:#3A3F44}.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(58,63,68,0.5);box-shadow:0 0 0 0.2rem rgba(58,63,68,0.5)}.btn-outline-secondary{color:#7A8288;background-color:transparent;background-image:none;border-color:#7A8288}.btn-outline-secondary:hover{color:#fff;background-color:#7A8288;border-color:#7A8288}.btn-outline-secondary:focus,.btn-outline-secondary.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(122,130,136,0.5);box-shadow:0 0 0 0.2rem rgba(122,130,136,0.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#7A8288;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled):active,.btn-outline-secondary:not(:disabled):not(.disabled).active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#7A8288;border-color:#7A8288}.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(122,130,136,0.5);box-shadow:0 0 0 0.2rem rgba(122,130,136,0.5)}.btn-outline-success{color:#62c462;background-color:transparent;background-image:none;border-color:#62c462}.btn-outline-success:hover{color:#272B30;background-color:#62c462;border-color:#62c462}.btn-outline-success:focus,.btn-outline-success.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(98,196,98,0.5);box-shadow:0 0 0 0.2rem rgba(98,196,98,0.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#62c462;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled):active,.btn-outline-success:not(:disabled):not(.disabled).active,.show>.btn-outline-success.dropdown-toggle{color:#272B30;background-color:#62c462;border-color:#62c462}.btn-outline-success:not(:disabled):not(.disabled):active:focus,.btn-outline-success:not(:disabled):not(.disabled).active:focus,.show>.btn-outline-success.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(98,196,98,0.5);box-shadow:0 0 0 0.2rem rgba(98,196,98,0.5)}.btn-outline-info{color:#5bc0de;background-color:transparent;background-image:none;border-color:#5bc0de}.btn-outline-info:hover{color:#272B30;background-color:#5bc0de;border-color:#5bc0de}.btn-outline-info:focus,.btn-outline-info.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(91,192,222,0.5);box-shadow:0 0 0 0.2rem rgba(91,192,222,0.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#5bc0de;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled):active,.btn-outline-info:not(:disabled):not(.disabled).active,.show>.btn-outline-info.dropdown-toggle{color:#272B30;background-color:#5bc0de;border-color:#5bc0de}.btn-outline-info:not(:disabled):not(.disabled):active:focus,.btn-outline-info:not(:disabled):not(.disabled).active:focus,.show>.btn-outline-info.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(91,192,222,0.5);box-shadow:0 0 0 0.2rem rgba(91,192,222,0.5)}.btn-outline-warning{color:#f89406;background-color:transparent;background-image:none;border-color:#f89406}.btn-outline-warning:hover{color:#272B30;background-color:#f89406;border-color:#f89406}.btn-outline-warning:focus,.btn-outline-warning.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(248,148,6,0.5);box-shadow:0 0 0 0.2rem rgba(248,148,6,0.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#f89406;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled):active,.btn-outline-warning:not(:disabled):not(.disabled).active,.show>.btn-outline-warning.dropdown-toggle{color:#272B30;background-color:#f89406;border-color:#f89406}.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(248,148,6,0.5);box-shadow:0 0 0 0.2rem rgba(248,148,6,0.5)}.btn-outline-danger{color:#ee5f5b;background-color:transparent;background-image:none;border-color:#ee5f5b}.btn-outline-danger:hover{color:#fff;background-color:#ee5f5b;border-color:#ee5f5b}.btn-outline-danger:focus,.btn-outline-danger.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(238,95,91,0.5);box-shadow:0 0 0 0.2rem rgba(238,95,91,0.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#ee5f5b;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled):active,.btn-outline-danger:not(:disabled):not(.disabled).active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#ee5f5b;border-color:#ee5f5b}.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(238,95,91,0.5);box-shadow:0 0 0 0.2rem rgba(238,95,91,0.5)}.btn-outline-light{color:#e9ecef;background-color:transparent;background-image:none;border-color:#e9ecef}.btn-outline-light:hover{color:#272B30;background-color:#e9ecef;border-color:#e9ecef}.btn-outline-light:focus,.btn-outline-light.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(233,236,239,0.5);box-shadow:0 0 0 0.2rem rgba(233,236,239,0.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#e9ecef;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled):active,.btn-outline-light:not(:disabled):not(.disabled).active,.show>.btn-outline-light.dropdown-toggle{color:#272B30;background-color:#e9ecef;border-color:#e9ecef}.btn-outline-light:not(:disabled):not(.disabled):active:focus,.btn-outline-light:not(:disabled):not(.disabled).active:focus,.show>.btn-outline-light.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(233,236,239,0.5);box-shadow:0 0 0 0.2rem rgba(233,236,239,0.5)}.btn-outline-dark{color:#272B30;background-color:transparent;background-image:none;border-color:#272B30}.btn-outline-dark:hover{color:#fff;background-color:#272B30;border-color:#272B30}.btn-outline-dark:focus,.btn-outline-dark.focus{-webkit-box-shadow:0 0 0 0.2rem rgba(39,43,48,0.5);box-shadow:0 0 0 0.2rem rgba(39,43,48,0.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#272B30;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled):active,.btn-outline-dark:not(:disabled):not(.disabled).active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#272B30;border-color:#272B30}.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 0.2rem rgba(39,43,48,0.5);box-shadow:0 0 0 0.2rem rgba(39,43,48,0.5)}.btn-link{font-weight:400;color:#fff;background-color:transparent}.btn-link:hover{color:#d9d9d9;text-decoration:underline;background-color:transparent;border-color:transparent}.btn-link:focus,.btn-link.focus{text-decoration:underline;border-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link:disabled,.btn-link.disabled{color:#7A8288;pointer-events:none}.btn-lg,.btn-group-lg>.btn{padding:0.5rem 1rem;font-size:1.171875rem;line-height:1.5;border-radius:0.3rem}.btn-sm,.btn-group-sm>.btn{padding:0.25rem 0.5rem;font-size:0.8203125rem;line-height:1.5;border-radius:0.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:0.5rem}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{-webkit-transition:opacity 0.15s linear;transition:opacity 0.15s linear}@media screen and (prefers-reduced-motion: reduce){.fade{-webkit-transition:none;transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition:height 0.35s ease;transition:height 0.35s ease}@media screen and (prefers-reduced-motion: reduce){.collapsing{-webkit-transition:none;transition:none}}.dropup,.dropright,.dropdown,.dropleft{position:relative}.dropdown-toggle::after{display:inline-block;width:0;height:0;margin-left:0.255em;vertical-align:0.255em;content:"";border-top:0.3em solid;border-right:0.3em solid transparent;border-bottom:0;border-left:0.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:0.5rem 0;margin:0.125rem 0 0;font-size:0.9375rem;color:#aaa;text-align:left;list-style:none;background-color:#3A3F44;background-clip:padding-box;border:1px solid rgba(0,0,0,0.15);border-radius:0.25rem}.dropdown-menu-right{right:0;left:auto}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:0.125rem}.dropup .dropdown-toggle::after{display:inline-block;width:0;height:0;margin-left:0.255em;vertical-align:0.255em;content:"";border-top:0;border-right:0.3em solid transparent;border-bottom:0.3em solid;border-left:0.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:0.125rem}.dropright .dropdown-toggle::after{display:inline-block;width:0;height:0;margin-left:0.255em;vertical-align:0.255em;content:"";border-top:0.3em solid transparent;border-right:0;border-bottom:0.3em solid transparent;border-left:0.3em solid}.dropright .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-toggle::after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:0.125rem}.dropleft .dropdown-toggle::after{display:inline-block;width:0;height:0;margin-left:0.255em;vertical-align:0.255em;content:""}.dropleft .dropdown-toggle::after{display:none}.dropleft .dropdown-toggle::before{display:inline-block;width:0;height:0;margin-right:0.255em;vertical-align:0.255em;content:"";border-top:0.3em solid transparent;border-right:0.3em solid;border-bottom:0.3em solid transparent}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-menu[x-placement^="top"],.dropdown-menu[x-placement^="right"],.dropdown-menu[x-placement^="bottom"],.dropdown-menu[x-placement^="left"]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:0.5rem 0;overflow:hidden;border-top:1px solid rgba(0,0,0,0.15)}.dropdown-item{display:block;width:100%;padding:0.25rem 1.5rem;clear:both;font-weight:400;color:#aaa;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:hover,.dropdown-item:focus{color:#fff;text-decoration:none;background-color:#272B30}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#3A3F44}.dropdown-item.disabled,.dropdown-item:disabled{color:#7A8288;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:0.5rem 1.5rem;margin-bottom:0;font-size:0.8203125rem;color:#7A8288;white-space:nowrap}.dropdown-item-text{display:block;padding:0.25rem 1.5rem;color:#aaa}.btn-group,.btn-group-vertical{position:relative;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;-webkit-box-flex:0;-ms-flex:0 1 auto;flex:0 1 auto}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover{z-index:1}.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn.active{z-index:1}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group,.btn-group-vertical .btn+.btn,.btn-group-vertical .btn+.btn-group,.btn-group-vertical .btn-group+.btn,.btn-group-vertical .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:not(:last-child):not(.dropdown-toggle),.btn-group>.btn-group:not(:last-child)>.btn{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:not(:first-child),.btn-group>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:0.75rem;padding-left:0.75rem}.dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after,.dropright .dropdown-toggle-split::after{margin-left:0}.dropleft .dropdown-toggle-split::before{margin-right:0}.btn-sm+.dropdown-toggle-split,.btn-group-sm>.btn+.dropdown-toggle-split{padding-right:0.375rem;padding-left:0.375rem}.btn-lg+.dropdown-toggle-split,.btn-group-lg>.btn+.dropdown-toggle-split{padding-right:0.75rem;padding-left:0.75rem}.btn-group-vertical{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.btn-group-vertical .btn,.btn-group-vertical .btn-group{width:100%}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle),.btn-group-vertical>.btn-group:not(:last-child)>.btn{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:not(:first-child),.btn-group-vertical>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn input[type="radio"],.btn-group-toggle>.btn input[type="checkbox"],.btn-group-toggle>.btn-group>.btn input[type="radio"],.btn-group-toggle>.btn-group>.btn input[type="checkbox"]{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.input-group{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group>.form-control,.input-group>.custom-select,.input-group>.custom-file{position:relative;-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;width:1%;margin-bottom:0}.input-group>.form-control+.form-control,.input-group>.form-control+.custom-select,.input-group>.form-control+.custom-file,.input-group>.custom-select+.form-control,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.custom-file,.input-group>.custom-file+.form-control,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.custom-file{margin-left:-1px}.input-group>.form-control:focus,.input-group>.custom-select:focus,.input-group>.custom-file .custom-file-input:focus ~ .custom-file-label{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.form-control:not(:last-child),.input-group>.custom-select:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.form-control:not(:first-child),.input-group>.custom-select:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label::after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-prepend,.input-group-append{display:-webkit-box;display:-ms-flexbox;display:flex}.input-group-prepend .btn,.input-group-append .btn{position:relative;z-index:2}.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.input-group-text,.input-group-append .input-group-text+.btn{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding:0.75rem 1rem;margin-bottom:0;font-size:0.9375rem;font-weight:400;line-height:1.5;color:#52575C;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:0.25rem}.input-group-text input[type="radio"],.input-group-text input[type="checkbox"]{margin-top:0}.input-group-lg>.form-control,.input-group-lg>.input-group-prepend>.input-group-text,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-append>.btn{height:calc(2.7578125rem + 2px);padding:0.5rem 1rem;font-size:1.171875rem;line-height:1.5;border-radius:0.3rem}.input-group-sm>.form-control,.input-group-sm>.input-group-prepend>.input-group-text,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-append>.btn{height:calc(1.73046875rem + 2px);padding:0.25rem 0.5rem;font-size:0.8203125rem;line-height:1.5;border-radius:0.2rem}.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text,.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;display:block;min-height:1.40625rem;padding-left:1.5rem}.custom-control-inline{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;z-index:-1;opacity:0}.custom-control-input:checked ~ .custom-control-label::before{color:#fff;background-color:#3A3F44}.custom-control-input:focus ~ .custom-control-label::before{-webkit-box-shadow:0 0 0 1px #272B30,0 0 0 0.2rem rgba(58,63,68,0.25);box-shadow:0 0 0 1px #272B30,0 0 0 0.2rem rgba(58,63,68,0.25)}.custom-control-input:active ~ .custom-control-label::before{color:#fff;background-color:#9098a0}.custom-control-input:disabled ~ .custom-control-label{color:#7A8288}.custom-control-input:disabled ~ .custom-control-label::before{background-color:#e9ecef}.custom-control-label{position:relative;margin-bottom:0}.custom-control-label::before{position:absolute;top:0.203125rem;left:-1.5rem;display:block;width:1rem;height:1rem;pointer-events:none;content:"";-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:#dee2e6}.custom-control-label::after{position:absolute;top:0.203125rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:"";background-repeat:no-repeat;background-position:center center;background-size:50% 50%}.custom-checkbox .custom-control-label::before{border-radius:0.25rem}.custom-checkbox .custom-control-input:checked ~ .custom-control-label::before{background-color:#3A3F44}.custom-checkbox .custom-control-input:checked ~ .custom-control-label::after{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E")}.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::before{background-color:#3A3F44}.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::after{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3E%3Cpath stroke='%23fff' d='M0 2h4'/%3E%3C/svg%3E")}.custom-checkbox .custom-control-input:disabled:checked ~ .custom-control-label::before{background-color:rgba(58,63,68,0.5)}.custom-checkbox .custom-control-input:disabled:indeterminate ~ .custom-control-label::before{background-color:rgba(58,63,68,0.5)}.custom-radio .custom-control-label::before{border-radius:50%}.custom-radio .custom-control-input:checked ~ .custom-control-label::before{background-color:#3A3F44}.custom-radio .custom-control-input:checked ~ .custom-control-label::after{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23fff'/%3E%3C/svg%3E")}.custom-radio .custom-control-input:disabled:checked ~ .custom-control-label::before{background-color:rgba(58,63,68,0.5)}.custom-select{display:inline-block;width:100%;height:calc(2.90625rem + 2px);padding:0.375rem 1.75rem 0.375rem 0.75rem;line-height:1.5;color:#52575C;vertical-align:middle;background:#fff url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3E%3Cpath fill='%233A3F44' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E") no-repeat right 0.75rem center;background-size:8px 10px;border:1px solid #ced4da;border-radius:0.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#757f89;outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(117,127,137,0.5);box-shadow:0 0 0 0.2rem rgba(117,127,137,0.5)}.custom-select:focus::-ms-value{color:#52575C;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:0.75rem;background-image:none}.custom-select:disabled{color:#7A8288;background-color:#e9ecef}.custom-select::-ms-expand{opacity:0}.custom-select-sm{height:calc(1.73046875rem + 2px);padding-top:0.375rem;padding-bottom:0.375rem;font-size:75%}.custom-select-lg{height:calc(2.7578125rem + 2px);padding-top:0.375rem;padding-bottom:0.375rem;font-size:125%}.custom-file{position:relative;display:inline-block;width:100%;height:calc(2.90625rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(2.90625rem + 2px);margin:0;opacity:0}.custom-file-input:focus ~ .custom-file-label{border-color:#757f89;-webkit-box-shadow:0 0 0 0.2rem rgba(58,63,68,0.25);box-shadow:0 0 0 0.2rem rgba(58,63,68,0.25)}.custom-file-input:focus ~ .custom-file-label::after{border-color:#757f89}.custom-file-input:disabled ~ .custom-file-label{background-color:#ccc}.custom-file-input:lang(en) ~ .custom-file-label::after{content:"Browse"}.custom-file-label{position:absolute;top:0;right:0;left:0;z-index:1;height:calc(2.90625rem + 2px);padding:0.75rem 1rem;line-height:1.5;color:#52575C;background-color:#fff;border:1px solid #ced4da;border-radius:0.25rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:2.90625rem;padding:0.75rem 1rem;line-height:1.5;color:#52575C;content:"Browse";background-color:#e9ecef;border-left:1px solid #ced4da;border-radius:0 0.25rem 0.25rem 0}.custom-range{width:100%;padding-left:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:none}.custom-range:focus::-webkit-slider-thumb{-webkit-box-shadow:0 0 0 1px #272B30,0 0 0 0.2rem rgba(58,63,68,0.25);box-shadow:0 0 0 1px #272B30,0 0 0 0.2rem rgba(58,63,68,0.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #272B30,0 0 0 0.2rem rgba(58,63,68,0.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #272B30,0 0 0 0.2rem rgba(58,63,68,0.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-0.25rem;background-color:#3A3F44;border:0;border-radius:1rem;-webkit-transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;-webkit-appearance:none;appearance:none}@media screen and (prefers-reduced-motion: reduce){.custom-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#9098a0}.custom-range::-webkit-slider-runnable-track{width:100%;height:0.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#3A3F44;border:0;border-radius:1rem;-webkit-transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;-moz-appearance:none;appearance:none}@media screen and (prefers-reduced-motion: reduce){.custom-range::-moz-range-thumb{-webkit-transition:none;transition:none}}.custom-range::-moz-range-thumb:active{background-color:#9098a0}.custom-range::-moz-range-track{width:100%;height:0.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:0.2rem;margin-left:0.2rem;background-color:#3A3F44;border:0;border-radius:1rem;-webkit-transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;appearance:none}@media screen and (prefers-reduced-motion: reduce){.custom-range::-ms-thumb{-webkit-transition:none;transition:none}}.custom-range::-ms-thumb:active{background-color:#9098a0}.custom-range::-ms-track{width:100%;height:0.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:0.5rem}.custom-range::-ms-fill-lower{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px;background-color:#dee2e6;border-radius:1rem}.custom-control-label::before,.custom-file-label,.custom-select{-webkit-transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;transition:background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out}@media screen and (prefers-reduced-motion: reduce){.custom-control-label::before,.custom-file-label,.custom-select{-webkit-transition:none;transition:none}}.nav{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:0.5rem 1rem}.nav-link:hover,.nav-link:focus{text-decoration:none}.nav-link.disabled{color:#7A8288}.nav-tabs{border-bottom:1px solid rgba(0,0,0,0.6)}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:0.25rem;border-top-right-radius:0.25rem}.nav-tabs .nav-link:hover,.nav-tabs .nav-link:focus{border-color:rgba(0,0,0,0.6)}.nav-tabs .nav-link.disabled{color:#7A8288;background-color:transparent;border-color:transparent}.nav-tabs .nav-link.active,.nav-tabs .nav-item.show .nav-link{color:#fff;background-color:#272B30;border-color:rgba(0,0,0,0.6)}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:0.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#3A3F44}.nav-fill .nav-item{-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;padding:0 1rem}.navbar>.container,.navbar>.container-fluid{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:0.32421875rem;padding-bottom:0.32421875rem;margin-right:1rem;font-size:1.171875rem;line-height:inherit;white-space:nowrap}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}.navbar-nav{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:0.5rem;padding-bottom:0.5rem}.navbar-collapse{-ms-flex-preferred-size:100%;flex-basis:100%;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.navbar-toggler{padding:0.25rem 0.75rem;font-size:1.171875rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:0.25rem}.navbar-toggler:hover,.navbar-toggler:focus{text-decoration:none}.navbar-toggler:not(:disabled):not(.disabled){cursor:pointer}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat center center;background-size:100% 100%}@media (max-width: 575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{padding-right:0;padding-left:0}}@media (min-width: 576px){.navbar-expand-sm{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:0.5rem;padding-left:0.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:-webkit-box !important;display:-ms-flexbox !important;display:flex !important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (max-width: 767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{padding-right:0;padding-left:0}}@media (min-width: 768px){.navbar-expand-md{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:0.5rem;padding-left:0.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:-webkit-box !important;display:-ms-flexbox !important;display:flex !important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (max-width: 991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{padding-right:0;padding-left:0}}@media (min-width: 992px){.navbar-expand-lg{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:0.5rem;padding-left:0.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:-webkit-box !important;display:-ms-flexbox !important;display:flex !important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (max-width: 1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{padding-right:0;padding-left:0}}@media (min-width: 1200px){.navbar-expand-xl{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:0.5rem;padding-left:0.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:-webkit-box !important;display:-ms-flexbox !important;display:flex !important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:0.5rem;padding-left:0.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:-webkit-box !important;display:-ms-flexbox !important;display:flex !important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:#3A3F44}.navbar-light .navbar-brand:hover,.navbar-light .navbar-brand:focus{color:#3A3F44}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,0.5)}.navbar-light .navbar-nav .nav-link:hover,.navbar-light .navbar-nav .nav-link:focus{color:#3A3F44}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,0.3)}.navbar-light .navbar-nav .show>.nav-link,.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .nav-link.active{color:#3A3F44}.navbar-light .navbar-toggler{color:rgba(0,0,0,0.5);border-color:rgba(0,0,0,0.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E")}.navbar-light .navbar-text{color:rgba(0,0,0,0.5)}.navbar-light .navbar-text a{color:#3A3F44}.navbar-light .navbar-text a:hover,.navbar-light .navbar-text a:focus{color:#3A3F44}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:hover,.navbar-dark .navbar-brand:focus{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,0.5)}.navbar-dark .navbar-nav .nav-link:hover,.navbar-dark .navbar-nav .nav-link:focus{color:#fff}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,0.25)}.navbar-dark .navbar-nav .show>.nav-link,.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .nav-link.active{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,0.5);border-color:rgba(255,255,255,0.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E")}.navbar-dark .navbar-text{color:rgba(255,255,255,0.5)}.navbar-dark .navbar-text a{color:#fff}.navbar-dark .navbar-text a:hover,.navbar-dark .navbar-text a:focus{color:#fff}.card{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#32383e;background-clip:border-box;border:1px solid rgba(0,0,0,0.6);border-radius:0.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group:first-child .list-group-item:first-child{border-top-left-radius:0.25rem;border-top-right-radius:0.25rem}.card>.list-group:last-child .list-group-item:last-child{border-bottom-right-radius:0.25rem;border-bottom-left-radius:0.25rem}.card-body{-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;padding:1.25rem}.card-title{margin-bottom:0.75rem}.card-subtitle{margin-top:-0.375rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:0.75rem 1.25rem;margin-bottom:0;background-color:#515960;border-bottom:1px solid rgba(0,0,0,0.6)}.card-header:first-child{border-radius:calc(0.25rem - 1px) calc(0.25rem - 1px) 0 0}.card-header+.list-group .list-group-item:first-child{border-top:0}.card-footer{padding:0.75rem 1.25rem;background-color:#515960;border-top:1px solid rgba(0,0,0,0.6)}.card-footer:last-child{border-radius:0 0 calc(0.25rem - 1px) calc(0.25rem - 1px)}.card-header-tabs{margin-right:-0.625rem;margin-bottom:-0.75rem;margin-left:-0.625rem;border-bottom:0}.card-header-pills{margin-right:-0.625rem;margin-left:-0.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img{width:100%;border-radius:calc(0.25rem - 1px)}.card-img-top{width:100%;border-top-left-radius:calc(0.25rem - 1px);border-top-right-radius:calc(0.25rem - 1px)}.card-img-bottom{width:100%;border-bottom-right-radius:calc(0.25rem - 1px);border-bottom-left-radius:calc(0.25rem - 1px)}.card-deck{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.card-deck .card{margin-bottom:15px}@media (min-width: 576px){.card-deck{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-flex:1;-ms-flex:1 0 0%;flex:1 0 0%;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.card-group>.card{margin-bottom:15px}@media (min-width: 576px){.card-group{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-webkit-box-flex:1;-ms-flex:1 0 0%;flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:first-child{border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:first-child .card-img-top,.card-group>.card:first-child .card-header{border-top-right-radius:0}.card-group>.card:first-child .card-img-bottom,.card-group>.card:first-child .card-footer{border-bottom-right-radius:0}.card-group>.card:last-child{border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:last-child .card-img-top,.card-group>.card:last-child .card-header{border-top-left-radius:0}.card-group>.card:last-child .card-img-bottom,.card-group>.card:last-child .card-footer{border-bottom-left-radius:0}.card-group>.card:only-child{border-radius:0.25rem}.card-group>.card:only-child .card-img-top,.card-group>.card:only-child .card-header{border-top-left-radius:0.25rem;border-top-right-radius:0.25rem}.card-group>.card:only-child .card-img-bottom,.card-group>.card:only-child .card-footer{border-bottom-right-radius:0.25rem;border-bottom-left-radius:0.25rem}.card-group>.card:not(:first-child):not(:last-child):not(:only-child){border-radius:0}.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-img-top,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-img-bottom,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-header,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-footer{border-radius:0}}.card-columns .card{margin-bottom:0.75rem}@media (min-width: 576px){.card-columns{-webkit-column-count:3;column-count:3;-webkit-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion .card:not(:first-of-type):not(:last-of-type){border-bottom:0;border-radius:0}.accordion .card:not(:first-of-type) .card-header:first-child{border-radius:0}.accordion .card:first-of-type{border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion .card:last-of-type{border-top-left-radius:0;border-top-right-radius:0}.breadcrumb{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:0.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:0.25rem}.breadcrumb-item+.breadcrumb-item{padding-left:0.5rem}.breadcrumb-item+.breadcrumb-item::before{display:inline-block;padding-right:0.5rem;color:#7A8288;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#999}.pagination{display:-webkit-box;display:-ms-flexbox;display:flex;padding-left:0;list-style:none;border-radius:0.25rem}.page-link{position:relative;display:block;padding:0.5rem 0.75rem;margin-left:-1px;line-height:1.25;color:#fff;background-color:transparent;border:1px solid rgba(0,0,0,0.6)}.page-link:hover{z-index:2;color:#fff;text-decoration:none;background-color:transparent;border-color:rgba(0,0,0,0.6)}.page-link:focus{z-index:2;outline:0;-webkit-box-shadow:0 0 0 0.2rem rgba(58,63,68,0.25);box-shadow:0 0 0 0.2rem rgba(58,63,68,0.25)}.page-link:not(:disabled):not(.disabled){cursor:pointer}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:0.25rem;border-bottom-left-radius:0.25rem}.page-item:last-child .page-link{border-top-right-radius:0.25rem;border-bottom-right-radius:0.25rem}.page-item.active .page-link{z-index:1;color:#fff;background-color:transparent;border-color:rgba(0,0,0,0.6)}.page-item.disabled .page-link{color:#7A8288;pointer-events:none;cursor:auto;background-color:transparent;border-color:rgba(0,0,0,0.6)}.pagination-lg .page-link{padding:0.75rem 1.5rem;font-size:1.171875rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:0.3rem;border-bottom-left-radius:0.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:0.3rem;border-bottom-right-radius:0.3rem}.pagination-sm .page-link{padding:0.25rem 0.5rem;font-size:0.8203125rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:0.2rem;border-bottom-left-radius:0.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:0.2rem;border-bottom-right-radius:0.2rem}.badge{display:inline-block;padding:0.25em 0.4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:0.25rem}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:0.6em;padding-left:0.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#3A3F44}.badge-primary[href]:hover,.badge-primary[href]:focus{color:#fff;text-decoration:none;background-color:#232628}.badge-secondary{color:#fff;background-color:#7A8288}.badge-secondary[href]:hover,.badge-secondary[href]:focus{color:#fff;text-decoration:none;background-color:#62686d}.badge-success{color:#272B30;background-color:#62c462}.badge-success[href]:hover,.badge-success[href]:focus{color:#272B30;text-decoration:none;background-color:#42b142}.badge-info{color:#272B30;background-color:#5bc0de}.badge-info[href]:hover,.badge-info[href]:focus{color:#272B30;text-decoration:none;background-color:#31b0d5}.badge-warning{color:#272B30;background-color:#f89406}.badge-warning[href]:hover,.badge-warning[href]:focus{color:#272B30;text-decoration:none;background-color:#c67605}.badge-danger{color:#fff;background-color:#ee5f5b}.badge-danger[href]:hover,.badge-danger[href]:focus{color:#fff;text-decoration:none;background-color:#e9322d}.badge-light{color:#272B30;background-color:#e9ecef}.badge-light[href]:hover,.badge-light[href]:focus{color:#272B30;text-decoration:none;background-color:#cbd3da}.badge-dark{color:#fff;background-color:#272B30}.badge-dark[href]:hover,.badge-dark[href]:focus{color:#fff;text-decoration:none;background-color:#101214}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#1c1e22;border-radius:0.3rem}@media (min-width: 576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:0.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:0.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:3.90625rem}.alert-dismissible .close{position:absolute;top:0;right:0;padding:0.75rem 1.25rem;color:inherit}.alert-primary{color:#1e2123;background-color:#d8d9da;border-color:#c8c9cb}.alert-primary hr{border-top-color:#bbbcbf}.alert-primary .alert-link{color:#060708}.alert-secondary{color:#3f4447;background-color:#e4e6e7;border-color:#dadcde}.alert-secondary hr{border-top-color:#cdcfd2}.alert-secondary .alert-link{color:#272a2c}.alert-success{color:#336633;background-color:#e0f3e0;border-color:#d3eed3}.alert-success hr{border-top-color:#c1e7c1}.alert-success .alert-link{color:#224422}.alert-info{color:#2f6473;background-color:#def2f8;border-color:#d1edf6}.alert-info hr{border-top-color:#bce5f2}.alert-info .alert-link{color:#20454f}.alert-warning{color:#814d03;background-color:#feeacd;border-color:#fde1b9}.alert-warning hr{border-top-color:#fcd6a0}.alert-warning .alert-link{color:#4f2f02}.alert-danger{color:#7c312f;background-color:#fcdfde;border-color:#fad2d1}.alert-danger hr{border-top-color:#f8bcba}.alert-danger .alert-link{color:#572221}.alert-light{color:#797b7c;background-color:#fbfbfc;border-color:#f9fafb}.alert-light hr{border-top-color:#eaedf1}.alert-light .alert-link{color:#606162}.alert-dark{color:#141619;background-color:#d4d5d6;border-color:#c3c4c5}.alert-dark hr{border-top-color:#b6b7b8}.alert-dark .alert-link{color:black}@-webkit-keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:-webkit-box;display:-ms-flexbox;display:flex;height:1rem;overflow:hidden;font-size:0.703125rem;background-color:#1c1e22;border-radius:0.25rem}.progress-bar{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;color:#7A8288;text-align:center;white-space:nowrap;background-color:#3A3F44;-webkit-transition:width 0.6s ease;transition:width 0.6s ease}@media screen and (prefers-reduced-motion: reduce){.progress-bar{-webkit-transition:none;transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:progress-bar-stripes 1s linear infinite;animation:progress-bar-stripes 1s linear infinite}.media{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start}.media-body{-webkit-box-flex:1;-ms-flex:1;flex:1}.list-group{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0}.list-group-item-action{width:100%;color:#fff;text-align:inherit}.list-group-item-action:hover,.list-group-item-action:focus{color:#fff;text-decoration:none;background-color:#3e444c}.list-group-item-action:active{color:#aaa;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:0.75rem 1.25rem;margin-bottom:-1px;background-color:#32383e;border:1px solid rgba(0,0,0,0.6)}.list-group-item:first-child{border-top-left-radius:0.25rem;border-top-right-radius:0.25rem}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:0.25rem;border-bottom-left-radius:0.25rem}.list-group-item:hover,.list-group-item:focus{z-index:1;text-decoration:none}.list-group-item.disabled,.list-group-item:disabled{color:#52575C;background-color:#32383e}.list-group-item.active{z-index:2;color:#fff;background-color:#3e444c;border-color:rgba(0,0,0,0.6)}.list-group-flush .list-group-item{border-right:0;border-left:0;border-radius:0}.list-group-flush:first-child .list-group-item:first-child{border-top:0}.list-group-flush:last-child .list-group-item:last-child{border-bottom:0}.list-group-item-primary{color:#1e2123;background-color:#c8c9cb}.list-group-item-primary.list-group-item-action:hover,.list-group-item-primary.list-group-item-action:focus{color:#1e2123;background-color:#bbbcbf}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#1e2123;border-color:#1e2123}.list-group-item-secondary{color:#3f4447;background-color:#dadcde}.list-group-item-secondary.list-group-item-action:hover,.list-group-item-secondary.list-group-item-action:focus{color:#3f4447;background-color:#cdcfd2}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#3f4447;border-color:#3f4447}.list-group-item-success{color:#336633;background-color:#d3eed3}.list-group-item-success.list-group-item-action:hover,.list-group-item-success.list-group-item-action:focus{color:#336633;background-color:#c1e7c1}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#336633;border-color:#336633}.list-group-item-info{color:#2f6473;background-color:#d1edf6}.list-group-item-info.list-group-item-action:hover,.list-group-item-info.list-group-item-action:focus{color:#2f6473;background-color:#bce5f2}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#2f6473;border-color:#2f6473}.list-group-item-warning{color:#814d03;background-color:#fde1b9}.list-group-item-warning.list-group-item-action:hover,.list-group-item-warning.list-group-item-action:focus{color:#814d03;background-color:#fcd6a0}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#814d03;border-color:#814d03}.list-group-item-danger{color:#7c312f;background-color:#fad2d1}.list-group-item-danger.list-group-item-action:hover,.list-group-item-danger.list-group-item-action:focus{color:#7c312f;background-color:#f8bcba}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#7c312f;border-color:#7c312f}.list-group-item-light{color:#797b7c;background-color:#f9fafb}.list-group-item-light.list-group-item-action:hover,.list-group-item-light.list-group-item-action:focus{color:#797b7c;background-color:#eaedf1}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#797b7c;border-color:#797b7c}.list-group-item-dark{color:#141619;background-color:#c3c4c5}.list-group-item-dark.list-group-item-action:hover,.list-group-item-dark.list-group-item-action:focus{color:#141619;background-color:#b6b7b8}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#141619;border-color:#141619}.close{float:right;font-size:1.40625rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:not(:disabled):not(.disabled){cursor:pointer}.close:not(:disabled):not(.disabled):hover,.close:not(:disabled):not(.disabled):focus{color:#000;text-decoration:none;opacity:.75}button.close{padding:0;background-color:transparent;border:0;-webkit-appearance:none}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:0.5rem;pointer-events:none}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform 0.3s ease-out;transition:-webkit-transform 0.3s ease-out;transition:transform 0.3s ease-out;transition:transform 0.3s ease-out, -webkit-transform 0.3s ease-out;-webkit-transform:translate(0, -25%);transform:translate(0, -25%)}@media screen and (prefers-reduced-motion: reduce){.modal.fade .modal-dialog{-webkit-transition:none;transition:none}}.modal.show .modal-dialog{-webkit-transform:translate(0, 0);transform:translate(0, 0)}.modal-dialog-centered{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;min-height:calc(100% - (0.5rem * 2))}.modal-dialog-centered::before{display:block;height:calc(100vh - (0.5rem * 2));content:""}.modal-content{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;width:100%;pointer-events:auto;background-color:#32383e;background-clip:padding-box;border:1px solid rgba(0,0,0,0.2);border-radius:0.3rem;outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:0.5}.modal-header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;padding:1rem;border-bottom:1px solid rgba(0,0,0,0.2);border-top-left-radius:0.3rem;border-top-right-radius:0.3rem}.modal-header .close{padding:1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end;padding:1rem;border-top:1px solid rgba(0,0,0,0.2)}.modal-footer>:not(:first-child){margin-left:.25rem}.modal-footer>:not(:last-child){margin-right:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width: 576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-centered{min-height:calc(100% - (1.75rem * 2))}.modal-dialog-centered::before{height:calc(100vh - (1.75rem * 2))}.modal-sm{max-width:300px}}@media (min-width: 992px){.modal-lg{max-width:800px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:0.8203125rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:0.9}.tooltip .arrow{position:absolute;display:block;width:0.8rem;height:0.4rem}.tooltip .arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-top,.bs-tooltip-auto[x-placement^="top"]{padding:0.4rem 0}.bs-tooltip-top .arrow,.bs-tooltip-auto[x-placement^="top"] .arrow{bottom:0}.bs-tooltip-top .arrow::before,.bs-tooltip-auto[x-placement^="top"] .arrow::before{top:0;border-width:0.4rem 0.4rem 0;border-top-color:#000}.bs-tooltip-right,.bs-tooltip-auto[x-placement^="right"]{padding:0 0.4rem}.bs-tooltip-right .arrow,.bs-tooltip-auto[x-placement^="right"] .arrow{left:0;width:0.4rem;height:0.8rem}.bs-tooltip-right .arrow::before,.bs-tooltip-auto[x-placement^="right"] .arrow::before{right:0;border-width:0.4rem 0.4rem 0.4rem 0;border-right-color:#000}.bs-tooltip-bottom,.bs-tooltip-auto[x-placement^="bottom"]{padding:0.4rem 0}.bs-tooltip-bottom .arrow,.bs-tooltip-auto[x-placement^="bottom"] .arrow{top:0}.bs-tooltip-bottom .arrow::before,.bs-tooltip-auto[x-placement^="bottom"] .arrow::before{bottom:0;border-width:0 0.4rem 0.4rem;border-bottom-color:#000}.bs-tooltip-left,.bs-tooltip-auto[x-placement^="left"]{padding:0 0.4rem}.bs-tooltip-left .arrow,.bs-tooltip-auto[x-placement^="left"] .arrow{right:0;width:0.4rem;height:0.8rem}.bs-tooltip-left .arrow::before,.bs-tooltip-auto[x-placement^="left"] .arrow::before{left:0;border-width:0.4rem 0 0.4rem 0.4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:0.25rem 0.5rem;color:#fff;text-align:center;background-color:#000;border-radius:0.25rem}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;font-family:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:0.8203125rem;word-wrap:break-word;background-color:#32383e;background-clip:padding-box;border:1px solid rgba(0,0,0,0.2);border-radius:0.3rem}.popover .arrow{position:absolute;display:block;width:1rem;height:0.5rem;margin:0 0.3rem}.popover .arrow::before,.popover .arrow::after{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-top,.bs-popover-auto[x-placement^="top"]{margin-bottom:0.5rem}.bs-popover-top .arrow,.bs-popover-auto[x-placement^="top"] .arrow{bottom:calc((0.5rem + 1px) * -1)}.bs-popover-top .arrow::before,.bs-popover-auto[x-placement^="top"] .arrow::before,.bs-popover-top .arrow::after,.bs-popover-auto[x-placement^="top"] .arrow::after{border-width:0.5rem 0.5rem 0}.bs-popover-top .arrow::before,.bs-popover-auto[x-placement^="top"] .arrow::before{bottom:0;border-top-color:rgba(0,0,0,0.25)}.bs-popover-top .arrow::after,.bs-popover-auto[x-placement^="top"] .arrow::after{bottom:1px;border-top-color:#32383e}.bs-popover-right,.bs-popover-auto[x-placement^="right"]{margin-left:0.5rem}.bs-popover-right .arrow,.bs-popover-auto[x-placement^="right"] .arrow{left:calc((0.5rem + 1px) * -1);width:0.5rem;height:1rem;margin:0.3rem 0}.bs-popover-right .arrow::before,.bs-popover-auto[x-placement^="right"] .arrow::before,.bs-popover-right .arrow::after,.bs-popover-auto[x-placement^="right"] .arrow::after{border-width:0.5rem 0.5rem 0.5rem 0}.bs-popover-right .arrow::before,.bs-popover-auto[x-placement^="right"] .arrow::before{left:0;border-right-color:rgba(0,0,0,0.25)}.bs-popover-right .arrow::after,.bs-popover-auto[x-placement^="right"] .arrow::after{left:1px;border-right-color:#32383e}.bs-popover-bottom,.bs-popover-auto[x-placement^="bottom"]{margin-top:0.5rem}.bs-popover-bottom .arrow,.bs-popover-auto[x-placement^="bottom"] .arrow{top:calc((0.5rem + 1px) * -1)}.bs-popover-bottom .arrow::before,.bs-popover-auto[x-placement^="bottom"] .arrow::before,.bs-popover-bottom .arrow::after,.bs-popover-auto[x-placement^="bottom"] .arrow::after{border-width:0 0.5rem 0.5rem 0.5rem}.bs-popover-bottom .arrow::before,.bs-popover-auto[x-placement^="bottom"] .arrow::before{top:0;border-bottom-color:rgba(0,0,0,0.25)}.bs-popover-bottom .arrow::after,.bs-popover-auto[x-placement^="bottom"] .arrow::after{top:1px;border-bottom-color:#32383e}.bs-popover-bottom .popover-header::before,.bs-popover-auto[x-placement^="bottom"] .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-0.5rem;content:"";border-bottom:1px solid #2c3036}.bs-popover-left,.bs-popover-auto[x-placement^="left"]{margin-right:0.5rem}.bs-popover-left .arrow,.bs-popover-auto[x-placement^="left"] .arrow{right:calc((0.5rem + 1px) * -1);width:0.5rem;height:1rem;margin:0.3rem 0}.bs-popover-left .arrow::before,.bs-popover-auto[x-placement^="left"] .arrow::before,.bs-popover-left .arrow::after,.bs-popover-auto[x-placement^="left"] .arrow::after{border-width:0.5rem 0 0.5rem 0.5rem}.bs-popover-left .arrow::before,.bs-popover-auto[x-placement^="left"] .arrow::before{right:0;border-left-color:rgba(0,0,0,0.25)}.bs-popover-left .arrow::after,.bs-popover-auto[x-placement^="left"] .arrow::after{right:1px;border-left-color:#32383e}.popover-header{padding:0.5rem 0.75rem;margin-bottom:0;font-size:0.9375rem;color:inherit;background-color:#2c3036;border-bottom:1px solid #202328;border-top-left-radius:calc(0.3rem - 1px);border-top-right-radius:calc(0.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:0.5rem 0.75rem;color:#aaa}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-item{position:relative;display:none;-webkit-box-align:center;-ms-flex-align:center;align-items:center;width:100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-item.active,.carousel-item-next,.carousel-item-prev{display:block;-webkit-transition:-webkit-transform 0.6s ease;transition:-webkit-transform 0.6s ease;transition:transform 0.6s ease;transition:transform 0.6s ease, -webkit-transform 0.6s ease}@media screen and (prefers-reduced-motion: reduce){.carousel-item.active,.carousel-item-next,.carousel-item-prev{-webkit-transition:none;transition:none}}.carousel-item-next,.carousel-item-prev{position:absolute;top:0}.carousel-item-next.carousel-item-left,.carousel-item-prev.carousel-item-right{-webkit-transform:translateX(0);transform:translateX(0)}@supports (-webkit-transform-style: preserve-3d) or (transform-style: preserve-3d){.carousel-item-next.carousel-item-left,.carousel-item-prev.carousel-item-right{-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0)}}.carousel-item-next,.active.carousel-item-right{-webkit-transform:translateX(100%);transform:translateX(100%)}@supports (-webkit-transform-style: preserve-3d) or (transform-style: preserve-3d){.carousel-item-next,.active.carousel-item-right{-webkit-transform:translate3d(100%, 0, 0);transform:translate3d(100%, 0, 0)}}.carousel-item-prev,.active.carousel-item-left{-webkit-transform:translateX(-100%);transform:translateX(-100%)}@supports (-webkit-transform-style: preserve-3d) or (transform-style: preserve-3d){.carousel-item-prev,.active.carousel-item-left{-webkit-transform:translate3d(-100%, 0, 0);transform:translate3d(-100%, 0, 0)}}.carousel-fade .carousel-item{opacity:0;-webkit-transition-duration:.6s;transition-duration:.6s;-webkit-transition-property:opacity;transition-property:opacity}.carousel-fade .carousel-item.active,.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right{opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{opacity:0}.carousel-fade .carousel-item-next,.carousel-fade .carousel-item-prev,.carousel-fade .carousel-item.active,.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-prev{-webkit-transform:translateX(0);transform:translateX(0)}@supports (-webkit-transform-style: preserve-3d) or (transform-style: preserve-3d){.carousel-fade .carousel-item-next,.carousel-fade .carousel-item-prev,.carousel-fade .carousel-item.active,.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-prev{-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0)}}.carousel-control-prev,.carousel-control-next{position:absolute;top:0;bottom:0;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:0.5}.carousel-control-prev:hover,.carousel-control-prev:focus,.carousel-control-next:hover,.carousel-control-next:focus{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-prev-icon,.carousel-control-next-icon{display:inline-block;width:20px;height:20px;background:transparent no-repeat center center;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3E%3C/svg%3E")}.carousel-control-next-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3E%3C/svg%3E")}.carousel-indicators{position:absolute;right:0;bottom:10px;left:0;z-index:15;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{position:relative;-webkit-box-flex:0;-ms-flex:0 1 auto;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:rgba(255,255,255,0.5)}.carousel-indicators li::before{position:absolute;top:-10px;left:0;display:inline-block;width:100%;height:10px;content:""}.carousel-indicators li::after{position:absolute;bottom:-10px;left:0;display:inline-block;width:100%;height:10px;content:""}.carousel-indicators .active{background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}.align-baseline{vertical-align:baseline !important}.align-top{vertical-align:top !important}.align-middle{vertical-align:middle !important}.align-bottom{vertical-align:bottom !important}.align-text-bottom{vertical-align:text-bottom !important}.align-text-top{vertical-align:text-top !important}.bg-primary{background-color:#3A3F44 !important}a.bg-primary:hover,a.bg-primary:focus,button.bg-primary:hover,button.bg-primary:focus{background-color:#232628 !important}.bg-secondary{background-color:#7A8288 !important}a.bg-secondary:hover,a.bg-secondary:focus,button.bg-secondary:hover,button.bg-secondary:focus{background-color:#62686d !important}.bg-success{background-color:#62c462 !important}a.bg-success:hover,a.bg-success:focus,button.bg-success:hover,button.bg-success:focus{background-color:#42b142 !important}.bg-info{background-color:#5bc0de !important}a.bg-info:hover,a.bg-info:focus,button.bg-info:hover,button.bg-info:focus{background-color:#31b0d5 !important}.bg-warning{background-color:#f89406 !important}a.bg-warning:hover,a.bg-warning:focus,button.bg-warning:hover,button.bg-warning:focus{background-color:#c67605 !important}.bg-danger{background-color:#ee5f5b !important}a.bg-danger:hover,a.bg-danger:focus,button.bg-danger:hover,button.bg-danger:focus{background-color:#e9322d !important}.bg-light{background-color:#e9ecef !important}a.bg-light:hover,a.bg-light:focus,button.bg-light:hover,button.bg-light:focus{background-color:#cbd3da !important}.bg-dark{background-color:#272B30 !important}a.bg-dark:hover,a.bg-dark:focus,button.bg-dark:hover,button.bg-dark:focus{background-color:#101214 !important}.bg-white{background-color:#fff !important}.bg-transparent{background-color:transparent !important}.border{border:1px solid #dee2e6 !important}.border-top{border-top:1px solid #dee2e6 !important}.border-right{border-right:1px solid #dee2e6 !important}.border-bottom{border-bottom:1px solid #dee2e6 !important}.border-left{border-left:1px solid #dee2e6 !important}.border-0{border:0 !important}.border-top-0{border-top:0 !important}.border-right-0{border-right:0 !important}.border-bottom-0{border-bottom:0 !important}.border-left-0{border-left:0 !important}.border-primary{border-color:#3A3F44 !important}.border-secondary{border-color:#7A8288 !important}.border-success{border-color:#62c462 !important}.border-info{border-color:#5bc0de !important}.border-warning{border-color:#f89406 !important}.border-danger{border-color:#ee5f5b !important}.border-light{border-color:#e9ecef !important}.border-dark{border-color:#272B30 !important}.border-white{border-color:#fff !important}.rounded{border-radius:0.25rem !important}.rounded-top{border-top-left-radius:0.25rem !important;border-top-right-radius:0.25rem !important}.rounded-right{border-top-right-radius:0.25rem !important;border-bottom-right-radius:0.25rem !important}.rounded-bottom{border-bottom-right-radius:0.25rem !important;border-bottom-left-radius:0.25rem !important}.rounded-left{border-top-left-radius:0.25rem !important;border-bottom-left-radius:0.25rem !important}.rounded-circle{border-radius:50% !important}.rounded-0{border-radius:0 !important}.clearfix::after{display:block;clear:both;content:""}.d-none{display:none !important}.d-inline{display:inline !important}.d-inline-block{display:inline-block !important}.d-block{display:block !important}.d-table{display:table !important}.d-table-row{display:table-row !important}.d-table-cell{display:table-cell !important}.d-flex{display:-webkit-box !important;display:-ms-flexbox !important;display:flex !important}.d-inline-flex{display:-webkit-inline-box !important;display:-ms-inline-flexbox !important;display:inline-flex !important}@media (min-width: 576px){.d-sm-none{display:none !important}.d-sm-inline{display:inline !important}.d-sm-inline-block{display:inline-block !important}.d-sm-block{display:block !important}.d-sm-table{display:table !important}.d-sm-table-row{display:table-row !important}.d-sm-table-cell{display:table-cell !important}.d-sm-flex{display:-webkit-box !important;display:-ms-flexbox !important;display:flex !important}.d-sm-inline-flex{display:-webkit-inline-box !important;display:-ms-inline-flexbox !important;display:inline-flex !important}}@media (min-width: 768px){.d-md-none{display:none !important}.d-md-inline{display:inline !important}.d-md-inline-block{display:inline-block !important}.d-md-block{display:block !important}.d-md-table{display:table !important}.d-md-table-row{display:table-row !important}.d-md-table-cell{display:table-cell !important}.d-md-flex{display:-webkit-box !important;display:-ms-flexbox !important;display:flex !important}.d-md-inline-flex{display:-webkit-inline-box !important;display:-ms-inline-flexbox !important;display:inline-flex !important}}@media (min-width: 992px){.d-lg-none{display:none !important}.d-lg-inline{display:inline !important}.d-lg-inline-block{display:inline-block !important}.d-lg-block{display:block !important}.d-lg-table{display:table !important}.d-lg-table-row{display:table-row !important}.d-lg-table-cell{display:table-cell !important}.d-lg-flex{display:-webkit-box !important;display:-ms-flexbox !important;display:flex !important}.d-lg-inline-flex{display:-webkit-inline-box !important;display:-ms-inline-flexbox !important;display:inline-flex !important}}@media (min-width: 1200px){.d-xl-none{display:none !important}.d-xl-inline{display:inline !important}.d-xl-inline-block{display:inline-block !important}.d-xl-block{display:block !important}.d-xl-table{display:table !important}.d-xl-table-row{display:table-row !important}.d-xl-table-cell{display:table-cell !important}.d-xl-flex{display:-webkit-box !important;display:-ms-flexbox !important;display:flex !important}.d-xl-inline-flex{display:-webkit-inline-box !important;display:-ms-inline-flexbox !important;display:inline-flex !important}}@media print{.d-print-none{display:none !important}.d-print-inline{display:inline !important}.d-print-inline-block{display:inline-block !important}.d-print-block{display:block !important}.d-print-table{display:table !important}.d-print-table-row{display:table-row !important}.d-print-table-cell{display:table-cell !important}.d-print-flex{display:-webkit-box !important;display:-ms-flexbox !important;display:flex !important}.d-print-inline-flex{display:-webkit-inline-box !important;display:-ms-inline-flexbox !important;display:inline-flex !important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive::before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9::before{padding-top:42.8571428571%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-4by3::before{padding-top:75%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{-webkit-box-orient:horizontal !important;-webkit-box-direction:normal !important;-ms-flex-direction:row !important;flex-direction:row !important}.flex-column{-webkit-box-orient:vertical !important;-webkit-box-direction:normal !important;-ms-flex-direction:column !important;flex-direction:column !important}.flex-row-reverse{-webkit-box-orient:horizontal !important;-webkit-box-direction:reverse !important;-ms-flex-direction:row-reverse !important;flex-direction:row-reverse !important}.flex-column-reverse{-webkit-box-orient:vertical !important;-webkit-box-direction:reverse !important;-ms-flex-direction:column-reverse !important;flex-direction:column-reverse !important}.flex-wrap{-ms-flex-wrap:wrap !important;flex-wrap:wrap !important}.flex-nowrap{-ms-flex-wrap:nowrap !important;flex-wrap:nowrap !important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse !important;flex-wrap:wrap-reverse !important}.flex-fill{-webkit-box-flex:1 !important;-ms-flex:1 1 auto !important;flex:1 1 auto !important}.flex-grow-0{-webkit-box-flex:0 !important;-ms-flex-positive:0 !important;flex-grow:0 !important}.flex-grow-1{-webkit-box-flex:1 !important;-ms-flex-positive:1 !important;flex-grow:1 !important}.flex-shrink-0{-ms-flex-negative:0 !important;flex-shrink:0 !important}.flex-shrink-1{-ms-flex-negative:1 !important;flex-shrink:1 !important}.justify-content-start{-webkit-box-pack:start !important;-ms-flex-pack:start !important;justify-content:flex-start !important}.justify-content-end{-webkit-box-pack:end !important;-ms-flex-pack:end !important;justify-content:flex-end !important}.justify-content-center{-webkit-box-pack:center !important;-ms-flex-pack:center !important;justify-content:center !important}.justify-content-between{-webkit-box-pack:justify !important;-ms-flex-pack:justify !important;justify-content:space-between !important}.justify-content-around{-ms-flex-pack:distribute !important;justify-content:space-around !important}.align-items-start{-webkit-box-align:start !important;-ms-flex-align:start !important;align-items:flex-start !important}.align-items-end{-webkit-box-align:end !important;-ms-flex-align:end !important;align-items:flex-end !important}.align-items-center{-webkit-box-align:center !important;-ms-flex-align:center !important;align-items:center !important}.align-items-baseline{-webkit-box-align:baseline !important;-ms-flex-align:baseline !important;align-items:baseline !important}.align-items-stretch{-webkit-box-align:stretch !important;-ms-flex-align:stretch !important;align-items:stretch !important}.align-content-start{-ms-flex-line-pack:start !important;align-content:flex-start !important}.align-content-end{-ms-flex-line-pack:end !important;align-content:flex-end !important}.align-content-center{-ms-flex-line-pack:center !important;align-content:center !important}.align-content-between{-ms-flex-line-pack:justify !important;align-content:space-between !important}.align-content-around{-ms-flex-line-pack:distribute !important;align-content:space-around !important}.align-content-stretch{-ms-flex-line-pack:stretch !important;align-content:stretch !important}.align-self-auto{-ms-flex-item-align:auto !important;align-self:auto !important}.align-self-start{-ms-flex-item-align:start !important;align-self:flex-start !important}.align-self-end{-ms-flex-item-align:end !important;align-self:flex-end !important}.align-self-center{-ms-flex-item-align:center !important;align-self:center !important}.align-self-baseline{-ms-flex-item-align:baseline !important;align-self:baseline !important}.align-self-stretch{-ms-flex-item-align:stretch !important;align-self:stretch !important}@media (min-width: 576px){.flex-sm-row{-webkit-box-orient:horizontal !important;-webkit-box-direction:normal !important;-ms-flex-direction:row !important;flex-direction:row !important}.flex-sm-column{-webkit-box-orient:vertical !important;-webkit-box-direction:normal !important;-ms-flex-direction:column !important;flex-direction:column !important}.flex-sm-row-reverse{-webkit-box-orient:horizontal !important;-webkit-box-direction:reverse !important;-ms-flex-direction:row-reverse !important;flex-direction:row-reverse !important}.flex-sm-column-reverse{-webkit-box-orient:vertical !important;-webkit-box-direction:reverse !important;-ms-flex-direction:column-reverse !important;flex-direction:column-reverse !important}.flex-sm-wrap{-ms-flex-wrap:wrap !important;flex-wrap:wrap !important}.flex-sm-nowrap{-ms-flex-wrap:nowrap !important;flex-wrap:nowrap !important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse !important;flex-wrap:wrap-reverse !important}.flex-sm-fill{-webkit-box-flex:1 !important;-ms-flex:1 1 auto !important;flex:1 1 auto !important}.flex-sm-grow-0{-webkit-box-flex:0 !important;-ms-flex-positive:0 !important;flex-grow:0 !important}.flex-sm-grow-1{-webkit-box-flex:1 !important;-ms-flex-positive:1 !important;flex-grow:1 !important}.flex-sm-shrink-0{-ms-flex-negative:0 !important;flex-shrink:0 !important}.flex-sm-shrink-1{-ms-flex-negative:1 !important;flex-shrink:1 !important}.justify-content-sm-start{-webkit-box-pack:start !important;-ms-flex-pack:start !important;justify-content:flex-start !important}.justify-content-sm-end{-webkit-box-pack:end !important;-ms-flex-pack:end !important;justify-content:flex-end !important}.justify-content-sm-center{-webkit-box-pack:center !important;-ms-flex-pack:center !important;justify-content:center !important}.justify-content-sm-between{-webkit-box-pack:justify !important;-ms-flex-pack:justify !important;justify-content:space-between !important}.justify-content-sm-around{-ms-flex-pack:distribute !important;justify-content:space-around !important}.align-items-sm-start{-webkit-box-align:start !important;-ms-flex-align:start !important;align-items:flex-start !important}.align-items-sm-end{-webkit-box-align:end !important;-ms-flex-align:end !important;align-items:flex-end !important}.align-items-sm-center{-webkit-box-align:center !important;-ms-flex-align:center !important;align-items:center !important}.align-items-sm-baseline{-webkit-box-align:baseline !important;-ms-flex-align:baseline !important;align-items:baseline !important}.align-items-sm-stretch{-webkit-box-align:stretch !important;-ms-flex-align:stretch !important;align-items:stretch !important}.align-content-sm-start{-ms-flex-line-pack:start !important;align-content:flex-start !important}.align-content-sm-end{-ms-flex-line-pack:end !important;align-content:flex-end !important}.align-content-sm-center{-ms-flex-line-pack:center !important;align-content:center !important}.align-content-sm-between{-ms-flex-line-pack:justify !important;align-content:space-between !important}.align-content-sm-around{-ms-flex-line-pack:distribute !important;align-content:space-around !important}.align-content-sm-stretch{-ms-flex-line-pack:stretch !important;align-content:stretch !important}.align-self-sm-auto{-ms-flex-item-align:auto !important;align-self:auto !important}.align-self-sm-start{-ms-flex-item-align:start !important;align-self:flex-start !important}.align-self-sm-end{-ms-flex-item-align:end !important;align-self:flex-end !important}.align-self-sm-center{-ms-flex-item-align:center !important;align-self:center !important}.align-self-sm-baseline{-ms-flex-item-align:baseline !important;align-self:baseline !important}.align-self-sm-stretch{-ms-flex-item-align:stretch !important;align-self:stretch !important}}@media (min-width: 768px){.flex-md-row{-webkit-box-orient:horizontal !important;-webkit-box-direction:normal !important;-ms-flex-direction:row !important;flex-direction:row !important}.flex-md-column{-webkit-box-orient:vertical !important;-webkit-box-direction:normal !important;-ms-flex-direction:column !important;flex-direction:column !important}.flex-md-row-reverse{-webkit-box-orient:horizontal !important;-webkit-box-direction:reverse !important;-ms-flex-direction:row-reverse !important;flex-direction:row-reverse !important}.flex-md-column-reverse{-webkit-box-orient:vertical !important;-webkit-box-direction:reverse !important;-ms-flex-direction:column-reverse !important;flex-direction:column-reverse !important}.flex-md-wrap{-ms-flex-wrap:wrap !important;flex-wrap:wrap !important}.flex-md-nowrap{-ms-flex-wrap:nowrap !important;flex-wrap:nowrap !important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse !important;flex-wrap:wrap-reverse !important}.flex-md-fill{-webkit-box-flex:1 !important;-ms-flex:1 1 auto !important;flex:1 1 auto !important}.flex-md-grow-0{-webkit-box-flex:0 !important;-ms-flex-positive:0 !important;flex-grow:0 !important}.flex-md-grow-1{-webkit-box-flex:1 !important;-ms-flex-positive:1 !important;flex-grow:1 !important}.flex-md-shrink-0{-ms-flex-negative:0 !important;flex-shrink:0 !important}.flex-md-shrink-1{-ms-flex-negative:1 !important;flex-shrink:1 !important}.justify-content-md-start{-webkit-box-pack:start !important;-ms-flex-pack:start !important;justify-content:flex-start !important}.justify-content-md-end{-webkit-box-pack:end !important;-ms-flex-pack:end !important;justify-content:flex-end !important}.justify-content-md-center{-webkit-box-pack:center !important;-ms-flex-pack:center !important;justify-content:center !important}.justify-content-md-between{-webkit-box-pack:justify !important;-ms-flex-pack:justify !important;justify-content:space-between !important}.justify-content-md-around{-ms-flex-pack:distribute !important;justify-content:space-around !important}.align-items-md-start{-webkit-box-align:start !important;-ms-flex-align:start !important;align-items:flex-start !important}.align-items-md-end{-webkit-box-align:end !important;-ms-flex-align:end !important;align-items:flex-end !important}.align-items-md-center{-webkit-box-align:center !important;-ms-flex-align:center !important;align-items:center !important}.align-items-md-baseline{-webkit-box-align:baseline !important;-ms-flex-align:baseline !important;align-items:baseline !important}.align-items-md-stretch{-webkit-box-align:stretch !important;-ms-flex-align:stretch !important;align-items:stretch !important}.align-content-md-start{-ms-flex-line-pack:start !important;align-content:flex-start !important}.align-content-md-end{-ms-flex-line-pack:end !important;align-content:flex-end !important}.align-content-md-center{-ms-flex-line-pack:center !important;align-content:center !important}.align-content-md-between{-ms-flex-line-pack:justify !important;align-content:space-between !important}.align-content-md-around{-ms-flex-line-pack:distribute !important;align-content:space-around !important}.align-content-md-stretch{-ms-flex-line-pack:stretch !important;align-content:stretch !important}.align-self-md-auto{-ms-flex-item-align:auto !important;align-self:auto !important}.align-self-md-start{-ms-flex-item-align:start !important;align-self:flex-start !important}.align-self-md-end{-ms-flex-item-align:end !important;align-self:flex-end !important}.align-self-md-center{-ms-flex-item-align:center !important;align-self:center !important}.align-self-md-baseline{-ms-flex-item-align:baseline !important;align-self:baseline !important}.align-self-md-stretch{-ms-flex-item-align:stretch !important;align-self:stretch !important}}@media (min-width: 992px){.flex-lg-row{-webkit-box-orient:horizontal !important;-webkit-box-direction:normal !important;-ms-flex-direction:row !important;flex-direction:row !important}.flex-lg-column{-webkit-box-orient:vertical !important;-webkit-box-direction:normal !important;-ms-flex-direction:column !important;flex-direction:column !important}.flex-lg-row-reverse{-webkit-box-orient:horizontal !important;-webkit-box-direction:reverse !important;-ms-flex-direction:row-reverse !important;flex-direction:row-reverse !important}.flex-lg-column-reverse{-webkit-box-orient:vertical !important;-webkit-box-direction:reverse !important;-ms-flex-direction:column-reverse !important;flex-direction:column-reverse !important}.flex-lg-wrap{-ms-flex-wrap:wrap !important;flex-wrap:wrap !important}.flex-lg-nowrap{-ms-flex-wrap:nowrap !important;flex-wrap:nowrap !important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse !important;flex-wrap:wrap-reverse !important}.flex-lg-fill{-webkit-box-flex:1 !important;-ms-flex:1 1 auto !important;flex:1 1 auto !important}.flex-lg-grow-0{-webkit-box-flex:0 !important;-ms-flex-positive:0 !important;flex-grow:0 !important}.flex-lg-grow-1{-webkit-box-flex:1 !important;-ms-flex-positive:1 !important;flex-grow:1 !important}.flex-lg-shrink-0{-ms-flex-negative:0 !important;flex-shrink:0 !important}.flex-lg-shrink-1{-ms-flex-negative:1 !important;flex-shrink:1 !important}.justify-content-lg-start{-webkit-box-pack:start !important;-ms-flex-pack:start !important;justify-content:flex-start !important}.justify-content-lg-end{-webkit-box-pack:end !important;-ms-flex-pack:end !important;justify-content:flex-end !important}.justify-content-lg-center{-webkit-box-pack:center !important;-ms-flex-pack:center !important;justify-content:center !important}.justify-content-lg-between{-webkit-box-pack:justify !important;-ms-flex-pack:justify !important;justify-content:space-between !important}.justify-content-lg-around{-ms-flex-pack:distribute !important;justify-content:space-around !important}.align-items-lg-start{-webkit-box-align:start !important;-ms-flex-align:start !important;align-items:flex-start !important}.align-items-lg-end{-webkit-box-align:end !important;-ms-flex-align:end !important;align-items:flex-end !important}.align-items-lg-center{-webkit-box-align:center !important;-ms-flex-align:center !important;align-items:center !important}.align-items-lg-baseline{-webkit-box-align:baseline !important;-ms-flex-align:baseline !important;align-items:baseline !important}.align-items-lg-stretch{-webkit-box-align:stretch !important;-ms-flex-align:stretch !important;align-items:stretch !important}.align-content-lg-start{-ms-flex-line-pack:start !important;align-content:flex-start !important}.align-content-lg-end{-ms-flex-line-pack:end !important;align-content:flex-end !important}.align-content-lg-center{-ms-flex-line-pack:center !important;align-content:center !important}.align-content-lg-between{-ms-flex-line-pack:justify !important;align-content:space-between !important}.align-content-lg-around{-ms-flex-line-pack:distribute !important;align-content:space-around !important}.align-content-lg-stretch{-ms-flex-line-pack:stretch !important;align-content:stretch !important}.align-self-lg-auto{-ms-flex-item-align:auto !important;align-self:auto !important}.align-self-lg-start{-ms-flex-item-align:start !important;align-self:flex-start !important}.align-self-lg-end{-ms-flex-item-align:end !important;align-self:flex-end !important}.align-self-lg-center{-ms-flex-item-align:center !important;align-self:center !important}.align-self-lg-baseline{-ms-flex-item-align:baseline !important;align-self:baseline !important}.align-self-lg-stretch{-ms-flex-item-align:stretch !important;align-self:stretch !important}}@media (min-width: 1200px){.flex-xl-row{-webkit-box-orient:horizontal !important;-webkit-box-direction:normal !important;-ms-flex-direction:row !important;flex-direction:row !important}.flex-xl-column{-webkit-box-orient:vertical !important;-webkit-box-direction:normal !important;-ms-flex-direction:column !important;flex-direction:column !important}.flex-xl-row-reverse{-webkit-box-orient:horizontal !important;-webkit-box-direction:reverse !important;-ms-flex-direction:row-reverse !important;flex-direction:row-reverse !important}.flex-xl-column-reverse{-webkit-box-orient:vertical !important;-webkit-box-direction:reverse !important;-ms-flex-direction:column-reverse !important;flex-direction:column-reverse !important}.flex-xl-wrap{-ms-flex-wrap:wrap !important;flex-wrap:wrap !important}.flex-xl-nowrap{-ms-flex-wrap:nowrap !important;flex-wrap:nowrap !important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse !important;flex-wrap:wrap-reverse !important}.flex-xl-fill{-webkit-box-flex:1 !important;-ms-flex:1 1 auto !important;flex:1 1 auto !important}.flex-xl-grow-0{-webkit-box-flex:0 !important;-ms-flex-positive:0 !important;flex-grow:0 !important}.flex-xl-grow-1{-webkit-box-flex:1 !important;-ms-flex-positive:1 !important;flex-grow:1 !important}.flex-xl-shrink-0{-ms-flex-negative:0 !important;flex-shrink:0 !important}.flex-xl-shrink-1{-ms-flex-negative:1 !important;flex-shrink:1 !important}.justify-content-xl-start{-webkit-box-pack:start !important;-ms-flex-pack:start !important;justify-content:flex-start !important}.justify-content-xl-end{-webkit-box-pack:end !important;-ms-flex-pack:end !important;justify-content:flex-end !important}.justify-content-xl-center{-webkit-box-pack:center !important;-ms-flex-pack:center !important;justify-content:center !important}.justify-content-xl-between{-webkit-box-pack:justify !important;-ms-flex-pack:justify !important;justify-content:space-between !important}.justify-content-xl-around{-ms-flex-pack:distribute !important;justify-content:space-around !important}.align-items-xl-start{-webkit-box-align:start !important;-ms-flex-align:start !important;align-items:flex-start !important}.align-items-xl-end{-webkit-box-align:end !important;-ms-flex-align:end !important;align-items:flex-end !important}.align-items-xl-center{-webkit-box-align:center !important;-ms-flex-align:center !important;align-items:center !important}.align-items-xl-baseline{-webkit-box-align:baseline !important;-ms-flex-align:baseline !important;align-items:baseline !important}.align-items-xl-stretch{-webkit-box-align:stretch !important;-ms-flex-align:stretch !important;align-items:stretch !important}.align-content-xl-start{-ms-flex-line-pack:start !important;align-content:flex-start !important}.align-content-xl-end{-ms-flex-line-pack:end !important;align-content:flex-end !important}.align-content-xl-center{-ms-flex-line-pack:center !important;align-content:center !important}.align-content-xl-between{-ms-flex-line-pack:justify !important;align-content:space-between !important}.align-content-xl-around{-ms-flex-line-pack:distribute !important;align-content:space-around !important}.align-content-xl-stretch{-ms-flex-line-pack:stretch !important;align-content:stretch !important}.align-self-xl-auto{-ms-flex-item-align:auto !important;align-self:auto !important}.align-self-xl-start{-ms-flex-item-align:start !important;align-self:flex-start !important}.align-self-xl-end{-ms-flex-item-align:end !important;align-self:flex-end !important}.align-self-xl-center{-ms-flex-item-align:center !important;align-self:center !important}.align-self-xl-baseline{-ms-flex-item-align:baseline !important;align-self:baseline !important}.align-self-xl-stretch{-ms-flex-item-align:stretch !important;align-self:stretch !important}}.float-left{float:left !important}.float-right{float:right !important}.float-none{float:none !important}@media (min-width: 576px){.float-sm-left{float:left !important}.float-sm-right{float:right !important}.float-sm-none{float:none !important}}@media (min-width: 768px){.float-md-left{float:left !important}.float-md-right{float:right !important}.float-md-none{float:none !important}}@media (min-width: 992px){.float-lg-left{float:left !important}.float-lg-right{float:right !important}.float-lg-none{float:none !important}}@media (min-width: 1200px){.float-xl-left{float:left !important}.float-xl-right{float:right !important}.float-xl-none{float:none !important}}.position-static{position:static !important}.position-relative{position:relative !important}.position-absolute{position:absolute !important}.position-fixed{position:fixed !important}.position-sticky{position:-webkit-sticky !important;position:sticky !important}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}@supports (position: -webkit-sticky) or (position: sticky){.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{-webkit-box-shadow:0 0.125rem 0.25rem rgba(0,0,0,0.075) !important;box-shadow:0 0.125rem 0.25rem rgba(0,0,0,0.075) !important}.shadow{-webkit-box-shadow:0 0.5rem 1rem rgba(0,0,0,0.15) !important;box-shadow:0 0.5rem 1rem rgba(0,0,0,0.15) !important}.shadow-lg{-webkit-box-shadow:0 1rem 3rem rgba(0,0,0,0.175) !important;box-shadow:0 1rem 3rem rgba(0,0,0,0.175) !important}.shadow-none{-webkit-box-shadow:none !important;box-shadow:none !important}.w-25{width:25% !important}.w-50{width:50% !important}.w-75{width:75% !important}.w-100{width:100% !important}.w-auto{width:auto !important}.h-25{height:25% !important}.h-50{height:50% !important}.h-75{height:75% !important}.h-100{height:100% !important}.h-auto{height:auto !important}.mw-100{max-width:100% !important}.mh-100{max-height:100% !important}.m-0{margin:0 !important}.mt-0,.my-0{margin-top:0 !important}.mr-0,.mx-0{margin-right:0 !important}.mb-0,.my-0{margin-bottom:0 !important}.ml-0,.mx-0{margin-left:0 !important}.m-1{margin:0.25rem !important}.mt-1,.my-1{margin-top:0.25rem !important}.mr-1,.mx-1{margin-right:0.25rem !important}.mb-1,.my-1{margin-bottom:0.25rem !important}.ml-1,.mx-1{margin-left:0.25rem !important}.m-2{margin:0.5rem !important}.mt-2,.my-2{margin-top:0.5rem !important}.mr-2,.mx-2{margin-right:0.5rem !important}.mb-2,.my-2{margin-bottom:0.5rem !important}.ml-2,.mx-2{margin-left:0.5rem !important}.m-3{margin:1rem !important}.mt-3,.my-3{margin-top:1rem !important}.mr-3,.mx-3{margin-right:1rem !important}.mb-3,.my-3{margin-bottom:1rem !important}.ml-3,.mx-3{margin-left:1rem !important}.m-4{margin:1.5rem !important}.mt-4,.my-4{margin-top:1.5rem !important}.mr-4,.mx-4{margin-right:1.5rem !important}.mb-4,.my-4{margin-bottom:1.5rem !important}.ml-4,.mx-4{margin-left:1.5rem !important}.m-5{margin:3rem !important}.mt-5,.my-5{margin-top:3rem !important}.mr-5,.mx-5{margin-right:3rem !important}.mb-5,.my-5{margin-bottom:3rem !important}.ml-5,.mx-5{margin-left:3rem !important}.p-0{padding:0 !important}.pt-0,.py-0{padding-top:0 !important}.pr-0,.px-0{padding-right:0 !important}.pb-0,.py-0{padding-bottom:0 !important}.pl-0,.px-0{padding-left:0 !important}.p-1{padding:0.25rem !important}.pt-1,.py-1{padding-top:0.25rem !important}.pr-1,.px-1{padding-right:0.25rem !important}.pb-1,.py-1{padding-bottom:0.25rem !important}.pl-1,.px-1{padding-left:0.25rem !important}.p-2{padding:0.5rem !important}.pt-2,.py-2{padding-top:0.5rem !important}.pr-2,.px-2{padding-right:0.5rem !important}.pb-2,.py-2{padding-bottom:0.5rem !important}.pl-2,.px-2{padding-left:0.5rem !important}.p-3{padding:1rem !important}.pt-3,.py-3{padding-top:1rem !important}.pr-3,.px-3{padding-right:1rem !important}.pb-3,.py-3{padding-bottom:1rem !important}.pl-3,.px-3{padding-left:1rem !important}.p-4{padding:1.5rem !important}.pt-4,.py-4{padding-top:1.5rem !important}.pr-4,.px-4{padding-right:1.5rem !important}.pb-4,.py-4{padding-bottom:1.5rem !important}.pl-4,.px-4{padding-left:1.5rem !important}.p-5{padding:3rem !important}.pt-5,.py-5{padding-top:3rem !important}.pr-5,.px-5{padding-right:3rem !important}.pb-5,.py-5{padding-bottom:3rem !important}.pl-5,.px-5{padding-left:3rem !important}.m-auto{margin:auto !important}.mt-auto,.my-auto{margin-top:auto !important}.mr-auto,.mx-auto{margin-right:auto !important}.mb-auto,.my-auto{margin-bottom:auto !important}.ml-auto,.mx-auto{margin-left:auto !important}@media (min-width: 576px){.m-sm-0{margin:0 !important}.mt-sm-0,.my-sm-0{margin-top:0 !important}.mr-sm-0,.mx-sm-0{margin-right:0 !important}.mb-sm-0,.my-sm-0{margin-bottom:0 !important}.ml-sm-0,.mx-sm-0{margin-left:0 !important}.m-sm-1{margin:0.25rem !important}.mt-sm-1,.my-sm-1{margin-top:0.25rem !important}.mr-sm-1,.mx-sm-1{margin-right:0.25rem !important}.mb-sm-1,.my-sm-1{margin-bottom:0.25rem !important}.ml-sm-1,.mx-sm-1{margin-left:0.25rem !important}.m-sm-2{margin:0.5rem !important}.mt-sm-2,.my-sm-2{margin-top:0.5rem !important}.mr-sm-2,.mx-sm-2{margin-right:0.5rem !important}.mb-sm-2,.my-sm-2{margin-bottom:0.5rem !important}.ml-sm-2,.mx-sm-2{margin-left:0.5rem !important}.m-sm-3{margin:1rem !important}.mt-sm-3,.my-sm-3{margin-top:1rem !important}.mr-sm-3,.mx-sm-3{margin-right:1rem !important}.mb-sm-3,.my-sm-3{margin-bottom:1rem !important}.ml-sm-3,.mx-sm-3{margin-left:1rem !important}.m-sm-4{margin:1.5rem !important}.mt-sm-4,.my-sm-4{margin-top:1.5rem !important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem !important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem !important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem !important}.m-sm-5{margin:3rem !important}.mt-sm-5,.my-sm-5{margin-top:3rem !important}.mr-sm-5,.mx-sm-5{margin-right:3rem !important}.mb-sm-5,.my-sm-5{margin-bottom:3rem !important}.ml-sm-5,.mx-sm-5{margin-left:3rem !important}.p-sm-0{padding:0 !important}.pt-sm-0,.py-sm-0{padding-top:0 !important}.pr-sm-0,.px-sm-0{padding-right:0 !important}.pb-sm-0,.py-sm-0{padding-bottom:0 !important}.pl-sm-0,.px-sm-0{padding-left:0 !important}.p-sm-1{padding:0.25rem !important}.pt-sm-1,.py-sm-1{padding-top:0.25rem !important}.pr-sm-1,.px-sm-1{padding-right:0.25rem !important}.pb-sm-1,.py-sm-1{padding-bottom:0.25rem !important}.pl-sm-1,.px-sm-1{padding-left:0.25rem !important}.p-sm-2{padding:0.5rem !important}.pt-sm-2,.py-sm-2{padding-top:0.5rem !important}.pr-sm-2,.px-sm-2{padding-right:0.5rem !important}.pb-sm-2,.py-sm-2{padding-bottom:0.5rem !important}.pl-sm-2,.px-sm-2{padding-left:0.5rem !important}.p-sm-3{padding:1rem !important}.pt-sm-3,.py-sm-3{padding-top:1rem !important}.pr-sm-3,.px-sm-3{padding-right:1rem !important}.pb-sm-3,.py-sm-3{padding-bottom:1rem !important}.pl-sm-3,.px-sm-3{padding-left:1rem !important}.p-sm-4{padding:1.5rem !important}.pt-sm-4,.py-sm-4{padding-top:1.5rem !important}.pr-sm-4,.px-sm-4{padding-right:1.5rem !important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem !important}.pl-sm-4,.px-sm-4{padding-left:1.5rem !important}.p-sm-5{padding:3rem !important}.pt-sm-5,.py-sm-5{padding-top:3rem !important}.pr-sm-5,.px-sm-5{padding-right:3rem !important}.pb-sm-5,.py-sm-5{padding-bottom:3rem !important}.pl-sm-5,.px-sm-5{padding-left:3rem !important}.m-sm-auto{margin:auto !important}.mt-sm-auto,.my-sm-auto{margin-top:auto !important}.mr-sm-auto,.mx-sm-auto{margin-right:auto !important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto !important}.ml-sm-auto,.mx-sm-auto{margin-left:auto !important}}@media (min-width: 768px){.m-md-0{margin:0 !important}.mt-md-0,.my-md-0{margin-top:0 !important}.mr-md-0,.mx-md-0{margin-right:0 !important}.mb-md-0,.my-md-0{margin-bottom:0 !important}.ml-md-0,.mx-md-0{margin-left:0 !important}.m-md-1{margin:0.25rem !important}.mt-md-1,.my-md-1{margin-top:0.25rem !important}.mr-md-1,.mx-md-1{margin-right:0.25rem !important}.mb-md-1,.my-md-1{margin-bottom:0.25rem !important}.ml-md-1,.mx-md-1{margin-left:0.25rem !important}.m-md-2{margin:0.5rem !important}.mt-md-2,.my-md-2{margin-top:0.5rem !important}.mr-md-2,.mx-md-2{margin-right:0.5rem !important}.mb-md-2,.my-md-2{margin-bottom:0.5rem !important}.ml-md-2,.mx-md-2{margin-left:0.5rem !important}.m-md-3{margin:1rem !important}.mt-md-3,.my-md-3{margin-top:1rem !important}.mr-md-3,.mx-md-3{margin-right:1rem !important}.mb-md-3,.my-md-3{margin-bottom:1rem !important}.ml-md-3,.mx-md-3{margin-left:1rem !important}.m-md-4{margin:1.5rem !important}.mt-md-4,.my-md-4{margin-top:1.5rem !important}.mr-md-4,.mx-md-4{margin-right:1.5rem !important}.mb-md-4,.my-md-4{margin-bottom:1.5rem !important}.ml-md-4,.mx-md-4{margin-left:1.5rem !important}.m-md-5{margin:3rem !important}.mt-md-5,.my-md-5{margin-top:3rem !important}.mr-md-5,.mx-md-5{margin-right:3rem !important}.mb-md-5,.my-md-5{margin-bottom:3rem !important}.ml-md-5,.mx-md-5{margin-left:3rem !important}.p-md-0{padding:0 !important}.pt-md-0,.py-md-0{padding-top:0 !important}.pr-md-0,.px-md-0{padding-right:0 !important}.pb-md-0,.py-md-0{padding-bottom:0 !important}.pl-md-0,.px-md-0{padding-left:0 !important}.p-md-1{padding:0.25rem !important}.pt-md-1,.py-md-1{padding-top:0.25rem !important}.pr-md-1,.px-md-1{padding-right:0.25rem !important}.pb-md-1,.py-md-1{padding-bottom:0.25rem !important}.pl-md-1,.px-md-1{padding-left:0.25rem !important}.p-md-2{padding:0.5rem !important}.pt-md-2,.py-md-2{padding-top:0.5rem !important}.pr-md-2,.px-md-2{padding-right:0.5rem !important}.pb-md-2,.py-md-2{padding-bottom:0.5rem !important}.pl-md-2,.px-md-2{padding-left:0.5rem !important}.p-md-3{padding:1rem !important}.pt-md-3,.py-md-3{padding-top:1rem !important}.pr-md-3,.px-md-3{padding-right:1rem !important}.pb-md-3,.py-md-3{padding-bottom:1rem !important}.pl-md-3,.px-md-3{padding-left:1rem !important}.p-md-4{padding:1.5rem !important}.pt-md-4,.py-md-4{padding-top:1.5rem !important}.pr-md-4,.px-md-4{padding-right:1.5rem !important}.pb-md-4,.py-md-4{padding-bottom:1.5rem !important}.pl-md-4,.px-md-4{padding-left:1.5rem !important}.p-md-5{padding:3rem !important}.pt-md-5,.py-md-5{padding-top:3rem !important}.pr-md-5,.px-md-5{padding-right:3rem !important}.pb-md-5,.py-md-5{padding-bottom:3rem !important}.pl-md-5,.px-md-5{padding-left:3rem !important}.m-md-auto{margin:auto !important}.mt-md-auto,.my-md-auto{margin-top:auto !important}.mr-md-auto,.mx-md-auto{margin-right:auto !important}.mb-md-auto,.my-md-auto{margin-bottom:auto !important}.ml-md-auto,.mx-md-auto{margin-left:auto !important}}@media (min-width: 992px){.m-lg-0{margin:0 !important}.mt-lg-0,.my-lg-0{margin-top:0 !important}.mr-lg-0,.mx-lg-0{margin-right:0 !important}.mb-lg-0,.my-lg-0{margin-bottom:0 !important}.ml-lg-0,.mx-lg-0{margin-left:0 !important}.m-lg-1{margin:0.25rem !important}.mt-lg-1,.my-lg-1{margin-top:0.25rem !important}.mr-lg-1,.mx-lg-1{margin-right:0.25rem !important}.mb-lg-1,.my-lg-1{margin-bottom:0.25rem !important}.ml-lg-1,.mx-lg-1{margin-left:0.25rem !important}.m-lg-2{margin:0.5rem !important}.mt-lg-2,.my-lg-2{margin-top:0.5rem !important}.mr-lg-2,.mx-lg-2{margin-right:0.5rem !important}.mb-lg-2,.my-lg-2{margin-bottom:0.5rem !important}.ml-lg-2,.mx-lg-2{margin-left:0.5rem !important}.m-lg-3{margin:1rem !important}.mt-lg-3,.my-lg-3{margin-top:1rem !important}.mr-lg-3,.mx-lg-3{margin-right:1rem !important}.mb-lg-3,.my-lg-3{margin-bottom:1rem !important}.ml-lg-3,.mx-lg-3{margin-left:1rem !important}.m-lg-4{margin:1.5rem !important}.mt-lg-4,.my-lg-4{margin-top:1.5rem !important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem !important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem !important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem !important}.m-lg-5{margin:3rem !important}.mt-lg-5,.my-lg-5{margin-top:3rem !important}.mr-lg-5,.mx-lg-5{margin-right:3rem !important}.mb-lg-5,.my-lg-5{margin-bottom:3rem !important}.ml-lg-5,.mx-lg-5{margin-left:3rem !important}.p-lg-0{padding:0 !important}.pt-lg-0,.py-lg-0{padding-top:0 !important}.pr-lg-0,.px-lg-0{padding-right:0 !important}.pb-lg-0,.py-lg-0{padding-bottom:0 !important}.pl-lg-0,.px-lg-0{padding-left:0 !important}.p-lg-1{padding:0.25rem !important}.pt-lg-1,.py-lg-1{padding-top:0.25rem !important}.pr-lg-1,.px-lg-1{padding-right:0.25rem !important}.pb-lg-1,.py-lg-1{padding-bottom:0.25rem !important}.pl-lg-1,.px-lg-1{padding-left:0.25rem !important}.p-lg-2{padding:0.5rem !important}.pt-lg-2,.py-lg-2{padding-top:0.5rem !important}.pr-lg-2,.px-lg-2{padding-right:0.5rem !important}.pb-lg-2,.py-lg-2{padding-bottom:0.5rem !important}.pl-lg-2,.px-lg-2{padding-left:0.5rem !important}.p-lg-3{padding:1rem !important}.pt-lg-3,.py-lg-3{padding-top:1rem !important}.pr-lg-3,.px-lg-3{padding-right:1rem !important}.pb-lg-3,.py-lg-3{padding-bottom:1rem !important}.pl-lg-3,.px-lg-3{padding-left:1rem !important}.p-lg-4{padding:1.5rem !important}.pt-lg-4,.py-lg-4{padding-top:1.5rem !important}.pr-lg-4,.px-lg-4{padding-right:1.5rem !important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem !important}.pl-lg-4,.px-lg-4{padding-left:1.5rem !important}.p-lg-5{padding:3rem !important}.pt-lg-5,.py-lg-5{padding-top:3rem !important}.pr-lg-5,.px-lg-5{padding-right:3rem !important}.pb-lg-5,.py-lg-5{padding-bottom:3rem !important}.pl-lg-5,.px-lg-5{padding-left:3rem !important}.m-lg-auto{margin:auto !important}.mt-lg-auto,.my-lg-auto{margin-top:auto !important}.mr-lg-auto,.mx-lg-auto{margin-right:auto !important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto !important}.ml-lg-auto,.mx-lg-auto{margin-left:auto !important}}@media (min-width: 1200px){.m-xl-0{margin:0 !important}.mt-xl-0,.my-xl-0{margin-top:0 !important}.mr-xl-0,.mx-xl-0{margin-right:0 !important}.mb-xl-0,.my-xl-0{margin-bottom:0 !important}.ml-xl-0,.mx-xl-0{margin-left:0 !important}.m-xl-1{margin:0.25rem !important}.mt-xl-1,.my-xl-1{margin-top:0.25rem !important}.mr-xl-1,.mx-xl-1{margin-right:0.25rem !important}.mb-xl-1,.my-xl-1{margin-bottom:0.25rem !important}.ml-xl-1,.mx-xl-1{margin-left:0.25rem !important}.m-xl-2{margin:0.5rem !important}.mt-xl-2,.my-xl-2{margin-top:0.5rem !important}.mr-xl-2,.mx-xl-2{margin-right:0.5rem !important}.mb-xl-2,.my-xl-2{margin-bottom:0.5rem !important}.ml-xl-2,.mx-xl-2{margin-left:0.5rem !important}.m-xl-3{margin:1rem !important}.mt-xl-3,.my-xl-3{margin-top:1rem !important}.mr-xl-3,.mx-xl-3{margin-right:1rem !important}.mb-xl-3,.my-xl-3{margin-bottom:1rem !important}.ml-xl-3,.mx-xl-3{margin-left:1rem !important}.m-xl-4{margin:1.5rem !important}.mt-xl-4,.my-xl-4{margin-top:1.5rem !important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem !important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem !important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem !important}.m-xl-5{margin:3rem !important}.mt-xl-5,.my-xl-5{margin-top:3rem !important}.mr-xl-5,.mx-xl-5{margin-right:3rem !important}.mb-xl-5,.my-xl-5{margin-bottom:3rem !important}.ml-xl-5,.mx-xl-5{margin-left:3rem !important}.p-xl-0{padding:0 !important}.pt-xl-0,.py-xl-0{padding-top:0 !important}.pr-xl-0,.px-xl-0{padding-right:0 !important}.pb-xl-0,.py-xl-0{padding-bottom:0 !important}.pl-xl-0,.px-xl-0{padding-left:0 !important}.p-xl-1{padding:0.25rem !important}.pt-xl-1,.py-xl-1{padding-top:0.25rem !important}.pr-xl-1,.px-xl-1{padding-right:0.25rem !important}.pb-xl-1,.py-xl-1{padding-bottom:0.25rem !important}.pl-xl-1,.px-xl-1{padding-left:0.25rem !important}.p-xl-2{padding:0.5rem !important}.pt-xl-2,.py-xl-2{padding-top:0.5rem !important}.pr-xl-2,.px-xl-2{padding-right:0.5rem !important}.pb-xl-2,.py-xl-2{padding-bottom:0.5rem !important}.pl-xl-2,.px-xl-2{padding-left:0.5rem !important}.p-xl-3{padding:1rem !important}.pt-xl-3,.py-xl-3{padding-top:1rem !important}.pr-xl-3,.px-xl-3{padding-right:1rem !important}.pb-xl-3,.py-xl-3{padding-bottom:1rem !important}.pl-xl-3,.px-xl-3{padding-left:1rem !important}.p-xl-4{padding:1.5rem !important}.pt-xl-4,.py-xl-4{padding-top:1.5rem !important}.pr-xl-4,.px-xl-4{padding-right:1.5rem !important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem !important}.pl-xl-4,.px-xl-4{padding-left:1.5rem !important}.p-xl-5{padding:3rem !important}.pt-xl-5,.py-xl-5{padding-top:3rem !important}.pr-xl-5,.px-xl-5{padding-right:3rem !important}.pb-xl-5,.py-xl-5{padding-bottom:3rem !important}.pl-xl-5,.px-xl-5{padding-left:3rem !important}.m-xl-auto{margin:auto !important}.mt-xl-auto,.my-xl-auto{margin-top:auto !important}.mr-xl-auto,.mx-xl-auto{margin-right:auto !important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto !important}.ml-xl-auto,.mx-xl-auto{margin-left:auto !important}}.text-monospace{font-family:SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace}.text-justify{text-align:justify !important}.text-nowrap{white-space:nowrap !important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left !important}.text-right{text-align:right !important}.text-center{text-align:center !important}@media (min-width: 576px){.text-sm-left{text-align:left !important}.text-sm-right{text-align:right !important}.text-sm-center{text-align:center !important}}@media (min-width: 768px){.text-md-left{text-align:left !important}.text-md-right{text-align:right !important}.text-md-center{text-align:center !important}}@media (min-width: 992px){.text-lg-left{text-align:left !important}.text-lg-right{text-align:right !important}.text-lg-center{text-align:center !important}}@media (min-width: 1200px){.text-xl-left{text-align:left !important}.text-xl-right{text-align:right !important}.text-xl-center{text-align:center !important}}.text-lowercase{text-transform:lowercase !important}.text-uppercase{text-transform:uppercase !important}.text-capitalize{text-transform:capitalize !important}.font-weight-light{font-weight:300 !important}.font-weight-normal{font-weight:400 !important}.font-weight-bold{font-weight:700 !important}.font-italic{font-style:italic !important}.text-white{color:#fff !important}.text-primary{color:#3A3F44 !important}a.text-primary:hover,a.text-primary:focus{color:#232628 !important}.text-secondary{color:#7A8288 !important}a.text-secondary:hover,a.text-secondary:focus{color:#62686d !important}.text-success{color:#62c462 !important}a.text-success:hover,a.text-success:focus{color:#42b142 !important}.text-info{color:#5bc0de !important}a.text-info:hover,a.text-info:focus{color:#31b0d5 !important}.text-warning{color:#f89406 !important}a.text-warning:hover,a.text-warning:focus{color:#c67605 !important}.text-danger{color:#ee5f5b !important}a.text-danger:hover,a.text-danger:focus{color:#e9322d !important}.text-light{color:#e9ecef !important}a.text-light:hover,a.text-light:focus{color:#cbd3da !important}.text-dark{color:#272B30 !important}a.text-dark:hover,a.text-dark:focus{color:#101214 !important}.text-body{color:#aaa !important}.text-muted{color:#7A8288 !important}.text-black-50{color:rgba(0,0,0,0.5) !important}.text-white-50{color:rgba(255,255,255,0.5) !important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.visible{visibility:visible !important}.invisible{visibility:hidden !important}@media print{*,*::before,*::after{text-shadow:none !important;-webkit-box-shadow:none !important;box-shadow:none !important}a:not(.btn){text-decoration:underline}abbr[title]::after{content:" (" attr(title) ")"}pre{white-space:pre-wrap !important}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}body{min-width:992px !important}.container{min-width:992px !important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse !important}.table td,.table th{background-color:#fff !important}.table-bordered th,.table-bordered td{border:1px solid #dee2e6 !important}.table-dark{color:inherit}.table-dark th,.table-dark td,.table-dark thead th,.table-dark tbody+tbody{border-color:rgba(0,0,0,0.6)}.table .thead-dark th{color:inherit;border-color:rgba(0,0,0,0.6)}}.navbar{border:1px solid rgba(0,0,0,0.6);text-shadow:1px 1px 1px rgba(0,0,0,0.3)}.navbar .container{padding:0}.navbar .navbar-toggler{border-color:rgba(0,0,0,0.6)}.navbar-fixed-top{border-width:0 0 1px 0}.navbar-fixed-bottom{border-width:1px 0 0 0}.navbar .nav-link{padding:1rem;border-left:1px solid rgba(255,255,255,0.1);border-right:1px solid rgba(0,0,0,0.2)}.navbar .nav-link:hover,.navbar .nav-link:focus{background-image:-webkit-gradient(linear, left top, left bottom, from(#101112), color-stop(40%, #17191b), to(#1b1e20));background-image:linear-gradient(#101112, #17191b 40%, #1b1e20);background-repeat:no-repeat;-webkit-filter:none;filter:none;border-left:1px solid rgba(0,0,0,0.2)}.navbar-brand{padding:0.75rem 1rem calc(54px - 0.75rem - 30px);margin-right:0;border-right:1px solid rgba(0,0,0,0.2)}.navbar .nav-item.active .nav-link{background-color:rgba(0,0,0,0.3);border-left:1px solid rgba(0,0,0,0.2)}.navbar-nav .nav-item+.nav-item{margin-left:0}.navbar.bg-light{text-shadow:1px 1px 1px rgba(0,0,0,0.1)}.navbar.bg-light .nav-link:hover,.navbar.bg-light .nav-link:focus{background-image:-webkit-gradient(linear, left top, left bottom, from(#4e5458), color-stop(40%, #565b60), to(#5b6165));background-image:linear-gradient(#4e5458, #565b60 40%, #5b6165);background-repeat:no-repeat;-webkit-filter:none;filter:none;border-left:1px solid rgba(0,0,0,0.2)}@media (max-width: 576px){.navbar-expand-sm .navbar-brand,.navbar-expand-sm .nav-link{border:none !important}}@media (max-width: 768px){.navbar-expand-md .navbar-brand,.navbar-expand-md .nav-link{border:none !important}}@media (max-width: 992px){.navbar-expand-lg .navbar-brand,.navbar-expand-lg .nav-link{border:none !important}}.btn{border-color:rgba(0,0,0,0.6);text-shadow:1px 1px 1px rgba(0,0,0,0.3)}.btn:not([disabled]):not(.disabled).active,.btn.disabled{border-color:rgba(0,0,0,0.6);-webkit-box-shadow:none;box-shadow:none}.btn:hover,.btn:focus,.btn:not([disabled]):not(.disabled):active,.btn:not([disabled]):not(.disabled):active:hover,.btn:not([disabled]):not(.disabled).active:hover{border-color:rgba(0,0,0,0.6)}.btn-primary{background-image:-webkit-gradient(linear, left top, left bottom, from(#484e55), color-stop(60%, #3A3F44), to(#313539));background-image:linear-gradient(#484e55, #3A3F44 60%, #313539);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-primary:not([disabled]):not(.disabled):hover,.btn-primary:not([disabled]):not(.disabled):focus,.btn-primary:not([disabled]):not(.disabled):active:hover,.btn-primary:not([disabled]):not(.disabled).active:hover{background-image:-webkit-gradient(linear, left top, left bottom, from(#101112), color-stop(40%, #17191b), to(#1b1e20));background-image:linear-gradient(#101112, #17191b 40%, #1b1e20);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-secondary{background-image:-webkit-gradient(linear, left top, left bottom, from(#8a9196), color-stop(60%, #7A8288), to(#70787d));background-image:linear-gradient(#8a9196, #7A8288 60%, #70787d);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-secondary:not([disabled]):not(.disabled):hover,.btn-secondary:not([disabled]):not(.disabled):focus,.btn-secondary:not([disabled]):not(.disabled):active,.btn-secondary:not([disabled]):not(.disabled).active{background-image:-webkit-gradient(linear, left top, left bottom, from(#4e5458), color-stop(40%, #565b60), to(#5b6165));background-image:linear-gradient(#4e5458, #565b60 40%, #5b6165);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-success{background-image:-webkit-gradient(linear, left top, left bottom, from(#78cc78), color-stop(60%, #62c462), to(#53be53));background-image:linear-gradient(#78cc78, #62c462 60%, #53be53);background-repeat:no-repeat;-webkit-filter:none;filter:none;color:#fff}.btn-success:not([disabled]):not(.disabled):hover,.btn-success:not([disabled]):not(.disabled):focus,.btn-success:not([disabled]):not(.disabled):active,.btn-success:not([disabled]):not(.disabled).active{background-image:-webkit-gradient(linear, left top, left bottom, from(#379337), color-stop(40%, #3b9e3b), to(#3ea63e));background-image:linear-gradient(#379337, #3b9e3b 40%, #3ea63e);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-info{background-image:-webkit-gradient(linear, left top, left bottom, from(#74cae3), color-stop(60%, #5bc0de), to(#4ab9db));background-image:linear-gradient(#74cae3, #5bc0de 60%, #4ab9db);background-repeat:no-repeat;-webkit-filter:none;filter:none;color:#fff}.btn-info:not([disabled]):not(.disabled):hover,.btn-info:not([disabled]):not(.disabled):focus,.btn-info:not([disabled]):not(.disabled):active,.btn-info:not([disabled]):not(.disabled).active{background-image:-webkit-gradient(linear, left top, left bottom, from(#2596b8), color-stop(40%, #28a1c5), to(#29a8cd));background-image:linear-gradient(#2596b8, #28a1c5 40%, #29a8cd);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-warning{background-image:-webkit-gradient(linear, left top, left bottom, from(#faa123), color-stop(60%, #f89406), to(#e48806));background-image:linear-gradient(#faa123, #f89406 60%, #e48806);background-repeat:no-repeat;-webkit-filter:none;filter:none;color:#fff}.btn-warning:not([disabled]):not(.disabled):hover,.btn-warning:not([disabled]):not(.disabled):focus,.btn-warning:not([disabled]):not(.disabled):active,.btn-warning:not([disabled]):not(.disabled).active{background-image:-webkit-gradient(linear, left top, left bottom, from(#9e5f04), color-stop(40%, #ad6704), to(#b76d04));background-image:linear-gradient(#9e5f04, #ad6704 40%, #b76d04);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-danger{background-image:-webkit-gradient(linear, left top, left bottom, from(#f17a77), color-stop(60%, #ee5f5b), to(#ec4d49));background-image:linear-gradient(#f17a77, #ee5f5b 60%, #ec4d49);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-danger:not([disabled]):not(.disabled):hover,.btn-danger:not([disabled]):not(.disabled):focus,.btn-danger:not([disabled]):not(.disabled):active,.btn-danger:not([disabled]):not(.disabled).active{background-image:-webkit-gradient(linear, left top, left bottom, from(#d71c16), color-stop(40%, #e51d18), to(#e8241f));background-image:linear-gradient(#d71c16, #e51d18 40%, #e8241f);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-link,.btn-link:hover{border-color:transparent}.btn-group .btn.active,.btn-group-vertical .btn.active{border-color:rgba(0,0,0,0.6)}h1,h2,h3,h4,h5,h6{text-shadow:-1px -1px 0 rgba(0,0,0,0.3)}.table-primary,.table-secondary,.table-success,.table-info,.table-warning,.table-danger{color:#fff}.table-primary,.table-primary>th,.table-primary>td{background-color:#3A3F44}.table-secondary,.table-secondary>th,.table-secondary>td{background-color:#7A8288}.table-light,.table-light>th,.table-light>td{background-color:#e9ecef}.table-dark,.table-dark>th,.table-dark>td{background-color:#272B30}.table-success,.table-success>th,.table-success>td{background-color:#62c462}.table-info,.table-info>th,.table-info>td{background-color:#5bc0de}.table-danger,.table-danger>th,.table-danger>td{background-color:#ee5f5b}.table-warning,.table-warning>th,.table-warning>td{background-color:#f89406}.table-active,.table-active>th,.table-active>td{background-color:rgba(255,255,255,0.075)}.table-hover .table-primary:hover,.table-hover .table-primary:hover>th,.table-hover .table-primary:hover>td{background-color:#2e3236}.table-hover .table-secondary:hover,.table-hover .table-secondary:hover>th,.table-hover .table-secondary:hover>td{background-color:#6e757b}.table-hover .table-light:hover,.table-hover .table-light:hover>th,.table-hover .table-light:hover>td{background-color:#dadfe4}.table-hover .table-dark:hover,.table-hover .table-dark:hover>th,.table-hover .table-dark:hover>td{background-color:#1c1e22}.table-hover .table-success:hover,.table-hover .table-success:hover>th,.table-hover .table-success:hover>td{background-color:#4fbd4f}.table-hover .table-info:hover,.table-hover .table-info:hover>th,.table-hover .table-info:hover>td{background-color:#46b8da}.table-hover .table-danger:hover,.table-hover .table-danger:hover>th,.table-hover .table-danger:hover>td{background-color:#ec4844}.table-hover .table-warning:hover,.table-hover .table-warning:hover>th,.table-hover .table-warning:hover>td{background-color:#df8505}.table-hover .table-active:hover,.table-hover .table-active:hover>th,.table-hover .table-active:hover>td{background-color:rgba(255,255,255,0.075)}legend{color:#fff}.input-group-addon{background-image:-webkit-gradient(linear, left top, left bottom, from(#8a9196), color-stop(60%, #7A8288), to(#70787d));background-image:linear-gradient(#8a9196, #7A8288 60%, #70787d);background-repeat:no-repeat;-webkit-filter:none;filter:none;text-shadow:1px 1px 1px rgba(0,0,0,0.3);color:#fff}.nav-tabs .nav-link,.nav-tabs .nav-link:hover{color:#fff}.nav-pills .nav-link{background-image:-webkit-gradient(linear, left top, left bottom, from(#484e55), color-stop(60%, #3A3F44), to(#313539));background-image:linear-gradient(#484e55, #3A3F44 60%, #313539);background-repeat:no-repeat;-webkit-filter:none;filter:none;border:1px solid rgba(0,0,0,0.6);text-shadow:1px 1px 1px rgba(0,0,0,0.3);color:#fff}.nav-pills .nav-link:hover{background-image:-webkit-gradient(linear, left top, left bottom, from(#101112), color-stop(40%, #17191b), to(#1b1e20));background-image:linear-gradient(#101112, #17191b 40%, #1b1e20);background-repeat:no-repeat;-webkit-filter:none;filter:none;border:1px solid rgba(0,0,0,0.6)}.nav-pills .nav-link.active,.nav-pills .nav-link:hover{background-color:transparent;background-image:-webkit-gradient(linear, left top, left bottom, from(#101112), color-stop(40%, #17191b), to(#1b1e20));background-image:linear-gradient(#101112, #17191b 40%, #1b1e20);background-repeat:no-repeat;-webkit-filter:none;filter:none;border:1px solid rgba(0,0,0,0.6)}.nav-pills .nav-link.disabled,.nav-pills .nav-link.disabled:hover{background-image:-webkit-gradient(linear, left top, left bottom, from(#484e55), color-stop(60%, #3A3F44), to(#313539));background-image:linear-gradient(#484e55, #3A3F44 60%, #313539);background-repeat:no-repeat;-webkit-filter:none;filter:none;color:#7A8288}.pagination .page-link{text-shadow:1px 1px 1px rgba(0,0,0,0.3);background-image:-webkit-gradient(linear, left top, left bottom, from(#484e55), color-stop(60%, #3A3F44), to(#313539));background-image:linear-gradient(#484e55, #3A3F44 60%, #313539);background-repeat:no-repeat;-webkit-filter:none;filter:none}.pagination .page-link:hover{background-image:-webkit-gradient(linear, left top, left bottom, from(#101112), color-stop(40%, #17191b), to(#1b1e20));background-image:linear-gradient(#101112, #17191b 40%, #1b1e20);background-repeat:no-repeat;-webkit-filter:none;filter:none;text-decoration:none}.pagination .page-item.active .page-link{background-image:-webkit-gradient(linear, left top, left bottom, from(#101112), color-stop(40%, #17191b), to(#1b1e20));background-image:linear-gradient(#101112, #17191b 40%, #1b1e20);background-repeat:no-repeat;-webkit-filter:none;filter:none}.pagination .page-item.disabled .page-link{background-image:-webkit-gradient(linear, left top, left bottom, from(#484e55), color-stop(60%, #3A3F44), to(#313539));background-image:linear-gradient(#484e55, #3A3F44 60%, #313539);background-repeat:no-repeat;-webkit-filter:none;filter:none}.breadcrumb{border:1px solid rgba(0,0,0,0.6);text-shadow:1px 1px 1px rgba(0,0,0,0.3);background-color:transparent;background-image:-webkit-gradient(linear, left top, left bottom, from(#484e55), color-stop(60%, #3A3F44), to(#313539));background-image:linear-gradient(#484e55, #3A3F44 60%, #313539);background-repeat:no-repeat;-webkit-filter:none;filter:none}.breadcrumb a,.breadcrumb a:hover{color:#fff}.alert .close{color:#000;text-decoration:none}.alert{border:none;color:#fff}.alert a,.alert .alert-link{color:#fff;text-decoration:underline}.alert-primary{background-color:#3A3F44}.alert-secondary{background-color:#7A8288}.alert-success{background-color:#62c462}.alert-info{background-color:#5bc0de}.alert-warning{background-color:#f89406}.alert-danger{background-color:#ee5f5b}.alert-light{background-color:#e9ecef}.alert-dark{background-color:#272B30}.alert-light,.alert-light a:not(.btn),.alert-light .alert-link{color:#272B30}.badge-success,.badge-warning,.badge-info{color:#fff}.jumbotron{border:1px solid rgba(0,0,0,0.6)}.list-group-item:hover{background-color:#1c1e22} diff --git a/static/browserconfig.xml b/static/browserconfig.xml new file mode 100644 index 00000000..c5541482 --- /dev/null +++ b/static/browserconfig.xml @@ -0,0 +1,2 @@ + +#ffffff \ No newline at end of file diff --git a/static/favicon.ico b/static/favicon.ico new file mode 100644 index 00000000..b70c1b1c Binary files /dev/null and b/static/favicon.ico differ diff --git a/static/favicon/android-icon-144x144.png b/static/favicon/android-icon-144x144.png new file mode 100644 index 00000000..f609cbdd Binary files /dev/null and b/static/favicon/android-icon-144x144.png differ diff --git a/static/favicon/android-icon-192x192.png b/static/favicon/android-icon-192x192.png new file mode 100644 index 00000000..e517e661 Binary files /dev/null and b/static/favicon/android-icon-192x192.png differ diff --git a/static/favicon/android-icon-36x36.png b/static/favicon/android-icon-36x36.png new file mode 100644 index 00000000..9f4ed222 Binary files /dev/null and b/static/favicon/android-icon-36x36.png differ diff --git a/static/favicon/android-icon-48x48.png b/static/favicon/android-icon-48x48.png new file mode 100644 index 00000000..d81cf14a Binary files /dev/null and b/static/favicon/android-icon-48x48.png differ diff --git a/static/favicon/android-icon-72x72.png b/static/favicon/android-icon-72x72.png new file mode 100644 index 00000000..4edbbf1a Binary files /dev/null and b/static/favicon/android-icon-72x72.png differ diff --git a/static/favicon/android-icon-96x96.png b/static/favicon/android-icon-96x96.png new file mode 100644 index 00000000..73aaf88d Binary files /dev/null and b/static/favicon/android-icon-96x96.png differ diff --git a/static/favicon/apple-icon-114x114.png b/static/favicon/apple-icon-114x114.png new file mode 100644 index 00000000..c0d7c9fe Binary files /dev/null and b/static/favicon/apple-icon-114x114.png differ diff --git a/static/favicon/apple-icon-120x120.png b/static/favicon/apple-icon-120x120.png new file mode 100644 index 00000000..72e89312 Binary files /dev/null and b/static/favicon/apple-icon-120x120.png differ diff --git a/static/favicon/apple-icon-144x144.png b/static/favicon/apple-icon-144x144.png new file mode 100644 index 00000000..f609cbdd Binary files /dev/null and b/static/favicon/apple-icon-144x144.png differ diff --git a/static/favicon/apple-icon-152x152.png b/static/favicon/apple-icon-152x152.png new file mode 100644 index 00000000..d197b973 Binary files /dev/null and b/static/favicon/apple-icon-152x152.png differ diff --git a/static/favicon/apple-icon-180x180.png b/static/favicon/apple-icon-180x180.png new file mode 100644 index 00000000..88690ae6 Binary files /dev/null and b/static/favicon/apple-icon-180x180.png differ diff --git a/static/favicon/apple-icon-57x57.png b/static/favicon/apple-icon-57x57.png new file mode 100644 index 00000000..2916b5d2 Binary files /dev/null and b/static/favicon/apple-icon-57x57.png differ diff --git a/static/favicon/apple-icon-60x60.png b/static/favicon/apple-icon-60x60.png new file mode 100644 index 00000000..c91c7eb2 Binary files /dev/null and b/static/favicon/apple-icon-60x60.png differ diff --git a/static/favicon/apple-icon-72x72.png b/static/favicon/apple-icon-72x72.png new file mode 100644 index 00000000..4edbbf1a Binary files /dev/null and b/static/favicon/apple-icon-72x72.png differ diff --git a/static/favicon/apple-icon-76x76.png b/static/favicon/apple-icon-76x76.png new file mode 100644 index 00000000..89ca4115 Binary files /dev/null and b/static/favicon/apple-icon-76x76.png differ diff --git a/static/favicon/apple-icon-precomposed.png b/static/favicon/apple-icon-precomposed.png new file mode 100644 index 00000000..94de25e3 Binary files /dev/null and b/static/favicon/apple-icon-precomposed.png differ diff --git a/static/favicon/apple-icon.png b/static/favicon/apple-icon.png new file mode 100644 index 00000000..94de25e3 Binary files /dev/null and b/static/favicon/apple-icon.png differ diff --git a/static/favicon/browserconfig.xml b/static/favicon/browserconfig.xml new file mode 100644 index 00000000..c5541482 --- /dev/null +++ b/static/favicon/browserconfig.xml @@ -0,0 +1,2 @@ + +#ffffff \ No newline at end of file diff --git a/static/favicon/favicon-16x16.png b/static/favicon/favicon-16x16.png new file mode 100644 index 00000000..c0b5d01d Binary files /dev/null and b/static/favicon/favicon-16x16.png differ diff --git a/static/favicon/favicon-32x32.png b/static/favicon/favicon-32x32.png new file mode 100644 index 00000000..0af5b4b7 Binary files /dev/null and b/static/favicon/favicon-32x32.png differ diff --git a/static/favicon/favicon-96x96.png b/static/favicon/favicon-96x96.png new file mode 100644 index 00000000..73aaf88d Binary files /dev/null and b/static/favicon/favicon-96x96.png differ diff --git a/static/favicon/favicon.ico b/static/favicon/favicon.ico new file mode 100644 index 00000000..5d58a5b3 Binary files /dev/null and b/static/favicon/favicon.ico differ diff --git a/static/favicon/manifest.json b/static/favicon/manifest.json new file mode 100644 index 00000000..013d4a6a --- /dev/null +++ b/static/favicon/manifest.json @@ -0,0 +1,41 @@ +{ + "name": "App", + "icons": [ + { + "src": "\/android-icon-36x36.png", + "sizes": "36x36", + "type": "image\/png", + "density": "0.75" + }, + { + "src": "\/android-icon-48x48.png", + "sizes": "48x48", + "type": "image\/png", + "density": "1.0" + }, + { + "src": "\/android-icon-72x72.png", + "sizes": "72x72", + "type": "image\/png", + "density": "1.5" + }, + { + "src": "\/android-icon-96x96.png", + "sizes": "96x96", + "type": "image\/png", + "density": "2.0" + }, + { + "src": "\/android-icon-144x144.png", + "sizes": "144x144", + "type": "image\/png", + "density": "3.0" + }, + { + "src": "\/android-icon-192x192.png", + "sizes": "192x192", + "type": "image\/png", + "density": "4.0" + } + ] +} \ No newline at end of file diff --git a/static/favicon/ms-icon-144x144.png b/static/favicon/ms-icon-144x144.png new file mode 100644 index 00000000..f609cbdd Binary files /dev/null and b/static/favicon/ms-icon-144x144.png differ diff --git a/static/favicon/ms-icon-150x150.png b/static/favicon/ms-icon-150x150.png new file mode 100644 index 00000000..52bd73e5 Binary files /dev/null and b/static/favicon/ms-icon-150x150.png differ diff --git a/static/favicon/ms-icon-310x310.png b/static/favicon/ms-icon-310x310.png new file mode 100644 index 00000000..7e1c61cf Binary files /dev/null and b/static/favicon/ms-icon-310x310.png differ diff --git a/static/favicon/ms-icon-70x70.png b/static/favicon/ms-icon-70x70.png new file mode 100644 index 00000000..aca18cbb Binary files /dev/null and b/static/favicon/ms-icon-70x70.png differ diff --git a/static/head.html b/static/head.html index c503fe6b..207b3f17 100644 --- a/static/head.html +++ b/static/head.html @@ -1,9 +1,39 @@ Clusterio - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/index.html b/static/index.html index e962c86d..b755490e 100644 --- a/static/index.html +++ b/static/index.html @@ -1,23 +1,49 @@ - <%- include("head.html") %> - + <%- include("head.html") %> + - <%- include("sidebar.html") %> -
    -
    -

    Storage

    - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    +<%- include("nav.html") %> +
    +
    + +
    + +
    +
    + + + + + + + + + +
    IconItem IDQuantity
    +
    +
    +
    +
    +
    +
    +
    +
    + + + \ No newline at end of file diff --git a/static/main.js b/static/main.js index a1b805f1..5316a6a7 100644 --- a/static/main.js +++ b/static/main.js @@ -49,6 +49,24 @@ debugTools = { } } +let cachedInstanceNames = {}; +async function getInstanceName(instanceID){ + let instance = cachedInstanceNames[instanceID]; + if(!instance){ + let slaves = await getJSON("/api/slaves"); + try { + for(id in slaves){ + cachedInstanceNames[id] = slaves[id].instanceName; + } + } catch(e){ + console.log("Error while looking for instance name!"); + console.log(e); + } + return cachedInstanceNames[instanceID] || instanceID; + } else { + return cachedInstanceNames[instanceID]; + } +} var g = {} contents = { "iron-plate":100, @@ -63,44 +81,66 @@ function djb2(str){ } return hash; } + +// get queryString parameters +function getParameterByName(name, url) { + if (!url) url = window.location.href; + name = name.replace(/[\[\]]/g, "\\$&"); + var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"), + results = regex.exec(url); + if (!results) return null; + if (!results[2]) return ''; + return decodeURIComponent(results[2].replace(/\+/g, " ")); +} + // callback(err, json) function getJSON(url, callback) { - var xhr = new XMLHttpRequest(); - xhr.open('GET', url, true); - xhr.responseType = 'json'; - xhr.onload = function() { - var status = xhr.status; - if (status == 200) { - callback(null, xhr.response); - } else { - callback(status); - } - }; - // triggers if connection is refused - xhr.onerror = function(e){ - callback(e); - }; - xhr.send(); + return new Promise((resolve, reject) => { + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, true); + xhr.responseType = 'json'; + xhr.onload = function() { + var status = xhr.status; + if (status == 200) { + if(callback) callback(null, xhr.response); + resolve(xhr.response); + } else { + if(callback) callback(status); + reject(status); + } + }; + // triggers if connection is refused + xhr.onerror = function(e){ + if(callback) callback(e); + reject(e); + }; + xhr.send(); + }); }; // callback(err, json) function postJSON(url, data, callback) { - var xhr = new XMLHttpRequest(); - xhr.open('POST', url, true); - xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); - xhr.responseType = 'json'; - xhr.onload = function() { - var status = xhr.status; - if (status == 200) { - callback(null, xhr.response); - } else { - callback(status); - } - }; - // triggers if connection is refused - xhr.onerror = function(e){ - callback(e); - }; - xhr.send(JSON.stringify(data)); + return new Promise((resolve, reject) => { + var xhr = new XMLHttpRequest(); + xhr.open('POST', url, true); + xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); + xhr.responseType = 'json'; + xhr.onload = function() { + var status = xhr.status; + if (status == 200) { + if(callback) callback(null, xhr.response); + resolve(xhr.response); + } else { + if(callback) callback(status); + reject(status); + } + }; + // triggers if connection is refused + xhr.onerror = function(e){ + if(callback) callback(e); + reject(e); + }; + xhr.send(JSON.stringify(data)); + }); }; // return Boolean function isJSON(string){ diff --git a/static/manifest.json b/static/manifest.json new file mode 100644 index 00000000..013d4a6a --- /dev/null +++ b/static/manifest.json @@ -0,0 +1,41 @@ +{ + "name": "App", + "icons": [ + { + "src": "\/android-icon-36x36.png", + "sizes": "36x36", + "type": "image\/png", + "density": "0.75" + }, + { + "src": "\/android-icon-48x48.png", + "sizes": "48x48", + "type": "image\/png", + "density": "1.0" + }, + { + "src": "\/android-icon-72x72.png", + "sizes": "72x72", + "type": "image\/png", + "density": "1.5" + }, + { + "src": "\/android-icon-96x96.png", + "sizes": "96x96", + "type": "image\/png", + "density": "2.0" + }, + { + "src": "\/android-icon-144x144.png", + "sizes": "144x144", + "type": "image\/png", + "density": "3.0" + }, + { + "src": "\/android-icon-192x192.png", + "sizes": "192x192", + "type": "image\/png", + "density": "4.0" + } + ] +} \ No newline at end of file diff --git a/static/nav.html b/static/nav.html new file mode 100644 index 00000000..44f90a62 --- /dev/null +++ b/static/nav.html @@ -0,0 +1,37 @@ + \ No newline at end of file diff --git a/static/nodeDetails.css b/static/nodeDetails.css index 47d7b191..347274ce 100644 --- a/static/nodeDetails.css +++ b/static/nodeDetails.css @@ -1,58 +1,3 @@ -#header .subbar h6 { - display:inline; - font-weight:normal; - color: #BBB -} -#header { - background-color: #222; - color: #DDD; - font-family: 'Roboto', sans-serif; - padding-left:16px; -} -#displayBody { - margin-left: 16px; -} -#hero { - background-color: #444; - height:400px; -} -#hero .subtitle { - margin-left:8px; -} - -#header h1 { - margin-left: 10px; - display:inline-block; -} -#header .subbar h6 { - margin-right: 16px; -} -#onlineIndicator { - background-color:red; - height:20px; - width:20px; - display:inline-block; - border-radius:16px; -} -.productionGraph { - background-color:black; -} -#leftHeroContainer { - float:left; -} -ul { - width:300px; - list-style:none; - max-height:400px; - min-height:100px; - margin:0; - overflow:auto; - padding:0; - text-indent:10px; -} -li{ - line-height:25px; -} -li:nth-child(even){ - background:#ccc; +#node-stats .breadcrumb-item { + height: auto; } \ No newline at end of file diff --git a/static/nodeDetails.html b/static/nodeDetails.html index e379e44e..4851b9e3 100644 --- a/static/nodeDetails.html +++ b/static/nodeDetails.html @@ -7,10 +7,9 @@ - <%- include("sidebar.html") %> -
    + <%- include("nav.html") %> +
    -

    Slave details

    diff --git a/static/nodeDetails.js b/static/nodeDetails.js index 0346e1e6..e95585c6 100644 --- a/static/nodeDetails.js +++ b/static/nodeDetails.js @@ -1,15 +1,15 @@ // string, object, function(object) function post(url, data, callback) { - console.log("POST " + url + JSON.stringify(data)) + console.log("POST " + url + JSON.stringify(data)); var xhr = new XMLHttpRequest(); xhr.open("POST", url, true); xhr.setRequestHeader("Content-type", "application/json"); xhr.onreadystatechange = function () { - if (xhr.readyState == 4 && xhr.status == 200) { + if (xhr.readyState === 4 && xhr.status === 200) { var json = JSON.parse(xhr.responseText); callback(json); } - } + }; xhr.send(JSON.stringify(data)); } // function to sort arrays of objects after a keys value @@ -38,22 +38,46 @@ var chartsByID = {}; // update the online indicator dot setInterval(function(){ getJSON("/api/slaves", function(err, slaveData){ - let indicator = document.querySelector("#onlineIndicator"); + let indicator = document.querySelector("#online-indicator"); + $(indicator).removeClass('fa-signal'); + $(indicator).removeClass('fa-dizzy'); + $(indicator).removeClass('fa-plug'); if(err){ // our request threw an error, most likely master is unavailable so we show yellow indicator - indicator.style.backgroundColor = "yellow"; + indicator.style.color = "yellow"; + $(indicator).addClass('fa-plug'); indicator.title = "Master server unavailable"; // compare with Math.floor(x/100000) allows us to check if they are within 10s of each other - } else if(Math.floor(slaveData[getParameterByName("instanceID")].time/100000) == Math.floor(Date.now()/100000)){ - indicator.style.backgroundColor = "green"; + } else if(Math.floor(slaveData[getParameterByName("instanceID")].time/100000) === Math.floor(Date.now()/100000)){ + indicator.style.color = "green"; + $(indicator).addClass('fa-signal'); indicator.title = "Slave is online"; } else { - indicator.style.backgroundColor = "red"; + indicator.style.color = "red"; + $(indicator).addClass('fa-dizzy'); indicator.title = "Slave is offline"; } }); }, 1000); +function regexModDetails(modFileName) { + let m; + let results = []; + const regex = /(.+)_(\d+\.\d+\.\d+)\.zip/gm; + while ((m = regex.exec(modFileName)) !== null) { + // This is necessary to avoid infinite loops with zero-width matches + if (m.index === regex.lastIndex) { + regex.lastIndex++; + } + + // The result can be accessed through the `m`-variable. + m.forEach((match, _) => { + results.push(match); + }); + } + return results +} + populateSlaveInfo(); function populateSlaveInfo(){ let instanceID = getParameterByName("instanceID"); @@ -86,23 +110,31 @@ function populateSlaveInfo(){ } */ let slave = slaveData[instanceID]; - let HTML = "