├── .gitignore ├── LICENSE ├── README.md ├── downloader.js ├── epic_api.js ├── index.js ├── manifests.js ├── package-lock.json ├── package.json └── ue4-mp-downloader.js /.gitignore: -------------------------------------------------------------------------------- 1 | download/ 2 | dump/ 3 | node_modules/ 4 | .vscode/ 5 | evil.js 6 | .env -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Michael Allar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ue4-mp-downloader 2 | Shell utility to download owned assets from the UE4 marketplace. 3 | 4 | ![Example](https://i.gyazo.com/e0db9e073c6be2907bb1275489675d39.gif) 5 | 6 | # Support 7 | 8 | I offer ZERO support. If you have a problem with this, please post an issue but I don't guarantee I'll resolve it. I wrote this tool for myself since I need to grab a bunch of marketplace assets on a Linux machine and was too lazy to copy paste files around. 9 | 10 | # Legal 11 | 12 | For this tool to work, you must have already accepted Epic's Terms (on account registration) and relevant EULAs (prompted when you open the Launcher for the first time or buy a marketplace item). 13 | 14 | This tool can only download assets you own. 15 | 16 | I have inquired Epic in the past about the legality of custom marketplace tools when developing other tools I have made. Epic Games seems to not have a problem with this. I mean no foul or infringement, and I will take this repo down immediately at the request of Epic Games if they do so. 17 | 18 | # Disclaimer 19 | 20 | Everything here is offered as-is. If bad things happen, including but not limited to burning down your house or gives your mom a rash, I am not responsible. 21 | 22 | # Installation 23 | 24 | 1. Install NodeJS if you don't already have it installed: https://nodejs.org/en/download/package-manager/ 25 | 1. `npm install -g Allar/ue4-mp-downloader` 26 | 27 | # Usage 28 | 29 | Run `ue4-mp-downloader` 30 | 31 | You will be prompted to log in. This tool does not save or record your credentials for your safety, so you will have to log in every time you use it. Once logged in, any assets downloaded will be downloaded to your current working directory in a folder called `download`. 32 | 33 | # Testing 34 | 35 | Tested and confirmed working on Windows 8 and 10 running both NodeJS v6 and v8. Tested and confirmed working on Ubuntu 16 with Node v8. 36 | 37 | # Known Issues 38 | 39 | If your machine doesn't have as much free ram as the asset you are downloading, you will get weird errors doing the download and extract process. My algorithim has no need to keep it all in memory, yet it still does. If anyone knows why my javascript download and extract process isn't freeing up memory after every asset file extract, please let me know, or even better, submit a fix! 40 | 41 | Code Plugins currently fail to download due to a 403 (Forbidden) error. 42 | -------------------------------------------------------------------------------- /downloader.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | // UE4-MP-DOWNLOADER 3 | // 4 | // This is a very hastly written commandline app so I can fetch marketplace items on Linux 5 | // Warning: Here be dragons 6 | 7 | 8 | // Normally don't need to do this global bullshit but 'epic_api.js' is used in another JS app that requires it 9 | // So instead of maintaining two copies of this api, we'll just re-use it like this 10 | // @TODO: Learn how to do all this the right way 11 | global.request = (global.request === undefined) ? require('request') : global.request; 12 | global.request = request.defaults({followRedirect: false, followAllRedirects: false}); 13 | global.epic_api = (global.epic_api === undefined) ? require('./epic_api.js') : global.epic_api; 14 | 15 | const prompt = require('prompt'); 16 | const cheerio = require('cheerio'); 17 | const menu = require('console-menu'); 18 | const dotenv = require('dotenv').config(); 19 | 20 | // Takes an HTML form from cheerio.serializeArray() and converts it to an object suitable for the 'request' module 21 | function SerializeLoginFormArray(form) { 22 | var result = {}; 23 | form.forEach((element) => { 24 | result[element.name] = element.value; 25 | }); 26 | return result; 27 | } 28 | 29 | // Ask for username/password from the user 30 | var promptSchema = { 31 | properties: { 32 | username: { 33 | required: true, 34 | type: 'string', 35 | default: process.env.UE4_ACCOUNT 36 | }, 37 | password: { 38 | required: true, 39 | type:'string', 40 | hidden: true, 41 | replace: '*', 42 | default: process.env.UE4_PASSWORD 43 | } 44 | } 45 | }; 46 | 47 | // Error handling is for smart people 48 | // We are not smart today 49 | 50 | function TryLogin() { 51 | // If Epic's login page is down for some reason, we should probably handle it somehow 52 | epic_api.GetWebLoginForm( (body) => { 53 | prompt.start(); 54 | prompt.get(promptSchema, (err, result) => { 55 | if (result == undefined || result.username == undefined) { 56 | process.exit(0); // Control+C 57 | } 58 | const $ = cheerio.load(body); 59 | var loginData = SerializeLoginFormArray($('form#loginForm').serializeArray()); 60 | loginData.epic_username = result.username; 61 | loginData.password = result.password; 62 | epic_api.WebLogin(loginData, OnLogin); 63 | }); 64 | }); 65 | } 66 | 67 | 68 | // Return error codes for WebLogin are retarded and should be hardcoded to sane values 69 | // I was probably drunk when writing epic_api.js 70 | function OnLogin(status, complete) { 71 | if (status === 'Failed') { 72 | console.log("Failed to log in."); 73 | TryLogin(); 74 | return; 75 | } 76 | 77 | console.log(status); 78 | 79 | // If for some reason the login chain fails but doesn't complete, theres no error handling 80 | // The log above *should* log the login chain failure and execution *should* just stop. 81 | // Theres a lot of assumptions being made because my test sample count is 1. 82 | if (complete == true) { 83 | epic_api.GetOwnedAssets( (success) => { 84 | var items = []; 85 | Object.keys(global.marketplace_ownedAssets_consolidated).forEach( (key) => { 86 | if (global.marketplace_ownedAssets_consolidated[key].assetId == "UE") // Skip the editor itself 87 | return; 88 | 89 | var isAsset = global.marketplace_ownedAssets_consolidated[key].categories.find ( (cat) => { 90 | return (cat.path == "assets" || cat.path == "projects" || cat.path == "plugins") 91 | }); 92 | if (isAsset) { 93 | items.push(global.marketplace_ownedAssets_consolidated[key]); 94 | } 95 | }); 96 | 97 | // Sort items alphabetically 98 | items.sort( (a, b) => { 99 | if (a.title < b.title) return -1; 100 | if (a.title > b.title) return 1; 101 | return 0; 102 | }); 103 | 104 | (function MenuLoop() { 105 | ShowDownloadAssetsMenu(items, () => { 106 | MenuLoop(); 107 | }); 108 | })(); 109 | }); 110 | }; 111 | } 112 | 113 | // The real meat of the program. Once items are fetched, this will handle any downloads 114 | function ShowDownloadAssetsMenu(items, cb) { 115 | console.log('\x1Bc'); // clear screen 116 | 117 | var helpMessage = "Scroll using Up/Down, arrow keys, or Page Up / Page Down. Press CTRL+C to quit."; 118 | 119 | menu(items, { header: 'Select a Marketplace Asset to Download', pageSize: 10, border: true, helpMessage: helpMessage}) 120 | .then( (item) => { 121 | if (item == undefined) { 122 | process.exit(0); // Control+C 123 | return; 124 | } 125 | 126 | var versions = global.epic_api.GetEngineVersionsForItem(item); 127 | menu(versions, { header: item.title + ' - Choose Engine Version', border: true, helpMessage: helpMessage}) 128 | .then( (version) => { 129 | if (version == undefined) { 130 | process.exit(0); // Control+C 131 | return; 132 | } 133 | global.epic_api.GetItemBuildInfo(item.id, version.appId, (error, buildinfo) => { 134 | if (error !== null) { 135 | console.error('Failed to get item build info. ' + error); 136 | return; 137 | } 138 | 139 | global.epic_api.GetItemManifest(buildinfo, (error, manifest) => { 140 | if (error !== null) { 141 | console.error('Failed to get item manifest. ' + error); 142 | return; 143 | } 144 | var chunkList = global.epic_api.BuildItemChunkListFromManifest(buildinfo, manifest); 145 | global.epic_api.DownloadItemChunkList(manifest, chunkList, "./download/", (finishedDownloading, chunkDir) => { 146 | if (finishedDownloading) { 147 | global.epic_api.ExtractAssetFilesFromChunks(manifest, chunkDir, "./download/", (finishedExtracting) => { 148 | if (finishedExtracting) { 149 | console.log(item.title + ' build ' + version.appId + ' successfully extracted. Going back to download menu...'); 150 | if (cb != undefined) { 151 | setTimeout(cb, 5000); 152 | } 153 | } 154 | }) 155 | } 156 | }); 157 | }); 158 | }); 159 | }); 160 | }); 161 | } 162 | 163 | TryLogin(); 164 | -------------------------------------------------------------------------------- /epic_api.js: -------------------------------------------------------------------------------- 1 | // API for interfacing with Epic's 'deep' API 2 | var epic_api = function () {}; 3 | 4 | const rimraf = require('rimraf'); 5 | const mkdirp = require('mkdirp'); 6 | const ProgressBar = require('progress'); 7 | const zlib = require('zlib'); 8 | const fs = require('fs'); 9 | var url = require('url') 10 | 11 | var slowRequestPool = {maxSockets: 2}; 12 | var downloadPool = { maxSockets: 10 }; 13 | 14 | global.marketplace = {}; 15 | global.marketplace_ajax = {}; 16 | global.marketplace_ownedAssets = {}; 17 | global.marketplace_ownedAssets_consolidated = {}; 18 | 19 | global.epic_oauth = (global.epic_oauth === undefined) ? undefined : global.epic_oauth; 20 | 21 | global.fakeJar = (global.fakeJar === undefined) ? {} : global.fakeJar; 22 | global.epic_Cookie = (global.epic_Cookie === undefined) ? undefined : global.epic_Cookie; 23 | global.epic_Country = (global.epic_Country === undefined) ? undefined : global.epic_Country; 24 | global.epic_SSO = (global.epic_SSO === undefined) ? undefined : global.epic_SSO; 25 | global.epic_SSO_RM = (global.epic_SSO_RM === undefined) ? undefined : global.epic_SSO_RM; 26 | 27 | // Grabs Epic's web login form 28 | // Callback has string parameter containing login form html, i.e. function (form) () 29 | epic_api.prototype.GetWebLoginForm = function (cb_form) { 30 | var opts = { 31 | uri: 'https://accounts.unrealengine.com/login/doLogin', 32 | }; 33 | 34 | request.get(opts, function (error, response, body) { 35 | global.epic_api.updateFakeJar(response.headers['set-cookie']); 36 | if (cb_form != undefined) { 37 | cb_form(body); 38 | } 39 | }); 40 | } 41 | 42 | epic_api.prototype.updateFakeJar = function(set_cookie_array) { 43 | for (var i = 0; i < set_cookie_array.length; ++i) { 44 | var cookie_pair = set_cookie_array[i].split(';',1)[0].split('='); 45 | global.fakeJar[cookie_pair[0]] = cookie_pair[1]; 46 | if(cookie_pair[1] == 'invalid') { 47 | delete global.fakeJar[cookie_pair[0]]; 48 | } 49 | } 50 | } 51 | 52 | epic_api.prototype.GetWebCookieString = function () { 53 | var cookieString = ""; 54 | for(var key in global.fakeJar) { 55 | cookieString += key + '=' + global.fakeJar[key] + '; '; 56 | } 57 | return cookieString; 58 | } 59 | 60 | epic_api.prototype.WebLogin = function(loginObject, cb_status) { 61 | var opts = { 62 | uri: 'https://accounts.unrealengine.com/login/doLogin', 63 | form: loginObject, 64 | headers: { Cookie: global.epic_api.GetWebCookieString(), Origin: 'allar_ue4_marketplace_commandline' }, 65 | qs: { client_id: '43e2dea89b054198a703f6199bee6d5b' } 66 | }; 67 | 68 | 69 | request.post(opts, function(error, response, body) { 70 | if (response.statusCode == 400) // login failure 71 | { 72 | cb_status('Failed', false); 73 | } else if (response.statusCode == 302) // success 74 | { 75 | global.epic_api.updateFakeJar(response.headers['set-cookie']); 76 | if (cb_status != undefined) { 77 | cb_status('Authorizing Web Login...', false); 78 | } 79 | global.epic_api.WebAuthorize(loginObject.epic_username, loginObject.password, cb_status); 80 | } 81 | else { 82 | cb_status(`Failed with status code: ${response.statusCode}`, false); 83 | } 84 | 85 | }); 86 | } 87 | 88 | epic_api.prototype.WebAuthorize = function (user, pass, cb_status) { 89 | var opts = { 90 | uri: 'https://accounts.unrealengine.com/authorize/index', 91 | headers: { Cookie: global.epic_api.GetWebCookieString(), Origin: 'allar_ue4_marketplace_commandline' }, 92 | qs: { client_id: '43e2dea89b054198a703f6199bee6d5b', response_type: 'code', forWidget: 'true' } 93 | }; 94 | 95 | request.get(opts, function(error, response, body) { 96 | global.epic_api.updateFakeJar(response.headers['set-cookie']); 97 | 98 | if (response.statusCode == 200) { 99 | var json = JSON.parse(body); 100 | var code = json.redirectURL.split('?code=')[1]; 101 | if (cb_status != undefined) { 102 | cb_status('Successfully Web Authorized! Performing Web Exchange...', false); 103 | } 104 | global.epic_api.WebExchange(user, pass, code, cb_status); 105 | } else { 106 | if (cb_status != undefined) { 107 | cb_status('Web Auth failed: ' + JSON.stringify(response, null, ' '), false); 108 | } 109 | } 110 | }); 111 | } 112 | 113 | epic_api.prototype.WebExchange = function (user, pass, code, cb_status) { 114 | var opts = { 115 | uri: 'https://www.unrealengine.com/exchange', 116 | headers: { Cookie: global.epic_api.GetWebCookieString() }, 117 | qs: { code: code } 118 | }; 119 | 120 | request.get(opts, function(error, response, body) { 121 | global.epic_api.updateFakeJar(response.headers['set-cookie']); 122 | 123 | if (response.statusCode == 302) { 124 | if (cb_status != undefined) { 125 | cb_status('Intentionally failed Web Exchage! Performing OAuth...', false); 126 | } 127 | global.epic_api.OAuthViaPassword(user, pass, cb_status); 128 | } else { 129 | if (cb_status != undefined) { 130 | cb_status('Web Exchange failed: ' + JSON.stringify(response, null, ' '), false); 131 | } 132 | } 133 | }); 134 | } 135 | 136 | // Go through Epic's OAuth chain using a username and password 137 | // cb_status is a callback with string parameter of current OAuth status and bool of whether complete. (status, bComplete) 138 | epic_api.prototype.OAuthViaPassword = function (user, pass, cb_status) { 139 | var opts = { 140 | uri: 'https://account-public-service-prod03.ol.epicgames.com/account/api/oauth/token', 141 | headers: { Authorization: 'basic MzRhMDJjZjhmNDQxNGUyOWIxNTkyMTg3NmRhMzZmOWE6ZGFhZmJjY2M3Mzc3NDUwMzlkZmZlNTNkOTRmYzc2Y2Y=', Origin: 'allar_ue4_marketplace_commandline' }, 142 | form: { grant_type: 'password', username: user, password: pass, includePerms: true } 143 | }; 144 | 145 | request.post(opts, function(error, response, body) { 146 | if (response.statusCode == 200) { 147 | global.epic_oauth = JSON.parse(body); 148 | if (cb_status != undefined) { 149 | cb_status('Got OAuth token, exchanging for code', false); 150 | } 151 | module.exports.OAuthExchange(cb_status); 152 | } else { 153 | if (cb_status != undefined) { 154 | cb_status('OAuth Via Password failed: ' + JSON.stringify(response, null, ' '), false); 155 | } 156 | } 157 | }); 158 | } 159 | 160 | // cb_status is a callback with string parameter of current OAuth status and bool of whether complete. (status, bComplete) 161 | epic_api.prototype.OAuthExchange = function(cb_status) { 162 | var opts = { 163 | uri: 'https://account-public-service-prod03.ol.epicgames.com/account/api/oauth/exchange', 164 | headers: { Authorization: 'bearer ' + global.epic_oauth.access_token, Origin: 'allar_ue4_marketplace_commandline' } 165 | }; 166 | 167 | request.get(opts, function(error, response, body) { 168 | if (response.statusCode == 200) { 169 | var json = JSON.parse(body); 170 | global.epic_oauth.code = json.code; 171 | if (cb_status != undefined) { 172 | if (global.epic_SSO === undefined) 173 | { 174 | cb_status('Got OAuth exchange code. Getting SSO.', false); 175 | } 176 | else 177 | { 178 | cb_status('Got OAuth exchange code. Skipping SSO.', true); 179 | } 180 | } 181 | // Grab our SSO token 182 | if (global.epic_SSO === undefined) { 183 | global.epic_api.GetSSOWithOAuthCode(cb_status); 184 | } 185 | // renew our token before it expires 186 | global.setTimeout(module.exports.OAuthExchange, 250 * 1000); 187 | } else { 188 | if (cb_status != undefined) { 189 | cb_status('OAuth renew failed: ' + JSON.stringify(response, null, ' '), false); 190 | } 191 | } 192 | }); 193 | } 194 | 195 | // cb_status is a callback with string parameter of current OAuth status and bool of whether complete. (status, bComplete) 196 | epic_api.prototype.GetSSOWithOAuthCode = function(cb_status) { 197 | var opts = { 198 | uri: 'https://accountportal-website-prod07.ol.epicgames.com/exchange?', 199 | headers: { Authorization: 'bearer ' + global.epic_oauth.access_token, Origin: 'allar_ue4_marketplace_commandline' }, 200 | qs: { exchangeCode: global.epic_oauth.code, state: '/getSsoStatus' } 201 | }; 202 | 203 | request.get(opts, function(error, response, body) { 204 | //module.exports.updateFakeJar(response.headers['set-cookie']); 205 | 206 | if (response.statusCode == 302) { 207 | if (cb_status != undefined) { 208 | cb_status('Successfully Authorized!', true); 209 | } 210 | } else { 211 | if (cb_status != undefined) { 212 | cb_status('Failed', false); 213 | } 214 | } 215 | }); 216 | } 217 | 218 | // Gets 'user-friendly' marketplace categories 219 | // namespace 'ue': Marketplace items 220 | // callback expects a bool indicating whether we fetched data, i.e. (success) 221 | epic_api.prototype.GetMarketplaceCategories = function (cb) { 222 | var opts = { 223 | uri: 'https://www.unrealengine.com/assets/ajax-get-categories', 224 | form: {category: 'assets/environments', start: 0}, 225 | headers: { Origin: 'allar_ue4_marketplace_commandline'} 226 | }; 227 | 228 | request.post(opts, function(error, response, body) { 229 | if (response.statusCode == 200) { 230 | global.marketplace_categories = JSON.parse(body).categories; 231 | if (cb != undefined) { 232 | cb(true); 233 | } 234 | } else { 235 | if (cb != undefined) { 236 | cb(false); 237 | } 238 | } 239 | }); 240 | } 241 | 242 | // Used for Catalog data path, currently irrelevant as Catalog data doesn't offer ratings 243 | // Get Category index by path 244 | epic_api.prototype.GetCategoryIndex = function (category_path) { 245 | // Due to outdated Epic data, we have to fix up some paths 246 | switch (category_path) { 247 | case 'assets/fx': 248 | case 'assets/textures': 249 | category_path = 'assets/textures-fx'; 250 | break; 251 | case 'assets/weapons': 252 | case 'assets/props': 253 | category_path = 'assets/weapons-props'; 254 | break; 255 | case 'assets/soundfx': 256 | case 'assets/music': 257 | category_path = 'assets/music-soundfx'; 258 | break; 259 | case 'assets/animations': 260 | case 'assets/characters': 261 | category_path = 'assets/characters-animations'; 262 | break; 263 | case 'assets': 264 | return -1; 265 | } 266 | for (var i = 0; i < global.marketplace_categories.length; ++i) { 267 | if (global.marketplace_categories[i].path == category_path) { 268 | return i; 269 | } 270 | } 271 | console.warn("Couldn't find category index for " + category_path); 272 | return -1; 273 | } 274 | 275 | // UNUSED: Epic's catalog API doesn't give us ratings information 276 | // Gets Catalog offers for the provided namespace 277 | // namespace 'ue': Marketplace items 278 | // callback expects a bool indicating whether we fetched data, i.e. (success) 279 | epic_api.prototype.CatalogItems = function (namespace, cb) { 280 | if (global.epic_oauth === undefined) { 281 | if (cb != undefined) { 282 | cb(false); 283 | } 284 | return; 285 | } 286 | var opts = { 287 | uri: 'https://catalog-public-service-prod06.ol.epicgames.com/catalog/api/shared/namespace/' + namespace + '/offers', 288 | headers: { Authorization: 'bearer ' + global.epic_oauth.access_token, Origin: 'allar_ue4_marketplace_commandline' }, 289 | qs: { status: 'SUNSET|ACTIVE', country: 'US', locale: 'en', start: 0, count: 1000, returnItemDetails: true } 290 | }; 291 | 292 | request.get(opts, function(error, response, body) { 293 | if (response.statusCode == 200) { 294 | global.marketplace = JSON.parse(body); 295 | if (cb != undefined) { 296 | cb(true); 297 | } 298 | } else { 299 | if (cb != undefined) { 300 | cb(false); 301 | } 302 | } 303 | }); 304 | } 305 | 306 | // Due to Epic's catalog API not offering ratings, we might as well use the 307 | // web API to grab everything as that does have all the data we'll ever need 308 | // Also lists all available categories 309 | // Takes function 'cb' with signature (json, path, finished) 310 | // json: The json object Epic returned for that single fetch 311 | // path: The category path that was fetched 312 | // finished: bool whether that category is finished fetching 313 | epic_api.prototype.getAssetsInCategory = function(category, start, addToTable, cb) { 314 | var opts = { 315 | uri: 'https://www.unrealengine.com/assets/ajax-get-categories', 316 | form: {category: category, start: start}, 317 | headers: { Cookie: global.epic_api.GetWebCookieString(), Origin: 'allar_ue4_marketplace_commandline' }, 318 | }; 319 | request.post(opts, function(error, response, body) { 320 | 321 | if (response.statusCode == 200) { 322 | var json = JSON.parse(body); 323 | var finished = false; 324 | if (addToTable == true) { 325 | 326 | // Add category definition if it doesn't exist (it should though) 327 | if (global.marketplace_ajax[json.categoryPath] === undefined) { 328 | global.marketplace_ajax[json.categoryPath] = { name: json.category.name }; 329 | } 330 | 331 | // Add first set of assets to this category definition 332 | if (global.marketplace_ajax[json.categoryPath].assets === undefined) { 333 | global.marketplace_ajax[json.categoryPath].assets = json.assets; 334 | } else { // Add assets to category definition 335 | json.assets.forEach(function(v) {global.marketplace_ajax[json.categoryPath].assets.push(v);}); 336 | } 337 | 338 | // If this is the first grab for assets of this category, kick off grabbing the rest 339 | if (start == 0) { 340 | global.marketplace_ajax[json.categoryPath].assetCount = json.assetCount; 341 | for (var i = 0; i < Math.floor((json.assetCount-1) / json.assetPerPage); ++i) { 342 | module.exports.getAssetsInCategory(json.categoryPath, (i+1)*json.assetPerPage, true, function (nextjson, nextpath, nextfinished) { 343 | cb(nextjson, nextpath, nextfinished); 344 | }); 345 | } 346 | } 347 | 348 | if (global.marketplace_ajax[json.categoryPath].assets.length == global.marketplace_ajax[json.categoryPath].assetCount) { 349 | console.log("Done getting assets for category: " + json.categoryPath); 350 | finished = true; 351 | } 352 | } 353 | cb(json, json.categoryPath, finished); 354 | } else { 355 | console.error(response); 356 | } 357 | }); 358 | } 359 | 360 | epic_api.prototype.getAllMarketplaceAssets = function(cb_done) { 361 | global.fetching = true; 362 | 363 | var categoriesLeft = global.marketplace_categories.length; 364 | 365 | // Build Category List 366 | for (var i = 0; i < global.marketplace_categories.length; ++i) { 367 | global.marketplace_ajax[global.marketplace_categories[i].path] = { name: global.marketplace_categories[i].name }; 368 | module.exports.getAssetsInCategory(global.marketplace_categories[i].path, 0, true, function (json, path, finished) { 369 | if(finished) { 370 | categoriesLeft--; 371 | if (categoriesLeft == 0) { 372 | global.fetching = false; 373 | if (cb_done != undefined) { 374 | cb_done(); 375 | } 376 | } 377 | } 378 | }); 379 | } 380 | } 381 | 382 | epic_api.prototype.GetOwnedAssets = function (cb) { 383 | if (global.epic_oauth === undefined) { 384 | if (cb != undefined) { 385 | cb(false); 386 | } 387 | return; 388 | } 389 | var opts = { 390 | // From launcher: https://launcher-public-service-prod06.ol.epicgames.com/launcher/api/public/assets/Windows?label=Live 391 | uri: 'https://launcher-public-service-prod06.ol.epicgames.com/launcher/api/public/assets/Windows', 392 | headers: { Authorization: 'bearer ' + global.epic_oauth.access_token, 'User-Agent': 'game=UELauncher, engine=UE4, build=allar_ue4_marketplace_commandline' }, 393 | qs: { label: 'Live' } 394 | }; 395 | 396 | console.log('Checking for EULA and fetching owned assets...'); 397 | 398 | request.get(opts, function(error, response, body) { 399 | if (response.statusCode == 200) { 400 | if (body == "[ ]") { 401 | console.error("Failed to fetch owned assets. This might be because you need to accept the Epic Launcher EULA for your account, or you own zero assets."); 402 | process.exit(0); 403 | return; 404 | } 405 | global.marketplace_ownedAssets = JSON.parse(body); 406 | 407 | var itemsPending = global.marketplace_ownedAssets.length; 408 | var itemsTotal = itemsPending; 409 | var itemsToIgnore = 0; 410 | 411 | var bar = new ProgressBar('Fetching Info: :bar :percent Completed. (ETA: :eta seconds)', {total: itemsPending}); 412 | 413 | global.marketplace_ownedAssets.forEach( (arrayItem) => { 414 | if (!global.marketplace_ownedAssets_consolidated.hasOwnProperty(arrayItem.catalogItemId)) { 415 | global.marketplace_ownedAssets_consolidated[arrayItem.catalogItemId] = arrayItem; 416 | global.epic_api.GetConsolidatedAssetInfo(arrayItem.catalogItemId, (success) => { 417 | itemsPending--; 418 | bar.update((itemsTotal - itemsToIgnore - itemsPending) / (itemsTotal - itemsToIgnore)); 419 | if (itemsPending == 0) { 420 | if (cb != undefined) { 421 | console.log(`Fetched all (${Object.keys(global.marketplace_ownedAssets_consolidated).length}) assets.`); 422 | cb(true); 423 | } 424 | } 425 | }); 426 | } else { 427 | itemsToIgnore++ 428 | itemsPending--; 429 | bar.update((itemsTotal - itemsToIgnore - itemsPending) / (itemsTotal - itemsToIgnore)); 430 | } 431 | }); 432 | } else { 433 | console.log('Failed to fetch assets.'); 434 | if (cb != undefined) { 435 | cb(false); 436 | } 437 | } 438 | }); 439 | } 440 | 441 | epic_api.prototype.GetConsolidatedAssetInfo = function (catalogItemId, cb) { 442 | if (global.epic_oauth === undefined) { 443 | if (cb != undefined) { 444 | cb(false); 445 | } 446 | return; 447 | } 448 | var opts = { 449 | // From launcher: https://catalog-public-service-prod06.ol.epicgames.com/catalog/api/shared/bulk/items?id=5e0f8343b8cd44a0817214ab0d39847f&country=US&locale=en-US 450 | uri: 'https://catalog-public-service-prod06.ol.epicgames.com/catalog/api/shared/bulk/items', 451 | headers: { Authorization: 'bearer ' + global.epic_oauth.access_token, Origin: 'allar_ue4_marketplace_commandline', 'User-Agent': 'game=UELauncher, engine=UE4, build=allar_ue4_marketplace_commandline' }, 452 | qs: { id: catalogItemId, country: 'US', locale: 'en-US' }, 453 | pool: slowRequestPool 454 | }; 455 | 456 | request.get(opts, function(error, response, body) { 457 | if (error !== null) { 458 | console.log('Error getting consolidated info.'); 459 | if (cb != undefined) { 460 | cb(false); 461 | } 462 | return; 463 | } 464 | if (response.statusCode == 200) { 465 | var itemInfo = JSON.parse(body); 466 | global.marketplace_ownedAssets_consolidated[Object.keys(itemInfo)[0]] = itemInfo[Object.keys(itemInfo)[0]]; 467 | if (cb != undefined) { 468 | cb(true); 469 | } 470 | } else { 471 | if (cb != undefined) { 472 | cb(false); 473 | } 474 | } 475 | }); 476 | } 477 | 478 | epic_api.prototype.GetEngineVersionsForItem = function (itemInfo) { 479 | var versions = []; 480 | itemInfo.releaseInfo.forEach( (releaseInfo) => { 481 | if (!releaseInfo.hasOwnProperty("compatibleApps")) { 482 | return versions; 483 | } 484 | releaseInfo.compatibleApps.forEach( (compatibleApp) => { 485 | var minorVersion = Number(compatibleApp.replace("UE_4.", "")); 486 | versions.push({title: `4.${minorVersion}`, appId: releaseInfo.appId, version: compatibleApp, minorVersion: minorVersion }); 487 | }); 488 | }); 489 | // Sorts latest version first 490 | versions.sort( (a, b) => { 491 | if (a.minorVersion > b.minorVersion) return -1; 492 | if (a.minorVersion < b.minorVersion) return 1; 493 | return 0; 494 | }); 495 | return versions; 496 | } 497 | 498 | // Gets an item's build info. Callback is of form (error, buildinfo) 499 | epic_api.prototype.GetItemBuildInfo = function (catalogItemId, appId, cb) { 500 | if (global.epic_oauth === undefined) { 501 | if (cb != undefined) { 502 | cb("Not authed.", null); 503 | } 504 | return; 505 | } 506 | var opts = { 507 | // From launcher: https://launcher-public-service-prod06.ol.epicgames.com/launcher/api/public/assets/Windows/cd2c274e32764e4b9bba09115e732fde/MagicEffects411?label=Live 508 | uri: `https://launcher-public-service-prod06.ol.epicgames.com/launcher/api/public/assets/Windows/${catalogItemId}/${appId}`, 509 | headers: { Authorization: 'bearer ' + global.epic_oauth.access_token, Origin: 'allar_ue4_marketplace_commandline', 'User-Agent': 'game=UELauncher, engine=UE4, build=allar_ue4_marketplace_commandline' }, 510 | qs: { label: 'Live' }, 511 | }; 512 | 513 | console.log("Getting item build info."); 514 | 515 | request.get(opts, function(error, response, body) { 516 | if (error !== null) { 517 | console.log('Error getting item build info.'); 518 | if (cb != undefined) { 519 | cb(error, null); 520 | } 521 | return; 522 | } 523 | if (response.statusCode == 200) { 524 | var manifest = JSON.parse(body); 525 | if (cb != undefined) { 526 | cb(null, manifest) 527 | } 528 | } else { 529 | if (cb != undefined) { 530 | console.log('Error getting item build info. Error code: ' + response.statusCode); 531 | cb(body, null); 532 | } 533 | } 534 | }); 535 | } 536 | 537 | // Gets an item's manifest. Callback is of form (error, manifest) 538 | // Note: This call does not require auth. (lol?) E: It now does by needing the signature. 539 | epic_api.prototype.GetItemManifest = function (itemBuildInfo, cb) { 540 | var opts = { 541 | uri: itemBuildInfo.items.MANIFEST.distribution + itemBuildInfo.items.MANIFEST.path + "?" + itemBuildInfo.items.MANIFEST.signature, 542 | headers: { Origin: 'allar_ue4_marketplace_commandline', 'User-Agent': 'game=UELauncher, engine=UE4, build=allar_ue4_marketplace_commandline' }, 543 | }; 544 | 545 | console.log("Getting item manifest."); 546 | 547 | request.get(opts, function(error, response, body) { 548 | if (error !== null) { 549 | console.log('Error getting item manifest.'); 550 | if (cb != undefined) { 551 | cb(error, null); 552 | } 553 | return; 554 | } 555 | if (response.statusCode == 200) { 556 | var manifest = JSON.parse(body); 557 | if (cb != undefined) { 558 | cb(null, manifest) 559 | } 560 | } else { 561 | if (cb != undefined) { 562 | console.log('Error getting item manifest. Error code: ' + response.statusCode); 563 | cb(body, null); 564 | } 565 | } 566 | }); 567 | } 568 | 569 | // Hash Functions 570 | 571 | var HexChars = ["0", "1", "2", "3", "4", "5", "6", "7","8", "9", "A", "B", "C", "D", "E", "F"]; 572 | 573 | function ByteToHex(b) { 574 | return HexChars[(b >> 4) & 0x0f] + HexChars[b & 0x0f]; 575 | } 576 | 577 | // Takes hash of 24-character decimal form (8 * 3char) and outputs 16-character hex in reverse byte order 578 | function ChunkHashToReverseHexEncoding(chunk_hash) { 579 | var out_hex = ''; 580 | 581 | for (var i = 0; i < 8; ++i) { 582 | out_hex = ByteToHex(parseInt(chunk_hash.substring(i*3, i*3+3))) + out_hex; 583 | } 584 | return out_hex; 585 | } 586 | 587 | // Pads a string with leading zeros or passed in string, i.e. padLeft(4,2) = "04" 588 | // http://stackoverflow.com/questions/5366849/convert-1-to-0001-in-javascript 589 | function padLeft(nr, n, str){ 590 | return Array(n-String(nr).length+1).join(str||'0')+nr; 591 | } 592 | 593 | function download(file, options, callback) { 594 | if (!file) throw("Need a file url to download") 595 | 596 | if (!callback && typeof options === 'function') { 597 | callback = options 598 | } 599 | 600 | options = typeof options === 'object' ? options : {} 601 | options.timeout = options.timeout || 20000 602 | options.directory = options.directory ? options.directory : '.' 603 | options.retries = options.retries || 3; 604 | 605 | var uri = file.split('/') 606 | options.filename = options.filename || uri[uri.length - 1] 607 | 608 | var path = options.directory + "/" + options.filename 609 | 610 | mkdirp(options.directory, function(err) { 611 | if (err) throw err 612 | var file_out = fs.createWriteStream(path); 613 | var url_options = { 614 | uri: file, 615 | pool: downloadPool, 616 | timeout: 60000, 617 | headers: { 'User-Agent': 'game=UELauncher, engine=UE4, build=allar_ue4_marketplace_commandline' }, 618 | }; 619 | request.get(url_options).on('response', function(response) { 620 | if (response.statusCode === 200) { 621 | response.pipe(file_out); 622 | file_out.on('finish', function() { 623 | file_out.close(function(){ 624 | if (callback) 625 | callback(false, path) 626 | }); 627 | }); 628 | } 629 | else{ 630 | if (callback) callback(response.statusCode) 631 | } 632 | }).on('error', function(err) { // Handle errors 633 | if(options.retries > 0){ 634 | options.retries -= 1; 635 | console.log("Retry to download: " + url_options.uri + ". Remaining: " + options.retries); 636 | download(file, options, callback); 637 | return; 638 | } 639 | fs.unlink(path); 640 | if (cb) cb(err.message); 641 | }); 642 | }); 643 | }; 644 | 645 | epic_api.prototype.BuildItemChunkListFromManifest = function (buildinfo, manifest) { 646 | // Build chunk URL list 647 | var chunks = []; 648 | //Ref: https://download.epicgames.com/Builds/Rocket/Automated/MagicEffects411/CloudDir/ChunksV3/22/AAC7EF867364B218_CE3BE4D54E7B4ECE663C8EAC2D8929D6.chunk 649 | var chunk_path = buildinfo.items.CHUNKS['path']; 650 | var chunkBaseURL = buildinfo.items.CHUNKS['distribution'] + chunk_path.substring(0, chunk_path.lastIndexOf('/')) + "/ChunksV3/"; 651 | for ( var chunk in manifest.ChunkHashList ) 652 | { 653 | var hash = ChunkHashToReverseHexEncoding(manifest.ChunkHashList[chunk]); 654 | var group = padLeft(parseInt(manifest.DataGroupList[chunk]), 2); 655 | var filename = chunk+'.chunk'; 656 | chunks.push({guid: chunk, hash: hash, url: chunkBaseURL + group + '/' + hash + '_' + chunk + '.chunk', filename: filename}); 657 | } 658 | return chunks; 659 | } 660 | 661 | // cb is in format (finished, chunkDir) 662 | epic_api.prototype.DownloadItemChunkList = function (manifest, chunkList, downloadDirBase, cb) { 663 | var downloadDir = `${downloadDirBase}${manifest.AppNameString}/chunks/`; 664 | rimraf.sync(downloadDir + '*.*'); // Purge chunk folder 665 | mkdirp.sync(downloadDir) // Ensure path exists after purge 666 | 667 | var chunkSize = 1000; 668 | var downloader = function (chunkList, cb) { 669 | var downloads = []; 670 | 671 | chunkList.forEach( (chunk) => { 672 | downloads.push(chunk.url); 673 | }); 674 | 675 | // Perform downloads 676 | var bar = new ProgressBar('Progress: (:current / :total) :bar :percent Completed. (ETA: :eta seconds)', {total: chunkList.length}); 677 | var downloadList = downloads; // really stupid code 678 | downloadList.forEach( (downloadItem) => { 679 | download(downloadItem, { directory: downloadDir, timeout: 50000 }, (err) => { 680 | if (err) throw err; 681 | bar.tick(); 682 | downloads.pop(); 683 | if (downloads.length == 0 && cb != undefined) { 684 | cb(chunkSize); 685 | } 686 | }); 687 | }); 688 | } 689 | 690 | var i = 0; 691 | var chunkCallback = function (inc) { 692 | var downloadArr; 693 | i += inc; 694 | if (i < chunkList.length) { 695 | var limit = Math.min(i+chunkSize, chunkList.length); 696 | console.log(`Downloading item chunks ${i}..${limit} of ${chunkList.length}`); 697 | downloadArr = chunkList.slice(i,limit); 698 | downloader(downloadArr, chunkCallback); 699 | } else { 700 | cb(true, downloadDir); 701 | } 702 | } 703 | 704 | chunkCallback(0) 705 | 706 | } 707 | 708 | // cb is in format (finished) 709 | epic_api.prototype.ExtractAssetFilesFromChunks = function (manifest, chunkDir, downloadDirBase, cb) { 710 | var extractDir = `${downloadDirBase}${manifest.AppNameString}/extracted/`; 711 | rimraf.sync(extractDir + '*.*'); // Purge chunk folder 712 | mkdirp.sync(extractDir) // Ensure path exists after purge 713 | 714 | console.log("Fixing up chunk files..."); 715 | var chunkFiles = fs.readdirSync(chunkDir); 716 | 717 | // strip chunk hashes from files, we do this to make some code simpler at the cost of IO 718 | var bar = new ProgressBar('Fixing Up Chunk Files: Progress: (:current / :totalMB) :bar :percent Completed. (ETA: :eta seconds)', {total: chunkFiles.length}); 719 | chunkFiles.forEach((file) => { 720 | fs.renameSync(chunkDir + file, chunkDir + file.substring(17)); 721 | bar.tick(); 722 | }); 723 | 724 | // Get renamed list of files 725 | chunkFiles = fs.readdirSync(chunkDir); 726 | 727 | console.log("Decompressing files..."); 728 | 729 | // decompress chunk files 730 | bar = new ProgressBar('Decompressing Chunk Files: Progress: (:current / :totalMB) :bar :percent Completed. (ETA: :eta seconds)', {total: chunkFiles.length}); 731 | chunkFiles.forEach( (chunkFileName) => { 732 | var file = fs.openSync(chunkDir + chunkFileName, 'r'); 733 | 734 | // We need to first read a chunk's header to find out where data begins and if its compressed 735 | // Header details can be found in Engine\Source\Runtime\Online\BuildPatchServices\Private\BuildPatchChunk.cpp 736 | // Header size is stored in the 9th byte (index 8) 737 | // Whether a file is compressed is always at header byte 41 (index 0) 738 | var headerBuffer = new Buffer(41); 739 | fs.readSync(file, headerBuffer, 0, 41, 0); 740 | 741 | var headerSize = headerBuffer[8]; 742 | var compressed = (headerBuffer[40] == 1); 743 | 744 | var stats = fs.statSync(chunkDir + chunkFileName); 745 | var chunkBuffer = new Buffer(stats['size'] - headerSize); 746 | fs.readSync(file, chunkBuffer, 0, stats['size']-headerSize, headerSize); 747 | fs.closeSync(file); 748 | 749 | if (compressed) { 750 | fs.writeFileSync(chunkDir + chunkFileName, zlib.unzipSync(chunkBuffer)); 751 | } else { 752 | fs.writeFileSync(chunkDir + chunkFileName, chunkBuffer); 753 | } 754 | 755 | headerBuffer = null; 756 | chunkBuffer = null; 757 | 758 | bar.tick(); 759 | }); 760 | 761 | // Extract assets from chunks 762 | bar = new ProgressBar('Extracting Asset Files: Progress: (:current / :total) :bar :percent Completed. (ETA: :eta seconds)', {total: manifest.FileManifestList.length}); 763 | manifest.FileManifestList.forEach( (fileList) => { 764 | var fileSize = 0; 765 | var fileName = extractDir + fileList.Filename; 766 | var fileDir = fileName.substring(0, fileName.lastIndexOf('/')); 767 | mkdirp.sync(fileDir); // Create asset file folder if it doesn't exist 768 | 769 | // Calculate total asset file size 770 | fileList.FileChunkParts.forEach( (chunkPart) => { 771 | fileSize += parseInt('0x'+ChunkHashToReverseHexEncoding(chunkPart.Size)); 772 | }); 773 | 774 | var buffer = new Buffer(fileSize); 775 | var bufferOffset = 0; 776 | 777 | // Start reading chunk data and assembling it into a buffer 778 | fileList.FileChunkParts.forEach( (chunkPart) => { 779 | var chunkGuid = chunkPart.Guid; 780 | var chunkOffset = parseInt('0x'+ChunkHashToReverseHexEncoding(chunkPart.Offset)); 781 | var chunkSize = parseInt('0x'+ChunkHashToReverseHexEncoding(chunkPart.Size)); 782 | 783 | var file = fs.openSync(chunkDir + chunkGuid + '.chunk', 'r'); 784 | fs.readSync(file, buffer, bufferOffset, chunkSize, chunkOffset); 785 | fs.closeSync(file); 786 | bufferOffset += chunkSize; 787 | }); 788 | 789 | // Write out the assembled buffer 790 | fs.writeFileSync(fileName, buffer); 791 | buffer = null; 792 | bar.tick(); 793 | }); 794 | 795 | console.log("Removing chunk files."); 796 | rimraf.sync(chunkDir + "*.*"); // Remove no-longer needed chunk dir 797 | 798 | if (cb != undefined) { 799 | cb(true); 800 | } 801 | } 802 | 803 | 804 | 805 | module.exports = new epic_api(); 806 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | "use strict"; 3 | const argv = require('yargs').argv; 4 | 5 | if (argv.manifests) { 6 | require('./manifests.js'); 7 | } else if (argv.evil) { 8 | require('./evil.js'); 9 | } else { 10 | require('./downloader.js'); 11 | } 12 | -------------------------------------------------------------------------------- /manifests.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | // Warning: Here be dragons 3 | 4 | 5 | // Normally don't need to do this global bullshit but 'epic_api.js' is used in another JS app that requires it 6 | // So instead of maintaining two copies of this api, we'll just re-use it like this 7 | // @TODO: Learn how to do all this the right way 8 | global.request = (global.request === undefined) ? require('request') : global.request; 9 | global.request = request.defaults({followRedirect: false, followAllRedirects: false}); 10 | global.epic_api = (global.epic_api === undefined) ? require('./epic_api.js') : global.epic_api; 11 | 12 | const prompt = require('prompt'); 13 | const cheerio = require('cheerio'); 14 | const menu = require('console-menu'); 15 | const fs = require('fs'); 16 | const path = require('path'); 17 | const mkdirp = require('mkdirp'); 18 | const dotenv = require('dotenv').config(); 19 | 20 | // Takes an HTML form from cheerio.serializeArray() and converts it to an object suitable for the 'request' module 21 | function SerializeLoginFormArray(form) { 22 | var result = {}; 23 | form.forEach((element) => { 24 | result[element.name] = element.value; 25 | }); 26 | return result; 27 | } 28 | 29 | // Ask for username/password from the user 30 | var promptSchema = { 31 | properties: { 32 | username: { 33 | required: true, 34 | type: 'string', 35 | default: process.env.UE4_ACCOUNT 36 | }, 37 | password: { 38 | required: true, 39 | type:'string', 40 | hidden: true, 41 | replace: '*', 42 | default: process.env.UE4_PASSWORD 43 | } 44 | } 45 | }; 46 | 47 | function cleanEmptyFoldersRecursively(folder) { 48 | var isDir = fs.statSync(folder).isDirectory(); 49 | if (!isDir) { 50 | return; 51 | } 52 | var files = fs.readdirSync(folder); 53 | if (files.length > 0) { 54 | files.forEach(function(file) { 55 | var fullPath = path.join(folder, file); 56 | cleanEmptyFoldersRecursively(fullPath); 57 | }); 58 | 59 | // re-evaluate files; after deleting subfolder 60 | // we may have parent folder empty now 61 | files = fs.readdirSync(folder); 62 | } 63 | 64 | if (files.length == 0) { 65 | console.log("removing: ", folder); 66 | fs.rmdirSync(folder); 67 | return; 68 | } 69 | } 70 | 71 | // Error handling is for smart people 72 | // We are not smart today 73 | 74 | function TryLogin() { 75 | // If Epic's login page is down for some reason, we should probably handle it somehow 76 | epic_api.GetWebLoginForm( (body) => { 77 | prompt.start(); 78 | prompt.get(promptSchema, (err, result) => { 79 | if (result == undefined || result.username == undefined) { 80 | process.exit(0); // Control+C 81 | } 82 | const $ = cheerio.load(body); 83 | var loginData = SerializeLoginFormArray($('form#loginForm').serializeArray()); 84 | loginData.epic_username = result.username; 85 | loginData.password = result.password; 86 | epic_api.WebLogin(loginData, OnLogin); 87 | }); 88 | }); 89 | } 90 | 91 | 92 | // Return error codes for WebLogin are retarded and should be hardcoded to sane values 93 | // I was probably drunk when writing epic_api.js 94 | function OnLogin(status, complete) { 95 | if (status === 'Failed') { 96 | console.log("Failed to log in."); 97 | TryLogin(); 98 | return; 99 | } 100 | 101 | console.log(status); 102 | 103 | // If for some reason the login chain fails but doesn't complete, theres no error handling 104 | // The log above *should* log the login chain failure and execution *should* just stop. 105 | // Theres a lot of assumptions being made because my test sample count is 1. 106 | if (complete == true) { 107 | epic_api.GetOwnedAssets( (success) => { 108 | var items = []; 109 | Object.keys(global.global.marketplace_ownedAssets_consolidated).forEach( (key) => { 110 | if (global.marketplace_ownedAssets_consolidated[key].developer == "Epic Games") // Epic examples returning 403? 111 | return; 112 | 113 | var isAsset = global.marketplace_ownedAssets_consolidated[key].categories.find ( (cat) => { 114 | return (cat.path == "assets" || cat.path == "projects" || cat.path == "plugins") 115 | }); 116 | if (isAsset) { 117 | items.push(global.marketplace_ownedAssets_consolidated[key]); 118 | mkdirp.sync('./dump/buildinfo/' + global.marketplace_ownedAssets_consolidated[key].id) 119 | mkdirp.sync('./dump/manifests/' + global.marketplace_ownedAssets_consolidated[key].id) 120 | } 121 | }); 122 | 123 | // Sort items alphabetically 124 | items.sort( (a, b) => { 125 | if (a.title < b.title) return -1; 126 | if (a.title > b.title) return 1; 127 | return 0; 128 | }); 129 | 130 | var itemVersions = []; 131 | 132 | items.forEach( (item) => { 133 | item.releaseInfo.forEach( (versionInfo) => { 134 | versionInfo.catalogItemId = item.id; 135 | versionInfo.title = item.title; 136 | itemVersions.push(versionInfo); 137 | }); 138 | }); 139 | 140 | var buildInfos = []; 141 | var failures = 0; 142 | 143 | itemVersions.forEach( (version) => { 144 | global.epic_api.GetItemBuildInfo(version.catalogItemId, version.appId, (error, buildinfo) => { 145 | if (error !== null) { 146 | failures++; 147 | } else { 148 | buildinfo.title = version.title; 149 | fs.writeFileSync('./dump/buildinfo/' + version.catalogItemId + '/' + version.appId, JSON.stringify(buildinfo, null, '\t')); 150 | buildInfos.push(buildinfo); 151 | } 152 | if (buildInfos.length + failures == itemVersions.length) { 153 | console.log("All possible build info items fetched."); 154 | var manifestWrites = 0; 155 | buildInfos.forEach( (storedBuildInfo) => { 156 | global.epic_api.GetItemManifest(storedBuildInfo, (manifestError, manifest) => { 157 | manifestWrites++; 158 | if (manifestError == null) { 159 | manifest.catalogItemId = storedBuildInfo.catalogItemId; 160 | manifest.title = storedBuildInfo.title; 161 | fs.writeFileSync('./dump/manifests/' + manifest.catalogItemId + '/' + storedBuildInfo.appName + '.json', JSON.stringify(manifest, null, '\t')); 162 | fs.writeFileSync('./dump/manifests/' + manifest.catalogItemId + '/title.json', JSON.stringify({title: storedBuildInfo.title}, null, '\t')); 163 | } 164 | if (manifestWrites == buildInfos.length) { 165 | cleanEmptyFoldersRecursively('./dump/'); 166 | console.log("Dumped all manifests."); 167 | process.exit(0); 168 | return; 169 | } 170 | }); 171 | }) 172 | } 173 | }); 174 | }); 175 | }); 176 | }; 177 | } 178 | 179 | 180 | TryLogin(); 181 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ue4-mp-downloader", 3 | "version": "1.3.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@types/node": { 8 | "version": "13.7.4", 9 | "resolved": "https://registry.npmjs.org/@types/node/-/node-13.7.4.tgz", 10 | "integrity": "sha512-oVeL12C6gQS/GAExndigSaLxTrKpQPxewx9bOcwfvJiJge4rr7wNaph4J+ns5hrmIV2as5qxqN8YKthn9qh0jw==" 11 | }, 12 | "ajv": { 13 | "version": "6.11.0", 14 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.11.0.tgz", 15 | "integrity": "sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA==", 16 | "requires": { 17 | "fast-deep-equal": "^3.1.1", 18 | "fast-json-stable-stringify": "^2.0.0", 19 | "json-schema-traverse": "^0.4.1", 20 | "uri-js": "^4.2.2" 21 | } 22 | }, 23 | "ansi-regex": { 24 | "version": "2.1.1", 25 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", 26 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" 27 | }, 28 | "asn1": { 29 | "version": "0.2.4", 30 | "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", 31 | "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", 32 | "requires": { 33 | "safer-buffer": "~2.1.0" 34 | } 35 | }, 36 | "assert-plus": { 37 | "version": "1.0.0", 38 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", 39 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" 40 | }, 41 | "async": { 42 | "version": "0.9.2", 43 | "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", 44 | "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=" 45 | }, 46 | "asynckit": { 47 | "version": "0.4.0", 48 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 49 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" 50 | }, 51 | "aws-sign2": { 52 | "version": "0.7.0", 53 | "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", 54 | "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" 55 | }, 56 | "aws4": { 57 | "version": "1.9.1", 58 | "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz", 59 | "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==" 60 | }, 61 | "balanced-match": { 62 | "version": "1.0.0", 63 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 64 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 65 | }, 66 | "bcrypt-pbkdf": { 67 | "version": "1.0.2", 68 | "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", 69 | "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", 70 | "requires": { 71 | "tweetnacl": "^0.14.3" 72 | } 73 | }, 74 | "boolbase": { 75 | "version": "1.0.0", 76 | "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", 77 | "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" 78 | }, 79 | "brace-expansion": { 80 | "version": "1.1.11", 81 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 82 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 83 | "requires": { 84 | "balanced-match": "^1.0.0", 85 | "concat-map": "0.0.1" 86 | } 87 | }, 88 | "camelcase": { 89 | "version": "4.1.0", 90 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", 91 | "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" 92 | }, 93 | "caseless": { 94 | "version": "0.12.0", 95 | "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", 96 | "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" 97 | }, 98 | "cheerio": { 99 | "version": "1.0.0-rc.3", 100 | "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.3.tgz", 101 | "integrity": "sha512-0td5ijfUPuubwLUu0OBoe98gZj8C/AA+RW3v67GPlGOrvxWjZmBXiBCRU+I8VEiNyJzjth40POfHiz2RB3gImA==", 102 | "requires": { 103 | "css-select": "~1.2.0", 104 | "dom-serializer": "~0.1.1", 105 | "entities": "~1.1.1", 106 | "htmlparser2": "^3.9.1", 107 | "lodash": "^4.15.0", 108 | "parse5": "^3.0.1" 109 | } 110 | }, 111 | "cliui": { 112 | "version": "3.2.0", 113 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", 114 | "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", 115 | "requires": { 116 | "string-width": "^1.0.1", 117 | "strip-ansi": "^3.0.1", 118 | "wrap-ansi": "^2.0.0" 119 | }, 120 | "dependencies": { 121 | "string-width": { 122 | "version": "1.0.2", 123 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", 124 | "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", 125 | "requires": { 126 | "code-point-at": "^1.0.0", 127 | "is-fullwidth-code-point": "^1.0.0", 128 | "strip-ansi": "^3.0.0" 129 | } 130 | } 131 | } 132 | }, 133 | "code-point-at": { 134 | "version": "1.1.0", 135 | "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", 136 | "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" 137 | }, 138 | "colors": { 139 | "version": "1.4.0", 140 | "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", 141 | "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" 142 | }, 143 | "combined-stream": { 144 | "version": "1.0.8", 145 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 146 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 147 | "requires": { 148 | "delayed-stream": "~1.0.0" 149 | } 150 | }, 151 | "concat-map": { 152 | "version": "0.0.1", 153 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 154 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 155 | }, 156 | "console-menu": { 157 | "version": "0.1.0", 158 | "resolved": "https://registry.npmjs.org/console-menu/-/console-menu-0.1.0.tgz", 159 | "integrity": "sha1-Zbqm2/OuoMFdmd3zAuQLvD1d8z4=", 160 | "requires": { 161 | "keypress": "^0.2.1" 162 | } 163 | }, 164 | "core-util-is": { 165 | "version": "1.0.2", 166 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 167 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 168 | }, 169 | "cross-spawn": { 170 | "version": "5.1.0", 171 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", 172 | "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", 173 | "requires": { 174 | "lru-cache": "^4.0.1", 175 | "shebang-command": "^1.2.0", 176 | "which": "^1.2.9" 177 | } 178 | }, 179 | "css-select": { 180 | "version": "1.2.0", 181 | "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", 182 | "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", 183 | "requires": { 184 | "boolbase": "~1.0.0", 185 | "css-what": "2.1", 186 | "domutils": "1.5.1", 187 | "nth-check": "~1.0.1" 188 | } 189 | }, 190 | "css-what": { 191 | "version": "2.1.3", 192 | "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", 193 | "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==" 194 | }, 195 | "cycle": { 196 | "version": "1.0.3", 197 | "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", 198 | "integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI=" 199 | }, 200 | "dashdash": { 201 | "version": "1.14.1", 202 | "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", 203 | "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", 204 | "requires": { 205 | "assert-plus": "^1.0.0" 206 | } 207 | }, 208 | "decamelize": { 209 | "version": "1.2.0", 210 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", 211 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" 212 | }, 213 | "deep-equal": { 214 | "version": "0.2.2", 215 | "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-0.2.2.tgz", 216 | "integrity": "sha1-hLdFiW80xoTpjyzg5Cq69Du6AX0=" 217 | }, 218 | "delayed-stream": { 219 | "version": "1.0.0", 220 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 221 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" 222 | }, 223 | "dom-serializer": { 224 | "version": "0.1.1", 225 | "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", 226 | "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", 227 | "requires": { 228 | "domelementtype": "^1.3.0", 229 | "entities": "^1.1.1" 230 | } 231 | }, 232 | "domelementtype": { 233 | "version": "1.3.1", 234 | "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", 235 | "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" 236 | }, 237 | "domhandler": { 238 | "version": "2.4.2", 239 | "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", 240 | "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", 241 | "requires": { 242 | "domelementtype": "1" 243 | } 244 | }, 245 | "domutils": { 246 | "version": "1.5.1", 247 | "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", 248 | "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", 249 | "requires": { 250 | "dom-serializer": "0", 251 | "domelementtype": "1" 252 | } 253 | }, 254 | "dotenv": { 255 | "version": "8.2.0", 256 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", 257 | "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" 258 | }, 259 | "ecc-jsbn": { 260 | "version": "0.1.2", 261 | "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", 262 | "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", 263 | "requires": { 264 | "jsbn": "~0.1.0", 265 | "safer-buffer": "^2.1.0" 266 | } 267 | }, 268 | "entities": { 269 | "version": "1.1.2", 270 | "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", 271 | "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" 272 | }, 273 | "error-ex": { 274 | "version": "1.3.2", 275 | "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", 276 | "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", 277 | "requires": { 278 | "is-arrayish": "^0.2.1" 279 | } 280 | }, 281 | "execa": { 282 | "version": "0.7.0", 283 | "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", 284 | "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", 285 | "requires": { 286 | "cross-spawn": "^5.0.1", 287 | "get-stream": "^3.0.0", 288 | "is-stream": "^1.1.0", 289 | "npm-run-path": "^2.0.0", 290 | "p-finally": "^1.0.0", 291 | "signal-exit": "^3.0.0", 292 | "strip-eof": "^1.0.0" 293 | } 294 | }, 295 | "extend": { 296 | "version": "3.0.2", 297 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", 298 | "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" 299 | }, 300 | "extsprintf": { 301 | "version": "1.3.0", 302 | "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", 303 | "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" 304 | }, 305 | "eyes": { 306 | "version": "0.1.8", 307 | "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", 308 | "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=" 309 | }, 310 | "fast-deep-equal": { 311 | "version": "3.1.1", 312 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", 313 | "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==" 314 | }, 315 | "fast-json-stable-stringify": { 316 | "version": "2.1.0", 317 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 318 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" 319 | }, 320 | "find-up": { 321 | "version": "2.1.0", 322 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", 323 | "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", 324 | "requires": { 325 | "locate-path": "^2.0.0" 326 | } 327 | }, 328 | "forever-agent": { 329 | "version": "0.6.1", 330 | "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", 331 | "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" 332 | }, 333 | "form-data": { 334 | "version": "2.3.3", 335 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", 336 | "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", 337 | "requires": { 338 | "asynckit": "^0.4.0", 339 | "combined-stream": "^1.0.6", 340 | "mime-types": "^2.1.12" 341 | } 342 | }, 343 | "fs": { 344 | "version": "0.0.1-security", 345 | "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", 346 | "integrity": "sha1-invTcYa23d84E/I4WLV+yq9eQdQ=" 347 | }, 348 | "fs.realpath": { 349 | "version": "1.0.0", 350 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 351 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 352 | }, 353 | "get-caller-file": { 354 | "version": "1.0.3", 355 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", 356 | "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" 357 | }, 358 | "get-stream": { 359 | "version": "3.0.0", 360 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", 361 | "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" 362 | }, 363 | "getpass": { 364 | "version": "0.1.7", 365 | "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", 366 | "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", 367 | "requires": { 368 | "assert-plus": "^1.0.0" 369 | } 370 | }, 371 | "glob": { 372 | "version": "7.1.6", 373 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", 374 | "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", 375 | "requires": { 376 | "fs.realpath": "^1.0.0", 377 | "inflight": "^1.0.4", 378 | "inherits": "2", 379 | "minimatch": "^3.0.4", 380 | "once": "^1.3.0", 381 | "path-is-absolute": "^1.0.0" 382 | } 383 | }, 384 | "graceful-fs": { 385 | "version": "4.2.3", 386 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", 387 | "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==" 388 | }, 389 | "har-schema": { 390 | "version": "2.0.0", 391 | "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", 392 | "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" 393 | }, 394 | "har-validator": { 395 | "version": "5.1.3", 396 | "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", 397 | "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", 398 | "requires": { 399 | "ajv": "^6.5.5", 400 | "har-schema": "^2.0.0" 401 | } 402 | }, 403 | "hosted-git-info": { 404 | "version": "2.8.5", 405 | "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.5.tgz", 406 | "integrity": "sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg==" 407 | }, 408 | "htmlparser2": { 409 | "version": "3.10.1", 410 | "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", 411 | "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", 412 | "requires": { 413 | "domelementtype": "^1.3.1", 414 | "domhandler": "^2.3.0", 415 | "domutils": "^1.5.1", 416 | "entities": "^1.1.1", 417 | "inherits": "^2.0.1", 418 | "readable-stream": "^3.1.1" 419 | } 420 | }, 421 | "http-signature": { 422 | "version": "1.2.0", 423 | "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", 424 | "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", 425 | "requires": { 426 | "assert-plus": "^1.0.0", 427 | "jsprim": "^1.2.2", 428 | "sshpk": "^1.7.0" 429 | } 430 | }, 431 | "i": { 432 | "version": "0.3.6", 433 | "resolved": "https://registry.npmjs.org/i/-/i-0.3.6.tgz", 434 | "integrity": "sha1-2WyScyB28HJxG2sQ/X1PZa2O4j0=" 435 | }, 436 | "inflight": { 437 | "version": "1.0.6", 438 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 439 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 440 | "requires": { 441 | "once": "^1.3.0", 442 | "wrappy": "1" 443 | } 444 | }, 445 | "inherits": { 446 | "version": "2.0.4", 447 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 448 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 449 | }, 450 | "invert-kv": { 451 | "version": "1.0.0", 452 | "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", 453 | "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" 454 | }, 455 | "is-arrayish": { 456 | "version": "0.2.1", 457 | "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", 458 | "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" 459 | }, 460 | "is-docker": { 461 | "version": "2.0.0", 462 | "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.0.0.tgz", 463 | "integrity": "sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ==" 464 | }, 465 | "is-fullwidth-code-point": { 466 | "version": "1.0.0", 467 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", 468 | "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", 469 | "requires": { 470 | "number-is-nan": "^1.0.0" 471 | } 472 | }, 473 | "is-stream": { 474 | "version": "1.1.0", 475 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", 476 | "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" 477 | }, 478 | "is-typedarray": { 479 | "version": "1.0.0", 480 | "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", 481 | "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" 482 | }, 483 | "is-wsl": { 484 | "version": "2.1.1", 485 | "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.1.1.tgz", 486 | "integrity": "sha512-umZHcSrwlDHo2TGMXv0DZ8dIUGunZ2Iv68YZnrmCiBPkZ4aaOhtv7pXJKeki9k3qJ3RJr0cDyitcl5wEH3AYog==" 487 | }, 488 | "isexe": { 489 | "version": "2.0.0", 490 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 491 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" 492 | }, 493 | "isstream": { 494 | "version": "0.1.2", 495 | "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", 496 | "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" 497 | }, 498 | "jquery": { 499 | "version": "3.4.1", 500 | "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.4.1.tgz", 501 | "integrity": "sha512-36+AdBzCL+y6qjw5Tx7HgzeGCzC81MDDgaUP8ld2zhx58HdqXGoBd+tHdrBMiyjGQs0Hxs/MLZTu/eHNJJuWPw==" 502 | }, 503 | "jsbn": { 504 | "version": "0.1.1", 505 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", 506 | "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" 507 | }, 508 | "json-schema": { 509 | "version": "0.2.3", 510 | "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", 511 | "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" 512 | }, 513 | "json-schema-traverse": { 514 | "version": "0.4.1", 515 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 516 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" 517 | }, 518 | "json-stringify-safe": { 519 | "version": "5.0.1", 520 | "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", 521 | "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" 522 | }, 523 | "jsprim": { 524 | "version": "1.4.1", 525 | "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", 526 | "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", 527 | "requires": { 528 | "assert-plus": "1.0.0", 529 | "extsprintf": "1.3.0", 530 | "json-schema": "0.2.3", 531 | "verror": "1.10.0" 532 | } 533 | }, 534 | "keypress": { 535 | "version": "0.2.1", 536 | "resolved": "https://registry.npmjs.org/keypress/-/keypress-0.2.1.tgz", 537 | "integrity": "sha1-HoBFQlABjbrUw/6USX1uZ7YmnHc=" 538 | }, 539 | "lcid": { 540 | "version": "1.0.0", 541 | "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", 542 | "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", 543 | "requires": { 544 | "invert-kv": "^1.0.0" 545 | } 546 | }, 547 | "load-json-file": { 548 | "version": "2.0.0", 549 | "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", 550 | "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", 551 | "requires": { 552 | "graceful-fs": "^4.1.2", 553 | "parse-json": "^2.2.0", 554 | "pify": "^2.0.0", 555 | "strip-bom": "^3.0.0" 556 | } 557 | }, 558 | "locate-path": { 559 | "version": "2.0.0", 560 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", 561 | "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", 562 | "requires": { 563 | "p-locate": "^2.0.0", 564 | "path-exists": "^3.0.0" 565 | } 566 | }, 567 | "lodash": { 568 | "version": "4.17.15", 569 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", 570 | "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" 571 | }, 572 | "lru-cache": { 573 | "version": "4.1.5", 574 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", 575 | "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", 576 | "requires": { 577 | "pseudomap": "^1.0.2", 578 | "yallist": "^2.1.2" 579 | } 580 | }, 581 | "map-age-cleaner": { 582 | "version": "0.1.3", 583 | "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", 584 | "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", 585 | "requires": { 586 | "p-defer": "^1.0.0" 587 | } 588 | }, 589 | "mem": { 590 | "version": "6.0.1", 591 | "resolved": "https://registry.npmjs.org/mem/-/mem-6.0.1.tgz", 592 | "integrity": "sha512-uIRYASflIsXqvKe+7aXbLrydaRzz4qiK6amqZDQI++eRtW3UoKtnDcGeCAOREgll7YMxO5E4VB9+3B0LFmy96g==", 593 | "requires": { 594 | "map-age-cleaner": "^0.1.3", 595 | "mimic-fn": "^3.0.0" 596 | } 597 | }, 598 | "mime-db": { 599 | "version": "1.43.0", 600 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz", 601 | "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==" 602 | }, 603 | "mime-types": { 604 | "version": "2.1.26", 605 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz", 606 | "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==", 607 | "requires": { 608 | "mime-db": "1.43.0" 609 | } 610 | }, 611 | "mimic-fn": { 612 | "version": "3.0.0", 613 | "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.0.0.tgz", 614 | "integrity": "sha512-PiVO95TKvhiwgSwg1IdLYlCTdul38yZxZMIcnDSFIBUm4BNZha2qpQ4GpJ++15bHoKDtrW2D69lMfFwdFYtNZQ==" 615 | }, 616 | "minimatch": { 617 | "version": "3.0.4", 618 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 619 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 620 | "requires": { 621 | "brace-expansion": "^1.1.7" 622 | } 623 | }, 624 | "minimist": { 625 | "version": "0.0.8", 626 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 627 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" 628 | }, 629 | "mkdirp": { 630 | "version": "0.5.1", 631 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 632 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 633 | "requires": { 634 | "minimist": "0.0.8" 635 | } 636 | }, 637 | "mute-stream": { 638 | "version": "0.0.8", 639 | "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", 640 | "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" 641 | }, 642 | "ncp": { 643 | "version": "1.0.1", 644 | "resolved": "https://registry.npmjs.org/ncp/-/ncp-1.0.1.tgz", 645 | "integrity": "sha1-0VNn5cuHQyuhF9K/gP30Wuz7QkY=" 646 | }, 647 | "normalize-package-data": { 648 | "version": "2.5.0", 649 | "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", 650 | "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", 651 | "requires": { 652 | "hosted-git-info": "^2.1.4", 653 | "resolve": "^1.10.0", 654 | "semver": "2 || 3 || 4 || 5", 655 | "validate-npm-package-license": "^3.0.1" 656 | } 657 | }, 658 | "npm-run-path": { 659 | "version": "2.0.2", 660 | "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", 661 | "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", 662 | "requires": { 663 | "path-key": "^2.0.0" 664 | } 665 | }, 666 | "nth-check": { 667 | "version": "1.0.2", 668 | "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", 669 | "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", 670 | "requires": { 671 | "boolbase": "~1.0.0" 672 | } 673 | }, 674 | "number-is-nan": { 675 | "version": "1.0.1", 676 | "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", 677 | "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" 678 | }, 679 | "oauth-sign": { 680 | "version": "0.9.0", 681 | "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", 682 | "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" 683 | }, 684 | "once": { 685 | "version": "1.4.0", 686 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 687 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 688 | "requires": { 689 | "wrappy": "1" 690 | } 691 | }, 692 | "open": { 693 | "version": "7.0.2", 694 | "resolved": "https://registry.npmjs.org/open/-/open-7.0.2.tgz", 695 | "integrity": "sha512-70E/pFTPr7nZ9nLDPNTcj3IVqnNvKuP4VsBmoKV9YGTnChe0mlS3C4qM7qKarhZ8rGaHKLfo+vBTHXDp6ZSyLQ==", 696 | "requires": { 697 | "is-docker": "^2.0.0", 698 | "is-wsl": "^2.1.1" 699 | } 700 | }, 701 | "os-locale": { 702 | "version": "2.1.0", 703 | "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", 704 | "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", 705 | "requires": { 706 | "execa": "^0.7.0", 707 | "lcid": "^1.0.0", 708 | "mem": "^1.1.0" 709 | }, 710 | "dependencies": { 711 | "mem": { 712 | "version": "1.1.0", 713 | "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", 714 | "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", 715 | "requires": { 716 | "mimic-fn": "^1.0.0" 717 | } 718 | }, 719 | "mimic-fn": { 720 | "version": "1.2.0", 721 | "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", 722 | "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" 723 | } 724 | } 725 | }, 726 | "p-defer": { 727 | "version": "1.0.0", 728 | "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", 729 | "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=" 730 | }, 731 | "p-finally": { 732 | "version": "1.0.0", 733 | "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", 734 | "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" 735 | }, 736 | "p-limit": { 737 | "version": "1.3.0", 738 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", 739 | "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", 740 | "requires": { 741 | "p-try": "^1.0.0" 742 | } 743 | }, 744 | "p-locate": { 745 | "version": "2.0.0", 746 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", 747 | "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", 748 | "requires": { 749 | "p-limit": "^1.1.0" 750 | } 751 | }, 752 | "p-try": { 753 | "version": "1.0.0", 754 | "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", 755 | "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" 756 | }, 757 | "parse-json": { 758 | "version": "2.2.0", 759 | "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", 760 | "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", 761 | "requires": { 762 | "error-ex": "^1.2.0" 763 | } 764 | }, 765 | "parse5": { 766 | "version": "3.0.3", 767 | "resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz", 768 | "integrity": "sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==", 769 | "requires": { 770 | "@types/node": "*" 771 | } 772 | }, 773 | "path-exists": { 774 | "version": "3.0.0", 775 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", 776 | "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" 777 | }, 778 | "path-is-absolute": { 779 | "version": "1.0.1", 780 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 781 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" 782 | }, 783 | "path-key": { 784 | "version": "2.0.1", 785 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", 786 | "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" 787 | }, 788 | "path-parse": { 789 | "version": "1.0.6", 790 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", 791 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" 792 | }, 793 | "path-type": { 794 | "version": "2.0.0", 795 | "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", 796 | "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", 797 | "requires": { 798 | "pify": "^2.0.0" 799 | } 800 | }, 801 | "performance-now": { 802 | "version": "2.1.0", 803 | "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", 804 | "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" 805 | }, 806 | "pify": { 807 | "version": "2.3.0", 808 | "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", 809 | "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" 810 | }, 811 | "pkginfo": { 812 | "version": "0.4.1", 813 | "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.4.1.tgz", 814 | "integrity": "sha1-tUGO8EOd5UJfxJlQQtztFPsqhP8=" 815 | }, 816 | "progress": { 817 | "version": "2.0.3", 818 | "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", 819 | "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" 820 | }, 821 | "prompt": { 822 | "version": "1.0.0", 823 | "resolved": "https://registry.npmjs.org/prompt/-/prompt-1.0.0.tgz", 824 | "integrity": "sha1-jlcSPDlquYiJf7Mn/Trtw+c15P4=", 825 | "requires": { 826 | "colors": "^1.1.2", 827 | "pkginfo": "0.x.x", 828 | "read": "1.0.x", 829 | "revalidator": "0.1.x", 830 | "utile": "0.3.x", 831 | "winston": "2.1.x" 832 | } 833 | }, 834 | "pseudomap": { 835 | "version": "1.0.2", 836 | "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", 837 | "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" 838 | }, 839 | "psl": { 840 | "version": "1.7.0", 841 | "resolved": "https://registry.npmjs.org/psl/-/psl-1.7.0.tgz", 842 | "integrity": "sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ==" 843 | }, 844 | "punycode": { 845 | "version": "2.1.1", 846 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 847 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" 848 | }, 849 | "qs": { 850 | "version": "6.5.2", 851 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", 852 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" 853 | }, 854 | "read": { 855 | "version": "1.0.7", 856 | "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", 857 | "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=", 858 | "requires": { 859 | "mute-stream": "~0.0.4" 860 | } 861 | }, 862 | "read-pkg": { 863 | "version": "2.0.0", 864 | "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", 865 | "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", 866 | "requires": { 867 | "load-json-file": "^2.0.0", 868 | "normalize-package-data": "^2.3.2", 869 | "path-type": "^2.0.0" 870 | } 871 | }, 872 | "read-pkg-up": { 873 | "version": "2.0.0", 874 | "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", 875 | "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", 876 | "requires": { 877 | "find-up": "^2.0.0", 878 | "read-pkg": "^2.0.0" 879 | } 880 | }, 881 | "readable-stream": { 882 | "version": "3.6.0", 883 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", 884 | "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", 885 | "requires": { 886 | "inherits": "^2.0.3", 887 | "string_decoder": "^1.1.1", 888 | "util-deprecate": "^1.0.1" 889 | } 890 | }, 891 | "request": { 892 | "version": "2.88.2", 893 | "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", 894 | "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", 895 | "requires": { 896 | "aws-sign2": "~0.7.0", 897 | "aws4": "^1.8.0", 898 | "caseless": "~0.12.0", 899 | "combined-stream": "~1.0.6", 900 | "extend": "~3.0.2", 901 | "forever-agent": "~0.6.1", 902 | "form-data": "~2.3.2", 903 | "har-validator": "~5.1.3", 904 | "http-signature": "~1.2.0", 905 | "is-typedarray": "~1.0.0", 906 | "isstream": "~0.1.2", 907 | "json-stringify-safe": "~5.0.1", 908 | "mime-types": "~2.1.19", 909 | "oauth-sign": "~0.9.0", 910 | "performance-now": "^2.1.0", 911 | "qs": "~6.5.2", 912 | "safe-buffer": "^5.1.2", 913 | "tough-cookie": "~2.5.0", 914 | "tunnel-agent": "^0.6.0", 915 | "uuid": "^3.3.2" 916 | } 917 | }, 918 | "require-directory": { 919 | "version": "2.1.1", 920 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 921 | "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" 922 | }, 923 | "require-main-filename": { 924 | "version": "1.0.1", 925 | "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", 926 | "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" 927 | }, 928 | "resolve": { 929 | "version": "1.15.1", 930 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", 931 | "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", 932 | "requires": { 933 | "path-parse": "^1.0.6" 934 | } 935 | }, 936 | "revalidator": { 937 | "version": "0.1.8", 938 | "resolved": "https://registry.npmjs.org/revalidator/-/revalidator-0.1.8.tgz", 939 | "integrity": "sha1-/s5hv6DBtSoga9axgZgYS91SOjs=" 940 | }, 941 | "rimraf": { 942 | "version": "2.7.1", 943 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", 944 | "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", 945 | "requires": { 946 | "glob": "^7.1.3" 947 | } 948 | }, 949 | "safe-buffer": { 950 | "version": "5.2.0", 951 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", 952 | "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" 953 | }, 954 | "safer-buffer": { 955 | "version": "2.1.2", 956 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 957 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 958 | }, 959 | "semver": { 960 | "version": "5.7.1", 961 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 962 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" 963 | }, 964 | "set-blocking": { 965 | "version": "2.0.0", 966 | "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", 967 | "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" 968 | }, 969 | "shebang-command": { 970 | "version": "1.2.0", 971 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", 972 | "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", 973 | "requires": { 974 | "shebang-regex": "^1.0.0" 975 | } 976 | }, 977 | "shebang-regex": { 978 | "version": "1.0.0", 979 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", 980 | "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" 981 | }, 982 | "signal-exit": { 983 | "version": "3.0.2", 984 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", 985 | "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" 986 | }, 987 | "spdx-correct": { 988 | "version": "3.1.0", 989 | "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", 990 | "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", 991 | "requires": { 992 | "spdx-expression-parse": "^3.0.0", 993 | "spdx-license-ids": "^3.0.0" 994 | } 995 | }, 996 | "spdx-exceptions": { 997 | "version": "2.2.0", 998 | "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", 999 | "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==" 1000 | }, 1001 | "spdx-expression-parse": { 1002 | "version": "3.0.0", 1003 | "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", 1004 | "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", 1005 | "requires": { 1006 | "spdx-exceptions": "^2.1.0", 1007 | "spdx-license-ids": "^3.0.0" 1008 | } 1009 | }, 1010 | "spdx-license-ids": { 1011 | "version": "3.0.5", 1012 | "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", 1013 | "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==" 1014 | }, 1015 | "sshpk": { 1016 | "version": "1.16.1", 1017 | "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", 1018 | "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", 1019 | "requires": { 1020 | "asn1": "~0.2.3", 1021 | "assert-plus": "^1.0.0", 1022 | "bcrypt-pbkdf": "^1.0.0", 1023 | "dashdash": "^1.12.0", 1024 | "ecc-jsbn": "~0.1.1", 1025 | "getpass": "^0.1.1", 1026 | "jsbn": "~0.1.0", 1027 | "safer-buffer": "^2.0.2", 1028 | "tweetnacl": "~0.14.0" 1029 | } 1030 | }, 1031 | "stack-trace": { 1032 | "version": "0.0.10", 1033 | "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", 1034 | "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" 1035 | }, 1036 | "string-width": { 1037 | "version": "2.1.1", 1038 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", 1039 | "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", 1040 | "requires": { 1041 | "is-fullwidth-code-point": "^2.0.0", 1042 | "strip-ansi": "^4.0.0" 1043 | }, 1044 | "dependencies": { 1045 | "ansi-regex": { 1046 | "version": "3.0.0", 1047 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", 1048 | "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" 1049 | }, 1050 | "is-fullwidth-code-point": { 1051 | "version": "2.0.0", 1052 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 1053 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" 1054 | }, 1055 | "strip-ansi": { 1056 | "version": "4.0.0", 1057 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", 1058 | "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", 1059 | "requires": { 1060 | "ansi-regex": "^3.0.0" 1061 | } 1062 | } 1063 | } 1064 | }, 1065 | "string_decoder": { 1066 | "version": "1.3.0", 1067 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 1068 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 1069 | "requires": { 1070 | "safe-buffer": "~5.2.0" 1071 | } 1072 | }, 1073 | "strip-ansi": { 1074 | "version": "3.0.1", 1075 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", 1076 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", 1077 | "requires": { 1078 | "ansi-regex": "^2.0.0" 1079 | } 1080 | }, 1081 | "strip-bom": { 1082 | "version": "3.0.0", 1083 | "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", 1084 | "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=" 1085 | }, 1086 | "strip-eof": { 1087 | "version": "1.0.0", 1088 | "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", 1089 | "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" 1090 | }, 1091 | "tough-cookie": { 1092 | "version": "2.5.0", 1093 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", 1094 | "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", 1095 | "requires": { 1096 | "psl": "^1.1.28", 1097 | "punycode": "^2.1.1" 1098 | } 1099 | }, 1100 | "tunnel-agent": { 1101 | "version": "0.6.0", 1102 | "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", 1103 | "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", 1104 | "requires": { 1105 | "safe-buffer": "^5.0.1" 1106 | } 1107 | }, 1108 | "tweetnacl": { 1109 | "version": "0.14.5", 1110 | "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", 1111 | "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" 1112 | }, 1113 | "uri-js": { 1114 | "version": "4.2.2", 1115 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", 1116 | "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", 1117 | "requires": { 1118 | "punycode": "^2.1.0" 1119 | } 1120 | }, 1121 | "util-deprecate": { 1122 | "version": "1.0.2", 1123 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 1124 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 1125 | }, 1126 | "utile": { 1127 | "version": "0.3.0", 1128 | "resolved": "https://registry.npmjs.org/utile/-/utile-0.3.0.tgz", 1129 | "integrity": "sha1-E1LDQOuCDk2N26A5pPv6oy7U7zo=", 1130 | "requires": { 1131 | "async": "~0.9.0", 1132 | "deep-equal": "~0.2.1", 1133 | "i": "0.3.x", 1134 | "mkdirp": "0.x.x", 1135 | "ncp": "1.0.x", 1136 | "rimraf": "2.x.x" 1137 | } 1138 | }, 1139 | "uuid": { 1140 | "version": "3.4.0", 1141 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", 1142 | "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" 1143 | }, 1144 | "validate-npm-package-license": { 1145 | "version": "3.0.4", 1146 | "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", 1147 | "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", 1148 | "requires": { 1149 | "spdx-correct": "^3.0.0", 1150 | "spdx-expression-parse": "^3.0.0" 1151 | } 1152 | }, 1153 | "verror": { 1154 | "version": "1.10.0", 1155 | "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", 1156 | "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", 1157 | "requires": { 1158 | "assert-plus": "^1.0.0", 1159 | "core-util-is": "1.0.2", 1160 | "extsprintf": "^1.2.0" 1161 | } 1162 | }, 1163 | "which": { 1164 | "version": "1.3.1", 1165 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", 1166 | "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", 1167 | "requires": { 1168 | "isexe": "^2.0.0" 1169 | } 1170 | }, 1171 | "which-module": { 1172 | "version": "2.0.0", 1173 | "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", 1174 | "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" 1175 | }, 1176 | "winston": { 1177 | "version": "2.1.1", 1178 | "resolved": "https://registry.npmjs.org/winston/-/winston-2.1.1.tgz", 1179 | "integrity": "sha1-PJNJ0ZYgf9G9/51LxD73JRDjoS4=", 1180 | "requires": { 1181 | "async": "~1.0.0", 1182 | "colors": "1.0.x", 1183 | "cycle": "1.0.x", 1184 | "eyes": "0.1.x", 1185 | "isstream": "0.1.x", 1186 | "pkginfo": "0.3.x", 1187 | "stack-trace": "0.0.x" 1188 | }, 1189 | "dependencies": { 1190 | "async": { 1191 | "version": "1.0.0", 1192 | "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", 1193 | "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=" 1194 | }, 1195 | "colors": { 1196 | "version": "1.0.3", 1197 | "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", 1198 | "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=" 1199 | }, 1200 | "pkginfo": { 1201 | "version": "0.3.1", 1202 | "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz", 1203 | "integrity": "sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE=" 1204 | } 1205 | } 1206 | }, 1207 | "wrap-ansi": { 1208 | "version": "2.1.0", 1209 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", 1210 | "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", 1211 | "requires": { 1212 | "string-width": "^1.0.1", 1213 | "strip-ansi": "^3.0.1" 1214 | }, 1215 | "dependencies": { 1216 | "string-width": { 1217 | "version": "1.0.2", 1218 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", 1219 | "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", 1220 | "requires": { 1221 | "code-point-at": "^1.0.0", 1222 | "is-fullwidth-code-point": "^1.0.0", 1223 | "strip-ansi": "^3.0.0" 1224 | } 1225 | } 1226 | } 1227 | }, 1228 | "wrappy": { 1229 | "version": "1.0.2", 1230 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1231 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 1232 | }, 1233 | "y18n": { 1234 | "version": "3.2.1", 1235 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", 1236 | "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" 1237 | }, 1238 | "yallist": { 1239 | "version": "2.1.2", 1240 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", 1241 | "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" 1242 | }, 1243 | "yargs": { 1244 | "version": "8.0.2", 1245 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-8.0.2.tgz", 1246 | "integrity": "sha1-YpmpBVsc78lp/355wdkY3Osiw2A=", 1247 | "requires": { 1248 | "camelcase": "^4.1.0", 1249 | "cliui": "^3.2.0", 1250 | "decamelize": "^1.1.1", 1251 | "get-caller-file": "^1.0.1", 1252 | "os-locale": "^2.0.0", 1253 | "read-pkg-up": "^2.0.0", 1254 | "require-directory": "^2.1.1", 1255 | "require-main-filename": "^1.0.1", 1256 | "set-blocking": "^2.0.0", 1257 | "string-width": "^2.0.0", 1258 | "which-module": "^2.0.0", 1259 | "y18n": "^3.2.1", 1260 | "yargs-parser": "^7.0.0" 1261 | } 1262 | }, 1263 | "yargs-parser": { 1264 | "version": "7.0.0", 1265 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz", 1266 | "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", 1267 | "requires": { 1268 | "camelcase": "^4.1.0" 1269 | } 1270 | } 1271 | } 1272 | } 1273 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ue4-mp-downloader", 3 | "version": "1.3.0", 4 | "description": "Downloads owned assets from the UE4 marketplace from a shell.", 5 | "main": "index.js", 6 | "bin": { 7 | "ue4-mp-downloader": "ue4-mp-downloader.js" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "author": "Michael Allar", 13 | "license": "MIT", 14 | "dependencies": { 15 | "cheerio": "^1.0.0-rc.2", 16 | "console-menu": "^0.1.0", 17 | "dotenv": "^8.2.0", 18 | "fs": "0.0.1-security", 19 | "jquery": "^3.2.1", 20 | "mkdirp": "^0.5.1", 21 | "open": "7.0.2", 22 | "progress": "^2.0.0", 23 | "prompt": "^1.0.0", 24 | "request": "^2.81.0", 25 | "rimraf": "^2.6.1", 26 | "yargs": "^8.0.2", 27 | "mem": ">=4.0.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /ue4-mp-downloader.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var mpdownloader = require('./index.js'); --------------------------------------------------------------------------------