diff --git a/app/index.js b/app/index.js index f5e1f9c..a001567 100644 --- a/app/index.js +++ b/app/index.js @@ -1,6 +1,7 @@ 'use strict'; const path = require('path'); const fs = require('fs'); +const UserData = require('./scripts/user-data').UserData; const LoginResult = require('./scripts/login-result').LoginResult; const GameInfo = require('./scripts/game-info').GameInfo; const GameDownload = require('./scripts/game-download').GameDownload; @@ -12,7 +13,8 @@ const urlExist = require('url-exist'); const F95_BASE_URL = 'https://f95zone.to'; const F95_SEARCH_URL = 'https://f95zone.to/search'; const F95_LATEST_UPDATES = 'https://f95zone.to/latest'; -const F95_LOGIN_URL = 'https://f95zone.to/login' +const F95_LOGIN_URL = 'https://f95zone.to/login'; +const F95_WATCHED_THREADS = 'https://f95zone.to/watched/threads'; //#endregion //#region CSS Selectors @@ -30,6 +32,16 @@ const THREAD_POSTS = 'article.message-body:first-child > div.bbWrapper:first-of- const GAME_TITLE = 'h1.p-title-value'; const GAME_IMAGES = 'img[src^="https://attachments.f95zone.to"]'; const LOGIN_MESSAGE_ERROR = 'div.blockMessage.blockMessage--error.blockMessage--iconic'; +const GAME_TAGS = 'a.tagItem'; +const USERNAME_ELEMENT = 'a[href="/account/"] > span.p-navgroup-linkText'; +const AVATAR_PIC = 'a[href="/account/"] > span.avatar > img[class^="avatar"]'; +const UNREAD_THREAD_CHECKBOX = 'input[type="checkbox"][name="unread"]'; +const ONLY_GAMES_THREAD_OPTION = 'select[name="nodes[]"] > option[value="2"]'; +const FILTER_THREADS_BUTTON = 'button[class="button--primary button"]'; +const GAME_TITLE_PREFIXES = 'h1.p-title-value > a.labelLink > span[dir="auto"]'; +const WATCHED_THREAD_URLS = 'a[href^="/threads/"][data-tp-primary]'; +const WATCHED_THREAD_NEXT_PAGE = 'a.pageNav-jump--next'; +const WATCHED_THREAD_FILTER_POPUP_BUTTON = 'a.filterBar-menuTrigger'; //#endregion CSS Selectors //#region Game prefixes @@ -83,16 +95,14 @@ let _debug = false; * * @param {Boolean} value */ -module.exports.debug = function(value){ +module.exports.debug = function (value) { _debug = value; } -module.exports.isLogged = function() { - return _isLogged; +module.exports.isLogged = function () { + return _isLogged; }; //#endregion Properties - - //#region Export methods /** * @public @@ -103,14 +113,14 @@ module.exports.isLogged = function() { * @returns {Promise} Result of the operation */ module.exports.login = async function (username, password) { - if(_isLogged){ - if(_debug) console.log("Already logged in"); + if (_isLogged) { + if (_debug) console.log("Already logged in"); let result = new LoginResult(); result.success = true; result.message = 'Already logged in'; return result; } - + // If cookies are loaded, use them to authenticate if (_cookies !== null) { if (_debug) console.log('Valid session, no need to re-authenticate'); @@ -150,7 +160,7 @@ module.exports.loadF95BaseData = async function () { return false; } - if(_debug) console.log('Loading base data...'); + if (_debug) console.log('Loading base data...'); // Prepare a new web page let browser = await prepareBrowser(); @@ -169,7 +179,7 @@ module.exports.loadF95BaseData = async function () { // Obtain statuses (disc/online) await page.waitForSelector(STATUS_ID_SELECTOR); _statuses = await loadValuesFromLatestPage(page, STATUSES_SAVE_PATH, STATUS_ID_SELECTOR, 'statuses'); - + await browser.close(); if (_debug) console.log('Base data loaded'); return true; @@ -199,7 +209,8 @@ module.exports.getGameVersion = async function (info) { * You **must** be logged in to the portal before calling this method. * @param {String} name Name of the game searched * @param {Boolean} includeMods Indicates whether to also take mods into account when searching - * @returns {Promise} Information about the game searched or null if no game were found + * @returns {Promise} List of information obtained where each item corresponds to + * an identified game (in the case of homonymy). If no games were found, null is returned */ module.exports.getGameData = async function (name, includeMods) { if (!_isLogged) { @@ -209,19 +220,21 @@ module.exports.getGameData = async function (name, includeMods) { // Gets the search results of the game being searched for let browser = await prepareBrowser(); - let infoList = await getSearchGameResults(browser, name, _cookies); + let urlList = await getSearchGameResults(browser, name, _cookies); // Process previous partial results - let result = null; - for (let info of infoList) { + let promiseList = []; + for (let url of urlList) { + // Start looking for information + promiseList.push(getGameInfo(browser, url)); + } + + // Filter for mods + let result = []; + for (let info of await Promise.all(promiseList)) { // Skip mods if not required if (info.isMod && !includeMods) continue; - - // TODO - // What if there are more games with the same name? - // For the moment, return the first - result = await getGameInfo(browser, info); - break; + else result.push(info); } await browser.close(); @@ -233,12 +246,12 @@ module.exports.getGameData = async function (name, includeMods) { * @param {*} platform * @param {*} url */ -module.exports.getDownloadLink = async function(platform, url){ +module.exports.getDownloadLink = async function (platform, url) { if (!_isLogged) { console.warn('user not authenticated, unable to continue'); return null; } - + // Gets the search results of the game being searched for let browser = await prepareBrowser(); getGameDownloadLink(browser, url); @@ -246,11 +259,44 @@ module.exports.getDownloadLink = async function(platform, url){ } /** * @public - * Properly closes all instances opened by the API. - * @returns {Promise} + * Gets the data of the currently logged in user. + * @returns {Promise} Data of the user currently logged in or null if an error arise */ -module.exports.close = async function() { - if(_debug) console.log("Closing F95API") +module.exports.getUserData = async function () { + if (!_isLogged) { + console.warn('user not authenticated, unable to continue'); + return null; + } + + // Prepare a new web page + let browser = await prepareBrowser(); + let page = await preparePage(browser); // Set new isolated page + await page.setCookie(..._cookies); // Set cookies to avoid login + await page.goto(F95_BASE_URL); // Go to base page + + // Explicitly wait for the required items to load + await page.waitForSelector(USERNAME_ELEMENT); + await page.waitForSelector(AVATAR_PIC); + + let threads = getUserWatchedGameThreads(browser); + + let username = await page.evaluate((selector) => + document.querySelector(selector).innerText, + USERNAME_ELEMENT); + + let avatarSrc = await page.evaluate((selector) => + document.querySelector(selector).getAttribute('src'), + AVATAR_PIC); + + let ud = new UserData(); + ud.username = username; + ud.avatarSrc = isStringAValidURL(avatarSrc) ? new URL(avatarSrc) : null; + ud.watchedThreads = await threads; + + await page.close(); + await browser.close(); + + return ud; } //#endregion @@ -266,7 +312,7 @@ module.exports.close = async function() { async function prepareBrowser() { // Create a headless browser let browser = await puppeteer.launch({ - headless: true, + headless: false, }); return browser; @@ -424,7 +470,7 @@ function isF95URL(url) { * @param {String} url String to check for correctness * @returns {Boolean} true if the string is a valid URL, false otherwise */ -function isStringAValidURL(url){ +function isStringAValidURL(url) { try { new URL(url); return true; @@ -479,25 +525,68 @@ async function loginF95(browser, username, password) { if (errorMessage === 'Incorrect password. Please try again.') result.message = 'Incorrect password'; else if (errorMessage === "The requested user '" + username + "' could not be found.") result.message = 'Incorrect username'; else result.message = errorMessage; - } - else result.message = "Unknown error"; + } else result.message = "Unknown error"; await page.close(); // Close the page return result; } /** - * @deprecated + * @private + * Gets the list of URLs of threads the user follows. + * @param {puppeteer.Browser} browser Browser object used for navigation + * @returns {Promise} URL list */ -async function getUserData(){ +async function getUserWatchedGameThreads(browser) { + let page = await preparePage(browser); // Set new isolated page + await page.goto(F95_WATCHED_THREADS); // Go to the thread page -} + // Explicitly wait for the required items to load + await page.waitForSelector(WATCHED_THREAD_FILTER_POPUP_BUTTON); -/** - * @deprecated - */ -async function getUserWatchedGameThreads(){ + // Show the popup + await page.click(WATCHED_THREAD_FILTER_POPUP_BUTTON); + await page.waitForSelector(UNREAD_THREAD_CHECKBOX); + await page.waitForSelector(ONLY_GAMES_THREAD_OPTION); + await page.waitForSelector(FILTER_THREADS_BUTTON); + // Set the filters + await page.evaluate((selector) => + document.querySelector(selector).removeAttribute('checked'), + UNREAD_THREAD_CHECKBOX); // Also read the threads already read + + await page.click(ONLY_GAMES_THREAD_OPTION); + + // Filter the threads + await page.click(FILTER_THREADS_BUTTON); + await page.waitForSelector(WATCHED_THREAD_URLS); + + // Get the threads urls + let urls = []; + let nextPageExists = false; + do { + // Get all the URLs + for (let handle of await page.$$(WATCHED_THREAD_URLS)) { + let src = await page.evaluate((element) => element.href, handle); + // If 'unread' is left, it will redirect to the last unread post + let url = new URL(src.replace('/unread', '')); + urls.push(url); + } + + nextPageExists = await page.evaluate((selector) => + document.querySelector(selector), + WATCHED_THREAD_NEXT_PAGE); + + // Click to next page + if (nextPageExists) { + await page.click(WATCHED_THREAD_NEXT_PAGE); + await page.waitForSelector(WATCHED_THREAD_URLS); + } + } + while (nextPageExists); + + await page.close(); + return urls; } //#endregion User @@ -506,33 +595,42 @@ async function getUserWatchedGameThreads(){ * @private * Get information from the game's main page. * @param {puppeteer.Browser} browser Browser object used for navigation - * @param {GameInfo} info Partial game information + * @param {URL} url URL of the game/mod to extract data from * @return {Promise} Complete information about the game you are looking for */ -async function getGameInfo(browser, info) { - if(_debug) console.log('Obtaining game info'); +async function getGameInfo(browser, url) { + if (_debug) console.log('Obtaining game info'); // Verify the correctness of the URL - if (!isF95URL(info.f95url)) throw info.f95url + ' is not a valid F95Zone URL'; - let exists = await urlExist(info.f95url.toString()); + if (!isF95URL(url)) throw url + ' is not a valid F95Zone URL'; + let exists = await urlExist(url.toString()); if (!exists) return new GameInfo(); let page = await preparePage(browser); // Set new isolated page await page.setCookie(..._cookies); // Set cookies to avoid login - await page.goto(info.f95url.toString(), { + await page.goto(url.toString(), { waitUntil: WAIT_STATEMENT }); // Go to the game page and wait until it loads + // Object to fill with information + let info = new GameInfo(); + // Get the game/mod name (without square brackets) let title = getGameTitle(page); // Get the game/mod author (without square brackets) let author = getGameAuthor(page); + // Get the game tags + let tags = getGameTags(page); + // Get the game title image (the first is what we are searching) let previewSource = await getGamePreviewSource(page); if (previewSource === null) console.warn('Cannot find game preview image for ' + await title); + // Parse the prefixes + info = await parsePrefixes(page, info); // Fill status/engines/isMod + // Gets the first post, where are listed all the game's informations let post = (await page.$$(THREAD_POSTS))[0]; @@ -553,12 +651,14 @@ async function getGameInfo(browser, info) { info.name = await title; info.author = await author; info.overview = overview; + info.tags = await tags; + info.f95url = url; info.version = info.isMod ? parsedInfos['MOD VERSION'] : parsedInfos['VERSION']; info.lastUpdate = info.isMod ? parsedInfos['UPDATED'] : parsedInfos['THREAD UPDATED']; info.previewSource = previewSource; - + await page.close(); // Close the page - if(_debug) console.log('Founded data for ' + await title); + if (_debug) console.log('Founded data for ' + info.name); return info; } @@ -640,11 +740,55 @@ async function getGameTitle(page) { const structuredTitle = HTMLParser.parse(titleHTML); // The last element **shoud be** the title without prefixes (engines, status, other...) - var gameTitle = structuredTitle.childNodes.pop().rawText; + let gameTitle = structuredTitle.childNodes.pop().rawText; const endTitleIndex = gameTitle.indexOf('['); return gameTitle.substring(0, endTitleIndex).trim(); } +/** + * @private + * Get the list of tags associated with the game. + * @param {puppeteer.Page} page Page containing the tags to be extrapolated + * @returns {Promise} List of uppercase tags + */ +async function getGameTags(page) { + let tags = []; + + // Get the game tags + for (let handle of await page.$$(GAME_TAGS)) { + let tag = await page.evaluate((element) => element.innerText, handle); + tags.push(tag.toUpperCase()); + } + return tags; +} + +/** + * @private + * Process the game title prefixes to extract information such as game status, + * graphics engine used, and whether it is a mod or original game. + * @param {puppeteer.Page} page Page containing the prefixes to be extrapolated + * @param {GameInfo} info Object to assign the identified information to + * @returns {Promise} GameInfo object passed in to which the identified information has been added + */ +async function parsePrefixes(page, info) { + // The 'Ongoing' status is not specified, only 'Abandoned'/'OnHold'/'Complete' + info.status = 'Ongoing'; + for (let handle of await page.$$(GAME_TITLE_PREFIXES)) { + let value = await page.evaluate((element) => element.innerText, handle); + + // Clean the prefix + let prefix = value.toUpperCase().replace('[', '').replace(']', '').trim(); + + // Getting infos... + if (_statuses.includes(prefix)) info.status = prefix; + else if (_engines.includes(prefix)) info.engine = prefix; + + // This is not a game but a mod + else if (prefix === MOD_PREFIX) info.isMod = true; + } + return info; +} + /** * @deprecated * @param {puppeteer.Browser} browser @@ -680,7 +824,7 @@ async function getGameDownloadLink(browser, url) { * Search the F95Zone portal to find possible conversations regarding the game you are looking for. * @param {puppeteer.Browser} browser Browser object used for navigation * @param {String} gamename Name of the game to search for - * @returns {Promise} List of information obtained from the preliminary research on the F95 portal + * @returns {Promise} List of URL of possible games obtained from the preliminary research on the F95 portal */ async function getSearchGameResults(browser, gamename) { if (_debug) console.log('Searching ' + gamename + ' on F95Zone'); @@ -690,10 +834,12 @@ async function getSearchGameResults(browser, gamename) { await page.goto(F95_SEARCH_URL, { waitUntil: WAIT_STATEMENT }); // Go to the search form and wait for it + // Explicitly wait for the required items to load await page.waitForSelector(SEARCH_FORM_TEXTBOX); await page.waitForSelector(TITLE_ONLY_CHECKBOX); await page.waitForSelector(SEARCH_BUTTON); + await page.type(SEARCH_FORM_TEXTBOX, gamename) // Type the game we desire await page.click(TITLE_ONLY_CHECKBOX) // Select only the thread with the game in the titles await Promise.all([ @@ -707,13 +853,13 @@ async function getSearchGameResults(browser, gamename) { let threadTitleList = await page.$$(THREAD_TITLE); // For each title extract the info about the conversation - if(_debug) console.log('Extracting info from conversation titles'); + if (_debug) console.log('Extracting info from conversation titles'); let results = []; - for (const title of threadTitleList) { - var info = await getSearchThreadInfo(page, title); + for (let title of threadTitleList) { + let gameUrl = await getOnlyGameThreads(page, title); // Append the game's informations - if (info !== null) results.push(info); + if (gameUrl !== null) results.push(gameUrl); } if (_debug) console.log('Find ' + results.length + ' conversations'); await page.close(); // Close the page @@ -723,42 +869,26 @@ async function getSearchGameResults(browser, gamename) { /** * @private - * Starting from the title of a conversation on the F95 portal, - * he obtains basic information about the element (in case it is a game) + * Return the link of a conversation if it is a game or a mod * @param {puppeteer.Page} page Page containing the conversation to be analyzed * @param {puppeteer.ElementHandle} titleHandle Title of the conversation to be analyzed - * @return {Promise} Object containing the information obtained from the analysis or null if it is not a game conversation + * @return {Promise} URL of the game/mod */ -async function getSearchThreadInfo(page, titleHandle) { - - let info = new GameInfo(); - +async function getOnlyGameThreads(page, titleHandle) { // Get the URL of the thread from the title let relativeURLThread = await page.evaluate((element) => element.querySelector('a').href, titleHandle); - info.f95url = new URL(relativeURLThread, F95_BASE_URL); + let url = new URL(relativeURLThread, F95_BASE_URL); - // Select infos like the engine used for the game or it's status - // parsing the title - let elements = await titleHandle.$$('span[dir="auto"]'); - - // The 'Ongoing' status is not specified, only 'Abandoned'/'OnHold'/'Complete' are - info.status = 'Ongoing'; - for (let element of elements) { + // Parse prefixes to ignore game recommendation + for (let element of await titleHandle.$$('span[dir="auto"]')) { // Elaborate the prefixes let prefix = await page.evaluate(element => element.textContent.toUpperCase(), element); prefix = prefix.replace('[', '').replace(']', ''); // This is not a game nor a mod, we can exit if (prefix === GAME_RECOMMENDATION_PREFIX) return null; - - // Getting infos... - else if (_statuses.includes(prefix)) info.status = prefix; - else if (_engines.includes(prefix)) info.engine = prefix; - - // This is probably a mod for the game we are searching - else if (prefix === MOD_PREFIX) info.isMod = true; } - return info; + return url; } //#endregion Game search diff --git a/app/scripts/game-info.js b/app/scripts/game-info.js index 8be466d..598c28a 100644 --- a/app/scripts/game-info.js +++ b/app/scripts/game-info.js @@ -1,17 +1,17 @@ +const UNKNOWN = 'Unknown'; + class GameInfo { constructor() { - this.UNKNOWN = 'Unknown'; - /** * Game name * @type String */ - this.name = this.UNKNOWN; + this.name = UNKNOWN; /** * Game author * @type String */ - this.author = this.UNKNOWN; + this.author = UNKNOWN; /** * URL to the game's official conversation on the F95Zone portal * @type URL @@ -21,17 +21,22 @@ class GameInfo { * Game description * @type String */ - this.overview = this.UNKNOWN; + this.overview = UNKNOWN; + /** + * List of tags associated with the game + * @type String[] + */ + this.tags = []; /** * Graphics engine used for game development * @type String */ - this.engine = this.UNKNOWN; + this.engine = UNKNOWN; /** * Progress of the game * @type String */ - this.status = this.UNKNOWN; + this.status = UNKNOWN; /** * Game description image URL * @type URL @@ -41,17 +46,17 @@ class GameInfo { * Game version * @type String */ - this.version = this.UNKNOWN; + this.version = UNKNOWN; /** * Last time the game underwent updates * @type String */ - this.lastUpdate = this.UNKNOWN; + this.lastUpdate = UNKNOWN; /** * Last time the local copy of the game was run * @type String */ - this.lastPlayed = this.UNKNOWN; + this.lastPlayed = UNKNOWN; /** * Specifies if the game is original or a mod * @type Boolean @@ -61,7 +66,7 @@ class GameInfo { * Directory containing the local copy of the game * @type String */ - this.gameDir = this.UNKNOWN; + this.gameDir = UNKNOWN; } /** diff --git a/app/scripts/user-data.js b/app/scripts/user-data.js index 7103c8d..31de140 100644 --- a/app/scripts/user-data.js +++ b/app/scripts/user-data.js @@ -1,6 +1,24 @@ +/** + * Class containing the data of the user currently connected to the F95Zone platform. + */ class UserData { -constructor(){ - this.username = ""; - this.avatarSrc = null; + constructor() { + /** + * User username. + * @type String + */ + this.username = ""; + /** + * Path to the user 's profile picture. + * @type URL + */ + this.avatarSrc = null; + /** + * List of followed thread URLs. + * @type URL[] + */ + this.watchedThreads = []; + } } -} \ No newline at end of file + +module.exports.UserData = UserData; \ No newline at end of file diff --git a/app/test.js b/app/test.js index 028173d..ea79f12 100644 --- a/app/test.js +++ b/app/test.js @@ -1,4 +1,4 @@ -const { debug, login, close, getGameData, loadF95BaseData } = require("../app/index"); +const { debug, login, getGameData, loadF95BaseData, getUserData } = require("../app/index"); debug(true); main(); @@ -7,9 +7,10 @@ async function main() { let loginResult = await login("MillenniumEarl", "f9vTcRNuvxj4YpK"); if (loginResult.success) { - await loadF95BaseData(); - let data = await getGameData("kingdom of deception", false); + // await loadF95BaseData(); + // let data = await getGameData("kingdom of deception", false); + // console.log(data.pop()); + let data = await getUserData(); console.log(data); } - await close(); -} +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 407fc84..ccaab6f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -251,14 +251,12 @@ "version": "14.11.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.11.2.tgz", "integrity": "sha512-jiE3QIxJ8JLNcb1Ps6rDbysDhN4xa8DJJvuC9prr6w+1tIh+QAbYyNF3tyiZNLDBIuBCf4KEcV2UvQm/V60xfA==", - "dev": true, "optional": true }, "@types/yauzl": { "version": "2.9.1", "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz", "integrity": "sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA==", - "dev": true, "optional": true, "requires": { "@types/node": "*" @@ -268,7 +266,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "dev": true, "requires": { "event-target-shim": "^5.0.0" } @@ -276,8 +273,7 @@ "agent-base": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-5.1.1.tgz", - "integrity": "sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==", - "dev": true + "integrity": "sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==" }, "aggregate-error": { "version": "3.1.0", @@ -365,14 +361,12 @@ "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "base64-js": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", - "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", - "dev": true + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" }, "binary-extensions": { "version": "2.1.0", @@ -384,7 +378,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.3.tgz", "integrity": "sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg==", - "dev": true, "requires": { "buffer": "^5.5.0", "inherits": "^2.0.4", @@ -395,7 +388,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -420,7 +412,6 @@ "version": "5.6.0", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", - "dev": true, "requires": { "base64-js": "^1.0.2", "ieee754": "^1.1.4" @@ -429,8 +420,7 @@ "buffer-crc32": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", - "dev": true + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" }, "caching-transform": { "version": "4.0.0", @@ -500,8 +490,7 @@ "chownr": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "dev": true + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" }, "clean-stack": { "version": "2.2.0", @@ -544,8 +533,7 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "convert-source-map": { "version": "1.7.0", @@ -571,7 +559,6 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", - "dev": true, "requires": { "ms": "2.1.2" } @@ -612,8 +599,7 @@ "devtools-protocol": { "version": "0.0.799653", "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.799653.tgz", - "integrity": "sha512-t1CcaZbvm8pOlikqrsIM9GOa7Ipp07+4h/q9u0JXBWjPCjHdBl9KkddX87Vv9vBHoBGtwV79sYQNGnQM6iS5gg==", - "dev": true + "integrity": "sha512-t1CcaZbvm8pOlikqrsIM9GOa7Ipp07+4h/q9u0JXBWjPCjHdBl9KkddX87Vv9vBHoBGtwV79sYQNGnQM6iS5gg==" }, "diff": { "version": "4.0.2", @@ -631,7 +617,6 @@ "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, "requires": { "once": "^1.4.0" } @@ -708,14 +693,12 @@ "event-target-shim": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "dev": true + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" }, "extract-zip": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", - "dev": true, "requires": { "@types/yauzl": "^2.9.1", "debug": "^4.1.1", @@ -727,7 +710,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", - "dev": true, "requires": { "pend": "~1.2.0" } @@ -756,7 +738,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, "requires": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -790,14 +771,12 @@ "fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "dev": true + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "fsevents": { "version": "2.1.3", @@ -840,7 +819,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, "requires": { "pump": "^3.0.0" } @@ -849,7 +827,6 @@ "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -920,8 +897,7 @@ "he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" }, "html-escaper": { "version": "2.0.2", @@ -933,7 +909,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz", "integrity": "sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==", - "dev": true, "requires": { "agent-base": "5", "debug": "4" @@ -942,8 +917,7 @@ "ieee754": { "version": "1.1.13", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", - "dev": true + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" }, "imurmurhash": { "version": "0.1.4", @@ -961,7 +935,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -970,14 +943,12 @@ "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "ip-regex": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.2.0.tgz", - "integrity": "sha512-n5cDDeTWWRwK1EBoWwRti+8nP4NbytBBY0pldmnIkq6Z55KNFmWofh4rl9dPZpj+U/nVq7gweR3ylrvMt4YZ5A==", - "dev": true + "integrity": "sha512-n5cDDeTWWRwK1EBoWwRti+8nP4NbytBBY0pldmnIkq6Z55KNFmWofh4rl9dPZpj+U/nVq7gweR3ylrvMt4YZ5A==" }, "is-arguments": { "version": "1.0.4", @@ -1097,7 +1068,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-url-superb/-/is-url-superb-3.0.0.tgz", "integrity": "sha512-3faQP+wHCGDQT1qReM5zCPx2mxoal6DzbzquFlCYJLWyy4WPTved33ea2xFbX37z4NoriEwZGIYhFtx8RUB5wQ==", - "dev": true, "requires": { "url-regex": "^5.0.0" } @@ -1269,14 +1239,12 @@ "ky": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/ky/-/ky-0.19.1.tgz", - "integrity": "sha512-ZwciYrfaWpDI72U2HAruuGYGFW3PCfGNdWWSANGGssg9BGm4rRJ9s/sApiiRpj+8Y245/hlZW9c60zudLr6iwA==", - "dev": true + "integrity": "sha512-ZwciYrfaWpDI72U2HAruuGYGFW3PCfGNdWWSANGGssg9BGm4rRJ9s/sApiiRpj+8Y245/hlZW9c60zudLr6iwA==" }, "ky-universal": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/ky-universal/-/ky-universal-0.5.0.tgz", "integrity": "sha512-O+0wjCua5i45lYBZrBy8TyRDRVodtsmzVC/MlE5FN7ZMFu/Icz7ekbZ85sdFw0F/JwGhXZTaKjXq9IgUGwGedQ==", - "dev": true, "requires": { "abort-controller": "^3.0.0", "node-fetch": "^2.6.0" @@ -1286,7 +1254,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, "requires": { "p-locate": "^4.1.0" } @@ -1377,7 +1344,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -1391,8 +1357,7 @@ "mkdirp-classic": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "dev": true + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" }, "mocha": { "version": "8.1.3", @@ -1632,20 +1597,17 @@ "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node-fetch": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", - "dev": true + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" }, "node-html-parser": { "version": "1.2.21", "resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-1.2.21.tgz", "integrity": "sha512-6vDhgen6J332syN5HUmeT4FfBG7m6bFRrPN+FXY8Am7FGuVpsIxTASVbeoO5PF2IHbX2s+WEIudb1hgxOjllNQ==", - "dev": true, "requires": { "he": "1.2.0" } @@ -1728,7 +1690,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1" } @@ -1737,7 +1698,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, "requires": { "p-try": "^2.0.0" } @@ -1746,7 +1706,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, "requires": { "p-limit": "^2.2.0" } @@ -1763,8 +1722,7 @@ "p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" }, "package-hash": { "version": "4.0.0", @@ -1781,14 +1739,12 @@ "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-key": { "version": "3.1.1", @@ -1811,8 +1767,7 @@ "pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", - "dev": true + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" }, "picomatch": { "version": "2.2.2", @@ -1824,7 +1779,6 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, "requires": { "find-up": "^4.0.0" } @@ -1841,8 +1795,7 @@ "progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" }, "promise.allsettled": { "version": "1.0.2", @@ -1860,14 +1813,12 @@ "proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "dev": true + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, "pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, "requires": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -1877,7 +1828,6 @@ "version": "5.3.1", "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-5.3.1.tgz", "integrity": "sha512-YTM1RaBeYrj6n7IlRXRYLqJHF+GM7tasbvrNFx6w1S16G76NrPq7oYFKLDO+BQsXNtS8kW2GxWCXjIMPvfDyaQ==", - "dev": true, "requires": { "debug": "^4.1.0", "devtools-protocol": "0.0.799653", @@ -1905,7 +1855,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -1961,7 +1910,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, "requires": { "glob": "^7.1.3" } @@ -2075,7 +2023,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, "requires": { "safe-buffer": "~5.2.0" }, @@ -2083,8 +2030,7 @@ "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" } } }, @@ -2122,7 +2068,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.0.tgz", "integrity": "sha512-9uW5iDvrIMCVpvasdFHW0wJPez0K4JnMZtsuIeDI7HyMGJNxmDZDOCQROr7lXyS+iL/QMpj07qcjGYTSdRFXUg==", - "dev": true, "requires": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", @@ -2134,7 +2079,6 @@ "version": "2.1.4", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.4.tgz", "integrity": "sha512-o3pS2zlG4gxr67GmFYBLlq+dM8gyRGUOvsrHclSkvtVtQbjV0s/+ZE8OpICbaj8clrX3tjeHngYGP7rweaBnuw==", - "dev": true, "requires": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", @@ -2157,14 +2101,12 @@ "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" }, "tlds": { "version": "1.210.0", "resolved": "https://registry.npmjs.org/tlds/-/tlds-1.210.0.tgz", - "integrity": "sha512-5bzt4JE+NlnwiKpVW9yzWxuc44m+t2opmPG+eSKDp5V5qdyGvjMngKgBb5ZK8GiheQMbRTCKpRwFJeIEO6pV7Q==", - "dev": true + "integrity": "sha512-5bzt4JE+NlnwiKpVW9yzWxuc44m+t2opmPG+eSKDp5V5qdyGvjMngKgBb5ZK8GiheQMbRTCKpRwFJeIEO6pV7Q==" }, "to-fast-properties": { "version": "2.0.0", @@ -2206,7 +2148,6 @@ "version": "1.4.3", "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", - "dev": true, "requires": { "buffer": "^5.2.1", "through": "^2.3.8" @@ -2216,7 +2157,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/url-exist/-/url-exist-2.0.2.tgz", "integrity": "sha512-JqLjYS8pU9xZtY3ro4c54CztoP5R8qRyMlg2Cxr4M9YD1NCe57MOsZHF1rP3y+qQcc7cqiZBBd4Cu5oehcJRlQ==", - "dev": true, "requires": { "is-url-superb": "^3.0.0", "ky": "^0.19.0", @@ -2227,7 +2167,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/url-regex/-/url-regex-5.0.0.tgz", "integrity": "sha512-O08GjTiAFNsSlrUWfqF1jH0H1W3m35ZyadHrGv5krdnmPPoxP27oDTqux/579PtaroiSGm5yma6KT1mHFH6Y/g==", - "dev": true, "requires": { "ip-regex": "^4.1.0", "tlds": "^1.203.0" @@ -2236,8 +2175,7 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "uuid": { "version": "3.4.0", @@ -2349,8 +2287,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "write-file-atomic": { "version": "3.0.3", @@ -2367,8 +2304,7 @@ "ws": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz", - "integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==", - "dev": true + "integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==" }, "y18n": { "version": "4.0.0", @@ -2547,7 +2483,6 @@ "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", - "dev": true, "requires": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" diff --git a/package.json b/package.json index fa59d3a..b6455ef 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,7 @@ "main": "./app/index.js", "scripts": { "unit-test": "nyc --reporter=text mocha", - "test": "node ./app/test.js", - "debug": "nodemon --inspect ." + "test": "node ./app/test.js" }, "keywords": [ "f95zone",