diff --git a/.github/Contributing.md b/.github/Contributing.md index 3febbca7..a4d076dc 100755 --- a/.github/Contributing.md +++ b/.github/Contributing.md @@ -1,4 +1,26 @@ -# Before submitting your contribution, please read the following: +# Contributing Guide +This guide will tell you how you can and should contribute to JokeAPI. +Not following it might cause me to reject your changes but at the very least we will both lose time. +So please read this guide before contributing. Thanks :) + +## Menu: +- [Submitting or editing jokes](#submitting-or-editing-jokes) +- [Contributing to JokeAPI's code](#submitting-code) +- [Submitting a translation](#submitting-translations) +- [Tips and Tricks for contributing](#other-nice-to-know-stuff) + +



+ +## Submitting or editing jokes: +To submit a joke manually, you can use the form on [this page.](https://sv443.net/jokeapi/v2/#submit) +To submit it through code, you can make use of the ["submit" endpoint.](https://sv443.net/jokeapi/v2/#submit-endpoint) + +If you instead want to *edit* a joke, you can find them in the `jokes-xy.json` files in [`data/jokes/`](../data/jokes/) +Please then follow the [code contribution section](#submitting-code) as well. + +

+ +## Submitting code: 1. [Read the Code_of_Conduct.md file](./Code_of_Conduct.md) (TLDR: just behave in a friendly manner). 2. [Click here](https://github.com/Sv443/JokeAPI/fork) to fork the repository. Afterwards, clone or download it and locate the folder where it is contained. 3. Make the changes you want to make to the code. @@ -7,8 +29,8 @@ - `npm run validate-ids` to verify that all jokes have the correct ID. - `npm run lint` to check the code for any warnings or errors. 5. Run JokeAPI locally by running the command `node JokeAPI`, request some jokes and test the areas you modified / added to make sure everything still works. -6. Add yourself to the `contributors` object in the file `package.json` :) - - **If it doesn't exist or is empty** please add it using the second format on [this website](https://flaviocopes.com/package-json/#contributors) +6. Add yourself to the `contributors` object in the [`package.json`](../package.json) file :) + 7. Submit a pull request on your forked repository, selecting `Sv443/JokeAPI` as the base repo and `master` as the base branch and selecting `YourUsername/JokeAPI` as the head repo and `YourBranch` as the compare branch - If your pull request is not ready to be merged yet, you can add `[WIP]` to the beginning of the title which will tell the repo maintainer(s) and automated scripts not to merge it yet. 8. Request a review from me (Sv443). @@ -19,9 +41,20 @@

-### Other nice-to-know stuff: +## Submitting Translations: +If you want to submit a translation, please follow these steps: +1. Find your language's two-character code in the file [`data/languages.json`](../data/languages.json). You'll need to specify it for every translation. +2. Translate coded error messages in the file [`data/errorMessages.js`](../data/errorMessages.js) by following the style of the other translations. +3. Translate the generic strings inside of the file [`data/translations.json`](../data/translations.json) by also following the style of the other translations. +4. Add yourself to the `contributors` object in the [`package.json`](../package.json) file :) + +

+ +## Other nice-to-know stuff: - I really recommend using [Visual Studio Code](https://code.visualstudio.com/) with the extension [`fabiospampinato.vscode-highlight`](https://marketplace.visualstudio.com/items?itemName=fabiospampinato.vscode-highlight) - it will add custom styling to the syntax highlighting in the editor and make the code easier to read and work with. -- If you want to generate a dependency graph, you need to install [Graphviz](https://graphviz.gitlab.io/download/) and add the path to the `bin` folder to your `%PATH%` environment vaiable. Then, run the command `npm run dependency-graph` and open the file `./dev/dependency-graph.html` in a browser. -- If you need to add an authorization token, you can generate one or multiple tokens with the command `npm run add-token [amount]`. If you omit the "amount" parameter, the script will generate a single token. After you run the command, the tokens will be listed in the console and you can now (after restarting JokeAPI) use it in the `x-api-token` header to gain unlimited access to JokeAPI. +- If you want to generate a dependency graph, you need to install [Graphviz](https://graphviz.gitlab.io/download/) and add the path to the `bin` folder to your `%PATH%` / `$PATH` environment vaiable. Then, run the command `npm run dependency-graph` and open the file [`dev/dependency-graph.html`](../dev/dependency-graph.html) in a browser. +- If you need to add an authorization token, you can generate one or multiple tokens with the command `npm run add-token [amount]`. If you omit the "amount" parameter, the script will generate a single token. After you run the command, the tokens will be listed in the console and you can now (after restarting JokeAPI) use it in the `Authorization` header to gain unlimited access to JokeAPI. + +

-## If you need any help, please feel free to contact me through [Discord](https://sv443.net/discord) or [E-Mail](mailto:sven.fehler@web.de?subject=Questions%20about%20contributing%20to%20JokeAPI) +## If you need any help, feel free to contact me through [Discord](https://sv443.net/discord) (fastest way to contact me) or [E-Mail](mailto:contact@sv443.net?subject=Questions%20about%20contributing%20to%20JokeAPI) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..9617f008 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,27 @@ + + +## Description + + +## Related Issue + + +## Motivation and Context + + +## How Has This Been Tested? + + + + +## Screenshots (if appropriate): + + +## Checklist +- [ ] I have read the [Contributing Guide](../Contributing.md) +- [ ] I read and accept the [Code of Conduct](../Code_of_Conduct.md) (TLDR: just behave in a friendly manner) +- [ ] My code follows the general style of this project +- [ ] I have commented my code, particularly in hard-to-understand areas +- [ ] I have made corresponding changes to the documentation (if applicable) +- [ ] I ensured that the automated checks that ran on this PR have completed successfully +- [ ] I added myself to the `package.json` file (optional) diff --git a/.github/SECURITY.md b/.github/SECURITY.md index 391e963d..fccc758c 100755 --- a/.github/SECURITY.md +++ b/.github/SECURITY.md @@ -6,9 +6,10 @@ Please **do not** create a GitHub issue! ## Supported Versions These versions are still supported and will be receiving security updates: -| Version | Supported | -| --- | --- | -| v1.0 | ❌ | -| v1.1 | ❌ | -| v2.0 | ❌ | -| v2.1 | ✅ | +| Version | Supported | Status | +| --- | --- | --- | +| [v1.0](https://github.com/Sv443/JokeAPI/releases/tag/v1.0.0) | ❌ | deprecated | +| [v1.1](https://github.com/Sv443/JokeAPI/releases/tag/v1.1.3) | ❌ | deprecated | +| [v2.0](https://github.com/Sv443/JokeAPI/releases/tag/v2.0.1) | ❌ | outdated | +| [v2.1](https://github.com/Sv443/JokeAPI/releases/tag/v2.1.5) | ❌ | outdated | +| [v2.2](https://github.com/Sv443/JokeAPI/releases/tag/v2.2.0) | ✅ | active | diff --git a/.gitignore b/.gitignore index 1b2256c7..5e597a59 100755 --- a/.gitignore +++ b/.gitignore @@ -16,19 +16,17 @@ node_modules/ dev/madge/ dependency-graph.html test.js +!tools/test.js # Docs: docs/documentation.html docs/compiled # Joke submissions: -data/submissions/submission_*.json - -# Re-inclusions: -!change.log* +data/submissions/*/*.json # Auth Tokens: data/tokens.json # Other potentially sensitive information: -lists/*.json +data/lists/*.json diff --git a/.snyk b/.snyk index b7b2e9f2..fab44285 100755 --- a/.snyk +++ b/.snyk @@ -1,5 +1,5 @@ # Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities. -version: v1.14.1 +version: v1.13.5 ignore: {} # patches apply the minimum changes required to fix a vulnerability patch: diff --git a/.vscode/launch.json b/.vscode/launch.json index 78c32d56..9c1b4bbf 100755 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,14 +4,30 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ + { + "program": "${workspaceFolder}/tools/add-joke.js", + "name": "Tools/xy", + "request": "launch", + "type": "node", + "skipFiles": [ + "/**" + ], + "args": [ + "--trace-deprecation" + ], + "console": "integratedTerminal" + }, { "type": "node", "request": "launch", - "name": "Launch Program", + "name": "Debug JokeAPI", "skipFiles": [ "/**" ], - "program": "${workspaceFolder}/JokeAPI.js" + "program": "${workspaceFolder}/JokeAPI.js", + "args": [ + "--trace-deprecation" + ] } ], "debug.javascript.usePreview": true diff --git a/.vscode/settings.json b/.vscode/settings.json index dab965ab..be99b74b 100755 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -13,7 +13,7 @@ "color": "#f88" } ], - "(#MARKER)": [ // #MARKER test + "(#MARKER)": [ //#MARKER test { "backgroundColor": "#f41", "color": "#fff", @@ -21,19 +21,27 @@ "overviewRulerColor": "#f41" } ], - "(#SECTION [a-zA-Z0-9_-]+)": [ //#SECTION test + "(#SECTION [a-zA-Z0-9_-,]+)": [ //#SECTION test, foo bar { "backgroundColor": "#44f", "color": "white", "overviewRulerColor": "#44f" } ], - "(#DEBUG#)": [ + "(#DEBUG)": [ //#DEBUG { "backgroundColor": "#ff0", "color": "blue", "overviewRulerColor": "#ff0" } + ], + "(?JokeAPI ### A REST API that serves uniformly and well formatted jokes and offers a great variety of filtering methods and response customization [![GitHub](https://img.shields.io/github/license/Sv443/JokeAPI)](https://sv443.net/LICENSE) -[![Uptime / 7 Days](https://img.shields.io/uptimerobot/ratio/7/m782998549-afcc0d10c27c87df47e73289)](https://status.sv443.net/) +[![Uptime / 7 Days](https://img.shields.io/uptimerobot/ratio/7/m784261094-bff76b959ebb8fc39f7eb2d0)](https://status.sv443.net/) [![Open Issues](https://img.shields.io/github/issues/Sv443/JokeAPI)](https://github.com/Sv443/JokeAPI/issues) [![Actions Status](https://github.com/Sv443/JokeAPI/workflows/build/badge.svg)](https://github.com/Sv443/JokeAPI/actions) [![Known Vulnerabilities](https://snyk.io/test/github/Sv443/JokeAPI/badge.svg)](https://snyk.io/test/github/Sv443/JokeAPI) [![Discord](https://img.shields.io/discord/565933531214118942)](https://discord.gg/aBH4uRG) -[![GitHub watchers](https://img.shields.io/github/watchers/Sv443/JokeAPI?style=social)](https://github.com/Sv443/JokeAPI/watchers) [![GitHub stars](https://img.shields.io/github/stars/Sv443/JokeAPI?style=social)](https://github.com/Sv443/JokeAPI/stargazers)

-# [Documentation](https://sv443.net/jokeapi/v2) • [Try it out](https://sv443.net/jokeapi/v2#try-it) • [Changelog](./changelog.txt) +# [Documentation](https://sv443.net/jokeapi/v2) +# [Try it out](https://sv443.net/jokeapi/v2#try-it) • [Changelog](./changelog.txt)

-## Before contributing to JokeAPI, please read the [contributing guide](./.github/Contributing.md) +## If you want to contribute to JokeAPI (code, jokes or translations), please read the [contributing guide.](./.github/Contributing.md)

@@ -37,6 +37,7 @@ - [Jokepy](https://github.com/aksty/Jokepy) by [aksty](https://github.com/aksty) - [Dark1](https://github.com/whiteadi/Dark1) by [whiteadi](https://github.com/whiteadi) - [Prejudice Networks](https://github.com/LiamTownsley/Prejudice-Networks) by [Liam Townsley](https://github.com/LiamTownsley) +- [https://irshad.ml/humour.html](https://irshad.ml/humour.html) by [draco-malfoy](https://github.com/draco-malfoy) ([Contact me](https://sv443.net/discord) to get your project added here) diff --git a/changelog.txt b/changelog.txt old mode 100755 new mode 100644 index 78b47916..b585d82a --- a/changelog.txt +++ b/changelog.txt @@ -1,29 +1,35 @@ -==================== +===================== + JokeAPI Changelog + - Version 2.2.0 - +===================== - JokeAPI Changelog - - Version 2.1.5 - - -==================== +[Planned for future releases] + - Allow definition of max requests per minute per each client (issue #37) + - Add positive flags and a "?whitelistFlags" param (issue #127) + - Add Unit Tests (issue #121) + - Serve docs with nginx to speed up page load times (issue #118) -[PLANNED: 2.2.0] - - Add joke category "Pun" (issue #105) - - Allow definition of max requests per minute per each client (issue #37) - - Add support for jokes and error messages of different languages (issue #75) +[CURRENT: 2.2.0] + - Added joke category "Pun" (issue #105) + - Added "?amount" parameter to joke endpoint so multiple jokes can be fetched at once (issue #126) + - Added support for jokes and error messages of different languages (issue #75) - /langcode/{LANGUAGE} endpoint - /languages endpoint - "?lang=code" URL parameter - - Fix ID caching (again, sigh) (issue #80) - - Add pm2 custom metrics (issue #91) - - Fix HTTP 403 errors (issue #96) - - Remake the URL parser using a "commercial" package (issue #97) - - Daemonize the API token refreshing (issue #102) - - Rate limit joke submissions more harshly (issue #104) - - Fix resources not being served with Brotli even though it is supported by the client (issue #106) - - -[CURRENT: 2.1.5] - 2020 Q3 general patch #2 + - Fixed ID caching (again, sigh) (issue #80) + - Added pm2 custom metrics (issue #91) + - Fixed HTTP 403 errors (issue #96) + - Remade the URL parser using a package (issue #97) + - Daemonized the API token refreshing (issue #102) + - Rate limiting joke submissions more harshly now (issue #104) + - Fixed error where the end of the payload were cut off, thus invalidating JSON (issue #119) + - Joke submission property order is now enforced, improving uniformity (issue #120) + - Joke submissions are now validated to make sure they don't contain fancy Unicode chars (issue #123) + + +[2.1.5] - 2020 Q3 general patch #2 - Ditched my botched rate limiting package for a "commercial" one (issue #113) - Added API token section to documentation (issue #114) - Client now receives a "Token-Valid" header with the value 0 or 1 depending on token validity (issue #115) diff --git a/data/errorMessages.js b/data/errorMessages.js new file mode 100644 index 00000000..fa1924d4 --- /dev/null +++ b/data/errorMessages.js @@ -0,0 +1,214 @@ +const settings = require("../settings"); + +module.exports = { + //#MARKER Class 1xx (HTTP) + "100": { + "errorInternal": true, + "errorMessage": { + "en": "Internal Error in HTTP Server", + "de": "Interner Error im HTTP Server", + "ru": "Внутренняя ошибка в HTTP-сервере" + }, + "causedBy": { + "en": [ + `An error in the code - please contact me through one of the options on my website (${settings.info.author.website}) with the additional info.` + ], + "de": [ + `Ein Error im Quellcode - bitte kontaktiere mich durch eine der Möglichkeiten auf meiner Website (${settings.info.author.website}) mit den zusätzlichen Informationen.` + ], + "ru": [ + `Ошибка в коде - пожалуйста, свяжитесь со мной через одну из опций на моем сайте (${settings.info.author.website}) с дополнительной информацией.` + ] + } + }, + "101": { + "errorInternal": false, + "errorMessage": { + "en": "Request blocked by Rate Limiting", + "de": "Anfrage blockiert durch Ratenbegrenzung", + "ru": "Запрос заблокирован ограничением скорости" + }, + "causedBy": { + "en": [ + `You have sent too many requests too quickly. The limit is ${settings.httpServer.rateLimiting} requests within ${settings.httpServer.timeFrame} ${settings.httpServer.timeFrame == 1 ? "second" : "seconds"}.\nIf you need more requests per minute, please contact me and we can try to figure things out: ${settings.info.author.website}` + ], + "de": [ + `Du hast zu viele Anfragen zu schnell gesendet. Das Limit ist ${settings.httpServer.rateLimiting} Anfragen innerhalb von ${settings.httpServer.timeFrame} ${settings.httpServer.timeFrame == 1 ? "Sekunde" : "Sekunden"}.\nWenn du mehr Anfragen pro Minute brauchst, kontaktiere mich bitte, damit wir es klären können: ${settings.info.author.website}` + ], + "ru": [ + `Вы отправили слишком много запросов слишком быстро. Лимит составляет ${settings.httpServer.rateLimiting} запросов в пределах ${settings.httpServer.timeFrame} ${settings.httpServer.timeFrame == 1 ? "секунда" : "секунды"}.\nЕсли Вам нужно больше запросов в минуту, пожалуйста, свяжитесь со мной, и мы попробуем разобраться в этом: ${settings.info.author.website}` + ] + } + }, + "102": { + "errorInternal": false, + "errorMessage": { + "en": "Requested Endpoint not found", + "de": "Angefragten Endpunkt nicht gefunden", + "ru": "Запрашиваемая конечная точка не найдена" + }, + "causedBy": { + "en": [ + "You sent a request to the wrong URL." + ], + "de": [ + "Du hast eine Anfrage an die falsche URL gesendet." + ], + "ru": [ + "Вы отправили запрос на неправильный URL-адрес." + ] + } + }, + "103": { + "errorInternal": false, + "errorMessage": { + "en": "Disreputable IP Address", + "de": "In schlechtem Ruf stehende IP Addresse", + "ru": "Дискредитирующий IP-адрес" + }, + "causedBy": { + "en": [ + `${settings.info.name} has found your IP address to be disreputable and added it to the blacklist.\nThis is probably because you have shown malicious behavior like an attempted interruption of ${settings.info.name}'s service.\n\nIf you believe this was done in error, please contact me (${settings.info.author.website}) so we can sort things out.`, + ], + "de": [ + `${settings.info.name} ist aufgefallen, dass deine IP Addresse einen schlechten Ruf hat und hat sie in die Ignorierungsliste eingetragen.\nDas ist wahrscheinlich passiert, weil du böswilliges Verhalten gezeigt hast, wie beispielsweise eine Unterbrechung von ${settings.info.name}'s Betrieb.\n\nWenn du meinst, das wurde fälschlicherweise gemacht, bitte kontaktiere mich hier: (${settings.info.author.website}).` + ], + "ru": [ + `${settings.info.name} нашел ваш IP-адрес неблаговидным и добавил его в черный список.\nВероятно, это потому, что вы показали вредоносное поведение, например, попытку прерывания сервиса ${settings.info.name}.\n\nЕсли вы считаете, что это было сделано по ошибке, пожалуйста, свяжитесь со мной (${settings.info.author.website}), чтобы мы могли разобраться в этом.` + ] + } + }, + "104": { + "errorInternal": true, + "errorMessage": { + "en": "Internal Error while calling Endpoint", + "de": "Interner Error während des Aufrufs eines Endpunktes", + "ru": "Внутренняя ошибка при вызове конечной точки" + }, + "causedBy": { + "en": [ + `An error in the code - please contact me through one of the options on my website (${settings.info.author.website}) with the additional info.` + ], + "de": [ + `Ein Error im Quellcode - bitte kontaktiere mich durch eine der Möglichkeiten auf meiner Website (${settings.info.author.website}) mit den zusätzlichen Informationen.` + ], + "ru": [ + `Ошибка в коде - пожалуйста, свяжитесь со мной через одну из опций на моем сайте (${settings.info.author.website}) с дополнительной информацией.` + ] + } + }, + "105": { + "errorInternal": false, + "errorMessage": { + "en": "Malformed Joke", + "de": "Falsch formatierter Witz", + "ru": "Малоформальная шутка" + }, + "causedBy": { + "en": [ + "This joke was formatted incorrectly." + ], + "de": [ + "Dieser Witz wurde nicht korrekt formatiert." + ], + "ru": [ + "Эта шутка была отформатирована неправильно." + ] + } + }, + "106": { + "errorInternal": false, + "errorMessage": { + "en": "No matching joke found", + "de": "Kein übereinstimmender Witz gefunden", + "ru": "Шутка не найдена" + }, + "causedBy": { + "en": [ + "No jokes were found that match your provided filter(s)." + ], + "de": [ + "Keine Witze wurden gefunden, die den Filtern entsprechen." + ], + "ru": [ + "Не было найдено ни одной шутки, которая бы соответствовала вашему фильтру(ам)." + ] + } + }, + "107": { + "errorInternal": false, + "errorMessage": { + "en": "Payload too large", + "de": "Anfrageinhalt zu groß", + "ru": "Слишком большое содержание запроса" + }, + "causedBy": { + "en": [ + `The provided payload exceeds the limit of ${settings.httpServer.maxPayloadSize} bytes (${(settings.httpServer.maxPayloadSize / 1024).toFixed(1)} kB).` + ], + "de": [ + `Der Anfrageinhalt ist größer als das Maximum von ${settings.httpServer.maxPayloadSize} bytes (${(settings.httpServer.maxPayloadSize / 1024).toFixed(1)} kB).` + ], + "ru": [ + `Содержимое запроса больше максимального значения ${settings.httpServer.maxPayloadSize} байт (${(settings.httpServer.maxPayloadSize / 1024).toFixed(1)} kB).` + ] + } + }, + "108": { + "errorInternal": false, + "errorMessage": { + "en": "URL too long", + "de": "URL zu lang", + "ru": "URL-адрес слишком длинный" + }, + "causedBy": { + "en": [ + `The URL (%1 characters) exceeds the maximum length of ${settings.httpServer.maxUrlLength} characters.` + ], + "de": [ + `Die angefragte URL (%1 Zeichen) überschreitet die Maximallänge von ${settings.httpServer.maxUrlLength} Zeichen.` + ], + "ru": [ + `Длина URL-адрес (%1 символов) превышает максимально допустимую длину в ${settings.httpServer.maxUrlLength} символа.` + ], + } + }, + "109": { + "errorInternal": false, + "errorMessage": { + "en": "Contains invalid characters", + "de": "Enthält unerlaubte Zeichen", + "ru": "Содержит недопустимые символы" + }, + "causedBy": { + "en": [ + `The joke submission contains invalid characters outside the Unicode range of 0x0000 to 0x0fff` + ], + "de": [ + `Der eingereichte Witz enthält unerlaubte Zeichen außerhalb des Unicode-Bereichs 0x0000 bis 0x0fff` + ], + "ru": [ + `Представленный анекдот содержит недопустимые символы вне диапазона Юникода от 0x0000 до 0x0fff` + ], + } + }, + "110": { + "errorInternal": false, + "errorMessage": { + "en": "Submission blocked by Rate Limiting", + "de": "Einreichung blockiert durch Ratenbegrenzung", + "ru": "Представление заблокировано ограничением по тарифу" + }, + "causedBy": { + "en": [ + `You have sent too many requests too quickly. The limit is ${settings.jokes.submissions.rateLimiting} submissions within ${settings.jokes.submissions.timeFrame} ${settings.jokes.submissions.timeFrame == 1 ? "second" : "seconds"}.\nIf you need to send more requests, please either wait for a bit or contact me and we can try to figure things out: ${settings.info.author.website}` + ], + "de": [ + `Du hast zu viele Anfragen zu schnell gesendet. Das Limit ist ${settings.jokes.submissions.rateLimiting} Einreichungen innerhalb von ${settings.jokes.submissions.timeFrame} ${settings.jokes.submissions.timeFrame == 1 ? "Sekunde" : "Sekunden"}.\nWenn du mehr Anfragen pro Minute brauchst, kontaktiere mich bitte, damit wir es klären können: ${settings.info.author.website}` + ], + "ru": [ + `Вы отправили слишком много запросов слишком быстро. Лимит - ${settings.jokes.submissions.rateLimiting} представления в пределах ${settings.jokes.submissions.timeFrame} ${settings.jokes.submissions.timeFrame == 1 ? "секунда" : "секунды" }.\nЕсли Вам нужно отправить больше запросов, пожалуйста, либо подождите немного, либо свяжитесь со мной, и мы попробуем разобраться в этом: ${settings.info.author.website}` + ] + } + } +} diff --git a/data/errorRegistry.js b/data/errorRegistry.js deleted file mode 100755 index 156f6981..00000000 --- a/data/errorRegistry.js +++ /dev/null @@ -1,69 +0,0 @@ -const settings = require("../settings"); - -module.exports = { - //#MARKER Class 1xx (HTTP) - "100": { - "errorInternal": true, - "errorMessage": "Internal Error in HTTP Server", - "causedBy": [ - `An error in the code - please contact me through one of the options on my website (${settings.info.author.website}) with the additional info below.` - ] - }, - "101": { - "errorInternal": false, - "errorMessage": "Request Blocked by Rate Limiting", - "causedBy": [ - `You have sent too many requests too quickly. The limit is ${settings.httpServer.rateLimiting} requests within ${settings.httpServer.timeFrame} ${settings.httpServer.timeFrame == 1 ? "minute" : "minutes"}.\nIf you need more requests per minute, please contact me and we can try to figure things out: ${settings.info.author.website}` - ] - }, - "102": { - "errorInternal": false, - "errorMessage": "Requested Endpoint not found", - "causedBy": [ - "You sent a request to the wrong URL." - ] - }, - "103": { - "errorInternal": false, - "errorMessage": "Disreputable IP Address", - "causedBy": [ - `${settings.info.name} has found your IP address to be disreputable and added it to the blacklist.\nThis is probably because you have shown malicious behavior like an attempted interruption of ${settings.info.name}'s service.\n\nIf you believe this was done in error, please contact me (${settings.info.author.website}) so we can sort things out.` - ] - }, - "104": { - "errorInternal": true, - "errorMessage": "Internal Error while calling Endpoint", - "causedBy": [ - `An error in the code - please contact me through one of the options on my website (${settings.info.author.website}) with the additional info below.` - ] - }, - "105": { - "errorInternal": false, - "errorMessage": "Malformed Joke", - "causedBy": [ - "This joke was formatted incorrectly." - ] - }, - "106": { - "errorInternal": false, - "errorMessage": "No matching joke found", - "causedBy": [ - "No jokes were found that match your provided filter(s)" - ] - }, - "107": { - "errorInternal": false, - "errorMessage": "Payload too large", - "causedBy": [ - `The provided payload exceeds the limit of ${settings.httpServer.maxPayloadSize} bytes (${(settings.httpServer.maxPayloadSize / 1024).toFixed(1)} kB)` - ] - }, - "108": { - "errorInternal": false, - "errorMessage": "URI Too Long", - "causedBy": [ - `The URL exceeds the maximum length of ${settings.httpServer.maxUrlLength} characters` - ] - } - //#MARKER Class 2xx -} \ No newline at end of file diff --git a/data/jokes/jokes-de.json b/data/jokes/jokes-de.json new file mode 100644 index 00000000..a962f50d --- /dev/null +++ b/data/jokes/jokes-de.json @@ -0,0 +1,376 @@ +{ + "info": { + "formatVersion": 3 + }, + "jokes": [ + { + "category": "Pun", + "type": "twopart", + "setup": "Was bist du, wenn du nicht weißt, was du tun sollst, nachdem dein Fahrrad geklaut wurde?", + "delivery": "Radlos.", + "flags": { + "nsfw": false, + "religious": false, + "political": false, + "racist": false, + "sexist": false + }, + "id": 0 + }, + { + "category": "Pun", + "type": "twopart", + "setup": "Wie nennt man einen schwulen, exotischen Vogel, der Kinder hat?", + "delivery": "Einen Papagay.", + "flags": { + "nsfw": false, + "religious": false, + "political": false, + "racist": false, + "sexist": false + }, + "id": 1 + }, + { + "category": "Pun", + "type": "twopart", + "setup": "Wie heißt ein dünner Mensch mit Bulimie?", + "delivery": "Brechstange.", + "flags": { + "nsfw": false, + "religious": false, + "political": false, + "racist": false, + "sexist": false + }, + "id": 2 + }, + { + "category": "Pun", + "type": "twopart", + "setup": "Was lebt im Wald und schreit \"Kugel\"?", + "delivery": "Der Kugelschreibär.", + "flags": { + "nsfw": false, + "religious": false, + "political": false, + "racist": false, + "sexist": false + }, + "id": 3 + }, + { + "category": "Pun", + "type": "twopart", + "setup": "Wie nennt sich ein Vogel mit Daddy-Fetisch?", + "delivery": "Ein Papageil.", + "flags": { + "nsfw": true, + "religious": false, + "political": false, + "racist": false, + "sexist": false + }, + "id": 4 + }, + { + "category": "Pun", + "type": "twopart", + "setup": "Im Bundestag gibt es neue Auflagen.", + "delivery": "Die Stühle waren zu unbequem.", + "flags": { + "nsfw": false, + "religious": false, + "political": false, + "racist": false, + "sexist": false + }, + "id": 5 + }, + { + "category": "Pun", + "type": "twopart", + "setup": "Warum hat niemand das Getränk des Soldaten gefunden?", + "delivery": "Es war in einer Camouflasche.", + "flags": { + "nsfw": false, + "religious": false, + "political": false, + "racist": false, + "sexist": false + }, + "id": 6 + }, + { + "category": "Pun", + "type": "twopart", + "setup": "Eine notgeile, inkontinente Henne flattert zum Arzt. Die Diagnose?", + "delivery": "Hahndrang.", + "flags": { + "nsfw": true, + "religious": false, + "political": false, + "racist": false, + "sexist": false + }, + "id": 7 + }, + { + "category": "Programming", + "type": "twopart", + "setup": "Kurzarbeit?", + "delivery": "Sind das dann 7 Bit?", + "flags": { + "nsfw": false, + "religious": false, + "political": false, + "racist": false, + "sexist": false + }, + "id": 8 + }, + { + "category": "Pun", + "type": "twopart", + "setup": "Wie schreibt ein Oktopus?", + "delivery": "Krakelig.", + "flags": { + "nsfw": false, + "religious": false, + "political": false, + "racist": false, + "sexist": false + }, + "id": 9 + }, + { + "category": "Pun", + "type": "twopart", + "setup": "Warum ist der ägyptische Totengott kein erfolgreicher Pro-Gamer?", + "delivery": "Weil er Anubis.", + "flags": { + "nsfw": false, + "religious": false, + "political": false, + "racist": false, + "sexist": false + }, + "id": 10 + }, + { + "category": "Pun", + "type": "twopart", + "setup": "Wie nennt man ein ungerubbeltes Rubbellos?", + "delivery": "Rubbellos.", + "flags": { + "nsfw": false, + "religious": false, + "political": false, + "racist": false, + "sexist": false + }, + "id": 11 + }, + { + "category": "Pun", + "type": "twopart", + "setup": "Ziemlich unfreundlich diese Älpler.", + "delivery": "Überall haben sie Schilder, auf denen steht \"Wander weg\"!", + "flags": { + "nsfw": false, + "religious": false, + "political": false, + "racist": false, + "sexist": false + }, + "id": 12 + }, + { + "category": "Pun", + "type": "twopart", + "setup": "Was verwendet eine ägyptische Pharaonin um ihren Hintern zu wischen?", + "delivery": "Kleopapier.", + "flags": { + "nsfw": false, + "religious": false, + "political": false, + "racist": false, + "sexist": false + }, + "id": 13 + }, + { + "category": "Programming", + "type": "single", + "joke": "Die Selbsthilfegruppe \"HTML-Sonderzeichen-Probleme\" trifft sich heute im großen Saal.", + "flags": { + "nsfw": false, + "religious": false, + "political": false, + "racist": false, + "sexist": false + }, + "id": 14 + }, + { + "category": "Programming", + "type": "single", + "joke": "Wenn man diese CD rückwärts spielt, sind satanische Verse zu hören.\nViel schlimmer, wenn man sie vorwärts spielt, installiert sie Windows.", + "flags": { + "nsfw": false, + "religious": false, + "political": false, + "racist": false, + "sexist": false + }, + "id": 15 + }, + { + "category": "Programming", + "type": "single", + "joke": "Die Mutter schickt ihren Sohn mit folgender Einkaufsliste in den Supermarkt: \"Eine Packung Milch, und wenn die Eier haben, bring drei Packungen mit.\"\nIm Supermarkt stellt der Sohn fest, es gibt dort Eier, also bringt er drei Packungen Milch mit.", + "flags": { + "nsfw": false, + "religious": false, + "political": false, + "racist": false, + "sexist": false + }, + "id": 16 + }, + { + "category": "Programming", + "type": "single", + "joke": "Täglich verschwinden hunderte Senioren im Netz, weil sie \"Alt\" und \"Entf\" drücken.", + "flags": { + "nsfw": false, + "religious": false, + "political": false, + "racist": false, + "sexist": false + }, + "id": 17 + }, + { + "category": "Programming", + "type": "single", + "joke": "Facebook ist wie ein Gefängnis. Man sitzt rum, verschwendet Zeit, schreibt an Wände und wird angestupst von Leuten die man nicht kennt.", + "flags": { + "nsfw": true, + "religious": false, + "political": false, + "racist": false, + "sexist": false + }, + "id": 18 + }, + { + "category": "Programming", + "type": "twopart", + "setup": "Es gibt 10 Arten von Menschen.", + "delivery": "Die einen verstehen das Binäre System, die anderen nicht.", + "flags": { + "nsfw": false, + "religious": false, + "political": false, + "racist": false, + "sexist": false + }, + "id": 19 + }, + { + "category": "Programming", + "type": "twopart", + "setup": "Was macht ein Informatiker wenn sein Wagen nicht mehr anspringt?", + "delivery": "Aussteigen, einsteigen und nochmal starten.", + "flags": { + "nsfw": false, + "religious": false, + "political": false, + "racist": false, + "sexist": false + }, + "id": 20 + }, + { + "category": "Programming", + "type": "single", + "joke": "Treffen sich ein Informatiker und ein Wirtschaftsinformatiker.\nInformatiker: \"Hast Du schon das neue Ubuntu?\"\nDer Wirtschaftsinformatiker: \"Nein, ich steh nicht auf Pokemon.\"", + "flags": { + "nsfw": false, + "religious": false, + "political": false, + "racist": false, + "sexist": false + }, + "id": 21 + }, + { + "category": "Programming", + "type": "twopart", + "setup": "Was stellt sich ein Informatiker zu Weihnachten in die Wohnung?", + "delivery": "Einen B-Baum.", + "flags": { + "nsfw": false, + "religious": false, + "political": false, + "racist": false, + "sexist": false + }, + "id": 22 + }, + { + "category": "Miscellaneous", + "type": "single", + "joke": "Wer zuletzt lacht ... hat den höchsten Ping.", + "flags": { + "nsfw": false, + "religious": false, + "political": false, + "racist": false, + "sexist": false + }, + "id": 23 + }, + { + "category": "Miscellaneous", + "type": "single", + "joke": "Das echte Leben nervt ... aber die Grafik ist gut.", + "flags": { + "nsfw": false, + "religious": false, + "political": false, + "racist": false, + "sexist": false + }, + "id": 24 + }, + { + "category": "Programming", + "type": "single", + "joke": "Deine Mutter ist wie ein L3-Cache. Sie wird zwischen allen 4 Kernen durchgereicht und jeder hat Zugriff.", + "flags": { + "nsfw": false, + "religious": false, + "political": false, + "racist": false, + "sexist": false + }, + "id": 25 + }, + { + "category": "Programming", + "type": "twopart", + "setup": "Was ist die Lieblingsbeschäftigung von Bits?", + "delivery": "Busfahren.", + "flags": { + "nsfw": false, + "religious": false, + "political": false, + "racist": false, + "sexist": false + }, + "id": 26 + } + ] +} \ No newline at end of file diff --git a/data/jokes.json b/data/jokes/jokes-en.json similarity index 96% rename from data/jokes.json rename to data/jokes/jokes-en.json index 35bee1b0..3dd0dad4 100755 --- a/data/jokes.json +++ b/data/jokes/jokes-en.json @@ -1,12 +1,12 @@ { "info": { - "formatVersion": 2 + "formatVersion": 3 }, "jokes": [ { "category": "Programming", "type": "single", - "joke": "I've got a really good UDP joke to tell you but I don't know if you'll get it.", + "joke": "I've got a really good UDP joke to tell you but I don’t know if you'll get it.", "flags": { "nsfw": false, "religious": false, @@ -797,7 +797,7 @@ "id": 58 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", "setup": "I asked my wife if I was the only one she's been with.", "delivery": "She said, \"Yes, the others were at least sevens or eights.\"", @@ -811,7 +811,7 @@ "id": 59 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", "setup": "Thank you student loans for getting me through college.", "delivery": "I don't think I'll ever be able to repay you.", @@ -825,7 +825,7 @@ "id": 60 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", "setup": "Why is crucified Jesus always depicted with six-pack abs?", "delivery": "He did CrossFit.", @@ -853,7 +853,7 @@ "id": 62 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", "setup": "One time I masturbated on a plane.", "delivery": "I called it \"highjacking\".", @@ -881,7 +881,7 @@ "id": 64 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", "setup": "What do you call a cop's penis after he's done masturbating?", "delivery": "Pulled pork.", @@ -895,7 +895,7 @@ "id": 65 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", "setup": "Did you hear about the cheese factory that exploded in France?", "delivery": "There was nothing but de brie.", @@ -923,10 +923,10 @@ "id": 67 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", "setup": "What kind of car did Whitney Houston drive?", - "delivery": "A Hyundaiiiiiiiiiiiiiiiiiiiiiiii", + "delivery": "A Hyundaiiiiiiiiiiii", "flags": { "nsfw": false, "religious": false, @@ -937,7 +937,7 @@ "id": 68 }, { - "category": "Miscellaneous", + "category": "Dark", "type": "twopart", "setup": "I had a granny that we couldn't decide whether to bury or cremate", "delivery": "In the end we decided to just let her live.", @@ -951,10 +951,10 @@ "id": 69 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", "setup": "What do you call crystal clear urine?", - "delivery": "1080pee.", + "delivery": "1080p.", "flags": { "nsfw": false, "religious": false, @@ -965,7 +965,7 @@ "id": 70 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", "setup": "What happens when you don't obey the KGB?", "delivery": "You get Putin jail.", @@ -1019,7 +1019,7 @@ "id": 74 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "single", "joke": "A horse walks into a bar.\n\"Hey\", the Bartender says.\n\"Sure\", the horse replies.", "flags": { @@ -1032,7 +1032,7 @@ "id": 75 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", "setup": "My girlfriend left me because I have a fetish for touching pasta.", "delivery": "I'm feeling cannelloni now. :'(", @@ -1088,7 +1088,7 @@ "id": 79 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", "setup": "What do Japanese cannibals eat?", "delivery": "Raw men.", @@ -1102,9 +1102,9 @@ "id": 80 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", - "setup": "So I made a graph of all my past relationships...", + "setup": "So I made a graph of all my past relationships.", "delivery": "It has an ex axis and a why axis.", "flags": { "nsfw": false, @@ -1130,7 +1130,7 @@ "id": 82 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "single", "joke": "I have these weird muscle spasms in my gluteus maximus.\nI figured out from my doctor that everything was alright:\nHe said \"Weird flex, butt okay.\"", "flags": { @@ -1185,7 +1185,7 @@ "id": 86 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", "setup": "Why did the Romanian stop reading?", "delivery": "They wanted to give the Bucharest.", @@ -1199,7 +1199,7 @@ "id": 87 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", "setup": "I hate Russian matryoshka dolls.", "delivery": "They're so full of themselves.", @@ -1230,7 +1230,7 @@ "category": "Miscellaneous", "type": "twopart", "setup": "What's green and smells like pork?", - "delivery": "Kermit's Fingers", + "delivery": "Kermit's Fingers.", "flags": { "nsfw": true, "religious": false, @@ -1241,7 +1241,7 @@ "id": 90 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "single", "joke": "Oysters hate to give away their pearls because they are shellfish.", "flags": { @@ -1256,7 +1256,7 @@ { "category": "Miscellaneous", "type": "twopart", - "setup": "How many friend-zoned guys does it take to change a lightbulb?", + "setup": "How many nice guys does it take to change a lightbulb?", "delivery": "None, they'll just compliment it and get pissed off when it won't screw.", "flags": { "nsfw": true, @@ -1324,7 +1324,7 @@ "id": 96 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", "setup": "What do Asian people call fingers?", "delivery": "Limb Limbs.", @@ -1351,7 +1351,7 @@ "id": 98 }, { - "category": "Dark", + "category": "Pun", "type": "twopart", "setup": "I'm thinking of setting up a comedy group to help people going through cancer treatment.", "delivery": "I'll call it \"A Sense of Tumor\".", @@ -1740,7 +1740,7 @@ "id": 126 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", "setup": "What time did the man go to the dentist?", "delivery": "Tooth hurt-y.", @@ -1754,7 +1754,7 @@ "id": 127 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "single", "joke": "I'm reading a book about anti-gravity. It's impossible to put down!", "flags": { @@ -1809,7 +1809,7 @@ "id": 131 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", "setup": "What do you call a pile of kittens?", "delivery": "A meowntain.", @@ -1823,7 +1823,7 @@ "id": 132 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", "setup": "How does a Jewish person make tea?", "delivery": "Hebrews it.", @@ -1891,7 +1891,7 @@ "id": 137 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", "setup": "Why do Hong Kong cops like to go to work early?", "delivery": "To beat the crowd.", @@ -1945,7 +1945,7 @@ "id": 141 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", "setup": "What kind of doctor is Dr. Pepper?", "delivery": "He's a fizzician.", @@ -2057,7 +2057,7 @@ "id": 149 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", "setup": "The gas Argon walks into a bar.\nThe barkeeper says \"What would you like to drink?\"", "delivery": "But Argon doesn't react.", @@ -2079,7 +2079,7 @@ "religious": false, "political": false, "racist": false, - "sexist": false + "sexist": true }, "id": 151 }, @@ -2153,7 +2153,7 @@ "id": 156 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", "setup": "What do you call a deaf gynecologist?", "delivery": "A lip reader.", @@ -2167,7 +2167,7 @@ "id": 157 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", "setup": "What do you call a cheap circumcision?", "delivery": "A rip off.", @@ -2183,7 +2183,7 @@ { "category": "Miscellaneous", "type": "twopart", - "setup": "What does a woman’s pussy and a chainsaw have in common?", + "setup": "What does a woman's pussy and a chainsaw have in common?", "delivery": "Miss by a few inches and you're in deep shit.", "flags": { "nsfw": true, @@ -2333,8 +2333,10 @@ "id": 169 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", + "setup": "Two guys walked into a bar.", + "delivery": "The third guy ducked.", "flags": { "nsfw": false, "religious": false, @@ -2342,13 +2344,13 @@ "racist": false, "sexist": false }, - "setup": "Two guys walked into a bar.", - "delivery": "The third guy ducked.", "id": 170 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", + "setup": "Two peanuts were walking.", + "delivery": "One was assaulted.", "flags": { "nsfw": false, "religious": false, @@ -2356,13 +2358,13 @@ "racist": false, "sexist": false }, - "setup": "Two peanuts were walking.", - "delivery": "One was assaulted.", "id": 171 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", + "setup": "What kind of bees produce milk?", + "delivery": "Boo-Bees.", "flags": { "nsfw": true, "religious": false, @@ -2370,13 +2372,13 @@ "racist": false, "sexist": false }, - "setup": "What kind of bees produce milk?", - "delivery": "Boo-Bees", "id": 172 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", + "setup": "A grocery store cashier asked if I would like my milk in a bag.", + "delivery": "I told her \"No, thanks. The carton works fine\".", "flags": { "nsfw": false, "religious": false, @@ -2384,13 +2386,13 @@ "racist": false, "sexist": false }, - "setup": "A grocery store cashier asked if I would like my milk in a bag.", - "delivery": "I told her \"No, thanks. The carton works fine\".", "id": 173 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", + "setup": "What does a perverted frog say?", + "delivery": "Rubbit.", "flags": { "nsfw": true, "religious": false, @@ -2398,13 +2400,13 @@ "racist": false, "sexist": false }, - "setup": "What does a perverted frog say?", - "delivery": "Rubbit", "id": 174 }, { "category": "Dark", "type": "twopart", + "setup": "How many Jews can you fit into a car?", + "delivery": "Two in the front, three in the back, and a hundred in the ashtray.", "flags": { "nsfw": false, "religious": false, @@ -2412,13 +2414,13 @@ "racist": true, "sexist": false }, - "setup": "How many Jews can you fit into a car?", - "delivery": "Two in the front, three in the back, and a hundred in the ashtray.", "id": 175 }, { "category": "Dark", "type": "twopart", + "setup": "What is the difference between a pizza and a black man?", + "delivery": "A pizza can feed a family of five.", "flags": { "nsfw": false, "religious": false, @@ -2426,13 +2428,13 @@ "racist": true, "sexist": false }, - "setup": "What is the difference between a pizza and a black man?", - "delivery": "A pizza can feed a family of five.", "id": 176 }, { "category": "Miscellaneous", "type": "twopart", + "setup": "What is the difference between an oral thermometer and a rectal thermometer?", + "delivery": "The taste.", "flags": { "nsfw": true, "religious": false, @@ -2440,13 +2442,13 @@ "racist": false, "sexist": false }, - "setup": "What is the difference between an oral thermometer and a rectal thermometer?", - "delivery": "The taste.", "id": 177 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", + "setup": "What do you call a witch at the beach?", + "delivery": "A Sandwich.", "flags": { "nsfw": false, "religious": false, @@ -2454,13 +2456,13 @@ "racist": false, "sexist": false }, - "setup": "What do you call a witch at the beach?", - "delivery": "A Sandwich.", "id": 178 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", + "setup": "What do you call 4 Mexicans in quicksand?", + "delivery": "Quatro Sinko.", "flags": { "nsfw": false, "religious": false, @@ -2468,13 +2470,12 @@ "racist": false, "sexist": false }, - "setup": "What do you call 4 Mexicans in quicksand?", - "delivery": "Quatro Sinko.", "id": 179 }, { "category": "Dark", "type": "single", + "joke": "My grandfather says I'm too reliant on technology.\nI called him a hypocrite and unplugged his life support.", "flags": { "nsfw": false, "religious": false, @@ -2482,12 +2483,13 @@ "racist": false, "sexist": false }, - "joke": "My grandfather says I'm too reliant on technology.\nI called him a hypocrite and unplugged his life support.", "id": 180 }, { "category": "Miscellaneous", "type": "twopart", + "setup": "Why do Jewish women prefer circumcised men?", + "delivery": "Cause they won't take anything until it's atleast 10% off.", "flags": { "nsfw": false, "religious": false, @@ -2495,8 +2497,6 @@ "racist": true, "sexist": false }, - "setup": "Why do Jewish women prefer circumcised men?", - "delivery": "Cause they won't take anything until it's atleast 10% off.", "id": 181 }, { @@ -2515,6 +2515,8 @@ { "category": "Miscellaneous", "type": "twopart", + "setup": "What is the difference between the Constitutions of the USA and the USSR? Don't both of them guarantee freedom of speech?", + "delivery": "Yes, but the Constitution of the USA also guarantees freedom after the speech.", "flags": { "nsfw": false, "religious": false, @@ -2522,13 +2524,13 @@ "racist": false, "sexist": false }, - "setup": "What is the difference between the Constitutions of the USA and the USSR? Both of them guarantee freedom of speech.", - "delivery": "Yes, but the Constitution of the USA also guarantees freedom after the speech.", "id": 183 }, { "category": "Miscellaneous", "type": "twopart", + "setup": "Why was the river rich?", + "delivery": "Because it had two banks.", "flags": { "nsfw": false, "religious": false, @@ -2536,13 +2538,13 @@ "racist": false, "sexist": false }, - "setup": "Why was the river rich?", - "delivery": "Because it had two banks.", "id": 184 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", + "setup": "Why didn't the skeleton go for prom?", + "delivery": "Because it had nobody.", "flags": { "nsfw": false, "religious": false, @@ -2550,13 +2552,12 @@ "racist": false, "sexist": false }, - "setup": "Why didn't the skeleton go for prom?", - "delivery": "Because it had nobody.", "id": 185 }, { "category": "Programming", "type": "single", + "joke": "Being a self-taught developer is almost the same as being a cut neck chicken because you have no sense of direction in the beginning.", "flags": { "nsfw": false, "religious": false, @@ -2564,12 +2565,12 @@ "racist": false, "sexist": false }, - "joke": "Being a self-thought developer is almost the same as being a cut neck chicken because you have no sense of direction in the beginning", "id": 186 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "single", + "joke": "Two fish in a tank. One turns to the other and says, \"Do you know how to drive this thing?\"", "flags": { "nsfw": false, "religious": false, @@ -2577,12 +2578,13 @@ "racist": false, "sexist": false }, - "joke": "Two fish in a tank. One turns to the other and says, \"Do you know how to drive this thing?\"", "id": 187 }, { "category": "Dark", "type": "twopart", + "setup": "Why can't orphans play baseball?", + "delivery": "They don't know where home is.", "flags": { "nsfw": false, "religious": false, @@ -2590,13 +2592,13 @@ "racist": false, "sexist": false }, - "setup": "Why can't orphans play baseball?", - "delivery": "They don't know where home is.", "id": 188 }, { "category": "Miscellaneous", "type": "twopart", + "setup": "I told my wife to shave her pussy.", + "delivery": "I woke up bald.", "flags": { "nsfw": true, "religious": false, @@ -2604,13 +2606,13 @@ "racist": false, "sexist": false }, - "setup": "I told my wife to shave her pussy.", - "delivery": "I woke up bald.", "id": 189 }, { "category": "Miscellaneous", "type": "twopart", + "setup": "What's the difference between a school bus and a cactus?", + "delivery": "A cactus keeps the little pricks on the outside.", "flags": { "nsfw": false, "religious": false, @@ -2618,13 +2620,13 @@ "racist": false, "sexist": false }, - "setup": "What's the difference between a school bus and a cactus?", - "delivery": "A cactus keeps the little pricks on the outside.", "id": 190 }, { "category": "Miscellaneous", "type": "twopart", + "setup": "Why are men like lawnmowers?", + "delivery": "They are very hard to get started, they make yucky smells and half the time they don't even work.", "flags": { "nsfw": false, "religious": false, @@ -2632,13 +2634,13 @@ "racist": false, "sexist": true }, - "setup": "Why are men like lawnmowers?", - "delivery": "They are very hard to get started, they make yucky smells and half the time they don’t even work.", "id": 191 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", + "setup": "What did the customer say to the waiter?", + "delivery": "I'm all fed up with your service.", "flags": { "nsfw": false, "religious": false, @@ -2646,13 +2648,12 @@ "racist": false, "sexist": false }, - "setup": "What did the customer say to the waiter?", - "delivery": "I'm all fed up with your service.", "id": 192 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "single", + "joke": "To whoever stole my copy of Microsoft Office, I will find you. You have my Word!", "flags": { "nsfw": false, "religious": false, @@ -2660,12 +2661,13 @@ "racist": false, "sexist": false }, - "joke": "To whoever stole my copy of Microsoft Office, I will find you. You have my Word!", "id": 193 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", + "setup": "Why did the koala get rejected?", + "delivery": "Because he did not have any koalafication.", "flags": { "nsfw": false, "religious": false, @@ -2673,13 +2675,13 @@ "racist": false, "sexist": false }, - "setup": "Why did the koala get rejected?", - "delivery": "Because he did not have any koalafication.", "id": 194 }, { "category": "Programming", "type": "twopart", + "setup": "What is the most used language in programming?", + "delivery": "Profanity.", "flags": { "nsfw": false, "religious": false, @@ -2687,15 +2689,13 @@ "racist": false, "sexist": false }, - "setup": "What is the most used language in programming?", - "delivery": "Profanity.", "id": 195 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", "setup": "What's the best thing about Switzerland?", - "delivery": "I don't know but the flag is a big plus.", + "delivery": "I don't know, but the flag is a big plus.", "flags": { "nsfw": false, "religious": false, @@ -2719,8 +2719,10 @@ "id": 197 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", + "setup": "What do you call a cow with no legs?", + "delivery": "Ground beef.", "flags": { "nsfw": false, "religious": false, @@ -2728,13 +2730,12 @@ "racist": false, "sexist": false }, - "setup": "What do you call a cow with no legs?", - "delivery": "Ground beef.", "id": 198 }, { "category": "Miscellaneous", "type": "single", + "joke": "Schrödinger's cat walks into a bar and doesn't.", "flags": { "nsfw": false, "religious": false, @@ -2742,12 +2743,13 @@ "racist": false, "sexist": false }, - "joke": "Schrödinger's cat walks into a bar and doesn't.", "id": 199 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", + "setup": "The past, the present and the future walk into a bar.", + "delivery": "It was tense.", "flags": { "nsfw": false, "religious": false, @@ -2755,13 +2757,13 @@ "racist": false, "sexist": false }, - "setup": "The past, the present and the future walk into a bar.", - "delivery": "It was tense.", "id": 200 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", + "setup": "What did the cell say when his sister cell stepped on his foot?", + "delivery": "Mitosis.", "flags": { "nsfw": false, "religious": false, @@ -2769,13 +2771,12 @@ "racist": false, "sexist": false }, - "setup": "What did the cell say when his sister cell stepped on his foot?", - "delivery": "Mitosis.", "id": 201 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "single", + "joke": "Today, my son asked \"Can I have a book mark?\" and I burst into tears.\n11 years old and he still doesn't know my name is Brian.", "flags": { "nsfw": false, "religious": false, @@ -2783,7 +2784,6 @@ "racist": false, "sexist": false }, - "joke": "Today, my son asked \"Can I have a book mark?\" and I burst into tears.\n11 years old and he still doesn't know my name is Brian.", "id": 202 }, { @@ -2800,8 +2800,9 @@ "id": 203 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "single", + "joke": "How do you make holy water? You boil the hell out of it.", "flags": { "nsfw": false, "religious": true, @@ -2809,12 +2810,12 @@ "racist": false, "sexist": false }, - "joke": "How do you make holy water? You boil the hell out of it.", "id": 204 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "single", + "joke": "How do you make holy water? You freeze it and drill holes in it.", "flags": { "nsfw": false, "religious": true, @@ -2822,12 +2823,12 @@ "racist": false, "sexist": false }, - "joke": "How do you make holy water? You freeze it and drill holes in it.", "id": 205 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "single", + "joke": "I bought some shoes from a drug dealer. I don't know what he laced them with, but I was tripping all day!", "flags": { "nsfw": false, "religious": false, @@ -2835,12 +2836,13 @@ "racist": false, "sexist": false }, - "joke": "I bought some shoes from a drug dealer. I don't know what he laced them with, but I was tripping all day!", "id": 206 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", + "setup": "What did the fish say when it swam into the wall?", + "delivery": "Dam.", "flags": { "nsfw": false, "religious": false, @@ -2848,12 +2850,10 @@ "racist": false, "sexist": false }, - "setup": "What did the fish say when it swam into the wall?", - "delivery": "Dam.", "id": 207 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", "setup": "How did Harry Potter get down the hill?", "delivery": "Walking...\nJK, Rolling.", @@ -2869,6 +2869,7 @@ { "category": "Dark", "type": "single", + "joke": "I was going to tell a dead baby joke. But I decided to abort.", "flags": { "nsfw": false, "religious": false, @@ -2876,12 +2877,12 @@ "racist": false, "sexist": false }, - "joke": "I was going to tell a dead baby joke. But I decided to abort.", "id": 209 }, { "category": "Dark", "type": "single", + "joke": "I'll never forget my Granddad's last words to me just before he died. \"Are you still holding the ladder?\"", "flags": { "nsfw": false, "religious": false, @@ -2889,12 +2890,13 @@ "racist": false, "sexist": false }, - "joke": "I'll never forget my Granddad's last words to me just before he died. \"Are you still holding the ladder?\"", "id": 210 }, { "category": "Miscellaneous", "type": "twopart", + "setup": "Why did the chicken cross the road, roll in the mud and cross the road again?", + "delivery": "He was a dirty double-crosser!", "flags": { "nsfw": false, "religious": false, @@ -2902,13 +2904,13 @@ "racist": false, "sexist": false }, - "setup": "Why did the chicken cross the road, roll in the mud and cross the road again?", - "delivery": "He was a dirty double-crosser!", "id": 211 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", + "setup": "What do you call a deer with no eyes?", + "delivery": "No eye deer.", "flags": { "nsfw": false, "religious": false, @@ -2916,13 +2918,13 @@ "racist": false, "sexist": false }, - "setup": "What do you call a deer with no eyes?", - "delivery": "No eye deer.", "id": 212 }, { "category": "Programming", "type": "twopart", + "setup": "What are bits?", + "delivery": "Tiny things left when you drop your computer down the stairs.", "flags": { "nsfw": false, "religious": false, @@ -2930,12 +2932,10 @@ "racist": false, "sexist": false }, - "setup": "What are bits?", - "delivery": "Tiny things left when you drop your computer down the stairs.", "id": 213 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", "setup": "Where do sick cruise ships go to get healthy?", "delivery": "The dock!", @@ -2949,7 +2949,7 @@ "id": 214 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", "setup": "Has COVID-19 forced you to wear glasses and a mask at the same time?", "delivery": "If so, you may be entitled to condensation.", @@ -2965,6 +2965,8 @@ { "category": "Programming", "type": "twopart", + "setup": "Why did the programmer jump on the table?", + "delivery": "Because debug was on his screen.", "flags": { "nsfw": false, "religious": false, @@ -2972,13 +2974,13 @@ "racist": false, "sexist": false }, - "setup": "Why did the programmer jump on the table?", - "delivery": "Because debug was on his screen.", "id": 216 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", + "setup": "I walked into a bar once.", + "delivery": "It really hurt my head.", "flags": { "nsfw": false, "religious": false, @@ -2986,13 +2988,13 @@ "racist": false, "sexist": false }, - "setup": "I walked into a bar once.", - "delivery": "It really hurt my head.", "id": 217 }, { - "category": "Miscellaneous", + "category": "Pun", "type": "twopart", + "setup": "What's grey and comes in pints?", + "delivery": "An elephant.", "flags": { "nsfw": true, "religious": false, @@ -3000,8 +3002,6 @@ "racist": false, "sexist": false }, - "setup": "What's grey and comes in pints?", - "delivery": "An elephant.", "id": 218 }, { @@ -3019,4 +3019,4 @@ "id": 219 } ] -} +} \ No newline at end of file diff --git a/data/jokes/template.json b/data/jokes/template.json new file mode 100644 index 00000000..8842b472 --- /dev/null +++ b/data/jokes/template.json @@ -0,0 +1,8 @@ +{ + "info": { + "formatVersion": 3 + }, + "jokes": [ + + ] +} \ No newline at end of file diff --git a/data/languages.json b/data/languages.json new file mode 100644 index 00000000..adbcfccf --- /dev/null +++ b/data/languages.json @@ -0,0 +1,186 @@ +{ + "aa": "Afar", + "ab": "Abkhazian", + "ae": "Avestan", + "af": "Afrikaans", + "ak": "Akan", + "am": "Amharic", + "an": "Aragonese", + "ar": "Arabic", + "as": "Assamese", + "av": "Avaric", + "ay": "Aymara", + "az": "Azerbaijani", + "ba": "Bashkir", + "be": "Belarusian", + "bg": "Bulgarian", + "bh": "Bihari Languages", + "bi": "Bislama", + "bm": "Bambara", + "bn": "Bengali", + "bo": "Tibetan", + "br": "Breton", + "bs": "Bosnian", + "ca": "Catalan", + "ce": "Chechen", + "ch": "Chamorro", + "co": "Corsican", + "cr": "Cree", + "cs": "Czech", + "cu": "Church Slavic", + "cv": "Chuvash", + "cy": "Welsh", + "da": "Danish", + "de": "German", + "dv": "Divehi", + "dz": "Dzongkha", + "ee": "Ewe", + "el": "Greek, Modern", + "en": "English", + "eo": "Esperanto", + "es": "Spanish", + "et": "Estonian", + "eu": "Basque", + "fa": "Persian", + "ff": "Fulah", + "fi": "Finnish", + "fj": "Fijian", + "fo": "Faroese", + "fr": "French", + "fy": "Western Frisian", + "ga": "Irish", + "gd": "Gaelic", + "gl": "Galician", + "gn": "Guarani", + "gu": "Gujarati", + "gv": "Manx", + "ha": "Hausa", + "he": "Hebrew", + "hi": "Hindi", + "ho": "Hiri Motu", + "hr": "Croatian", + "ht": "Haitian", + "hu": "Hungarian", + "hy": "Armenian", + "hz": "Herero", + "ia": "Interlingua", + "id": "Indonesian", + "ie": "Interlingue", + "ig": "Igbo", + "ii": "Sichuan Yi", + "ik": "Inupiaq", + "io": "Ido", + "is": "Icelandic", + "it": "Italian", + "iu": "Inuktitut", + "ja": "Japanese", + "jv": "Javanese", + "ka": "Georgian", + "kg": "Kongo", + "ki": "Kikuyu", + "kj": "Kuanyama", + "kk": "Kazakh", + "kl": "Kalaallisut", + "km": "Central Khmer", + "kn": "Kannada", + "ko": "Korean", + "kr": "Kanuri", + "ks": "Kashmiri", + "ku": "Kurdish", + "kv": "Komi", + "kw": "Cornish", + "ky": "Kirghiz", + "la": "Latin", + "lb": "Luxembourgish", + "lg": "Ganda", + "li": "Limburgan", + "ln": "Lingala", + "lo": "Lao", + "lt": "Lithuanian", + "lu": "Luba-Katanga", + "lv": "Latvian", + "mg": "Malagasy", + "mh": "Marshallese", + "mi": "Maori", + "mk": "Macedonian", + "ml": "Malayalam", + "mn": "Mongolian", + "mr": "Marathi", + "ms": "Malay", + "mt": "Maltese", + "my": "Burmese", + "na": "Nauru", + "nb": "Bokmål, Norwegian", + "nd": "Ndebele, North", + "ne": "Nepali", + "ng": "Ndonga", + "nl": "Dutch", + "nn": "Norwegian Nynorsk", + "no": "Norwegian", + "nr": "Ndebele, South", + "nv": "Navajo", + "ny": "Chichewa", + "oc": "Occitan", + "oj": "Ojibwa", + "om": "Oromo", + "or": "Oriya", + "os": "Ossetian", + "pa": "Panjabi", + "pi": "Pali", + "pl": "Polish", + "ps": "Pushto", + "pt": "Portuguese", + "qu": "Quechua", + "rm": "Romansh", + "rn": "Rundi", + "ro": "Romanian", + "ru": "Russian", + "rw": "Kinyarwanda", + "sa": "Sanskrit", + "sc": "Sardinian", + "sd": "Sindhi", + "se": "Northern Sami", + "sg": "Sango", + "si": "Sinhala", + "sk": "Slovak", + "sl": "Slovenian", + "sm": "Samoan", + "sn": "Shona", + "so": "Somali", + "sq": "Albanian", + "sr": "Serbian", + "ss": "Swati", + "st": "Sotho, Southern", + "su": "Sundanese", + "sv": "Swedish", + "sw": "Swahili", + "ta": "Tamil", + "te": "Telugu", + "tg": "Tajik", + "th": "Thai", + "ti": "Tigrinya", + "tk": "Turkmen", + "tl": "Tagalog", + "tn": "Tswana", + "to": "Tonga", + "tr": "Turkish", + "ts": "Tsonga", + "tt": "Tatar", + "tw": "Twi", + "ty": "Tahitian", + "ug": "Uighur", + "uk": "Ukrainian", + "ur": "Urdu", + "uz": "Uzbek", + "ve": "Venda", + "vi": "Vietnamese", + "vo": "Volapük", + "wa": "Walloon", + "wo": "Wolof", + "xh": "Xhosa", + "yi": "Yiddish", + "yo": "Yoruba", + "za": "Zhuang", + "zh": "Chinese", + "zu": "Zulu" +} \ No newline at end of file diff --git a/data/translations.json b/data/translations.json new file mode 100644 index 00000000..9434efff --- /dev/null +++ b/data/translations.json @@ -0,0 +1,246 @@ +{ + "info": "This file contains all kinds of different translations scattered throughout the API and is one of two files that contain translations - the other one being data/errorMessages.js", + "defaultLang": "en", + "languages": [ + "en", + "de", + "ru" + ], + "tr": { + "test": { + "en": "Test. Hello, world!", + "de": "Test. Hallo, Welt!", + "ru": "Тест. Привет, мир!" + }, + "messageOfTheDay": { + "en": "If you want to stay up to date on the status and future updates of %1 or need some help, consider joining my Discord server: https://sv443.net/discord", + "de": "Wenn du über den Status und die zukünftigen Updates von %1 auf dem Laufenden bleiben willst oder Hilfe benötigst, kannst du auf meinen Discord-Server kommen: https://sv443.net/discord", + "ru": "Если вы хотите быть в курсе состояния и будущих обновлений %1 или вам нужна помощь, вы можете прийти на мой Discord-сервер: https://sv443.net/discord" + }, + "generalInternalError": { + "en": "There was an internal error: %1", + "de": "Es gab einen internen Fehler: %1", + "ru": "Произошла внутренняя ошибка: %1" + }, + "invalidLangCode": { + "en": "The specified language code \"%1\" is invalid. Please see https://sv443.net/jokeapi/v2#langcodes-endpoint for more info.", + "de": "Der angegebene Sprach-code \"%1\" ist nicht korrekt. Bitte besuche https://sv443.net/jokeapi/v2#langcodes-endpoint für mehr Info.", + "ru": "Указанный код языка \"%1\" является недействительным. Дополнительную информацию можно получить на сайте https://sv443.net/jokeapi/v2#langcodes-endpoint" + }, + "invalidLangCodeNoArg": { + "en": "The specified language code is invalid. Please see http://localhost:8076/#langcodes-endpoint for more info.", + "de": "Der angegebene Sprach-code ist nicht korrekt. Bitte besuche https://sv443.net/jokeapi/v2#langcodes-endpoint für mehr Info.", + "ru": "Указанный код языка является недействительным. Дополнительную информацию можно получить на сайте https://sv443.net/jokeapi/v2#langcodes-endpoint" + }, + "uriTooLong": { + "en": "The provided URI (%1 characters) exceeds the maximum allowed length of %2 characters.", + "de": "Der angegebene URI (%1 Zeichen) überschreitet die Maximallänge von %2 Zeichen.", + "ru": "Поставляемый URI (%1 символ) превышает максимально допустимую длину %2 символов." + }, + "ipBlacklisted": { + "en": "You were blacklisted by the API and cannot send any requests anymore. If you believe this was done in error, please contact me: %1", + "de": "Du wurdest von der API auf eine Blockierliste gesetzt und kannst keine Anfragen mehr senden. Wenn du glaubst, dass dies irrtümlicherweise geschehen ist, kontaktiere mich bitte: %1", + "ru": "Вы были помещены в список блоков API и больше не можете отправлять запросы. Если вы думаете, что это произошло по ошибке, пожалуйста, свяжитесь со мной: %1" + }, + "errSetupHttpResponse": { + "en": "There was an internal error while setting up the HTTP response: %1", + "de": "Es gab einen internen Fehler während die HTTP-Antwort aufgebaut wurde: %1", + "ru": "При настройке ответа HTTP произошла внутренняя ошибка: %1" + }, + "rateLimited": { + "en": "You are being rate limited because you have sent too many requests. The maximum is %1 requests every %2 seconds.", + "de": "Deine Anfrage wurde blockiert, weil du zu viele Anfragen gesendet hast. Das Maximum sind %1 Anfragen innerhalb von %2 Sekunden.", + "ru": "Вы ограничены в тарифах, потому что отправили слишком много запросов. Максимальное значение составляет %1 запрос каждые %2 секунды." + }, + "rateLimitedShort": { + "en": "Blocked by rate limiting.", + "de": "Blockiert durch Ratenbegrenzung.", + "ru": "Блокировано ограничением скорости." + }, + "endpointInternalError": { + "en": "Internal Error while calling Endpoint: %1", + "de": "Interner Error während des Aufrufs eines Endpunktes: %1", + "ru": "Внутренняя ошибка при вызове конечной точки: %1" + }, + "endpointNotFound": { + "en": "Endpoint \"%1\" not found - Please read the documentation at https://sv443.net/jokeapi/v2#endpoints to see all available endpoints.", + "de": "Endpunkt \"%1\" nicht gefunden - Bitte lies die Dokumentation (https://sv443.net/jokeapi/v2#endpoints) um alle verfügbaren Endpunkte zu sehen.", + "ru": "Конечная точка \"%1\" не найдена - с документацией на https://sv443.net/jokeapi/v2#endpoints можно ознакомиться во всех доступных конечные точки." + }, + "payloadTooLarge": { + "en": "Payload too large (%1 Bytes - maximum is %2 Bytes).", + "de": "Anfrageinhalt zu groß (%1 Bytes - maximal erlaubt sind %2 Bytes).", + "ru": "Слишком большая грузоподъемность (%1 байт - максимум %2 байта)." + }, + "requestEmptyOrTimedOut": { + "en": "Request body is empty or request timed out.", + "de": "Anfrageinhalt ist leer oder es gab eine Zeitüberschreitung.", + "ru": "Тело запроса пустое или запрос таймаут." + }, + "invalidSubmissionOrWrongEndpoint": { + "en": "Request body is invalid or was sent to the wrong endpoint \"%1\", please refer to the documentation at https://sv443.net/jokeapi/v2#submit-joke to see how to correctly structure a joke submission.", + "de": "Anfrageinhalt ist ungültig oder wurde an den falschen Endpunkt \"%1\" gesendet, lies bitte in der Dokumentation unter https://sv443.net/jokeapi/v2#submit-joke nach, wie eine Witzeinreichung korrekt strukturiert wird.", + "ru": "Тело запроса является недействительным или было отправлено не на ту конечную точку \"%1\", смотрите документацию на https://sv443.net/jokeapi/v2#submit-joke, чтобы узнать, как правильно структурировать шутку." + }, + "requestBodyIsInvalid": { + "en": "Request body is empty.", + "de": "Anfrageinhalt ist leer.", + "ru": "Тело запроса пусто." + }, + "invalidCategory": { + "en": "The specified category/ies is/are invalid. Got: \"%1\" but possible categories are: \"%2\" (case insensitive).", + "de": "Die angegebene Kategorie/n ist/sind inkorrekt. Angefragt wurde \"%1\", möglich sind jedoch nur \"%2\" (Groß- und Kleinschreibung wird ignoriert).", + "ru": "Указанная категория/единицы являются/являются недействительными. Запрос был \"%1\", но возможен только \"%2\". (Верхний и нижний регистр игнорируются)." + }, + "invalidType": { + "en": "The specified type is invalid. Got: \"%1\" but possible types are: \"%2\".", + "de": "Der angegebene Dateityp ist inkorrekt. Angefragt wurde \"%1\", möglich sind jedoch nur \"%2\".", + "ru": "Указанный тип файла неверен. Запрос был \"%1\", но возможен только \"%2\"." + }, + "idRangeInvalid": { + "en": "The specified ID range is invalid. Got: \"%1 to %2\" but max possible ID range is: \"0-%3\".", + "de": "Die angegebene ID-Bereich ist inkorrekt. Angefragt wurde: \"%1 bis %2\" aber der maximal mögliche Bereich ist: \"0-%3\".", + "ru": "Указанный диапазон ID неверен. Запрос был сделан: \"%1 - %2\", но это максимально возможный диапазон: \"0-%3\"." + }, + "idRangeInvalidSingle": { + "en": "The specified ID range is invalid. Got: \"%1\" but max possible ID range is: \"0-%2\".", + "de": "Die angegebene ID-Bereich ist inkorrekt. Angefragt wurde: \"%1\" aber der maximal mögliche Bereich ist: \"0-%2\".", + "ru": "Указанный диапазон ID неверен. Запрос был сделан: \"%1\", но это максимально возможный диапазон: \"0-%2\"." + }, + "idRangeInvalidGeneric": { + "en": "The values in the \"idRange\" parameter are invalid or are not numbers. Error: %1", + "de": "Der angegebene ID-Bereich im URL-parameter \"idRange\" ist inkorrekt oder enthält keine Zahl/en. Error: %1", + "ru": "Значения в параметре \"idRange\" недействительны или не являются номерами. Ошибка: %1" + }, + "invalidFlags": { + "en": "The specified flags are invalid. Got: \"%1\" - Possible flags are: \"%2\".", + "de": "Die angegebene/n Markierung/en ist/sind inkorrekt. Angefragt wurde: \"%1\" - Possible flags are: \"%2\".", + "ru": "Указанные флаги недействительны. Понял: \"%1\" - Возможные флаги: \"%2\"." + }, + "amountInternalError": { + "en": "Internal error while setting joke amount: %1", + "de": "Interner Fehler bei der Einstellung der Witzanzahl: %1", + "ru": "Внутренняя ошибка при установке суммы шуток: %1" + }, + "errorWhileFinalizing": { + "en": "Error while finalizing joke filtering: %1", + "de": "Fehler beim Abschließen der Witz-Filterung: %1", + "ru": "Ошибка при завершении фильтрации шуток: %1" + }, + "invalidChars": { + "en": "These are the invalid characters: %1", + "de": "Dies sind die ungültigen Zeichen: %1", + "ru": "Это недопустимые символы: %1" + }, + "submittedJokeFormatInvalid": { + "en": "Submitted joke format is incorrect. Encountered error/s: %1", + "de": "Eingesandtes Witzformat ist falsch. Aufgetretene/r Fehler: %1", + "ru": "Отправленный формат шутки неверен. Ошибка/Ошибки при встрече: %1" + }, + "errWhileSavingSubmission": { + "en": "Internal error while saving joke: %1", + "de": "Interner Fehler beim Speichern des Witzes: %1", + "ru": "Внутренняя ошибка при сохранении шутки: %1" + }, + "wrongFormatVersion": { + "en": "Joke format version is incorrect. expected \"%1\" but got \"%2\".", + "de": "Die Format-Version des Witzes ist falsch. Erwartet wurde \"%1\", bekam stattdessen \"%2\".", + "ru": "Версия формата шуток некорректна. Ожидаемый \"%1\", получил \"%2\"." + }, + "invalidJSON": { + "en": "Request body contains invalid JSON: %1", + "de": "Anfrage enthält ungültiges JSON: %1", + "ru": "Тело запроса содержит недействительный JSON: %1" + }, + "noConversionMapping": { + "en": "Internal Error - no conversion mapping for data with keys \"%1\" - %2", + "de": "Interner Fehler - keine Konvertierungszuordnung für Daten mit Schlüsseln \"%1\" - %2", + "ru": "Внутренняя ошибка - нет отображения преобразования данных с помощью ключей \"%1\". - %2" + }, + "cantConvertToPlainText": { + "en": "Internal Error - could not convert data to plain text - %1", + "de": "Interner Fehler - konnte Daten nicht zu Text konvertieren - %1", + "ru": "Внутренняя ошибка - не удалось преобразовать данные в обычный текст - %1" + }, + "conversionGeneralError": { + "en": "Error %1 - %2\nThis error is caused by:\n- %3\n\nAdditional info: %4", + "de": "Fehler %1 - %2\nDieser Fehler kann verursacht werden durch:\n- %3\n\nZusätzliche Informationen: %4", + "ru": "Ошибка %1 - %2\nЭта ошибка может быть вызвана:\n- %3\n\nДополнительная информация: %4" + }, + "conversionInternalError": { + "en": "Internal Error %1 - %2\nThis error is caused by:\n- %3\n\nAdditional info: %4", + "de": "Interner Fehler %1 - %2\nDieser Fehler kann verursacht werden durch:\n- %3\n\nZusätzliche Informationen: %4", + "ru": "Внутренняя ошибка %1 - %2\nЭта ошибка может быть вызвана:\n- %3\n\nДополнительная информация: %4" + }, + "availableCategories": { + "en": "Available categories are: \"%1\"", + "de": "Verfügbare Kategorien sind: \"%1\"", + "ru": "Доступны следующие категории: \"%1\"" + }, + "availableFlags": { + "en": "Available flags are: \"%1\"", + "de": "Verfügbare Markierungen sind: \"%1\"", + "ru": "Доступны флаги: \"%1\"" + }, + "timestamp": { + "en": "Timestamp: %1", + "de": "Zeitstempel: %1", + "ru": "Таймштамп: %1" + }, + "genericError": { + "en": "Error: %1", + "de": "Fehler: %1", + "ru": "Ошибка: %1" + }, + "languageCode": { + "en": "Language Code: %1", + "de": "Sprach-Code: %1", + "ru": "Код языка: %1" + }, + "languagesEndpoint": { + "en": "Default Language: %1 [%2]\nJoke Languages (%3): %4\nSystem Languages (%5): %6\n\nPossible Language Values:\n%7", + "de": "Standardsprache: %1 [%2]\nWitz-Sprachen (%3): %4\nSystemsprachen (%5): %6\n\nMögliche Sprachwerte:\n%7", + "ru": "Язык по умолчанию: %1 [%2]\nЯзыки шуток (%3): %4\nСистемные языки (%5): %6\n\nВозможные значения языка:\n%7" + }, + "infoEndpoint": { + "en": "%1 v%2 - Info:\n\nJoke Count: %3\nCategories: \"%4\"\nFlags: \"%5\"\nResponse Formats: \"%6\"\nJoke Types: \"%7\"\nSubmission URL: %8\n\nID Ranges:\n%9\n\nJoke Languages (%10): %11\nSystem Languages (%12): %13\n\nMessage of the Day: %14", + "de": "%1 v%2 - Info:\n\nWitz-Anzahl: %3\nKategorien: \"%4\"\nMerkmale: \"%5\"\nAntwort-Dateiformate: \"%6\"\nWitz-Typen: \"%7\"\nEinreichungs-URL: %8\n\nID-Begrenzung:\n%9\n\nWitz-Sprachen (%10): %11\nSystemsprachen (%12): %13\n\nNachricht des Tages: %14", + "ru": "%1 v%2 - Информация:\n\nКоличество шуток: %3\nКатегории: \"%4\"\nФлаги: \"%5\"\nФорматы ответов: \"%6\"\nТипы шуток: \"%7\"\nАдрес отправки: %8\n\nID Диапазоны: \n%9\n\nЯзыки шуток (%10): %11\nСистемные языки (%12): %13\n\nПослание дня: %14" + }, + "availableFormats": { + "en": "Available formats are: %1", + "de": "Verfügbare Dateiformate sind: %1", + "ru": "Доступные форматы: %1" + }, + "endpointsWord": { + "en": "Endpoints", + "de": "Endpunkte", + "ru": "Конечные точки" + }, + "endpointDetails": { + "en": "%1 - %2\n Usage: %3 %4\n Supported parameters: %5", + "de": "%1 - %2\n Benutzung: %3 %4\n Unterstützte URL-Parameter: %5", + "ru": "%1 - %2\n Использование: %3 %4\n Поддерживаемые параметры URL: %5" + }, + "foundNoMatchingJokes": { + "en": "No jokes were found that match your provided filter(s).", + "de": "Es wurden keine Witze gefunden, die mit dem/den bereitgestellten Filter(n) übereinstimmen.", + "ru": "Не было найдено ни одной шутки, которая бы соответствовала вашему фильтру(ам)." + }, + "noLangCodeSpecified": { + "en": "You need to specify a language in the URL. Example: /langcode/english", + "de": "Du musst eine Sprache in der URL angeben. Beispiel: /langcode/german", + "ru": "Вам необходимо указать язык в URL-адресе. Пример: /langcode/russian" + }, + "pingPong": { + "en": "Pong!", + "de": "Pong!", + "ru": "Понг!" + }, + "submissionSaved": { + "en": "Joke submission was successfully saved. It will soon be checked out by the author.", + "de": "Die Witz-Einreichung wurde erfolgreich gespeichert. Sie wird demnächst vom Autor überprüft.", + "ru": "Представление шутки было успешно сохранено. Вскоре он будет проверен автором." + } + } +} \ No newline at end of file diff --git a/dev/madge.js b/dev/madge.js index c238cbdd..277433aa 100755 --- a/dev/madge.js +++ b/dev/madge.js @@ -23,7 +23,7 @@ const otherFiles = [ const madge = require("madge"); -const fs = require("fs"); +const fs = require("fs-extra"); const settings = require("../settings"); var fileList = []; @@ -260,7 +260,7 @@ const getIndex = () => `\
    - ${fileList.join("\n\t\t\t")} + ${fileList.join("\n\t\t\t\t\t")}


Blue has dependencies.
@@ -281,26 +281,36 @@ const getIndex = () => `\ - -try +async function exec() { - if(!fs.existsSync("./dev/madge")) - fs.mkdirSync("./dev/madge"); - - generateForOther().then(() => { - generateForSrc().then(() => { - generateForEndpoints().then(() => { - generateForTools().then(() => { - generateForClasses().then(() => { - writeIndex(); - }); - }); - }); - }); - }); + try + { + if(!fs.existsSync("./dev/madge")) + fs.mkdirSync("./dev/madge"); + + await generateForOther(); + process.stdout.write("."); + + await generateForSrc(); + process.stdout.write("."); + + await generateForEndpoints(); + process.stdout.write("."); + + await generateForTools(); + process.stdout.write("."); + + await generateForClasses(); + process.stdout.write("."); + + writeIndex(); + process.stdout.write(".\n"); + } + catch(err) + { + console.log(`\n\n\x1b[31m\x1b[1mError while generating dependency graphs:\n\x1b[0m${err}\n`); + process.exit(1); + } } -catch(err) -{ - console.log(`\n\n\x1b[31m\x1b[1mError while generating dependency graphs:\n\x1b[0m${err}\n`); - process.exit(1); -} \ No newline at end of file + +exec(); diff --git a/docs/raw/index.css b/docs/raw/index.css index b85a69f0..8fd0f6cd 100755 --- a/docs/raw/index.css +++ b/docs/raw/index.css @@ -51,7 +51,7 @@ body { background-color: var(--bg-color); color: #fff; font-family: "Roboto", "Segoe UI", "Arial", sans-serif; - font-size: 14px; + font-size: 15px; overflow-y: hidden; @@ -278,6 +278,25 @@ label { filter: drop-shadow(1px 1px 3px rgba(0, 0, 0, 0.5)); } +img.badge { + /* badges slide up a few pixels for some reason */ + height: 1.15em; + position: relative; + bottom: -2.5px; +} + +#f_langHideContainer.hidden { + visibility: hidden; +} + +input#f_customLang { + width: 3em; +} + +#uptimeTable tr td:last-child { + padding-left: 5px; +} + #statusCodeTable { margin: 0; padding: 0; @@ -349,8 +368,9 @@ mark { border-radius: 2px; background-color: var(--bg-accent-color); display: inline-block; - padding: 3px; - font-family: "Cascadia Code", "Roboto", "Courier New", monospace; + padding: 2px; + font-family: "Cascadia Code", "Roboto Mono", "Courier New", monospace; + font-variant-ligatures: none; color: #ccc; transition: background-color ease-out var(--sidenav-colorblur-speed); } @@ -407,6 +427,7 @@ body[data-sidenav="opened"] mark { body[data-sidenav="closed"] code, body code { font-family: "Cascadia Code", "Inconsolata", "Courier New", "Consolas", monospace; + font-variant-ligatures: none; font-size: 14px; white-space: pre; @@ -464,7 +485,8 @@ body code .nocode.codeheader::before { } kbd { - font-family: "Cascadia Code", monospace; + font-family: "Cascadia Code", "Inconsolata", "Roboto Mono", "Courier New", monospace; + font-variant-ligatures: none; font-size: 13px; display: inline-block; padding: 3.5px 4px; @@ -495,6 +517,21 @@ ul.lispacer li { margin-bottom: 5px; } +.antiBotE:not(.noul):not(.shown) { + cursor: pointer; + color: #8f9aff; + text-decoration: none; +} + +.antiBotE:not(.noul):not(.shown):hover { + color: #bcc2ff; + text-decoration: underline; +} + +.antiBotE.shown { + font-family: "Roboto Mono", "Cascadia Code", "Courier New", monospace; +} + /*#SECTION Endpoints*/ .requestURLwrapper { border: 1px solid #777; @@ -894,6 +931,11 @@ textarea { padding-bottom: 15px; } +#tryItFormLatency { + margin-top: 5px; + font-size: 90%; +} + .highlightedContainer { display: block; background-color: var(--bg-accent-color); diff --git a/docs/raw/index.html b/docs/raw/index.html old mode 100755 new mode 100644 index 3fadc056..9763792c --- a/docs/raw/index.html +++ b/docs/raw/index.html @@ -19,14 +19,14 @@ - - + + - + - + @@ -35,33 +35,20 @@ - - + + - + - - - + - + @@ -187,20 +174,38 @@

By using this website and API you are agreeing to is a RESTful API that serves uniformly and well formatted jokes.
It can be used without any API token, membership, registration or payment.
It supports a variety of filters that can be applied to get just the right jokes you need.
- The usage is very simple and similar to other RESTful APIs and requires only basic knowledge of HTTP/HTTPS requests and JSON, XML or YAML.
-
+ The usage is very simple and similar to other RESTful APIs and requires only basic knowledge of HTTP requests and JSON, XML, YAML or plain text.

If you come across a term you don't understand, you can try opening the menu and clicking on the section "Terminology".
- currently serves jokes. To submit a new joke, please click here.
+ currently serves jokes from different languages. To submit a new joke, please click here.

- If you want to report a bug or an illegal joke, please use the GitHub issue tracker.
+ To report a bug or a grammar mistake or to suggest a feature, please use the GitHub issue tracker.
If you want to contribute to , please read the Contributing Guide.

-
If you are new to , please start by reading the Getting Started Guide

+ Note: + has been a target of DoS attacks in the past, which is why there is now a limit of requests per minute and why joke submissions are manually curated.
+ All dependencies are tested for vulnerabilities by Snyk, you can find a summary of the known vulnerabilities here.
+ If you enjoy using and want me to keep making free APIs and scripts, please consider donating here ♥
+ Page security score (powered by Mozilla Observatory):

- Note: has been a target of DoS attacks in the past, which is why there is now a limit of requests per minute and why joke submissions are manually curated.
+ Uptime: + + + + + + + + + + + + + +
Last day:
Last week:
Last month:
+ (more info) @@ -222,11 +227,22 @@

By using this website and API you are agreeing to +

+ + + Select language: + + +
+ +
+ + Select flags to blacklist: @@ -288,6 +304,16 @@

By using this website and API you are agreeing to + + + Amount of jokes: + + +
+ +
+ +



@@ -297,6 +323,7 @@

By using this website and API you are agreeing to Result:
(Set parameters and click "Send Request" above)
+

@@ -305,10 +332,14 @@

By using this website and API you are agreeing to doesn't require any API token, authorization or payment to work, although there is a hard limit of requests per minute due to some DoS attacks I have been getting.
To start using , you can do one of these two things:
@@ -318,12 +349,14 @@

By using this website and API you are agreeing to - A wrapper is generally a library that wraps around a certain function to make your life easier.
+ A wrapper is generally a library that wraps around a certain function or API to make your life easier.
In the case of RESTful APIs like , a wrapper library makes it so you can use functions and objects to communicate with the API and the wrapper will do all the HTTP requests for you in the background.
+ Disclaimer: I will not take any responsibility for the wrappers, they are community-made and I can't ensure security and validity.
The following is a list of officially supported wrappers:

+ @@ -357,6 +390,7 @@

By using this website and API you are agreeing to +

@@ -369,6 +403,16 @@

By using this website and API you are agreeing to +

+ + +
Language
Language: + + +
Flags: @@ -401,6 +445,8 @@

By using this website and API you are agreeing to + Note: Only Unicode characters in the range U+0000 to U+0FFF are allowed (more info) + @@ -410,12 +456,14 @@

By using this website and API you are agreeing to Format: + ?format=format

This parameter is global - it can be used on every single endpoint of (excluding the documentation and static content).
It is used to change the response format from the default (JSON) to (Loading...), depending on what you need.
If the format parameter is invalid or not specified at all, the default value of JSON will be used.
Available formats are (Loading...)

Example 1 (click to view) URL: /joke/Any?format=xml

+
<data> <category>Programming</category> @@ -428,7 +476,8 @@

By using this website and API you are agreeing to <racist>false</racist> <sexist>false</sexist> </flags> - <id>12</id> + <id>12</id> + <lang>en</lang> </data>


@@ -445,6 +494,7 @@

By using this website and API you are agreeing to

Blacklist Flags: + ?blacklistFlags=flag1[,flag2,...]

This parameter can only be used on the "joke" endpoint.
If it is used, jokes that match the specified flag(s) will not be served (what is a flag?).
If you want to use multiple flags, separate them with a comma (,) or a plus sign (+).
@@ -461,8 +511,31 @@

By using this website and API you are agreeing to +

+ Language: + ?lang=langcode

+ This parameter can be used on every endpoint, but it is possible that no translations or jokes exist yet for your language.
+
+ There are two types of languages; system languages and joke languages. Both are separate from each other.
+ All system messages like errors can have a certain system language, while jokes can only have a joke language.
+ It is possible, that system languages don't yet exist for your language while jokes already do.
+ If no suitable system language is found, will default to English.
+
+ If you are interested in providing translations, please read the Contributing Guide; it will explain it.
+ You can also submit jokes in different languages. Please understand that I will not be able to ensure their proper formatting or legitimacy.
+
+ Currently available system languages: (Loading...)
+ Currently available joke languages: (Loading...)

+ +
Example 1 (click to view) + URL: /joke/Any?lang=ru
+ Using the above URL will fetch only jokes that are Russian. If an error gets returned, it will also be in Russian, provided a translation was found. +

+ +

Joke Type: + ?type=type

This parameter can only be used on the "joke" endpoint.
If it is set, you will only receive jokes with the specified joke type (what is a joke type?).
If it is not set or set to an invalid value, you will receive jokes of both types.
@@ -483,7 +556,8 @@

By using this website and API you are agreeing to "racist": false, "sexist": false }, - "id": 58 + "id": 58, + "lang": "en" }
(no jokes with type "single" will be served) @@ -492,9 +566,10 @@

By using this website and API you are agreeing to

Contains: + ?contains=string

This parameter can only be used on the "joke" endpoint.
If this parameter is specified, only jokes that contain the value of this parameter will be served (case insensitive).
- IMPORTANT: If the value of this parameter contains special characters, it needs to be percent-encoded according to the URI standard (MDN Reference), otherwise you might get an error or a wrong response.

+ IMPORTANT: If the value of this parameter contains special characters, it needs to be percent-encoded according to the URI syntax standard, otherwise you might get an error or a wrong response.

Example (click to view) URL: /joke/Any?contains=C%23 (%23 = pound character / hashtag / # - so the thing searched for here is "C#")

@@ -511,7 +586,8 @@

By using this website and API you are agreeing to "racist": false, "sexist": false }, - "id": 51 + "id": 51, + "lang": "en" }

@@ -519,6 +595,7 @@

By using this website and API you are agreeing to

ID Range: + ?idRange=number[-number]

This parameter can only be used on the "joke" endpoint.
If it is used, you will only get jokes that are inside the specified range of IDs.
The lower and upper boundary of the ID range needs to be delimited with a minus (-), comma (,) or a plus sign (+).
@@ -536,6 +613,43 @@

By using this website and API you are agreeing to + +

+ Joke Amount: + ?amount=number

+ This parameter can only be used on the "joke" endpoint.
+ If it is used, you will get a certain number of jokes in a single request.
+ If has less jokes than demanded, it will not error out but instead send the maximum amount of jokes it can serve.
+ Maximum possible number is and using an invalid value will make default to 1.
+ The joke format will change from the default when you set the amount parameter to 2 or more.
+
+ +
Click to view the format when fetching multiple jokes + +
{ + "error": false, + "jokes": [ + { + "category": "Miscellaneous", + "type": "single", + ... + }, + ... + ], + "amount": 10 +} +

+
+ +
+ +
Example (click to view) + URL: /joke/Any?amount=5
+ Using the above URL will make serve you five jokes at once. +

+ + + In all response data, no matter from which endpoint will have a boolean "error" parameter.
@@ -546,6 +660,7 @@

By using this website and API you are agreeing to The "message" parameter will contain a short version of the error message and the "timestamp" parameter will contain a 13-character Unix timestamp.
The "causedBy" parameter is an array of possible causes of this error and the "addidionalInfo" parameter contains a more descriptive error message.

+ If possible, will try to translate the error messages but keep in mind there might not exist a translation for your language yet or it is not 100% perfect.
You can view an example of an error, where an invalid category was used, just below this paragraph.
Together with the HTTP status code, which will also be set according to the type of error, these parameters can be used to improve error handling massively from what was the error system in previous versions of .


@@ -558,13 +673,14 @@

By using this website and API you are agreeing to "causedBy": [ "No jokes were found that match your provided filter(s)" ], - "additionalInfo": "The specified category is invalid - Got: \"foo\" - Possible categories are: \"Any, Miscellaneous, Programming, Dark\" (case insensitive)", + "additionalInfo": "The specified category is invalid - Got: \"foo\" - Possible categories are: \"Any, Miscellaneous, Programming, Dark, Pun\" (case insensitive)", "timestamp": 1579170794412 }
Status Codes: + @@ -629,6 +745,21 @@

By using this website and API you are agreeing to + + + HTTP Error 403: + First, please check if you can access all endpoints with your web browser. If not, your IP address has most likely been acting malicious and was permanently blacklisted.
+ If you keep getting HTTP 403 (Forbidden) response codes in your program but not in your browser and have never shown malicious behavior before, the issue is most likely that Cloudflare is blocking your request.
+ This might be due to inconsistent request data or headers that are often used in malicious requests or cyber attacks (most of the time it's gonna be the fault of the library you are using).
+ A fix to this issue would be to explicitly set a User-Agent header in each of your requests.
+ This is the user agent of your browser which you should be able to use in the fix described above:
+ (Something didn't work, please enter "navigator.userAgent" without quotes in your JavaScript console instead)

+ + HTTP Error 429: + You will get 429 errors occasionally if you exceed the maximum of requests per minute.
+ In this case, you will need to wait up to one minute to be able to talk to the API again.
+ If you want to index the API or fetch all jokes, are better off going to the GitHub repository, where you will find everything you need and you don't need to spam . + @@ -648,11 +779,11 @@

By using this website and API you are agreeing to These are all the available categories: (Loading...)
To see how this URL is built with certain parameters set, visit the "Try It" section.
- Example - click to view + Full example - click to view - /joke/Programming,Miscellaneous?format=xml&blacklistFlags=nsfw,sexist&type=single + /joke/Programming,Miscellaneous?format=xml&blacklistFlags=nsfw,sexist&type=single&lang=ru&amount=2

- Will give a joke from either the Programming or the Miscellaneous category, with the payload format XML,
+ Will give two Russian joke from either the Programming or the Miscellaneous category, with the payload format XML,
with the joke type "single", while also preventing all jokes that are potentially not safe for work and sexist from being served.


@@ -660,9 +791,11 @@

By using this website and API you are agreeing to (More info on filtering parameters)

@@ -681,12 +814,14 @@

By using this website and API you are agreeing to - All the available categories, flags, types and formats
- A 13-character UNIX timestamp
- The URL to a joke submission form
+ - A list of languages (code and name) JokeAPI currently supports
- The minimum and maximum values of an ID range
- A string with some information, like a message of the day

Supported URL parameters:


@@ -703,6 +838,103 @@

By using this website and API you are agreeing to Supported URL parameters:
+

+ + + + Language Code: + + GET + /langcode/[Language] + +

+ This endpoint returns the language code of a provided language. It is searched with a fuzzy search, so you just have to provide the approximate language name.
+ This language code is to be used in fetching and submitting jokes in different languages.
+
+
+ Example 1 - click to view +
+ + /langcode/german +

+
+{ + "error": false, + "code": "de" +} +
+
+
+
+ Example 2 - click to view +
+ + /langcode/sw3d1sh +

+
+{ + "error": false, + "code": "sv" +} +
+
+

+ Supported URL parameters:
+ +

+ + + + Supported Languages: + + GET + /languages + +

+ This endpoint returns lists of supported languages in jokes and supported languages in system messages (error messages).
+ Also, it returns a list of possible language values you can use to submit a joke or add a translation.
+
+ Example - click to view +
+ + /languages +

+
+{ + "defaultLanguage": "en", + "jokeLanguages": [ + "de", + "en" + ], + "systemLanguages": [ + "de", + "en", + "ru" + ], + "possibleLanguages": [ + { + "code": "aa", + "name": "Afar" + }, + { + ... + }, + ... + ], + "timestamp": 1590929517702 +} +
+
+

+ Supported URL parameters:
+

@@ -719,6 +951,7 @@

By using this website and API you are agreeing to Supported URL parameters:


@@ -735,6 +968,7 @@

By using this website and API you are agreeing to Supported URL parameters:


@@ -752,6 +986,7 @@

By using this website and API you are agreeing to Supported URL parameters:


@@ -762,9 +997,12 @@

By using this website and API you are agreeing to GET /endpoints -

- This endpoint returns a list / an array of all available endpoints, their usage (method, url and supported parameters) and a short description each. -

+
+
+ This endpoint returns a list / an array of all available endpoints, their usage (method, url and supported parameters) and a short description each.
+ Note: the lang parameter will not work here due to JokeAPI's fundamental architecture not allowing it. +
+
Supported URL parameters:
  • ?format
  • @@ -784,8 +1022,8 @@

    By using this website and API you are agreeing to The payload needs to be sent as JSON. Different formats are not allowed here.
    The request payload has to follow the same object structure that the jokes are served in when using the "joke" endpoint.

    - Make sure to add a "formatVersion" property, which just contains the current joke format version ().
    - This is needed since there were a few changes to the object format since the previous versions and this ensures consistency.
    + Make sure to add a "formatVersion" property, though, which just contains the current joke format version ().
    + This is needed since there were a few changes to the object format since the previous versions and this serves as a kind of acknowledgement.

    The joke category has to be any valid category, excluding "Any".
    If the joke type is set to "single", the properties "setup" and "delivery" will need to be omitted and the joke is to be put in the "joke" property.
    @@ -808,7 +1046,8 @@

    By using this website and API you are agreeing to "political": true, "racist": false, "sexist": false - } + }, + "lang": "en" } @@ -833,7 +1072,8 @@

    By using this website and API you are agreeing to "political": true, "racist": false, "sexist": false - } + }, + "lang": "en" }, "timestamp": 1579685839560 } @@ -855,6 +1095,7 @@

    By using this website and API you are agreeing to
  • Joke Type
  • Search String
  • ID Range
  • +
  • Language
@@ -879,7 +1120,7 @@

By using this website and API you are agreeing to
const https = require("https"); // Native module, no need to explicitly install const baseURL = ""; -const categories = ["Programming", "Miscellaneous"]; +const categories = ["Programming", "Miscellaneous", "Pun"]; const params = [ "blacklistFlags=nsfw,religious,racist", "idRange=0-100" @@ -920,7 +1161,7 @@

By using this website and API you are agreeing to
Web-JavaScript (click to show) Web-JS:
var baseURL = ""; -var categories = ["Programming", "Miscellaneous"]; +var categories = ["Programming", "Miscellaneous", "Pun"]; var params = [ "blacklistFlags=nsfw,religious,racist", "idRange=0-100" @@ -968,7 +1209,7 @@

By using this website and API you are agreeing to private void getJoke() { string baseURL = ""; - string[] categories = { "Programming", "Miscellaneous" }; + string[] categories = { "Programming", "Miscellaneous", "Pun" }; string[] parameters = { "blacklistFlags=nsfw,religious,racist", "idRange=0-100" @@ -986,7 +1227,7 @@

By using this website and API you are agreeing to // Deserialize the string into an object of the Joke class JavaScriptSerializer ser = new JavaScriptSerializer(); - Joke randomJoke = ser.Deserialize(responseFromServer); + Joke randomJoke = ser.Deserialize<Joke>(responseFromServer); if (randomJoke.type == "single") { @@ -1082,6 +1323,15 @@

By using this website and API you are agreeing to As there are currently jokes, the ID range can be anywhere in the range of 0 to
+ + Joke Amount: + This filter allows you to set a certain amount of jokes to receive in a single call to the "Get Joke" endpoint.
+ You can set it using the ?amount URL parameter.
+ Setting the filter to an invalid number will result in the API defaulting to serving a single joke.
+ Setting it to a number larger than will make default to the maximum ().
+ If you request more than jokes, the API will try to serve them encoded with Brotli, Gzip or Deflate, depending on whether or not you set a Accept-Encoding header.
+ + @@ -1110,7 +1360,7 @@

By using this website and API you are agreeing to Additionally, I will only be able to provide security updates for a small selection of versions, a list of which you can find here.
I am doing my best to ensure security and stability but there's only so much a single developer can do.
Please report any issue that may arise to the GitHub issue tracker and I will try my best to fix it as soon as possible.
- If you want to contact me, you can join my Discord server (fastest way to contact me) or send me an E-Mail at sven.fehler@web.de + If you want to contact me, you can join my Discord server (fastest way to contact me) or send me an E-Mail: (click to show)

@@ -1138,16 +1388,22 @@

By using this website and API you are agreeing to
These are the packages depends on (excluding dependencies of dependencies and devDependencies):
    +
  • @pm2/io
  • dotenv
  • farmhash
  • +
  • fs-extra
  • +
  • fuse.js
  • +
  • http-ratelimit
  • js2xmlparser
  • json-to-pretty-yaml
  • -
  • MySQL
  • +
  • mysql
  • node-wrap
  • promise-all-sequential
  • rate-limiter-flexible
  • request-ip
  • -
  • JSLib
  • +
  • snyk
  • +
  • svjsl
  • +
  • url-parse
  • xss
  • ", + // baseURL: "", + baseURL: "http://127.0.0.1:8076/", // DEBUG jokeEndpoint: "joke", anyCategoryName: "Any", defaultFormat: "json", - submitUrl: "/submit" + // submitUrl: "/submit", + submitUrl: "http://127.0.0.1:8076/submit", + defaultLang: "en", + formatVersion: 3 }; var submission = {}; @@ -182,22 +186,22 @@ function onLoad() fileFormats.splice(fileFormats.indexOf("JSON"), 1); } Array.from(document.getElementsByClassName("insFormatsS")).forEach(function(el) { - el.innerHTML = fileFormats.join(" and "); + el.innerText = fileFormats.join(" and "); }); var flags = JSON.parse(''); Array.from(document.getElementsByClassName("insFlags")).forEach(function(el) { - el.innerHTML = flags.join(", "); + el.innerText = flags.join(", "); }); var formats = JSON.parse(''); Array.from(document.getElementsByClassName("insFormats")).forEach(function(el) { - el.innerHTML = formats.join(", ").toLowerCase(); + el.innerText = formats.join(", ").toLowerCase(); }); var categories = JSON.parse(''); Array.from(document.getElementsByClassName("insCategories")).forEach(function(el) { - el.innerHTML = categories.join(", "); + el.innerText = categories.join(", "); }); } catch(err) @@ -215,12 +219,13 @@ function onLoad() "f_flags_racist", "f_flags_sexist", "f_setup", - "f_delivery" + "f_delivery", + "f_language" ]; for(var ii = 0; ii < inputElems.length; ii++) { - var elm = document.getElementById(inputElems[ii]); + var elm = gebid(inputElems[ii]); if(elm.tagName.toLowerCase() != "textarea") { @@ -238,11 +243,108 @@ function onLoad() } } - document.getElementById("submitBtn").addEventListener("click", function() { + var langXhr = new XMLHttpRequest(); + var langUrl = settings.baseURL + "/languages"; + var langSelectElems = document.getElementsByClassName("appendLangOpts"); + + var otherOpt = document.createElement("option"); + otherOpt.innerText = "Other / Custom"; + otherOpt.value = "other"; + gebid("f_language").appendChild(otherOpt); + + var sysLangsText = ""; + var jokeLangsText = ""; + + langXhr.open("GET", langUrl); + langXhr.onreadystatechange = function() { + var xErrElem; + if(langXhr.readyState == 4 && langXhr.status < 300) + { + var respJSON = JSON.parse(langXhr.responseText.toString()); + var languagesArray = respJSON.jokeLanguages; + + sysLangsText = respJSON.systemLanguages.join(", "); + jokeLangsText = respJSON.jokeLanguages.join(", "); + + for(var i = 0; i < languagesArray.length; i++) + { + for(var j = 0; j < langSelectElems.length; j++) + { + var langName = ""; + + for(var k = 0; k < respJSON.possibleLanguages.length; k++) + { + if(respJSON.possibleLanguages[k].code == languagesArray[i]) + { + langName = respJSON.possibleLanguages[k].name; + } + } + + xErrElem = document.createElement("option"); + xErrElem.value = languagesArray[i]; + if(languagesArray[i] == settings.defaultLang) + { + xErrElem.selected = true; + } + xErrElem.innerText = languagesArray[i] + " - " + langName; + + if(languagesArray[i] == settings.defaultLang) + xErrElem.selected = true; + + langSelectElems[j].appendChild(xErrElem); + langSelectElems[j].value = settings.defaultLang; + } + } + + var sysLangsElems = document.getElementsByClassName("insSysLangs"); + var jokeLangsElems = document.getElementsByClassName("insJokeLangs"); + + for(var sI = 0; sI < sysLangsElems.length; sI++) + { + sysLangsElems[sI].innerText = sysLangsText; + } + + for(var jI = 0; jI < sysLangsElems.length; jI++) + { + jokeLangsElems[jI].innerText = jokeLangsText; + } + } + else if(langXhr.readyState == 4 && langXhr.status >= 300) + { + for(var ii = 0; ii < langSelectElems.length; ii++) + { + xErrElem = document.createElement("option"); + xErrElem.value = "en"; + xErrElem.innerText = "en - English"; + xErrElem.selected = true; + + langSelectElems[ii].appendChild(xErrElem); + } + } + }; + langXhr.send(); + + gebid("submitBtn").addEventListener("click", function() { submitJoke(); }); buildSubmission(); + setTimeout(function() { buildSubmission() }, 2000); + + gebid("insUserAgent").innerText = navigator.userAgent; + + var abElems = document.getElementsByClassName("antiBotE"); + for(var l = 0; l < abElems.length; l++) + { + abElems[l].onclick = function() + { + if(!this.classList.contains("shown")) + { + this.innerText = atob(this.dataset.enc); + this.classList.add("shown"); + } + } + } } function addCodeTabs() @@ -339,14 +441,14 @@ function reRender() if(el.value == "any") { isValid = true; - ["cat-cb1", "cat-cb2", "cat-cb3"].forEach(function(cat) { + ["cat-cb1", "cat-cb2", "cat-cb3", "cat-cb4"].forEach(function(cat) { gebid(cat).disabled = true; }); } else { var isChecked = false; - ["cat-cb1", "cat-cb2", "cat-cb3"].forEach(function(cat) { + ["cat-cb1", "cat-cb2", "cat-cb3", "cat-cb4"].forEach(function(cat) { var cel = gebid(cat); cel.disabled = false; @@ -401,6 +503,18 @@ function reRender() gebid("idRangeWrapper").style.borderColor = "initial"; } + var jokesAmount = parseInt(gebid("jokesAmountInput").value); + + if(jokesAmount > parseInt("") || jokesAmount < 1 || isNaN(jokesAmount)) + { + allOk = false; + gebid("jokeAmountWrapper").style.borderColor = "red"; + } + else + { + gebid("jokeAmountWrapper").style.borderColor = "initial"; + } + if(allOk) { tryItOk = true; @@ -435,6 +549,10 @@ function buildURL() { selectedCategories.push("Dark"); } + if(gebid("cat-cb4").checked) + { + selectedCategories.push("Pun"); + } if(selectedCategories.length == 0) { @@ -443,6 +561,12 @@ function buildURL() } + //#SECTION language + var langCode = gebid("lcodeSelect").value || settings.defaultLang; + if(langCode != settings.defaultLang) + queryParams.push("lang=" + langCode); + + //#SECTION flags var flagElems = [gebid("blf-cb1"), gebid("blf-cb2"), gebid("blf-cb3"), gebid("blf-cb4"), gebid("blf-cb5")]; var flagNames = JSON.parse(''); @@ -511,6 +635,14 @@ function buildURL() } + //#SECTION amount + var jokeAmount = parseInt(gebid("jokesAmountInput").value); + if(jokeAmount != 1 && !isNaN(jokeAmount) && jokeAmount > 0 && jokeAmount <= parseInt("")) + { + queryParams.push("amount=" + jokeAmount); + } + + tryItURL = settings.baseURL + "/" + settings.jokeEndpoint + "/" + selectedCategories.join(","); if(queryParams.length > 0) @@ -518,12 +650,13 @@ function buildURL() tryItURL += "?" + queryParams.join("&"); } - gebid("urlBuilderUrl").innerHTML = tryItURL; + gebid("urlBuilderUrl").innerText = tryItURL; } //#MARKER send request function sendTryItRequest() { + var sendStartTimestamp = new Date().getTime(); var prpr = gebid("urlBuilderPrettyprint"); var tryItRequestError = function(err) { if(prpr.classList.contains("prettyprint")) @@ -601,7 +734,8 @@ function sendTryItRequest() result = result.replace(/[>]/gm, ">"); } - gebid("tryItResult").innerHTML = result; + gebid("tryItResult").innerText = result; + gebid("tryItFormLatency").innerText = "Latency: " + (new Date().getTime() - sendStartTimestamp) + " ms"; PR.prettyPrint(); // eslint-disable-line no-undef } @@ -621,7 +755,7 @@ function sendTryItRequest() //#MARKER interactive elements function resetTryItForm() { - ["cat-cb1", "cat-cb2", "cat-cb3"].forEach(function(cat) { + ["cat-cb1", "cat-cb2", "cat-cb3", "cat-cb4"].forEach(function(cat) { gebid(cat).checked = false; }); @@ -642,13 +776,15 @@ function resetTryItForm() gebid("idRangeInputFrom").value = 0; gebid("idRangeInputTo").value = parseInt(""); + gebid("jokesAmountInput").value = 1; + reRender(); } //#MARKER submit joke function submitJoke() { - var submitBtn = document.getElementById("submitBtn"); + var submitBtn = gebid("submitBtn"); if(submitBtn.disabled == true) { return; @@ -670,8 +806,8 @@ function submitJoke() alert(res.message); - setTimeout(() => { - document.getElementById("submitBtn").disabled = false; + setTimeout(function() { + gebid("submitBtn").disabled = false; }, 2000); } else if(res.error == true) @@ -700,15 +836,27 @@ function valChanged(element) { if(element.value == "single") { - document.getElementById("f_setup").placeholder = "Joke"; + gebid("f_setup").placeholder = "Joke"; - document.getElementById("f_delivery").style.display = "none"; + gebid("f_delivery").style.display = "none"; } else if(element.value == "twopart") { - document.getElementById("f_setup").placeholder = "Setup"; + gebid("f_setup").placeholder = "Setup"; + + gebid("f_delivery").style.display = "initial"; + } + } - document.getElementById("f_delivery").style.display = "initial"; + if(element.id == "f_language") + { + if(element.value == "other") + { + gebid("f_langHideContainer").classList.remove("hidden"); + } + else + { + gebid("f_langHideContainer").classList.add("hidden"); } } @@ -717,33 +865,53 @@ function valChanged(element) function buildSubmission() { - var category = document.getElementById("f_category").value; - var type = document.getElementById("f_type").value; + var category = gebid("f_category").value; + var type = gebid("f_type").value; submission = { - formatVersion: 2, + formatVersion: settings.formatVersion, category: category, - type: type, - flags: { - nsfw: document.getElementById("f_flags_nsfw").checked, - religious: document.getElementById("f_flags_religious").checked, - political: document.getElementById("f_flags_political").checked, - racist: document.getElementById("f_flags_racist").checked, - sexist: document.getElementById("f_flags_sexist").checked, - } + type: type } if(type == "single") { - submission.joke = document.getElementById("f_setup").value; + submission.joke = gebid("f_setup").value; } else if(type == "twopart") { - submission.setup = document.getElementById("f_setup").value; - submission.delivery = document.getElementById("f_delivery").value; + submission.setup = gebid("f_setup").value; + submission.delivery = gebid("f_delivery").value; + } + + var sLang = gebid("f_language").value || settings.defaultLang; + + if(sLang == "other") + { + var elVal = gebid("f_customLang").value; + if(elVal && elVal.length == 2) + { + sLang = elVal; + } + else + { + sLang = "Please enter 2 char language code"; + } } - var subDisp = document.getElementById("submissionDisplay"); + submission = { + ...submission, + flags: { + nsfw: gebid("f_flags_nsfw").checked, + religious: gebid("f_flags_religious").checked, + political: gebid("f_flags_political").checked, + racist: gebid("f_flags_racist").checked, + sexist: gebid("f_flags_sexist").checked, + }, + lang: sLang + }; + + var subDisp = gebid("submissionDisplay"); var escapedSubmission = JSON.parse(JSON.stringify(submission)); // copy value without reference if(type == "single") @@ -755,9 +923,9 @@ function buildSubmission() escapedSubmission.setup = htmlEscape(submission.setup); escapedSubmission.delivery = htmlEscape(submission.delivery); } - subDisp.innerHTML = JSON.stringify(escapedSubmission, null, 4); + subDisp.innerText = JSON.stringify(escapedSubmission, null, 4); - var subCodeElem = document.getElementById("submissionCodeElement"); + var subCodeElem = gebid("submissionCodeElement"); if(!subCodeElem.classList.contains("prettyprint")) { @@ -875,4 +1043,4 @@ function unused(...args) }); } -unused(openNav, closeNav, onLoad, openChangelog, reRender, privPolMoreInfo, hideUsageTerms, sendTryItRequest, submitRestartForm); \ No newline at end of file +unused(openNav, closeNav, onLoad, openChangelog, reRender, privPolMoreInfo, hideUsageTerms, sendTryItRequest, submitRestartForm); diff --git a/endpoints/categories.js b/endpoints/categories.js index 655a6902..53f8b3f5 100755 --- a/endpoints/categories.js +++ b/endpoints/categories.js @@ -15,7 +15,8 @@ const meta = { "method": "GET", "url": `${settings.info.docsURL}/categories`, "supportedParams": [ - "format" + "format", + "lang" ] } }; @@ -34,13 +35,15 @@ const call = (req, res, url, params, format) => { let responseText = ""; let categories = [settings.jokes.possible.anyCategoryName, ...settings.jokes.possible.categories]; + let lang = (params && params["lang"]) ? params["lang"] : settings.languages.defaultLanguage; + if(format != "xml") { responseText = convertFileFormat.auto(format, { "error": false, "categories": categories, "timestamp": new Date().getTime() - }); + }, lang); } else if(format == "xml") { @@ -48,10 +51,10 @@ const call = (req, res, url, params, format) => { "error": false, "categories": {"category": categories}, "timestamp": new Date().getTime() - }); + }, lang); } httpServer.pipeString(res, responseText, parseURL.getMimeTypeFromFileFormatString(format)); }; -module.exports = { meta, call }; \ No newline at end of file +module.exports = { meta, call }; diff --git a/endpoints/endpoints.js b/endpoints/endpoints.js index b7c20239..0fe7883f 100755 --- a/endpoints/endpoints.js +++ b/endpoints/endpoints.js @@ -2,8 +2,9 @@ const http = require("http"); const convertFileFormat = require("../src/fileFormatConverter"); const httpServer = require("../src/httpServer"); const parseURL = require("../src/parseURL"); +const languages = require("../src/languages"); const jsl = require("svjsl"); -const fs = require("fs"); +const fs = require("fs-extra"); const settings = require("../settings"); jsl.unused(http); @@ -34,6 +35,8 @@ const call = (req, res, url, params, format) => { let endpointList = []; + let lang = (params && params["lang"]) ? params["lang"] : settings.languages.defaultLanguage; + try { fs.readdir(settings.endpoints.dirPath, (err, files) => { @@ -97,14 +100,19 @@ const call = (req, res, url, params, format) => { } catch(err) { - return epError(res, format, err); + return epError(res, format, err, lang); } }; -const epError = (res, format, err) => { - let errFromRegistry = require("." + settings.errors.errorRegistryIncludePath)["100"]; - +const epError = (res, format, err, lang) => { let errObj = {}; + let errFromRegistry = require("../data/errorMessages")["100"]; + + if(errFromRegistry == undefined) + throw new Error(`Couldn't find errorMessages module or Node is using an outdated, cached version`); + + if(!lang || !languages.isValidLang(lang)) + lang = settings.languages.defaultLanguage; if(format != "xml") { @@ -112,8 +120,8 @@ const epError = (res, format, err) => { "error": true, "internalError": true, "code": 100, - "message": errFromRegistry.errorMessage, - "causedBy": errFromRegistry.causedBy, + "message": errFromRegistry.errorMessage[lang] || errFromRegistry.errorMessage[settings.languages.defaultLanguage], + "causedBy": errFromRegistry.causedBy[lang] || errFromRegistry.causedBy[settings.languages.defaultLanguage], "additionalInfo": err, "timestamp": new Date().getTime() } @@ -124,14 +132,14 @@ const epError = (res, format, err) => { "error": true, "internalError": true, "code": 100, - "message": errFromRegistry.errorMessage, - "causedBy": {"cause": errFromRegistry.causedBy}, + "message": errFromRegistry.errorMessage[lang] || errFromRegistry.errorMessage[settings.languages.defaultLanguage], + "causedBy": { "cause": (errFromRegistry.causedBy[lang] || errFromRegistry.causedBy[settings.languages.defaultLanguage]) }, "additionalInfo": err, "timestamp": new Date().getTime() } } - httpServer.pipeString(res, convertFileFormat.auto(format, errObj), parseURL.getMimeTypeFromFileFormatString(format)); + httpServer.pipeString(res, convertFileFormat.auto(format, errObj), parseURL.getMimeTypeFromFileFormatString(format), lang); }; -module.exports = { meta, call }; \ No newline at end of file +module.exports = { meta, call }; diff --git a/endpoints/flags.js b/endpoints/flags.js index 304a23fb..4f204337 100755 --- a/endpoints/flags.js +++ b/endpoints/flags.js @@ -15,7 +15,8 @@ const meta = { "method": "GET", "url": `${settings.info.docsURL}/flags`, "supportedParams": [ - "format" + "format", + "lang" ] } }; @@ -33,13 +34,15 @@ const call = (req, res, url, params, format) => { let responseText = ""; + let lang = (params && params["lang"]) ? params["lang"] : settings.languages.defaultLanguage; + if(format != "xml") { responseText = convertFileFormat.auto(format, { "error": false, "flags": settings.jokes.possible.flags, "timestamp": new Date().getTime() - }); + }, lang); } else if(format == "xml") { @@ -47,10 +50,10 @@ const call = (req, res, url, params, format) => { "error": false, "flags": {"flag": settings.jokes.possible.flags}, "timestamp": new Date().getTime() - }); + }, lang); } httpServer.pipeString(res, responseText, parseURL.getMimeTypeFromFileFormatString(format)); }; -module.exports = { meta, call }; \ No newline at end of file +module.exports = { meta, call }; diff --git a/endpoints/formats.js b/endpoints/formats.js index cc91db92..dedbeab3 100755 --- a/endpoints/formats.js +++ b/endpoints/formats.js @@ -15,7 +15,8 @@ const meta = { "method": "GET", "url": `${settings.info.docsURL}/formats`, "supportedParams": [ - "format" + "format", + "lang" ] } }; @@ -33,13 +34,15 @@ const call = (req, res, url, params, format) => { let responseText = ""; + let lang = (params && params["lang"]) ? params["lang"] : settings.languages.defaultLanguage; + if(format != "xml") { responseText = convertFileFormat.auto(format, { "error": false, "formats": settings.jokes.possible.formats, "timestamp": new Date().getTime() - }); + }, lang); } else if(format == "xml") { @@ -47,10 +50,10 @@ const call = (req, res, url, params, format) => { "error": false, "formats": {"format": settings.jokes.possible.formats}, "timestamp": new Date().getTime() - }); + }, lang); } httpServer.pipeString(res, responseText, parseURL.getMimeTypeFromFileFormatString(format)); }; -module.exports = { meta, call }; \ No newline at end of file +module.exports = { meta, call }; diff --git a/endpoints/info.js b/endpoints/info.js index 18b550ed..78f82e4d 100755 --- a/endpoints/info.js +++ b/endpoints/info.js @@ -4,7 +4,9 @@ const httpServer = require("../src/httpServer"); const parseURL = require("../src/parseURL"); const jsl = require("svjsl"); const parseJokes = require("../src/parseJokes"); +const languages = require("../src/languages"); const settings = require("../settings"); +const translate = require("../src/translate"); jsl.unused(http); @@ -16,7 +18,8 @@ const meta = { "method": "GET", "url": `${settings.info.docsURL}/info`, "supportedParams": [ - "format" + "format", + "lang" ] } }; @@ -32,7 +35,9 @@ const meta = { const call = (req, res, url, params, format) => { jsl.unused([req, url, params]); - let errFromRegistry = require("." + settings.errors.errorRegistryIncludePath)["100"]; + let supportedLangsLength = languages.jokeLangs().length; + + let errFromRegistry = require("." + settings.errors.errorMessagesPath)["100"]; let responseText = {}; if(format != "xml") { @@ -58,6 +63,22 @@ const call = (req, res, url, params, format) => { } let totalJokesCount = (!jsl.isEmpty(parseJokes.jokeCount) ? parseJokes.jokeCount : 0); + + let idRangePerLang = {}; + let idRangePerLangXml = []; + Object.keys(parseJokes.jokeCountPerLang).forEach(lc => { + let to = (parseJokes.jokeCountPerLang[lc] - 1); + idRangePerLang[lc] = [ 0, to ]; + idRangePerLangXml.push({ + langCode: lc, + from: 0, + to: to + }); + }); + + let systemLanguagesLength = translate.systemLangs().length; + + let lang = (params && params["lang"]) ? params["lang"] : settings.languages.defaultLanguage; if(format != "xml") { @@ -71,12 +92,14 @@ const call = (req, res, url, params, format) => { "flags": settings.jokes.possible.flags, "types": settings.jokes.possible.types, "submissionURL": settings.jokes.jokeSubmissionURL, - "idRange": [ 0, --totalJokesCount ] + "idRange": idRangePerLang }, "formats": settings.jokes.possible.formats, - "info": settings.info.infoMsg, + "jokeLanguages": supportedLangsLength, + "systemLanguages": systemLanguagesLength, + "info": translate(lang, "messageOfTheDay", settings.info.name), "timestamp": new Date().getTime() - }); + }, lang); } else if(format == "xml") { @@ -90,12 +113,14 @@ const call = (req, res, url, params, format) => { "flags": {"flag": settings.jokes.possible.flags}, "types": {"type": settings.jokes.possible.types}, "submissionURL": settings.jokes.jokeSubmissionURL, - "idRange": [ 0, --totalJokesCount ] + "idRanges": { "idRange": idRangePerLangXml } }, "formats": {"format": settings.jokes.possible.formats}, - "info": settings.info.infoMsg, + "jokeLanguages": supportedLangsLength, + "systemLanguages": systemLanguagesLength, + "info": translate(lang, "messageOfTheDay", settings.info.name), "timestamp": new Date().getTime() - }); + }, lang); } httpServer.pipeString(res, responseText, parseURL.getMimeTypeFromFileFormatString(format)); diff --git a/endpoints/joke.js b/endpoints/joke.js index a607af8f..18415d05 100755 --- a/endpoints/joke.js +++ b/endpoints/joke.js @@ -3,6 +3,8 @@ const convertFileFormat = require("../src/fileFormatConverter"); const httpServer = require("../src/httpServer"); const parseURL = require("../src/parseURL"); const parseJokes = require("../src/parseJokes"); +const languages = require("../src/languages"); +const tr = require("../src/translate"); const FilteredJoke = require("../src/classes/FilteredJoke"); const jsl = require("svjsl"); const settings = require("../settings"); @@ -21,7 +23,9 @@ const meta = { "blacklistFlags", "type", "contains", - "idRange" + "idRange", + "lang", + "amount" ] } }; @@ -58,7 +62,7 @@ const call = (req, res, url, params, format) => { if(category.toLowerCase() == cat.toLowerCase()) categoryValid = true; } - else if(typeof category == "object") + else if(Array.isArray(category)) { if(category.map(c => c.toLowerCase()).includes(cat.toLowerCase())) categoryValid = true; @@ -70,23 +74,50 @@ const call = (req, res, url, params, format) => { fCat = filterJoke.setAllowedCategories([category]); else fCat = filterJoke.setAllowedCategories(category); + let langCode = settings.languages.defaultLanguage; + + //#SECTION language + if(params && !jsl.isEmpty(params["lang"])) + { + try + { + langCode = params["lang"].toString(); + + if(languages.isValidLang(langCode)) + filterJoke.setLanguage(langCode); + else + return isErrored(res, format, tr(langCode, "invalidLangCode", langCode), langCode); + } + catch(err) + { + return isErrored(res, format, tr(langCode, "invalidLangCodeNoArg"), langCode); + } + } + if(!fCat || !categoryValid) - return isErrored(res, format, `The specified categor${category.length == undefined || typeof category != "object" || category.length == 1 ? "y is" : "ies are"} invalid - Got: "${category.length == undefined || typeof category != "object" ? category : category.join(", ")}" - Possible categories are: "${[settings.jokes.possible.anyCategoryName, ...settings.jokes.possible.categories].join(", ")}" (case insensitive)`); + { + let avlCats = [settings.jokes.possible.anyCategoryName, ...settings.jokes.possible.categories].join(", "); + let catName = category.length == undefined || typeof category != "object" ? category : category.join(", "); + + return isErrored(res, format, tr(langCode, "invalidCategory", catName, avlCats), langCode); + } + let jokeAmount = 1; + if(!jsl.isEmpty(params)) { //#SECTION type if(!jsl.isEmpty(params["type"]) && settings.jokes.possible.types.map(t => t.toLowerCase()).includes(params["type"].toLowerCase())) { if(!filterJoke.setAllowedType(params["type"].toLowerCase())) - return isErrored(res, format, `The specified type is invalid - Got: "${params["type"]}" - Possible types are: "${settings.jokes.possible.types}"`); + return isErrored(res, format, tr(langCode, "invalidType", params["type"], settings.jokes.possible.types.join(", ")), langCode); } //#SECTION contains if(!jsl.isEmpty(params["contains"])) { if(!filterJoke.setSearchString(params["contains"].toLowerCase())) - return isErrored(res, format, `The specified type is invalid - Got: "${params["type"]}" - Possible types are: "${settings.jokes.possible.types.join(", ")}"`); + return isErrored(res, format, tr(langCode, "invalidType", params["type"], settings.jokes.possible.types.join(", ")), langCode); } //#SECTION idRange @@ -98,19 +129,25 @@ const call = (req, res, url, params, format) => { { let splitParams = params["idRange"].split(settings.jokes.splitCharRegex); + if(!splitParams[0] && splitParams[1]) + splitParams[0] = splitParams[1]; + + if(!splitParams[1] && splitParams[0]) + splitParams[1] = splitParams[0]; + if(!filterJoke.setIdRange(parseInt(splitParams[0]), parseInt(splitParams[1]))) - return isErrored(res, format, `The specified ID range is invalid - Got: "${splitParams[0]} to ${splitParams[1]}" - ID range is: "0-${(parseJokes.jokeCount - 1)}"`); + return isErrored(res, format, tr(langCode, "idRangeInvalid", splitParams[0], splitParams[1], (parseJokes.jokeCountPerLang[langCode] - 1)), langCode); } else { let id = parseInt(params["idRange"]); - if(!filterJoke.setIdRange(id, id)) - return isErrored(res, format, `The specified ID range is invalid - Got: "${params["idRange"]}" - ID range is: "0-${(parseJokes.jokeCount - 1)}"`); + if(!filterJoke.setIdRange(id, id, langCode)) + return isErrored(res, format, tr(langCode, "idRangeInvalidSingle", params["idRange"], (parseJokes.jokeCountPerLang[langCode] - 1)), langCode); } } catch(err) { - return isErrored(res, format, `The values in the "idRange" parameter are invalid or are not numbers - ${err}`); + return isErrored(res, format, tr(langCode, "idRangeInvalidGeneric", err), langCode); } } @@ -125,22 +162,74 @@ const call = (req, res, url, params, format) => { }); if(erroredFlags.length > 0) - return isErrored(res, format, `The specified flags are invalid - Got: "${flags.join(", ")}" - Possible flags are: "${settings.jokes.possible.flags.join(", ")}"`); + return isErrored(res, format, tr(langCode, "invalidFlags", flags.join(", "), settings.jokes.possible.flags.join(", ")), langCode); + tr(langCode, "invalidFlags", flags.join(", "), settings.jokes.possible.flags.join(", ")) let fFlg = filterJoke.setBlacklistFlags(flags); if(!fFlg) - return isErrored(res, format, `The specified flags are invalid - Got: "${flags.join(", ")}" - Possible flags are: "${settings.jokes.possible.flags.join(", ")}"`); + return isErrored(res, format, tr(langCode, "invalidFlags", flags.join(", "), settings.jokes.possible.flags.join(", ")), langCode); + } + + //#SECTION amount + if(!jsl.isEmpty(params["amount"])) + { + jokeAmount = parseInt(params["amount"]); + + if(isNaN(jokeAmount) || jokeAmount < 1) + jokeAmount = 1; + + if(jokeAmount > settings.jokes.maxAmount) + jokeAmount = settings.jokes.maxAmount; + + let fAmt = filterJoke.setAmount(jokeAmount); + if(!fAmt) + return isErrored(res, format, tr(langCode, "amountInternalError", fAmt), langCode); } } - filterJoke.getJoke().then(joke => { - if(!joke["error"]) - joke["error"] = false; - let responseText = convertFileFormat.auto(format, joke); - httpServer.pipeString(res, responseText, parseURL.getMimeTypeFromFileFormatString(format)); + filterJoke.getJokes(filterJoke.getAmount()).then(jokesArray => { + let responseText = ""; + + if(jokeAmount == 1) + { + let singleObj = { + error: false, + ...jokesArray[0] + }; + + responseText = convertFileFormat.auto(format, singleObj, langCode); + } + else + { + let multiObj = {}; + + if(format != "xml") + { + multiObj = { + error: false, + jokes: jokesArray, + amount: jokesArray.length || 1 + }; + } + else + { + multiObj = { + error: false, + jokes: { "joke": jokesArray }, + amount: jokesArray.length || 1 + }; + } + + responseText = convertFileFormat.auto(format, multiObj, langCode); + } + + if(jokeAmount > settings.jokes.encodeAmount) + httpServer.tryServeEncoded(req, res, responseText, parseURL.getMimeTypeFromFileFormatString(format)); + else + httpServer.pipeString(res, responseText, parseURL.getMimeTypeFromFileFormatString(format)); }).catch(err => { - return isErrored(res, format, `Error while finalizing joke filtering: ${Array.isArray(err) ? err.join("; ") : err}`); + return isErrored(res, format, tr(langCode, "errorWhileFinalizing", Array.isArray(err) ? err.join("; ") : err), langCode); }); }; @@ -149,18 +238,36 @@ const call = (req, res, url, params, format) => { * @param {http.ServerResponse} res * @param {String} format * @param {String} msg + * @param {String} lang 2-char lang code + * @param {...any} args Arguments to replace numbered %-placeholders with. Only use objects that are strings or convertable to them with `.toString()`! */ -const isErrored = (res, format, msg) => { - let errFromRegistry = require("." + settings.errors.errorRegistryIncludePath)["106"]; - let errorObj = {} +const isErrored = (res, format, msg, lang, ...args) => { + let errFromRegistry = require("." + settings.errors.errorMessagesPath)["106"]; + let errorObj = {}; + + let insArgs = (texts, insertions) => { + if(!Array.isArray(insertions) || insertions.length <= 0) + return texts; + + insertions.forEach((ins, i) => { + + if(Array.isArray(texts)) + texts = texts.map(tx => tx.replace(`%${i + 1}`, ins)); + else if(typeof texts == "string") + texts = texts.replace(`%${i + 1}`, ins); + }); + + return texts; + }; + if(format != "xml") { errorObj = { error: true, internalError: false, code: 106, - message: errFromRegistry.errorMessage, - causedBy: errFromRegistry.causedBy, + message: insArgs(errFromRegistry.errorMessage[lang], args) || insArgs(errFromRegistry.errorMessage[settings.languages.defaultLanguage], args), + causedBy: insArgs(errFromRegistry.causedBy[lang], args) || insArgs(errFromRegistry.causedBy[settings.languages.defaultLanguage], args), additionalInfo: msg, timestamp: new Date().getTime() }; @@ -171,15 +278,15 @@ const isErrored = (res, format, msg) => { error: true, internalError: false, code: 106, - message: errFromRegistry.errorMessage, - causedBy: {"cause": errFromRegistry.causedBy}, + message: insArgs(errFromRegistry.errorMessage[lang], args) || insArgs(errFromRegistry.errorMessage[settings.languages.defaultLanguage], args), + causedBy: {"cause": insArgs(errFromRegistry.causedBy[lang], args) || insArgs(errFromRegistry.causedBy[settings.languages.defaultLanguage], args)}, additionalInfo: msg, timestamp: new Date().getTime() }; } - let responseText = convertFileFormat.auto(format, errorObj); + let responseText = convertFileFormat.auto(format, errorObj, lang); httpServer.pipeString(res, responseText, parseURL.getMimeTypeFromFileFormatString(format)); }; -module.exports = { meta, call }; \ No newline at end of file +module.exports = { meta, call }; diff --git a/endpoints/langcode.js b/endpoints/langcode.js new file mode 100644 index 00000000..84865119 --- /dev/null +++ b/endpoints/langcode.js @@ -0,0 +1,81 @@ +const http = require("http"); +const convertFileFormat = require("../src/fileFormatConverter"); +const httpServer = require("../src/httpServer"); +const parseURL = require("../src/parseURL"); +const languages = require("../src/languages"); +const jsl = require("svjsl"); +const settings = require("../settings"); +const translate = require("../src/translate"); + +jsl.unused(http); + + +const meta = { + "name": "LangCodes", + "desc": "Returns the language code of the specified language", + "usage": { + "method": "GET", + "url": `${settings.info.docsURL}/langcode/{LANGUAGE}`, + "supportedParams": [ + "format", + "lang" + ] + } +}; + +/** + * Calls this endpoint + * @param {http.IncomingMessage} req The HTTP server request + * @param {http.ServerResponse} res The HTTP server response + * @param {Array} url URL path array gotten from the URL parser module + * @param {Object} params URL query params gotten from the URL parser module + * @param {String} format The file format to respond with + */ +const call = (req, res, url, params, format) => { + jsl.unused([req, params]); + + let statusCode = 200; + + let lang = (params && params["lang"]) ? params["lang"] : settings.languages.defaultLanguage; + + if(url[1] == undefined) + { + statusCode = 400; + return httpServer.pipeString(res, convertFileFormat.auto(format, { + "error": true, + "message": translate(lang, "noLangCodeSpecified") + }, lang), parseURL.getMimeTypeFromFileFormatString(format), statusCode); + } + + let defaultValDisabled = (params && params.noDefault && params.noDefault == true); + + let responseText = ""; + let langCode = null; + // if(!defaultValDisabled) + // langCode = settings.languages.defaultLanguage; + let language = url[1].toString().toLowerCase(); + + let ltc = languages.languageToCode(language); + langCode = (ltc === false ? (defaultValDisabled ? null : settings.languages.defaultLanguage) : ltc); + + if(langCode == null || ltc === false) + { + // error + statusCode = 400; + responseText = convertFileFormat.auto(format, { + "error": true, + "message": `The provided language "${decodeURIComponent(language)}" could not be resolved.` + }, lang); + } + else + { + responseText = convertFileFormat.auto(format, { + "error": false, + "code": langCode + }, lang); + } + + httpServer.pipeString(res, responseText, parseURL.getMimeTypeFromFileFormatString(format), statusCode); +}; + +module.exports = { meta, call }; diff --git a/endpoints/languages.js b/endpoints/languages.js new file mode 100644 index 00000000..1fe92e2a --- /dev/null +++ b/endpoints/languages.js @@ -0,0 +1,77 @@ +const http = require("http"); +const convertFileFormat = require("../src/fileFormatConverter"); +const httpServer = require("../src/httpServer"); +const parseURL = require("../src/parseURL"); +const jsl = require("svjsl"); +const languages = require("../src/languages"); +const settings = require("../settings"); + +jsl.unused(http); + + +const meta = { + "name": "Languages", + "desc": `Returns a list of supported and partially supported languages`, + "usage": { + "method": "GET", + "url": `${settings.info.docsURL}/languages`, + "supportedParams": [ + "format", + "lang" + ] + } +}; + +/** + * Calls this endpoint + * @param {http.IncomingMessage} req The HTTP server request + * @param {http.ServerResponse} res The HTTP server response + * @param {Array} url URL path array gotten from the URL parser module + * @param {Object} params URL query params gotten from the URL parser module + * @param {String} format The file format to respond with + */ +const call = (req, res, url, params, format) => { + jsl.unused([req, url, params]); + + let jokeLangs = languages.jokeLangs().map(jl => jl.code).sort(); + let sysLangs = languages.systemLangs().sort(); + + let responseText = ""; + + let langArray = []; + let pl = languages.getPossibleLanguages(); + + let lang = (params && params["lang"]) ? params["lang"] : settings.languages.defaultLanguage; + + Object.keys(pl).forEach(lc => { + langArray.push({ + "code": lc, + "name": pl[lc] + }); + }); + + if(format == "xml") + { + responseText = convertFileFormat.auto(format, { + "defaultLanguage": settings.languages.defaultLanguage, + "jokeLanguages": { "code": jokeLangs }, + "systemLanguages": { "code": sysLangs }, + "possibleLanguages": { "language": langArray }, + "timestamp": new Date().getTime() + }, lang); + } + else + { + responseText = convertFileFormat.auto(format, { + "defaultLanguage": settings.languages.defaultLanguage, + "jokeLanguages": jokeLangs, + "systemLanguages": sysLangs, + "possibleLanguages": langArray, + "timestamp": new Date().getTime() + }, lang); + } + + return httpServer.pipeString(res, responseText, parseURL.getMimeTypeFromFileFormatString(format)); +}; + +module.exports = { meta, call }; diff --git a/endpoints/ping.js b/endpoints/ping.js index cbdc94d7..fd5ad629 100755 --- a/endpoints/ping.js +++ b/endpoints/ping.js @@ -2,6 +2,7 @@ const http = require("http"); const convertFileFormat = require("../src/fileFormatConverter"); const httpServer = require("../src/httpServer"); const parseURL = require("../src/parseURL"); +const tr = require("../src/translate"); const jsl = require("svjsl"); const settings = require("../settings"); @@ -15,7 +16,8 @@ const meta = { "method": "GET", "url": `${settings.info.docsURL}/ping`, "supportedParams": [ - "format" + "format", + "lang" ] } }; @@ -31,13 +33,15 @@ const meta = { const call = (req, res, url, params, format) => { jsl.unused([req, url, params]); + let lang = (params && params["lang"]) ? params["lang"] : settings.languages.defaultLanguage; + let responseText = convertFileFormat.auto(format, { "error": false, - "ping": "Pong!", + "ping": tr(lang, "pingPong"), "timestamp": new Date().getTime() - }); + }, lang); httpServer.pipeString(res, responseText, parseURL.getMimeTypeFromFileFormatString(format)); }; -module.exports = { meta, call }; \ No newline at end of file +module.exports = { meta, call }; diff --git a/endpoints/static.js b/endpoints/static.js index 409d3881..b1a90d07 100755 --- a/endpoints/static.js +++ b/endpoints/static.js @@ -2,7 +2,7 @@ const http = require("http"); const httpServer = require("../src/httpServer"); const jsl = require("svjsl"); const settings = require("../settings"); -const fs = require("fs"); +const fs = require("fs-extra"); const debug = require("../src/verboseLogging"); jsl.unused(http); @@ -95,6 +95,7 @@ const call = (req, res, url, params, format) => { debug("Static", `Serving static content "${requestedFile}" with encoding "${selectedEncoding}"`); res.setHeader("Content-Encoding", selectedEncoding); + res.setHeader("Cache-Control", "max-age=86400"); return httpServer.pipeFile(res, filePath, mimeType, statusCode); } @@ -106,4 +107,4 @@ const call = (req, res, url, params, format) => { }); }; -module.exports = { call, meta }; \ No newline at end of file +module.exports = { call, meta }; diff --git a/package-lock.json b/package-lock.json index b9e84ead..f094e0ca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,9 +1,18 @@ { "name": "@sv443/jokeapi", - "version": "2.1.5", + "version": "2.2.0", "lockfileVersion": 1, "requires": true, "dependencies": { + "@arcanis/slice-ansi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@arcanis/slice-ansi/-/slice-ansi-1.0.2.tgz", + "integrity": "sha512-lDL63z0W/L/WTgqrwVOuNyMAsTv+pvjybd21z9SWdStmQoXT59E/iVWwat3gYjcdTNBf6oHAMoyFm8dtjpXEYw==", + "dev": true, + "requires": { + "grapheme-splitter": "^1.0.4" + } + }, "@babel/code-frame": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", @@ -36,52 +45,216 @@ "integrity": "sha512-AoeIEJn8vt+d/6+PXDRPaksYhnlbMIiejioBZvvMQsOjW/JYK6k/0dKnvvP3EhK5GfMBWDPtrxRtegWdAcdq9Q==", "dev": true }, + "@nodelib/fs.scandir": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", + "integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.3", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz", + "integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz", + "integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.3", + "fastq": "^1.6.0" + } + }, + "@opencensus/core": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/@opencensus/core/-/core-0.0.9.tgz", + "integrity": "sha512-31Q4VWtbzXpVUd2m9JS6HEaPjlKvNMOiF7lWKNmXF84yUcgfAFL5re7/hjDmdyQbOp32oGc+RFV78jXIldVz6Q==", + "requires": { + "continuation-local-storage": "^3.2.1", + "log-driver": "^1.2.7", + "semver": "^5.5.0", + "shimmer": "^1.2.0", + "uuid": "^3.2.1" + } + }, + "@opencensus/propagation-b3": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/@opencensus/propagation-b3/-/propagation-b3-0.0.8.tgz", + "integrity": "sha512-PffXX2AL8Sh0VHQ52jJC4u3T0H6wDK6N/4bg7xh4ngMYOIi13aR1kzVvX1sVDBgfGwDOkMbl4c54Xm3tlPx/+A==", + "requires": { + "@opencensus/core": "^0.0.8", + "uuid": "^3.2.1" + }, + "dependencies": { + "@opencensus/core": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/@opencensus/core/-/core-0.0.8.tgz", + "integrity": "sha512-yUFT59SFhGMYQgX0PhoTR0LBff2BEhPrD9io1jWfF/VDbakRfs6Pq60rjv0Z7iaTav5gQlttJCX2+VPxFWCuoQ==", + "requires": { + "continuation-local-storage": "^3.2.1", + "log-driver": "^1.2.7", + "semver": "^5.5.0", + "shimmer": "^1.2.0", + "uuid": "^3.2.1" + } + } + } + }, + "@pm2/agent-node": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@pm2/agent-node/-/agent-node-1.1.10.tgz", + "integrity": "sha512-xRcrk7OEwhS3d/227/kKGvxgmbIi6Yyp27FzGlFNermEKhgddmFaRnmd7GRLIsBM/KB28NrwflBZulzk/mma6g==", + "requires": { + "debug": "^3.1.0", + "eventemitter2": "^5.0.1", + "proxy-agent": "^3.0.3", + "ws": "^6.0.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "eventemitter2": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-5.0.1.tgz", + "integrity": "sha1-YZegldX7a1folC9v1+qtY6CclFI=" + } + } + }, + "@pm2/io": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@pm2/io/-/io-4.3.5.tgz", + "integrity": "sha512-CY/a6Nw72vrlp/FPx38l4jfEHp4gNEbo8i+WlSJ2cnWO6VE6CKmnC1zb4yQLvdP8f3EuzzoOBZVq6aGN20M82Q==", + "requires": { + "@opencensus/core": "0.0.9", + "@opencensus/propagation-b3": "0.0.8", + "@pm2/agent-node": "^1.1.10", + "async": "~2.6.1", + "debug": "4.1.1", + "eventemitter2": "^6.3.1", + "require-in-the-middle": "^5.0.0", + "semver": "6.3.0", + "shimmer": "^1.2.0", + "signal-exit": "^3.0.3", + "tslib": "1.9.3" + }, + "dependencies": { + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "requires": { + "lodash": "^4.17.14" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" + }, + "tslib": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==" + } + } + }, + "@sindresorhus/is": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-3.0.0.tgz", + "integrity": "sha512-kqA5I6Yun7PBHk8WN9BBP1c7FfN2SrD05GuVSEYPqDb4nerv7HqYfgBfMIKmT/EuejURkJKLZuLyGKGs6WEG9w==", + "dev": true + }, "@snyk/cli-interface": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@snyk/cli-interface/-/cli-interface-2.6.0.tgz", - "integrity": "sha512-jtk0gf80v4mFyDqaQNokD8GOPMTXpIUL35ewg6jtmuZw41xt56WF9kqCjiiViSRRRYA0RK+RuiVfmJA5pxvMUQ==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@snyk/cli-interface/-/cli-interface-2.8.0.tgz", + "integrity": "sha512-St/G39iJG1zQK15L24kcVYM2gmFc/ylBCcBqU2DMZKJKwOPccKLUO6s+dWIUXMccQ+DFS6TuHPvuAKQNi9C4Yg==", + "dev": true, "requires": { + "@snyk/dep-graph": "1.19.0", "@snyk/graphlib": "2.1.9-patch", "tslib": "^1.9.3" + }, + "dependencies": { + "@snyk/dep-graph": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@snyk/dep-graph/-/dep-graph-1.19.0.tgz", + "integrity": "sha512-/0phOICMk4hkX2KtZgi+4KNd5G9oYDIlxQDQk+ui2xl4gonPvK6Q5MFzHP7Xet1YY/XoU33ox41i+IO48qZ+zQ==", + "dev": true, + "requires": { + "@snyk/graphlib": "2.1.9-patch", + "lodash.isequal": "^4.5.0", + "object-hash": "^2.0.3", + "semver": "^6.0.0", + "source-map-support": "^0.5.19", + "tslib": "^2.0.0" + }, + "dependencies": { + "tslib": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.0.tgz", + "integrity": "sha512-lTqkx847PI7xEDYJntxZH89L2/aXInsyF2luSafe/+0fHOMjlBNXdH6th7f70qxLDhul7KZK0zC8V5ZIyHl0/g==", + "dev": true + } + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "@snyk/cocoapods-lockfile-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@snyk/cocoapods-lockfile-parser/-/cocoapods-lockfile-parser-3.2.0.tgz", - "integrity": "sha512-DyFqZudOlGXHBOVneLnQnyQ97xZLq+PTF9PhWOmrEzH/tKcLyXhdW/WmDPVNJVyNvogyRZ4cXIj487xy/EeZEw==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@snyk/cocoapods-lockfile-parser/-/cocoapods-lockfile-parser-3.4.0.tgz", + "integrity": "sha512-mAWgKIHFv0QEGpRvocVMxLAdJx7BmXtVOyQN/VtsGBoGFKqhO0jbtKUUVJC4b0jyKfVmEF2puo94i+1Uqz5q6A==", + "dev": true, "requires": { - "@snyk/dep-graph": "1.18.2", + "@snyk/dep-graph": "1.18.4", "@snyk/ruby-semver": "^2.0.4", "@types/js-yaml": "^3.12.1", - "core-js": "^3.2.0", "js-yaml": "^3.13.1", "source-map-support": "^0.5.7", "tslib": "^1.10.0" }, "dependencies": { "@snyk/dep-graph": { - "version": "1.18.2", - "resolved": "https://registry.npmjs.org/@snyk/dep-graph/-/dep-graph-1.18.2.tgz", - "integrity": "sha512-v7tIiCH4LmYOSc0xGHKSxSZ2PEDv8zDlYU7ZKSH+1Hk8Qvj3YYEFvtV1iFBHUEQFUen4kQA6lWxlwF8chsNw+w==", + "version": "1.18.4", + "resolved": "https://registry.npmjs.org/@snyk/dep-graph/-/dep-graph-1.18.4.tgz", + "integrity": "sha512-SePWsDyD7qrLxFifIieEl4GqyOODfOnP0hmUweTG5YcMroAV5nARGAUcjxREGzbXMcUpPfZhAaqFjYgzUDH8dQ==", + "dev": true, "requires": { "@snyk/graphlib": "2.1.9-patch", "@snyk/lodash": "4.17.15-patch", - "object-hash": "^1.3.1", - "prettier": "^1.19.1", - "semver": "^6.0.0", - "source-map-support": "^0.5.11", - "tslib": "^1.10.0" + "object-hash": "^2.0.3", + "semver": "^7.3.2", + "source-map-support": "^0.5.19", + "tslib": "^1.11.1" } }, - "object-hash": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.3.1.tgz", - "integrity": "sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA==" - }, "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true } } }, @@ -89,42 +262,16 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/@snyk/composer-lockfile-parser/-/composer-lockfile-parser-1.4.0.tgz", "integrity": "sha512-ga4YTRjJUuP0Ufr+t1IucwVjEFAv66JSBB/zVHP2zy/jmfA3l3ZjlGQSjsRC6Me9P2Z0esQ83AYNZvmIf9pq2w==", + "dev": true, "requires": { "@snyk/lodash": "^4.17.15-patch" } }, - "@snyk/configstore": { - "version": "3.2.0-rc1", - "resolved": "https://registry.npmjs.org/@snyk/configstore/-/configstore-3.2.0-rc1.tgz", - "integrity": "sha512-CV3QggFY8BY3u8PdSSlUGLibqbqCG1zJRmGM2DhnhcxQDRRPTGTP//l7vJphOVsUP1Oe23+UQsj7KRWpRUZiqg==", - "requires": { - "dot-prop": "^5.2.0", - "graceful-fs": "^4.1.2", - "make-dir": "^1.0.0", - "unique-string": "^1.0.0", - "write-file-atomic": "^2.0.0", - "xdg-basedir": "^3.0.0" - }, - "dependencies": { - "make-dir": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", - "requires": { - "pify": "^3.0.0" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" - } - } - }, "@snyk/dep-graph": { "version": "1.18.3", "resolved": "https://registry.npmjs.org/@snyk/dep-graph/-/dep-graph-1.18.3.tgz", "integrity": "sha512-7qWRTIJdZuc5VzDjdV2+03AHElyAZmhq7eV9BRu+jqrYjo9ohWBGEZgYslrTdvfqfJ8rkdrG3j0/0Aa25IxJcg==", + "dev": true, "requires": { "@snyk/graphlib": "2.1.9-patch", "@snyk/lodash": "4.17.15-patch", @@ -137,19 +284,33 @@ "semver": { "version": "7.3.2", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==" + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true } } }, + "@snyk/docker-registry-v2-client": { + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@snyk/docker-registry-v2-client/-/docker-registry-v2-client-1.13.5.tgz", + "integrity": "sha512-lgJiC071abCpFVLp47OnykU8MMrhdQe386Wt6QaDmjI0s2DQn/S58NfdLrPU7s6l4zoGT7UwRW9+7paozRgFTA==", + "dev": true, + "requires": { + "needle": "^2.5.0", + "parse-link-header": "^1.0.1", + "tslib": "^1.10.0" + } + }, "@snyk/gemfile": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@snyk/gemfile/-/gemfile-1.2.0.tgz", - "integrity": "sha512-nI7ELxukf7pT4/VraL4iabtNNMz8mUo7EXlqCFld8O5z6mIMLX9llps24iPpaIZOwArkY3FWA+4t+ixyvtTSIA==" + "integrity": "sha512-nI7ELxukf7pT4/VraL4iabtNNMz8mUo7EXlqCFld8O5z6mIMLX9llps24iPpaIZOwArkY3FWA+4t+ixyvtTSIA==", + "dev": true }, "@snyk/graphlib": { "version": "2.1.9-patch", "resolved": "https://registry.npmjs.org/@snyk/graphlib/-/graphlib-2.1.9-patch.tgz", "integrity": "sha512-uFO/pNMm3pN15QB+hVMU7uaQXhsBNwEA8lOET/VDcdOzLptODhXzkJqSHqt0tZlpdAz6/6Uaj8jY00UvPFgFMA==", + "dev": true, "requires": { "@snyk/lodash": "4.17.15-patch" } @@ -158,6 +319,7 @@ "version": "6.2.2-patch", "resolved": "https://registry.npmjs.org/@snyk/inquirer/-/inquirer-6.2.2-patch.tgz", "integrity": "sha512-IUq5bHRL0vtVKtfvd4GOccAIaLYHbcertug2UVZzk5+yY6R/CxfYsnFUTho1h4BdkfNdin2tPjE/5jRF4SKSrw==", + "dev": true, "requires": { "@snyk/lodash": "4.17.15-patch", "ansi-escapes": "^3.2.0", @@ -174,46 +336,38 @@ "through": "^2.3.6" }, "dependencies": { - "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==" - }, "ansi-regex": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true }, "cli-cursor": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, "requires": { "restore-cursor": "^2.0.0" } }, - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, "mimic-fn": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true }, "mute-stream": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=" + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true }, "onetime": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, "requires": { "mimic-fn": "^1.0.0" } @@ -222,6 +376,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, "requires": { "onetime": "^2.0.0", "signal-exit": "^3.0.2" @@ -231,6 +386,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, "requires": { "ansi-regex": "^4.1.0" } @@ -238,12 +394,12 @@ } }, "@snyk/java-call-graph-builder": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@snyk/java-call-graph-builder/-/java-call-graph-builder-1.8.1.tgz", - "integrity": "sha512-2G96dChYYXV73G8y9U0fi45dH6ybOjUSRBTJrMnmNkHJoOp1bzz8L4p5rkRypHQqr4SBS1EdCQeRw1eWRLm+Lg==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@snyk/java-call-graph-builder/-/java-call-graph-builder-1.10.0.tgz", + "integrity": "sha512-x3vKElHJRsPjlMBRACeD6kHtki54ffahYeAm4ny5epVpxm16/OT6f6AjNjPuX8DbxcauaD7wqirtc62OPH3YqA==", + "dev": true, "requires": { "@snyk/graphlib": "2.1.9-patch", - "@snyk/lodash": "4.17.15-patch", "ci-info": "^2.0.0", "debug": "^4.1.1", "glob": "^7.1.6", @@ -254,82 +410,123 @@ "source-map-support": "^0.5.7", "temp-dir": "^2.0.0", "tslib": "^1.9.3" - }, - "dependencies": { - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" - } } }, "@snyk/lodash": { "version": "4.17.15-patch", "resolved": "https://registry.npmjs.org/@snyk/lodash/-/lodash-4.17.15-patch.tgz", - "integrity": "sha512-e4+t34bGyjjRnwXwI14hqye9J/nRbG9iwaqTgXWHskm5qC+iK0UrjgYdWXiHJCf3Plbpr+1rpW+4LPzZnCGMhQ==" + "integrity": "sha512-e4+t34bGyjjRnwXwI14hqye9J/nRbG9iwaqTgXWHskm5qC+iK0UrjgYdWXiHJCf3Plbpr+1rpW+4LPzZnCGMhQ==", + "dev": true }, "@snyk/rpm-parser": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@snyk/rpm-parser/-/rpm-parser-1.1.0.tgz", - "integrity": "sha512-+DyCagvnpyBjwYTxaPMQGLW4rkpKAw1Jrh8YbZCg7Ix172InBxdve/0zud18Lu2H6xWtDDdMvRDdfl82wlTBvA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@snyk/rpm-parser/-/rpm-parser-2.0.0.tgz", + "integrity": "sha512-bWjQY5Xk3TcfVpeo8M5BhhSUEdPr2P19AWW13CHPu6sFZkckLWEcjQycnBsVD6RBmxGXecJ1YNui8dq6soHoYQ==", + "dev": true, "requires": { - "event-loop-spinner": "1.1.0", - "typescript": "3.8.3" + "event-loop-spinner": "^2.0.0" + }, + "dependencies": { + "event-loop-spinner": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/event-loop-spinner/-/event-loop-spinner-2.0.0.tgz", + "integrity": "sha512-1y4j/Mhttr8ordvHkbDsGzGrlQaSYJoXD/3YKUxiOXIk7myEn9UPfybEk/lLtrcU3D4QvCNmVUxVQaPtvAIaUw==", + "dev": true, + "requires": { + "tslib": "^1.10.0" + } + } } }, "@snyk/ruby-semver": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@snyk/ruby-semver/-/ruby-semver-2.2.0.tgz", "integrity": "sha512-FqUayoVjcyCsQFYPm3DcaCKdFR4xmapUkCGY+bcNBs3jqCUw687PoP9CPQ1Jvtaw5YpfBNl/62jyntsWCeciuA==", + "dev": true, "requires": { "@snyk/lodash": "4.17.15-patch" } }, "@snyk/snyk-cocoapods-plugin": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@snyk/snyk-cocoapods-plugin/-/snyk-cocoapods-plugin-2.2.0.tgz", - "integrity": "sha512-Ux7hXKawbk30niGBToGkKqHyKzhT3E7sCl0FNkPkHaaGZwPwhFCDyNFxBd4uGgWiQ+kT+RjtH5ahna+bSP69Yg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@snyk/snyk-cocoapods-plugin/-/snyk-cocoapods-plugin-2.3.0.tgz", + "integrity": "sha512-4V1xJMqsK6J3jHu9UufKySorzA8O1vNLRIK1JgJf5KcXQCP44SJI5dk9Xr9iFGXXtGo8iI9gmokQcHlGpkPSJg==", + "dev": true, "requires": { "@snyk/cli-interface": "1.5.0", - "@snyk/cocoapods-lockfile-parser": "3.2.0", + "@snyk/cocoapods-lockfile-parser": "3.4.0", "@snyk/dep-graph": "^1.18.2", "source-map-support": "^0.5.7", - "tslib": "^1.10.0" + "tslib": "^2.0.0" }, "dependencies": { "@snyk/cli-interface": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@snyk/cli-interface/-/cli-interface-1.5.0.tgz", "integrity": "sha512-+Qo+IO3YOXWgazlo+CKxOuWFLQQdaNCJ9cSfhFQd687/FuesaIxWdInaAdfpsLScq0c6M1ieZslXgiZELSzxbg==", + "dev": true, "requires": { "tslib": "^1.9.3" + }, + "dependencies": { + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "dev": true + } } + }, + "tslib": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.0.tgz", + "integrity": "sha512-lTqkx847PI7xEDYJntxZH89L2/aXInsyF2luSafe/+0fHOMjlBNXdH6th7f70qxLDhul7KZK0zC8V5ZIyHl0/g==", + "dev": true } } }, - "@snyk/update-notifier": { - "version": "2.5.1-rc2", - "resolved": "https://registry.npmjs.org/@snyk/update-notifier/-/update-notifier-2.5.1-rc2.tgz", - "integrity": "sha512-dlled3mfpnAt3cQb5hxkFiqfPCj4Yk0xV8Yl5P8PeVv1pUmO7vI4Ka4Mjs4r6CYM5f9kZhviFPQQcWOIDlMRcw==", + "@snyk/snyk-docker-pull": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@snyk/snyk-docker-pull/-/snyk-docker-pull-3.2.0.tgz", + "integrity": "sha512-uWKtjh29I/d0mfmfBN7w6RwwNBQxQVKrauF5ND/gqb0PVsKV22GIpkI+viWjI7KNKso6/B0tMmsv7TX2tsNcLQ==", + "dev": true, "requires": { - "@snyk/configstore": "3.2.0-rc1", - "boxen": "^1.3.0", - "chalk": "^2.3.2", - "import-lazy": "^2.1.0", - "is-ci": "^1.0.10", - "is-installed-globally": "^0.1.0", - "is-npm": "^1.0.0", - "latest-version": "^3.1.0", - "semver-diff": "^2.0.0", - "xdg-basedir": "^3.0.0" + "@snyk/docker-registry-v2-client": "^1.13.5", + "child-process": "^1.0.2", + "tar-stream": "^2.1.2", + "tmp": "^0.1.0" + }, + "dependencies": { + "tmp": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz", + "integrity": "sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==", + "dev": true, + "requires": { + "rimraf": "^2.6.3" + } + } } }, - "@types/agent-base": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@types/agent-base/-/agent-base-4.2.1.tgz", - "integrity": "sha512-GRmnDTq6ajyRyT8Ybg4IVVOyYqqFIAR4Zo9L+fdMAP+IJxd0nlTV99/IelJCBF629WOj6MpE9ohLHYCmkeJqRA==", + "@szmarczak/http-timer": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz", + "integrity": "sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ==", + "dev": true, "requires": { - "@types/node": "*" + "defer-to-connect": "^2.0.0" + } + }, + "@types/cacheable-request": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz", + "integrity": "sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ==", + "dev": true, + "requires": { + "@types/http-cache-semantics": "*", + "@types/keyv": "*", + "@types/node": "*", + "@types/responselike": "*" } }, "@types/color-name": { @@ -341,71 +538,409 @@ "@types/debug": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.5.tgz", - "integrity": "sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ==" + "integrity": "sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ==", + "dev": true + }, + "@types/emscripten": { + "version": "1.39.4", + "resolved": "https://registry.npmjs.org/@types/emscripten/-/emscripten-1.39.4.tgz", + "integrity": "sha512-k3LLVMFrdNA9UCvMDPWMbFrGPNb+GcPyw29ktJTo1RCN7RmxFG5XzPZcPKRlnLuLT/FRm8wp4ohvDwNY7GlROQ==", + "dev": true + }, + "@types/glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==", + "dev": true, + "requires": { + "@types/minimatch": "*", + "@types/node": "*" + } }, "@types/hosted-git-info": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/@types/hosted-git-info/-/hosted-git-info-2.7.0.tgz", - "integrity": "sha512-OW/D8GqCyQtH8F7xDdDxzPJTBgknZeZhlCakUcBCya2rYPRN53F+0YJVwSPyiyAhrknnjkl3P9qVk0oBI4S1qw==" + "integrity": "sha512-OW/D8GqCyQtH8F7xDdDxzPJTBgknZeZhlCakUcBCya2rYPRN53F+0YJVwSPyiyAhrknnjkl3P9qVk0oBI4S1qw==", + "dev": true + }, + "@types/http-cache-semantics": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz", + "integrity": "sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A==", + "dev": true }, "@types/js-yaml": { - "version": "3.12.3", - "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-3.12.3.tgz", - "integrity": "sha512-otRe77JNNWzoVGLKw8TCspKswRoQToys4tuL6XYVBFxjgeM0RUrx7m3jkaTdxILxeGry3zM8mGYkGXMeQ02guA==" + "version": "3.12.5", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-3.12.5.tgz", + "integrity": "sha512-JCcp6J0GV66Y4ZMDAQCXot4xprYB+Zfd3meK9+INSJeVZwJmHAW30BBEEkPzXswMXuiyReUGOP3GxrADc9wPww==", + "dev": true + }, + "@types/keyv": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz", + "integrity": "sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", + "dev": true }, "@types/node": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.1.tgz", - "integrity": "sha512-FAYBGwC+W6F9+huFIDtn43cpy7+SzG+atzRiTfdp3inUKL2hXnd4rG8hylJLIh4+hqrQy1P17kvJByE/z825hA==" + "version": "13.13.14", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.14.tgz", + "integrity": "sha512-Az3QsOt1U/K1pbCQ0TXGELTuTkPLOiFIQf3ILzbOyo0FqgV9SxRnxbxM5QlAveERZMHpZY+7u3Jz2tKyl+yg6g==", + "dev": true + }, + "@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "dev": true, + "requires": { + "@types/node": "*" + } }, "@types/semver": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-41qEJgBH/TWgo5NFSvBCJ1qkoi3Q6ONSF2avrHq1LVEZfYpdHmj0y9SuTK+u9ZhG1sYQKBL1AWXKyLWP4RaUoQ==" + "integrity": "sha512-41qEJgBH/TWgo5NFSvBCJ1qkoi3Q6ONSF2avrHq1LVEZfYpdHmj0y9SuTK+u9ZhG1sYQKBL1AWXKyLWP4RaUoQ==", + "dev": true }, "@types/xml2js": { "version": "0.4.5", "resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.5.tgz", "integrity": "sha512-yohU3zMn0fkhlape1nxXG2bLEGZRc1FeqF80RoHaYXJN7uibaauXfhzhOJr1Xh36sn+/tx21QAOf07b/xYVk1w==", + "dev": true, "requires": { "@types/node": "*" } }, - "@typescript-eslint/typescript-estree": { - "version": "2.31.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.31.0.tgz", - "integrity": "sha512-vxW149bXFXXuBrAak0eKHOzbcu9cvi6iNcJDzEtOkRwGHxJG15chiAQAwhLOsk+86p9GTr/TziYvw+H9kMaIgA==", + "@yarnpkg/core": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@yarnpkg/core/-/core-2.1.1.tgz", + "integrity": "sha512-qeBxz8nHjKAbGTP2ZcXBnXGfM7+cN0A73mIai/24uru1ayvCIgfjWL1uIj/MM+m+K5lJX0Dcn94ZBHWits9JWQ==", "dev": true, "requires": { - "debug": "^4.1.1", - "eslint-visitor-keys": "^1.1.0", - "glob": "^7.1.6", - "is-glob": "^4.0.1", - "lodash": "^4.17.15", - "semver": "^6.3.0", - "tsutils": "^3.17.1" + "@arcanis/slice-ansi": "^1.0.2", + "@yarnpkg/fslib": "^2.1.0", + "@yarnpkg/json-proxy": "^2.1.0", + "@yarnpkg/libzip": "^2.1.0", + "@yarnpkg/parsers": "^2.1.0", + "@yarnpkg/pnp": "^2.1.0", + "@yarnpkg/shell": "^2.1.0", + "camelcase": "^5.3.1", + "chalk": "^3.0.0", + "ci-info": "^2.0.0", + "clipanion": "^2.4.2", + "cross-spawn": "7.0.3", + "diff": "^4.0.1", + "globby": "^10.0.1", + "got": "^11.1.3", + "json-file-plus": "^3.3.1", + "logic-solver": "^2.0.1", + "micromatch": "^4.0.2", + "mkdirp": "^0.5.1", + "p-limit": "^2.2.0", + "pluralize": "^7.0.0", + "pretty-bytes": "^5.1.0", + "semver": "^7.1.2", + "stream-to-promise": "^2.2.0", + "tar": "^4.4.6", + "tslib": "^1.13.0", + "tunnel": "^0.0.6" }, "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true - } - } - }, - "@yarnpkg/lockfile": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", - "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==" - }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, - "acorn": { - "version": "7.3.1", + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "pluralize": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", + "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", + "dev": true + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "@yarnpkg/fslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/fslib/-/fslib-2.1.0.tgz", + "integrity": "sha512-E+f8w5yQZnTf1soyTWy7qdf+GmHsY+A0yEN4Di44/Txk6XRIMruyc1ShDi93mOI6ilnXxD87rNms18zJ8WnspA==", + "dev": true, + "requires": { + "@yarnpkg/libzip": "^2.1.0", + "tslib": "^1.13.0" + }, + "dependencies": { + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "dev": true + } + } + }, + "@yarnpkg/json-proxy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/json-proxy/-/json-proxy-2.1.0.tgz", + "integrity": "sha512-rOgCg2DkyviLgr80mUMTt9vzdf5RGOujQB26yPiXjlz4WNePLBshKlTNG9rKSoKQSOYEQcw6cUmosfOKDatrCw==", + "dev": true, + "requires": { + "@yarnpkg/fslib": "^2.1.0", + "tslib": "^1.13.0" + }, + "dependencies": { + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "dev": true + } + } + }, + "@yarnpkg/libzip": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/libzip/-/libzip-2.1.0.tgz", + "integrity": "sha512-39c7KuSWcYUqVxlBLZwfqdD/D6lS+jplNVWd6uAnk8EpnacaYGJRegvkqWyfw5c8KHukNMeEGF5JHrXPZYBM0w==", + "dev": true, + "requires": { + "@types/emscripten": "^1.38.0", + "tslib": "^1.13.0" + }, + "dependencies": { + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "dev": true + } + } + }, + "@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true + }, + "@yarnpkg/parsers": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/parsers/-/parsers-2.1.0.tgz", + "integrity": "sha512-75OYQ6PMs1C3zm+W+T1xhLyVDX78zXQGEVHpWd4o/QwpAbhneB3/5FXVGRzI3gjPPWWSb/pKOPB1S6p0xmQD2Q==", + "dev": true, + "requires": { + "js-yaml": "^3.10.0", + "tslib": "^1.13.0" + }, + "dependencies": { + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "dev": true + } + } + }, + "@yarnpkg/pnp": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/pnp/-/pnp-2.1.0.tgz", + "integrity": "sha512-b8NlB71EFifv1jDX47nFaRXrykROxHcS7YuGb2dQ+Gp9gqJ0thIaZ3yB9+qWF8acdWtNcMpjCug4xkfAAR5Odw==", + "dev": true, + "requires": { + "@types/node": "^13.7.0", + "@yarnpkg/fslib": "^2.1.0", + "tslib": "^1.13.0" + }, + "dependencies": { + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "dev": true + } + } + }, + "@yarnpkg/shell": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/shell/-/shell-2.1.0.tgz", + "integrity": "sha512-9i9ZWqeKHGV0DOfdxTVq5zl73Li8Fg947v57uLBEaytNF+HywkDfouNkg/6HfgBrpI0WH8OJ9Pz/uDaE5cpctw==", + "dev": true, + "requires": { + "@yarnpkg/fslib": "^2.1.0", + "@yarnpkg/parsers": "^2.1.0", + "clipanion": "^2.4.2", + "cross-spawn": "7.0.3", + "fast-glob": "^3.2.2", + "stream-buffers": "^3.0.2", + "tslib": "^1.13.0" + }, + "dependencies": { + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "acorn": { + "version": "7.3.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz", "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==", "dev": true @@ -437,11 +972,46 @@ } }, "ansi-align": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", - "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", + "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", + "dev": true, "requires": { - "string-width": "^2.0.0" + "string-width": "^3.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } } }, "ansi-colors": { @@ -450,6 +1020,12 @@ "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true }, + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true + }, "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", @@ -459,6 +1035,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, "requires": { "color-convert": "^1.9.0" } @@ -466,7 +1043,14 @@ "ansicolors": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz", - "integrity": "sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk=" + "integrity": "sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk=", + "dev": true + }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", + "dev": true }, "app-module-path": { "version": "2.2.0", @@ -482,7 +1066,8 @@ "archy": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=" + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", + "dev": true }, "are-we-there-yet": { "version": "1.1.5", @@ -497,14 +1082,31 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, "requires": { "sprintf-js": "~1.0.2" } }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, "asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", + "dev": true + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } }, "ast-module-types": { "version": "2.6.0", @@ -526,18 +1128,48 @@ "async": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + }, + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + }, + "async-listener": { + "version": "0.6.10", + "resolved": "https://registry.npmjs.org/async-listener/-/async-listener-0.6.10.tgz", + "integrity": "sha512-gpuo6xOyF4D5DE5WvyqZdPA3NGhiT6Qf07l7DCB0wwDEsLvDIbCr6j9S5aj5Ch96dLace5tXVzWBZkxU/c5ohw==", + "requires": { + "semver": "^5.3.0", + "shimmer": "^1.1.0" + } + }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true }, "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==" }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, "bignumber.js": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", @@ -566,28 +1198,130 @@ } }, "boxen": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", - "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", + "integrity": "sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==", + "dev": true, "requires": { - "ansi-align": "^2.0.0", - "camelcase": "^4.0.0", - "chalk": "^2.0.1", - "cli-boxes": "^1.0.0", - "string-width": "^2.0.0", - "term-size": "^1.2.0", - "widest-line": "^2.0.0" + "ansi-align": "^3.0.0", + "camelcase": "^5.3.1", + "chalk": "^3.0.0", + "cli-boxes": "^2.2.0", + "string-width": "^4.1.0", + "term-size": "^2.1.0", + "type-fest": "^0.8.1", + "widest-line": "^3.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, "brace-expansion": { "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" } }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browserify-zlib": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz", + "integrity": "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=", + "dev": true, + "requires": { + "pako": "~0.2.0" + } + }, "buffer": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", @@ -600,13 +1334,46 @@ "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true }, "bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" }, + "cacheable-lookup": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.3.tgz", + "integrity": "sha512-W+JBqF9SWe18A72XFzN/V/CULFzPm7sBXzzR6ekkE+3tLG72wFZrBiBZhrZuDoYexop4PHJVdFAKb/Nj9+tm9w==", + "dev": true + }, + "cacheable-request": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.1.tgz", + "integrity": "sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw==", + "dev": true, + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^2.0.0" + }, + "dependencies": { + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + } + } + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -614,19 +1381,16 @@ "dev": true }, "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" - }, - "capture-stack-trace": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz", - "integrity": "sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==" + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -636,7 +1400,14 @@ "chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "child-process": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/child-process/-/child-process-1.0.2.tgz", + "integrity": "sha1-mJdNx+0e5MYin44wX6cxOmiFp/I=", + "dev": true }, "chownr": { "version": "1.1.4", @@ -644,14 +1415,16 @@ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" }, "ci-info": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", - "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true }, "cli-boxes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", - "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=" + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.0.tgz", + "integrity": "sha512-gpaBrMAizVEANOpfZp/EEUixTXDyGt7DFzdK5hU+UbWt/J0lB0w20ncZj59Z9a93xHb9u12zF5BS6i9RKbtg4w==", + "dev": true }, "cli-cursor": { "version": "3.1.0", @@ -665,7 +1438,8 @@ "cli-spinner": { "version": "0.2.10", "resolved": "https://registry.npmjs.org/cli-spinner/-/cli-spinner-0.2.10.tgz", - "integrity": "sha512-U0sSQ+JJvSLi1pAYuJykwiA8Dsr15uHEy85iCJ6A+0DjVxivr3d+N2Wjvodeg89uP5K6TswFkKBfAD7B3YSn/Q==" + "integrity": "sha512-U0sSQ+JJvSLi1pAYuJykwiA8Dsr15uHEy85iCJ6A+0DjVxivr3d+N2Wjvodeg89uP5K6TswFkKBfAD7B3YSn/Q==", + "dev": true }, "cli-spinners": { "version": "2.3.0", @@ -674,14 +1448,22 @@ "dev": true }, "cli-width": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", - "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=" + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", + "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", + "dev": true + }, + "clipanion": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/clipanion/-/clipanion-2.4.2.tgz", + "integrity": "sha512-kBCYtQKI4/R/zjierdwoDAsNUSvoh4pX2tseYxgLYQcKIpdPsHZrFWiQOfbe2Scd/btsqJEc4q6g55q0p5DZAw==", + "dev": true }, "cliui": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "dev": true, "requires": { "string-width": "^1.0.1", "strip-ansi": "^3.0.1", @@ -692,6 +1474,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -702,6 +1485,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, "requires": { "ansi-regex": "^2.0.0" } @@ -710,6 +1494,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, "requires": { "string-width": "^1.0.1", "strip-ansi": "^3.0.1" @@ -723,6 +1508,23 @@ "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", "dev": true }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + }, + "dependencies": { + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true + } + } + }, "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -737,6 +1539,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, "requires": { "color-name": "1.1.3" } @@ -744,7 +1547,8 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true }, "commander": { "version": "5.1.0", @@ -761,45 +1565,77 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "dev": true, + "requires": { + "dot-prop": "^5.2.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" + }, + "dependencies": { + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } }, "console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" }, - "core-js": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", - "integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==" + "continuation-local-storage": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/continuation-local-storage/-/continuation-local-storage-3.2.1.tgz", + "integrity": "sha512-jx44cconVqkCEEyLSKWwkvUXwO561jXMa3LPjTPsm5QR22PA0/mhe33FT4Xb5y74JDvt/Cq+5lm8S8rskLv9ZA==", + "requires": { + "async-listener": "^0.6.0", + "emitter-listener": "^1.1.1" + } }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, - "create-error-class": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", - "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", - "requires": { - "capture-stack-trace": "^1.0.0" - } - }, "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, "requires": { - "lru-cache": "^4.0.1", + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", "shebang-command": "^1.2.0", "which": "^1.2.9" } }, "crypto-random-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", - "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "dev": true }, "cssfilter": { "version": "0.0.10", @@ -822,7 +1658,8 @@ "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true }, "decomment": { "version": "0.9.2", @@ -860,6 +1697,21 @@ "clone": "^1.0.2" } }, + "defer-to-connect": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.0.tgz", + "integrity": "sha512-bYL2d05vOSf1JEZNx5vSAtPuBMkX8K9EUutg7zlKvTqKXHt7RhWJFbmd7qakVuf13i+IkGmp6FwSsONOf6VYIg==", + "dev": true + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, "degenerator": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-1.0.4.tgz", @@ -1005,17 +1857,76 @@ "ast-module-types": "^2.6.0", "node-source-walk": "^4.2.0", "typescript": "^3.8.3" + }, + "dependencies": { + "@typescript-eslint/typescript-estree": { + "version": "2.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.34.0.tgz", + "integrity": "sha512-OMAr+nJWKdlVM9LOqCqh3pQQPwxHAN7Du8DR6dmwCrAmxtiXQnhHJ6tBNtf+cggqfo51SG/FCwnKhXCIM7hnVg==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "eslint-visitor-keys": "^1.1.0", + "glob": "^7.1.6", + "is-glob": "^4.0.1", + "lodash": "^4.17.15", + "semver": "^7.3.2", + "tsutils": "^3.17.1" + } + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true + } } }, "diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==" + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "docker-modem": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-2.1.3.tgz", + "integrity": "sha512-cwaRptBmYZwu/FyhGcqBm2MzXA77W2/E6eVkpOZVDk6PkI9Bjj84xPrXiHMA+OWjzNy+DFjgKh8Q+1hMR7/OHg==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "readable-stream": "^3.5.0", + "split-ca": "^1.0.1", + "ssh2": "^0.8.7" + }, + "dependencies": { + "readable-stream": { + "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", + "util-deprecate": "^1.0.1" + } + } + } }, "dockerfile-ast": { "version": "0.0.19", "resolved": "https://registry.npmjs.org/dockerfile-ast/-/dockerfile-ast-0.0.19.tgz", "integrity": "sha512-iDRNFeAB2j4rh/Ecc2gh3fjciVifCMsszfCfHlYF5Wv8yybjZLiRDZUBt/pS3xrAz8uWT8fCHLq4pOQMmwCDwA==", + "dev": true, "requires": { "vscode-languageserver-types": "^3.5.0" } @@ -1033,6 +1944,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz", "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==", + "dev": true, "requires": { "is-obj": "^2.0.0" }, @@ -1040,7 +1952,8 @@ "is-obj": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true } } }, @@ -1053,6 +1966,7 @@ "version": "4.10.0", "resolved": "https://registry.npmjs.org/dotnet-deps-parser/-/dotnet-deps-parser-4.10.0.tgz", "integrity": "sha512-dEO1oTvreaDCtcvhRdOmmAMubyC+MWqVr1c/1Wvasi+NW4NZeB67qGh1taqowUFh+aCXtPw3SP2eExn6aNkhwA==", + "dev": true, "requires": { "@snyk/lodash": "4.17.15-patch", "@types/xml2js": "0.4.5", @@ -1064,12 +1978,34 @@ "duplexer3": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", + "dev": true + }, + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "dev": true, + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } }, "email-validator": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/email-validator/-/email-validator-2.0.4.tgz", - "integrity": "sha512-gYCwo7kh5S3IDyZPLZf6hSS0MnZT8QmJFqYvbqlDZSbwdZlY6QZWxJ4i/6UhITOJ4XzyI647Bm2MXKCLqnJ4nQ==" + "integrity": "sha512-gYCwo7kh5S3IDyZPLZf6hSS0MnZT8QmJFqYvbqlDZSbwdZlY6QZWxJ4i/6UhITOJ4XzyI647Bm2MXKCLqnJ4nQ==", + "dev": true + }, + "emitter-listener": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/emitter-listener/-/emitter-listener-1.1.2.tgz", + "integrity": "sha512-Bt1sBAGFHY9DKY+4/2cV6izcKJUf5T7/gkdmkxzX/qv9CcGH8xSwVRW5mtX03SWJtRTWSOpzCuWN9rBFYZepZQ==", + "requires": { + "shimmer": "^1.2.0" + } }, "emoji-regex": { "version": "7.0.3", @@ -1127,10 +2063,17 @@ "es6-promise": "^4.0.3" } }, + "escape-goat": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", + "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", + "dev": true + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true }, "escodegen": { "version": "1.14.1", @@ -1427,17 +2370,24 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/event-loop-spinner/-/event-loop-spinner-1.1.0.tgz", "integrity": "sha512-YVFs6dPpZIgH665kKckDktEVvSBccSYJmoZUfhNUdv5d3Xv+Q+SKF4Xis1jolq9aBzuW1ZZhQh/m/zU/TPdDhw==", + "dev": true, "requires": { "tslib": "^1.10.0" } }, + "eventemitter2": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.3.tgz", + "integrity": "sha512-t0A2msp6BzOf+QAcI6z9XMktLj52OjGQg+8SJH6v5+3uxNpWYRR3wQmfA+6xtMU9kOC59qk9licus5dYcrYkMQ==" + }, "execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", "is-stream": "^1.1.0", "npm-run-path": "^2.0.0", "p-finally": "^1.0.0", @@ -1459,6 +2409,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, "requires": { "chardet": "^0.7.0", "iconv-lite": "^0.4.24", @@ -1480,6 +2431,20 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "fast-glob": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.4.tgz", + "integrity": "sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.0", + "merge2": "^1.3.0", + "micromatch": "^4.0.2", + "picomatch": "^2.2.1" + } + }, "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -1491,6 +2456,24 @@ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" }, + "fastq": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.8.0.tgz", + "integrity": "sha512-SMIZoZdLh/fgofivvIkmknUXyPnvxRE3DhtZ5Me3Mrsk5gyPL42F0xr51TdRXskBxHfMp+07bcYzfsYEsSQA9Q==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, "file-entry-cache": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", @@ -1540,6 +2523,15 @@ } } }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, "find": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/find/-/find-0.3.0.tgz", @@ -1577,10 +2569,31 @@ "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" }, + "fs-extra": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", + "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^1.0.0" + } + }, + "fs-minipass": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", + "dev": true, + "requires": { + "minipass": "^2.6.0" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true }, "ftp": { "version": "0.3.10", @@ -1614,12 +2627,23 @@ } } }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, "functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "fuse.js": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-5.2.3.tgz", + "integrity": "sha512-ld3AEgKtKnnXCtJavtygAb+aLlD5aVvLwTocXXBSStLA6JGFI6oMxTvumwh46N2/3gs3A7JNDu1px5F1/cq84g==" + }, "gauge": { "version": "2.7.4", "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", @@ -1672,9 +2696,13 @@ "dev": true }, "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } }, "get-uri": { "version": "2.0.4", @@ -1704,23 +2732,6 @@ } } }, - "git-up": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/git-up/-/git-up-4.0.1.tgz", - "integrity": "sha512-LFTZZrBlrCrGCG07/dm1aCjjpL1z9L3+5aEeI9SBhAqSc+kiA9Or1bgZhQFNppJX6h/f5McrvJt1mQXTFm6Qrw==", - "requires": { - "is-ssh": "^1.3.0", - "parse-url": "^5.0.0" - } - }, - "git-url-parse": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-11.1.2.tgz", - "integrity": "sha512-gZeLVGY8QVKMIkckncX+iCq2/L8PlwncvDFKiWkBn9EtCfYDbliRTTp6qzyQ1VMdITUfq7293zDzfpjdiGASSQ==", - "requires": { - "git-up": "^4.0.0" - } - }, "github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", @@ -1730,6 +2741,7 @@ "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", @@ -1749,11 +2761,12 @@ } }, "global-dirs": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", - "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.0.1.tgz", + "integrity": "sha512-5HqUqdhkEovj2Of/ms3IeS/EekcO54ytHRLV4PEY2rhRwrHXLQjeVEES0Lhka0xwNDtGYn58wyC4s5+MHsOO6A==", + "dev": true, "requires": { - "ini": "^1.3.4" + "ini": "^1.3.5" } }, "globals": { @@ -1765,6 +2778,30 @@ "type-fest": "^0.8.1" } }, + "globby": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz", + "integrity": "sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.0.3", + "glob": "^7.1.3", + "ignore": "^5.1.1", + "merge2": "^1.2.3", + "slash": "^3.0.0" + }, + "dependencies": { + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true + } + } + }, "gonzales-pe": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/gonzales-pe/-/gonzales-pe-4.3.0.tgz", @@ -1775,21 +2812,39 @@ } }, "got": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", - "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", - "requires": { - "create-error-class": "^3.0.0", - "duplexer3": "^0.1.4", - "get-stream": "^3.0.0", - "is-redirect": "^1.0.0", - "is-retry-allowed": "^1.0.0", - "is-stream": "^1.0.0", - "lowercase-keys": "^1.0.0", - "safe-buffer": "^5.0.1", - "timed-out": "^4.0.0", - "unzip-response": "^2.0.1", - "url-parse-lax": "^1.0.0" + "version": "11.5.0", + "resolved": "https://registry.npmjs.org/got/-/got-11.5.0.tgz", + "integrity": "sha512-vOZEcEaK0b6x11uniY0HcblZObKPRO75Jvz53VKuqGSaKCM/zEt0sj2LGYVdqDYJzO3wYdG+FPQQ1hsgoXy7vQ==", + "dev": true, + "requires": { + "@sindresorhus/is": "^3.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.1", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.4.8", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "dependencies": { + "decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "requires": { + "mimic-response": "^3.1.0" + } + }, + "mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true + } } }, "graceful-fs": { @@ -1797,6 +2852,12 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==" }, + "grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, "graphviz": { "version": "0.0.9", "resolved": "https://registry.npmjs.org/graphviz/-/graphviz-0.0.9.tgz", @@ -1806,20 +2867,66 @@ "temp": "~0.4.0" } }, + "gunzip-maybe": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/gunzip-maybe/-/gunzip-maybe-1.4.2.tgz", + "integrity": "sha512-4haO1M4mLO91PW57BMsDFf75UmwoRX0GkdD+Faw+Lr+r/OZrOCS0pIBwOL1xCKQqnQzbNFGgK2V2CpBUPeFNTw==", + "dev": true, + "requires": { + "browserify-zlib": "^0.1.4", + "is-deflate": "^1.0.0", + "is-gzip": "^1.0.0", + "peek-stream": "^1.1.0", + "pumpify": "^1.3.3", + "through2": "^2.0.3" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true }, "has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" }, + "has-yarn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", + "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", + "dev": true + }, "hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==" + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.5.tgz", + "integrity": "sha512-i4dpK6xj9BIpVOTboXIlKG9+8HMKggcrMX7WA24xZtKwX0TPelq/rbaS5rCKeNX8sJXZJGdSxpnEGtta+wismQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "dev": true }, "http-errors": { "version": "1.7.3", @@ -1862,6 +2969,16 @@ "resolved": "https://registry.npmjs.org/http-ratelimit/-/http-ratelimit-0.2.3.tgz", "integrity": "sha512-bp9qBHmsuvlF2eagibxWn9kKxqTVkf89K678hquVq/7LOvAeEGe65Z6tpO4YTHkCCyse/7i+uo9HaAJto1QLcg==" }, + "http2-wrapper": { + "version": "1.0.0-beta.5.2", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.0-beta.5.2.tgz", + "integrity": "sha512-xYz9goEyBnC8XwXDTuC/MZ6t+MrKVQZOk4s7+PaDkwIsQd8IwqvM+0M6bA/2lvG8GHXcPdf+MejTUeO2LCPCeQ==", + "dev": true, + "requires": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + } + }, "https-proxy-agent": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-3.0.1.tgz", @@ -1903,7 +3020,8 @@ "immediate": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=" + "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=", + "dev": true }, "import-fresh": { "version": "3.2.1", @@ -1918,12 +3036,14 @@ "import-lazy": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", - "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=" + "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", + "dev": true }, "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true }, "indexes-of": { "version": "1.0.1", @@ -1935,6 +3055,7 @@ "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" @@ -1953,25 +3074,46 @@ "invert-kv": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "dev": true }, "ip": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" }, + "is": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/is/-/is-3.3.0.tgz", + "integrity": "sha512-nW24QBoPcFGGHJGUwnfpI7Yc5CdqWNdsyHQszVE/z2pKHXzh7FZ5GWhJqSyaQ9wMkQnsTx+kAI8bHlCX4tKdbg==", + "dev": true + }, + "is-callable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", + "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", + "dev": true + }, "is-ci": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz", - "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, "requires": { - "ci-info": "^1.5.0" + "ci-info": "^2.0.0" } }, + "is-deflate": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-deflate/-/is-deflate-1.0.0.tgz", + "integrity": "sha1-yGKQHDwWH7CdrHzcfnhPgOmPLxQ=", + "dev": true + }, "is-docker": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.0.0.tgz", - "integrity": "sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ==" + "integrity": "sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ==", + "dev": true }, "is-extglob": { "version": "2.1.1", @@ -1996,13 +3138,20 @@ "is-extglob": "^2.1.1" } }, + "is-gzip": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-gzip/-/is-gzip-1.0.0.tgz", + "integrity": "sha1-bKiwe5nHeZgCWQDlVc7Y7YCHmoM=", + "dev": true + }, "is-installed-globally": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", - "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz", + "integrity": "sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==", + "dev": true, "requires": { - "global-dirs": "^0.1.0", - "is-path-inside": "^1.0.0" + "global-dirs": "^2.0.1", + "is-path-inside": "^3.0.1" } }, "is-interactive": { @@ -2012,33 +3161,28 @@ "dev": true }, "is-npm": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", - "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-4.0.0.tgz", + "integrity": "sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==", + "dev": true }, - "is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, - "is-path-inside": { + "is-obj": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", - "requires": { - "path-is-inside": "^1.0.1" - } - }, - "is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "dev": true }, - "is-redirect": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", - "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=" + "is-path-inside": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", + "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==", + "dev": true }, "is-regexp": { "version": "1.0.0", @@ -2052,23 +3196,17 @@ "integrity": "sha1-CRtGoNZ8HtD+hfH4z93gBrslHUY=", "dev": true }, - "is-retry-allowed": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", - "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==" - }, - "is-ssh": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.3.1.tgz", - "integrity": "sha512-0eRIASHZt1E68/ixClI8bp2YK2wmBPVWEismTs6M+M099jKgrzl/3E976zIbImSIob48N2/XGe9y7ZiYdImSlg==", - "requires": { - "protocols": "^1.1.0" - } - }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true }, "is-url": { "version": "1.2.4", @@ -2080,10 +3218,17 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, "requires": { "is-docker": "^2.0.0" } }, + "is-yarn-global": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", + "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", + "dev": true + }, "is_js": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/is_js/-/is_js-0.9.0.tgz", @@ -2097,7 +3242,8 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true }, "js-tokens": { "version": "4.0.0", @@ -2109,6 +3255,7 @@ "version": "3.13.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, "requires": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -2129,6 +3276,25 @@ } } }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "json-file-plus": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/json-file-plus/-/json-file-plus-3.3.1.tgz", + "integrity": "sha512-wo0q1UuiV5NsDPQDup1Km8IwEeqe+olr8tkWxeJq9Bjtcp7DZ0l+yrg28fSC3DEtrE311mhTZ54QGS6oiqnZEA==", + "dev": true, + "requires": { + "is": "^3.2.1", + "node.extend": "^2.0.0", + "object.assign": "^4.1.0", + "promiseback": "^2.0.2", + "safer-buffer": "^2.0.2" + } + }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -2150,29 +3316,58 @@ "remove-trailing-spaces": "^1.0.6" } }, + "jsonfile": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", + "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^1.0.0" + } + }, "jszip": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.4.0.tgz", - "integrity": "sha512-gZAOYuPl4EhPTXT0GjhI3o+ZAz3su6EhLrKUoAivcKqyqC7laS5JEv4XWZND9BgcDcF83vI85yGbDmDR6UhrIg==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.5.0.tgz", + "integrity": "sha512-WRtu7TPCmYePR1nazfrtuF216cIVon/3GWOvHS9QR5bIwSbnxtdpma6un3jyGGNhHsKCSzn5Ypk+EkDRvTGiFA==", + "dev": true, "requires": { "lie": "~3.3.0", "pako": "~1.0.2", "readable-stream": "~2.3.6", "set-immediate-shim": "~1.0.1" + }, + "dependencies": { + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + } + } + }, + "keyv": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.1.tgz", + "integrity": "sha512-xz6Jv6oNkbhrFCvCP7HQa8AaII8y8LRpoSm661NOKLr4uHuBwhX4epXrPQgF3+xdJnN4Esm5X0xwY4bOlALOtw==", + "dev": true, + "requires": { + "json-buffer": "3.0.1" } }, "latest-version": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", - "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", + "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", + "dev": true, "requires": { - "package-json": "^4.0.0" + "package-json": "^6.3.0" } }, "lcid": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, "requires": { "invert-kv": "^1.0.0" } @@ -2190,6 +3385,7 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, "requires": { "immediate": "~3.0.5" } @@ -2197,43 +3393,78 @@ "lodash": { "version": "4.17.19", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", - "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", - "dev": true + "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==" }, "lodash.assign": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", - "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=" + "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=", + "dev": true }, "lodash.assignin": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz", - "integrity": "sha1-uo31+4QesKPoBEIysOJjqNxqKKI=" + "integrity": "sha1-uo31+4QesKPoBEIysOJjqNxqKKI=", + "dev": true }, "lodash.clone": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz", - "integrity": "sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y=" + "integrity": "sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y=", + "dev": true }, "lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true + }, + "lodash.flatmap": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.flatmap/-/lodash.flatmap-4.5.0.tgz", + "integrity": "sha1-74y/QI9uSCaGYzRTBcaswLd4cC4=", + "dev": true }, "lodash.flatten": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" + "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=", + "dev": true }, "lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", + "dev": true + }, + "lodash.isempty": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz", + "integrity": "sha1-b4bL7di+TsmHvpqvM8loTbGzHn4=", + "dev": true + }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=", + "dev": true }, "lodash.set": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", - "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=" + "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=", + "dev": true + }, + "lodash.topairs": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.topairs/-/lodash.topairs-4.3.0.tgz", + "integrity": "sha1-O23qo31g+xFnE8RsXxfqGQ7EjWQ=", + "dev": true + }, + "log-driver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz", + "integrity": "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg==" }, "log-symbols": { "version": "3.0.0", @@ -2244,24 +3475,35 @@ "chalk": "^2.4.2" } }, + "logic-solver": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/logic-solver/-/logic-solver-2.0.1.tgz", + "integrity": "sha1-6fpHAC612M2nYW1BY5uXVS62dL4=", + "dev": true, + "requires": { + "underscore": "^1.7.0" + } + }, "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true }, "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" + "yallist": "^4.0.0" } }, "macos-release": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.3.0.tgz", - "integrity": "sha512-OHhSbtcviqMPt7yfw5ef5aghS2jzFVKEFyCJndQt2YpSQ9qRVSEv2axSJI1paVThEu+FFGs584h/1YhxjVqajA==" + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.4.0.tgz", + "integrity": "sha512-ko6deozZYiAkqa/0gmcsz+p4jSy3gY7/ZsCEokPaYd8k+6/aXGkiTgr61+Owup7Sf+xjqW8u2ElhoM9SEcEfuA==", + "dev": true }, "madge": { "version": "3.9.0", @@ -2372,6 +3614,22 @@ "readable-stream": "^2.0.1" } }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, "mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -2387,6 +3645,7 @@ "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" } @@ -2396,6 +3655,33 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" }, + "minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + }, + "dependencies": { + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + } + } + }, + "minizlib": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", + "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", + "dev": true, + "requires": { + "minipass": "^2.9.0" + } + }, "mkdirp": { "version": "0.5.5", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", @@ -2419,6 +3705,11 @@ "node-source-walk": "^4.0.0" } }, + "module-details-from-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz", + "integrity": "sha1-EUyUlnPiqKNenTV4hSeqN7Z52is=" + }, "module-lookup-amd": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/module-lookup-amd/-/module-lookup-amd-6.2.0.tgz", @@ -2494,6 +3785,7 @@ "version": "0.10.0", "resolved": "https://registry.npmjs.org/nconf/-/nconf-0.10.0.tgz", "integrity": "sha512-fKiXMQrpP7CYWJQzKkPPx9hPgmq+YLDyxcG9N8RpiE9FoCkCbzD0NyW0YhE3xn3Aupe7nnDeIx4PFzYehpHT9Q==", + "dev": true, "requires": { "async": "^1.4.0", "ini": "^1.3.0", @@ -2502,9 +3794,10 @@ } }, "needle": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.4.1.tgz", - "integrity": "sha512-x/gi6ijr4B7fwl6WYL9FwlCvRQKGlUNvnceho8wxkwXqN8jvVmmmATTmZPRRG7b/yC1eode26C2HO9jl78Du9g==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.5.0.tgz", + "integrity": "sha512-o/qITSDR0JCyCKEQ1/1bnUXMmznxabbwi/Y4WwJElf+evwJNFNwIDMCCt5IigFVxgeGBJESLohGtIS9gEzo1fA==", + "dev": true, "requires": { "debug": "^3.2.6", "iconv-lite": "^0.4.4", @@ -2515,6 +3808,7 @@ "version": "3.2.6", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, "requires": { "ms": "^2.1.1" } @@ -2529,7 +3823,8 @@ "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true }, "node-abi": { "version": "2.16.0", @@ -2558,20 +3853,32 @@ "resolved": "https://registry.npmjs.org/node-wrap/-/node-wrap-0.2.0.tgz", "integrity": "sha512-q5nI3nfk1fHEYRNkDiqP04s9GrvhmQ+RxH+a/wE21GXbl+hcphlDaxrtO7duiXM41ilxaw4MtcXoV4pzDWdXnQ==" }, + "node.extend": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node.extend/-/node.extend-2.0.2.tgz", + "integrity": "sha512-pDT4Dchl94/+kkgdwyS2PauDFjZG0Hk0IcHIB+LkW27HLDtdoeMxHTxZh39DYbPP8UflWXWj9JcdDozF+YDOpQ==", + "dev": true, + "requires": { + "has": "^1.0.3", + "is": "^3.2.1" + } + }, "noop-logger": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz", "integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI=" }, "normalize-url": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", - "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==" + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", + "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", + "dev": true }, "npm-run-path": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, "requires": { "path-key": "^2.0.0" } @@ -2600,7 +3907,26 @@ "object-hash": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.0.3.tgz", - "integrity": "sha512-JPKn0GMu+Fa3zt3Bmr66JhokJU5BaNBIh4ZeTlaCBzrBsOeXzwcKKAK1tbLiPKgvwmPXsDvvLHoWh5Bm7ofIYg==" + "integrity": "sha512-JPKn0GMu+Fa3zt3Bmr66JhokJU5BaNBIh4ZeTlaCBzrBsOeXzwcKKAK1tbLiPKgvwmPXsDvvLHoWh5Bm7ofIYg==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } }, "once": { "version": "1.4.0", @@ -2620,9 +3946,10 @@ } }, "open": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/open/-/open-7.0.3.tgz", - "integrity": "sha512-sP2ru2v0P290WFfv49Ap8MF6PkzGNnGlAwHweB4WR4mr5d2d0woiCluUeJ218w7/+PmoBy9JmYgD5A4mLcWOFA==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/open/-/open-7.0.4.tgz", + "integrity": "sha512-brSA+/yq+b08Hsr4c8fsEW2CRzk1BmfN3SAK/5VCHQ9bdoZJ4qa/+AfR0xHjlbbZUyPkUHs1b8x1RqdyZdkVqQ==", + "dev": true, "requires": { "is-docker": "^2.0.0", "is-wsl": "^2.1.1" @@ -2713,6 +4040,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "dev": true, "requires": { "lcid": "^1.0.0" } @@ -2721,6 +4049,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/os-name/-/os-name-3.1.0.tgz", "integrity": "sha512-h8L+8aNjNcMpo/mAIBPn5PXCM16iyPGjHNWo6U1YO8sJTMHtEtyczI6QJnLoplswm6goopQkqc7OAnjhWcugVg==", + "dev": true, "requires": { "macos-release": "^2.2.0", "windows-release": "^3.1.0" @@ -2729,17 +4058,41 @@ "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "p-cancelable": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.0.0.tgz", + "integrity": "sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg==", + "dev": true }, "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-limit": { + "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" + } }, "p-map": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", - "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==" + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true + }, + "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 }, "pac-proxy-agent": { "version": "3.0.1", @@ -2769,20 +4122,153 @@ } }, "package-json": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", - "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", + "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", + "dev": true, "requires": { - "got": "^6.7.1", - "registry-auth-token": "^3.0.1", - "registry-url": "^3.0.3", - "semver": "^5.1.0" + "got": "^9.6.0", + "registry-auth-token": "^4.0.0", + "registry-url": "^5.0.0", + "semver": "^6.2.0" + }, + "dependencies": { + "@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", + "dev": true + }, + "@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "dev": true, + "requires": { + "defer-to-connect": "^1.0.1" + } + }, + "cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "dev": true, + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "dependencies": { + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true + } + } + }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, + "defer-to-connect": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", + "dev": true + }, + "got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "dev": true, + "requires": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + } + }, + "json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", + "dev": true + }, + "keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "dev": true, + "requires": { + "json-buffer": "3.0.0" + } + }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true + }, + "p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", + "dev": true + }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "dev": true, + "requires": { + "lowercase-keys": "^1.0.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=", + "dev": true }, "parent-module": { "version": "1.0.1", @@ -2793,51 +4279,59 @@ "callsites": "^3.0.0" } }, + "parse-link-header": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-link-header/-/parse-link-header-1.0.1.tgz", + "integrity": "sha1-vt/g0hGK64S+deewJUGeyKYRQKc=", + "dev": true, + "requires": { + "xtend": "~4.0.1" + } + }, "parse-ms": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz", "integrity": "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==", "dev": true }, - "parse-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-4.0.1.tgz", - "integrity": "sha512-d7yhga0Oc+PwNXDvQ0Jv1BuWkLVPXcAoQ/WREgd6vNNoKYaW52KI+RdOFjI63wjkmps9yUE8VS4veP+AgpQ/hA==", - "requires": { - "is-ssh": "^1.3.0", - "protocols": "^1.4.0" - } - }, - "parse-url": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-5.0.1.tgz", - "integrity": "sha512-flNUPP27r3vJpROi0/R3/2efgKkyXqnXwyP1KQ2U0SfFRgdizOdWfvrrvJg1LuOoxs7GQhmxJlq23IpQ/BkByg==", - "requires": { - "is-ssh": "^1.3.0", - "normalize-url": "^3.3.0", - "parse-path": "^4.0.0", - "protocols": "^1.4.0" - } - }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true }, "path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true }, "path-parse": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "peek-stream": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/peek-stream/-/peek-stream-1.1.3.tgz", + "integrity": "sha512-FhJ+YbOSBb9/rIl2ZeE/QHEsWn7PqNYt8ARAY3kIgNGOk13g9FGyIY6JIl/xB/3TFRVoTv5as0l11weORrTekA==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "duplexify": "^3.5.0", + "through2": "^2.0.3" + } + }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", "dev": true }, "pify": { @@ -2942,14 +4436,16 @@ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" }, "prepend-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", + "dev": true }, - "prettier": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", - "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==" + "pretty-bytes": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.3.0.tgz", + "integrity": "sha512-hjGrh+P926p4R4WbaB6OckyRtO0F0/lQBiT+0gnxjV+5kjPBrfVBFCsCLbMqVQeydvIoouYTCmmEURiH3R1Bdg==", + "dev": true }, "pretty-ms": { "version": "7.0.0", @@ -2968,12 +4464,14 @@ "progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true }, "promise": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "dev": true, "requires": { "asap": "~2.0.3" } @@ -2983,10 +4481,24 @@ "resolved": "https://registry.npmjs.org/promise-all-sequential/-/promise-all-sequential-1.0.0.tgz", "integrity": "sha512-XQPGPgQZERb1mYEpqSR5tIsC2E5amQZgnqVUPK6gsDtcOv/Yjxmz99cOA1MxLWcuWXz1lZZiD3hERA+rFVdP2w==" }, - "protocols": { - "version": "1.4.7", - "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.7.tgz", - "integrity": "sha512-Fx65lf9/YDn3hUX08XUc0J8rSux36rEsyiv21ZGUC1mOyeM3lTRpZLcrm8aAolzS4itwVfm7TAPyxC2E5zd6xg==" + "promise-deferred": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/promise-deferred/-/promise-deferred-2.0.3.tgz", + "integrity": "sha512-n10XaoznCzLfyPFOlEE8iurezHpxrYzyjgq/1eW9Wk1gJwur/N7BdBmjJYJpqMeMcXK4wEbzo2EvZQcqjYcKUQ==", + "dev": true, + "requires": { + "promise": "^7.3.1" + } + }, + "promiseback": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/promiseback/-/promiseback-2.0.3.tgz", + "integrity": "sha512-VZXdCwS0ppVNTIRfNsCvVwJAaP2b+pxQF7lM8DMWfmpNWyTxB6O5YNbzs+8z0ki/KIBHKHk308NTIl4kJUem3w==", + "dev": true, + "requires": { + "is-callable": "^1.1.5", + "promise-deferred": "^2.0.3" + } }, "proxy-agent": { "version": "3.1.1", @@ -3032,7 +4544,8 @@ "pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true }, "pump": { "version": "3.0.0", @@ -3043,12 +4556,55 @@ "once": "^1.3.1" } }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "dev": true, + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, + "pupa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.0.1.tgz", + "integrity": "sha512-hEJH0s8PXLY/cdXh66tNEQGndDrIKNqNC5xmrysZy3i5C3oEoLna7YAOad+7u125+zH1HNXUmGEkrhb3c2VriA==", + "dev": true, + "requires": { + "escape-goat": "^2.0.0" + } + }, + "querystringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz", + "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==" + }, + "quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true + }, "rate-limiter-flexible": { "version": "2.1.9", "resolved": "https://registry.npmjs.org/rate-limiter-flexible/-/rate-limiter-flexible-2.1.9.tgz", @@ -3104,20 +4660,21 @@ "dev": true }, "registry-auth-token": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz", - "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.1.1.tgz", + "integrity": "sha512-9bKS7nTl9+/A1s7tnPeGrUpRcVY+LUh7bfFgzpndALdPfXQBfQV77rQVtqgUV3ti4vc/Ik81Ex8UJDWDQ12zQA==", + "dev": true, "requires": { - "rc": "^1.1.6", - "safe-buffer": "^5.0.1" + "rc": "^1.2.8" } }, "registry-url": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", - "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", + "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", + "dev": true, "requires": { - "rc": "^1.0.1" + "rc": "^1.2.8" } }, "remedial": { @@ -3138,6 +4695,16 @@ "is_js": "^0.9.0" } }, + "require-in-the-middle": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-5.0.3.tgz", + "integrity": "sha512-p/ICV8uMlqC4tjOYabLMxAWCIKa0YUQgZZ6KDM0xgXJNgdGQ1WmL2A07TwmrZw+wi6ITUFKzH5v3n+ENEyXVkA==", + "requires": { + "debug": "^4.1.1", + "module-details-from-path": "^1.0.3", + "resolve": "^1.12.0" + } + }, "requirejs": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.6.tgz", @@ -3155,15 +4722,25 @@ "stringify-object": "^3.2.1" } }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + }, "resolve": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", - "dev": true, "requires": { "path-parse": "^1.0.6" } }, + "resolve-alpn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.0.0.tgz", + "integrity": "sha512-rTuiIEqFmGxne4IovivKSDzld2lWW9QCjqv80SYjPgf+gS35eaCAjaP54CCwGAwBtnCsvNLYtqxe1Nw+i6JEmA==", + "dev": true + }, "resolve-dependency-path": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/resolve-dependency-path/-/resolve-dependency-path-2.0.0.tgz", @@ -3176,6 +4753,15 @@ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, + "responselike": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", + "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", + "dev": true, + "requires": { + "lowercase-keys": "^2.0.0" + } + }, "restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", @@ -3186,26 +4772,38 @@ "signal-exit": "^3.0.2" } }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, "rimraf": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, "requires": { "glob": "^7.1.3" } }, "run-async": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.0.tgz", - "integrity": "sha512-xJTbh/d7Lm7SBhc1tNvTpeCHaEzoyxPrqNlvSdMfBTYwaY++UJFyXUOxAtsRUXjlqOfj8luNaR9vjCh4KeV+pg==", - "requires": { - "is-promise": "^2.1.0" - } + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true + }, + "run-parallel": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", + "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", + "dev": true }, "rxjs": { - "version": "6.5.5", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.5.tgz", - "integrity": "sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.0.tgz", + "integrity": "sha512-3HMA8z/Oz61DUHe+SdOiQyzIf4tOx5oQHmMir7IZEu6TMqCLHT4LRcmNaUS0NwOz8VLvmmBduMsoaUvMaIiqzg==", + "dev": true, "requires": { "tslib": "^1.9.0" } @@ -3240,12 +4838,14 @@ "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true }, "secure-keys": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/secure-keys/-/secure-keys-1.0.0.tgz", - "integrity": "sha1-8MgtmKOxOah3aogIBQuCRDEIf8o=" + "integrity": "sha1-8MgtmKOxOah3aogIBQuCRDEIf8o=", + "dev": true }, "semver": { "version": "5.7.1", @@ -3253,11 +4853,20 @@ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" }, "semver-diff": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", - "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", + "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", + "dev": true, "requires": { - "semver": "^5.0.3" + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "set-blocking": { @@ -3268,7 +4877,8 @@ "set-immediate-shim": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", - "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=" + "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", + "dev": true }, "setprototypeof": { "version": "1.1.1", @@ -3279,6 +4889,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, "requires": { "shebang-regex": "^1.0.0" } @@ -3286,7 +4897,13 @@ "shebang-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "shimmer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", + "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==" }, "signal-exit": { "version": "3.0.2", @@ -3308,6 +4925,12 @@ "simple-concat": "^1.0.0" } }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, "slice-ansi": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", @@ -3333,42 +4956,40 @@ "integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw==" }, "snyk": { - "version": "1.321.0", - "resolved": "https://registry.npmjs.org/snyk/-/snyk-1.321.0.tgz", - "integrity": "sha512-0uczPo7A/XD5g8HZwfmO3kwp4QUtuuHelg97O9JcnfLT7Hh6RoyJuuBdEmIECgdCIFZXVV9kAfMAAs6hpgOXsA==", + "version": "1.361.3", + "resolved": "https://registry.npmjs.org/snyk/-/snyk-1.361.3.tgz", + "integrity": "sha512-93SxV9WD+pN/9bGRizfoiYwfKHy5mDyTCdOYtWcVbTFMi7Gf+I4Q5YprunHBTeJLLh0+qsD6l77QBo9GiYyiaA==", + "dev": true, "requires": { - "@snyk/cli-interface": "2.6.0", - "@snyk/configstore": "^3.2.0-rc1", + "@snyk/cli-interface": "2.8.0", "@snyk/dep-graph": "1.18.3", "@snyk/gemfile": "1.2.0", "@snyk/graphlib": "2.1.9-patch", "@snyk/inquirer": "6.2.2-patch", "@snyk/lodash": "^4.17.15-patch", "@snyk/ruby-semver": "2.2.0", - "@snyk/snyk-cocoapods-plugin": "2.2.0", - "@snyk/update-notifier": "^2.5.1-rc2", - "@types/agent-base": "^4.2.0", + "@snyk/snyk-cocoapods-plugin": "2.3.0", "abbrev": "^1.1.1", "ansi-escapes": "3.2.0", "chalk": "^2.4.2", "cli-spinner": "0.2.10", - "debug": "^3.1.0", + "configstore": "^5.0.1", + "debug": "^4.1.1", "diff": "^4.0.1", - "git-url-parse": "11.1.2", "glob": "^7.1.3", - "needle": "^2.2.4", + "needle": "^2.5.0", "open": "^7.0.3", "os-name": "^3.0.0", "proxy-agent": "^3.1.1", "proxy-from-env": "^1.0.0", "semver": "^6.0.0", "snyk-config": "3.1.0", - "snyk-docker-plugin": "3.2.0", - "snyk-go-plugin": "1.14.0", - "snyk-gradle-plugin": "3.2.7", - "snyk-module": "1.9.1", - "snyk-mvn-plugin": "2.15.2", - "snyk-nodejs-lockfile-parser": "1.22.0", + "snyk-docker-plugin": "3.13.1", + "snyk-go-plugin": "1.14.2", + "snyk-gradle-plugin": "3.5.1", + "snyk-module": "3.1.0", + "snyk-mvn-plugin": "2.17.1", + "snyk-nodejs-lockfile-parser": "1.26.3", "snyk-nuget-plugin": "1.18.1", "snyk-php-plugin": "1.9.0", "snyk-policy": "1.14.1", @@ -3381,38 +5002,28 @@ "source-map-support": "^0.5.11", "strip-ansi": "^5.2.0", "tempfile": "^2.0.0", - "then-fs": "^2.0.0", + "update-notifier": "^4.1.0", "uuid": "^3.3.2", "wrap-ansi": "^5.1.0" }, "dependencies": { - "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==" - }, "ansi-regex": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "requires": { - "ms": "^2.1.1" - } + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true }, "strip-ansi": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, "requires": { "ansi-regex": "^4.1.0" } @@ -3423,6 +5034,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/snyk-config/-/snyk-config-3.1.0.tgz", "integrity": "sha512-3UlyogA67/9WOssJ7s4d7gqWQRWyO/LbgdBBNMhhmFDKa7eTUSW+A782CVHgyDSJZ2kNANcMWwMiOL+h3p6zQg==", + "dev": true, "requires": { "@snyk/lodash": "4.17.15-patch", "debug": "^4.1.1", @@ -3430,44 +5042,87 @@ } }, "snyk-docker-plugin": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/snyk-docker-plugin/-/snyk-docker-plugin-3.2.0.tgz", - "integrity": "sha512-LKsvGcRVBYzyTNT/Z5kImm6uHMX3wAs7gvR4dO8zqBVzCsn3zfi//kmRHWh7zhgvIb6reuhUqY1hMXaz0q/mBw==", + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/snyk-docker-plugin/-/snyk-docker-plugin-3.13.1.tgz", + "integrity": "sha512-4GEZ8Rx+1K33i0SuMRO03T+n8jAosLae4iuo1TZu9ugkxdQzbBStzVFz2N6x1s0GI/psWQNOWSLKfzFJYMOctw==", + "dev": true, "requires": { - "@snyk/rpm-parser": "^1.1.0", + "@snyk/rpm-parser": "^2.0.0", + "@snyk/snyk-docker-pull": "^3.1.3", "debug": "^4.1.1", + "docker-modem": "2.1.3", "dockerfile-ast": "0.0.19", "event-loop-spinner": "^1.1.0", + "gunzip-maybe": "^1.4.2", "semver": "^6.1.0", "snyk-nodejs-lockfile-parser": "1.22.0", "tar-stream": "^2.1.0", + "tmp": "^0.2.1", "tslib": "^1" }, "dependencies": { + "rimraf": { + "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" + } + }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "snyk-nodejs-lockfile-parser": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/snyk-nodejs-lockfile-parser/-/snyk-nodejs-lockfile-parser-1.22.0.tgz", + "integrity": "sha512-l6jLoJxqcIIkQopSdQuAstXdMw5AIgLu+uGc5CYpHyw8fYqOwna8rawwofNeGuwJAAv4nEiNiexeYaR88OCq6Q==", + "dev": true, + "requires": { + "@snyk/graphlib": "2.1.9-patch", + "@snyk/lodash": "^4.17.15-patch", + "@yarnpkg/lockfile": "^1.0.2", + "event-loop-spinner": "^1.1.0", + "p-map": "2.1.0", + "snyk-config": "^3.0.0", + "source-map-support": "^0.5.7", + "tslib": "^1.9.3", + "uuid": "^3.3.2" + } + }, + "tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "requires": { + "rimraf": "^3.0.0" + } } } }, "snyk-go-parser": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/snyk-go-parser/-/snyk-go-parser-1.4.0.tgz", - "integrity": "sha512-zcLA8u/WreycCjFKBblYfxszg7Fmnemuu9Ug/CE/jqF0yBXsI5DCWMteUvFkoa8DRntfGTlgf98TRl2aTSc2MQ==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/snyk-go-parser/-/snyk-go-parser-1.4.1.tgz", + "integrity": "sha512-StU3uHB85VMEkcgXta63M0Fgd+9cs5sMCjQXTBoYTdE4dxarPn7U67yCuwkRRdZdny1ZXtzfY8LKns9i0+dy9w==", + "dev": true, "requires": { "toml": "^3.0.0", "tslib": "^1.10.0" } }, "snyk-go-plugin": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/snyk-go-plugin/-/snyk-go-plugin-1.14.0.tgz", - "integrity": "sha512-9L+76De8F6yXWb+O3DA8QUi7+eDF2mOzCOveEPUJGkqWIDmurIiFcVxHJoj0EStjcxb3dX367KKlDlfFx+HiyA==", + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/snyk-go-plugin/-/snyk-go-plugin-1.14.2.tgz", + "integrity": "sha512-r/uaM3gk/RF7m/VGYswxlnA6I+kMgK3eVPsPyf7400BhqF8noh8K7v10CEg67mHA4JM0l7dZASqejr/5kKw9ZQ==", + "dev": true, "requires": { "@snyk/graphlib": "2.1.9-patch", "debug": "^4.1.1", - "snyk-go-parser": "1.4.0", + "snyk-go-parser": "1.4.1", "tmp": "0.1.0", "tslib": "^1.10.0" }, @@ -3476,6 +5131,7 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz", "integrity": "sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==", + "dev": true, "requires": { "rimraf": "^2.6.3" } @@ -3483,56 +5139,116 @@ } }, "snyk-gradle-plugin": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/snyk-gradle-plugin/-/snyk-gradle-plugin-3.2.7.tgz", - "integrity": "sha512-fBgQpRwfuHGuPvYGEjgNBtuwnkNAV9sv17lG+eWdQO3ntkAKnt1RyzQydAdffK2e8339XM6Mg/1EPYuIGIY3TA==", + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/snyk-gradle-plugin/-/snyk-gradle-plugin-3.5.1.tgz", + "integrity": "sha512-8tZwQCqRbjp1azvc+bBRXSbn2AjbUpFAM6qoSpM/IZpfGl1RaOtz4/JTkGFxj+iBhTFoAkGxEunT66eO0pHZZw==", + "dev": true, "requires": { - "@snyk/cli-interface": "2.3.2", + "@snyk/cli-interface": "2.8.0", + "@snyk/dep-graph": "^1.17.0", "@types/debug": "^4.1.4", - "chalk": "^2.4.2", + "chalk": "^3.0.0", "debug": "^4.1.1", - "tmp": "0.0.33", - "tslib": "^1.9.3" + "tmp": "0.2.1", + "tslib": "^2.0.0" }, "dependencies": { - "@snyk/cli-interface": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/@snyk/cli-interface/-/cli-interface-2.3.2.tgz", - "integrity": "sha512-jmZyxVHqzYU1GfdnWCGdd68WY/lAzpPVyqalHazPj4tFJehrSfEFc82RMTYAMgXEJuvFRFIwhsvXh3sWUhIQmg==", + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, "requires": { - "tslib": "^1.9.3" + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "rimraf": { + "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" + } + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "requires": { + "rimraf": "^3.0.0" } + }, + "tslib": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.0.tgz", + "integrity": "sha512-lTqkx847PI7xEDYJntxZH89L2/aXInsyF2luSafe/+0fHOMjlBNXdH6th7f70qxLDhul7KZK0zC8V5ZIyHl0/g==", + "dev": true } } }, "snyk-module": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/snyk-module/-/snyk-module-1.9.1.tgz", - "integrity": "sha512-A+CCyBSa4IKok5uEhqT+hV/35RO6APFNLqk9DRRHg7xW2/j//nPX8wTSZUPF8QeRNEk/sX+6df7M1y6PBHGSHA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/snyk-module/-/snyk-module-3.1.0.tgz", + "integrity": "sha512-HHuOYEAACpUpkFgU8HT57mmxmonaJ4O3YADoSkVhnhkmJ+AowqZyJOau703dYHNrq2DvQ7qYw81H7yyxS1Nfjw==", + "dev": true, "requires": { - "debug": "^3.1.0", - "hosted-git-info": "^2.7.1" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "requires": { - "ms": "^2.1.1" - } - } + "debug": "^4.1.1", + "hosted-git-info": "^3.0.4" } }, "snyk-mvn-plugin": { - "version": "2.15.2", - "resolved": "https://registry.npmjs.org/snyk-mvn-plugin/-/snyk-mvn-plugin-2.15.2.tgz", - "integrity": "sha512-2TTRizQxfUrA9w0pjxxsvGE+FgFSgog2wwpm378jNiKAZazGgV0txVMM4CoZJMz/tbUmzaJSS8DMQe1C7wlBFQ==", + "version": "2.17.1", + "resolved": "https://registry.npmjs.org/snyk-mvn-plugin/-/snyk-mvn-plugin-2.17.1.tgz", + "integrity": "sha512-U7ZKrKVnUW2gcyYIzvc0w9nRYh4NXv+wShTIcs++ARCAJOG9A4nxh+ZRmINQ7Sy7EB2qLIRwN4Ssr17+y2mhuA==", + "dev": true, "requires": { "@snyk/cli-interface": "2.5.0", - "@snyk/java-call-graph-builder": "1.8.1", + "@snyk/java-call-graph-builder": "1.10.0", "debug": "^4.1.1", - "needle": "^2.4.0", + "needle": "^2.5.0", "tmp": "^0.1.0", "tslib": "1.11.1" }, @@ -3541,6 +5257,7 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/@snyk/cli-interface/-/cli-interface-2.5.0.tgz", "integrity": "sha512-XMc2SCFH4RBSncZgoPb+BBlNq0NYpEpCzptKi69qyMpBy0VsRqIQqddedaazMCU1xEpXTytq6KMYpzUafZzp5Q==", + "dev": true, "requires": { "tslib": "^1.9.3" } @@ -3549,6 +5266,7 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz", "integrity": "sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==", + "dev": true, "requires": { "rimraf": "^2.6.3" } @@ -3556,25 +5274,44 @@ } }, "snyk-nodejs-lockfile-parser": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/snyk-nodejs-lockfile-parser/-/snyk-nodejs-lockfile-parser-1.22.0.tgz", - "integrity": "sha512-l6jLoJxqcIIkQopSdQuAstXdMw5AIgLu+uGc5CYpHyw8fYqOwna8rawwofNeGuwJAAv4nEiNiexeYaR88OCq6Q==", + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/snyk-nodejs-lockfile-parser/-/snyk-nodejs-lockfile-parser-1.26.3.tgz", + "integrity": "sha512-mBQ6vhnXAeyMxlnl9amjJWpA+/3qqXwM8Sj/P+9uH2TByOFLxdGzMNQFcl3q/H2yUdcs/epVdXJp09A2dK2glA==", + "dev": true, "requires": { "@snyk/graphlib": "2.1.9-patch", - "@snyk/lodash": "^4.17.15-patch", - "@yarnpkg/lockfile": "^1.0.2", - "event-loop-spinner": "^1.1.0", + "@yarnpkg/core": "^2.0.0-rc.29", + "@yarnpkg/lockfile": "^1.1.0", + "event-loop-spinner": "^2.0.0", + "lodash.clonedeep": "^4.5.0", + "lodash.flatmap": "^4.5.0", + "lodash.isempty": "^4.4.0", + "lodash.set": "^4.3.2", + "lodash.topairs": "^4.3.0", "p-map": "2.1.0", "snyk-config": "^3.0.0", "source-map-support": "^0.5.7", "tslib": "^1.9.3", - "uuid": "^3.3.2" + "uuid": "^3.3.2", + "yaml": "^1.9.2" + }, + "dependencies": { + "event-loop-spinner": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/event-loop-spinner/-/event-loop-spinner-2.0.0.tgz", + "integrity": "sha512-1y4j/Mhttr8ordvHkbDsGzGrlQaSYJoXD/3YKUxiOXIk7myEn9UPfybEk/lLtrcU3D4QvCNmVUxVQaPtvAIaUw==", + "dev": true, + "requires": { + "tslib": "^1.10.0" + } + } } }, "snyk-nuget-plugin": { "version": "1.18.1", "resolved": "https://registry.npmjs.org/snyk-nuget-plugin/-/snyk-nuget-plugin-1.18.1.tgz", "integrity": "sha512-Bq+IzbyewxIrUhgdFaDKS5wCNixERC7QBitKsZGM3uCOr9fJM8rr5qg5SS9UIU7eyeKvzuVO/V1yDzjo1cKvUw==", + "dev": true, "requires": { "@snyk/lodash": "4.17.15-patch", "debug": "^4.1.1", @@ -3589,6 +5326,7 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.3.0.tgz", "integrity": "sha512-EJ9k766htB1ZWnsV5ZMDkKLgA+201r/ouFF8R2OigVjVdcm2rurcBrrdXaeqBJbqnUVMko512PYmlncBKE1Huw==", + "dev": true, "requires": { "lie": "~3.3.0", "pako": "~1.0.2", @@ -3596,10 +5334,17 @@ "set-immediate-shim": "~1.0.1" } }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, "tslib": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "dev": true } } }, @@ -3607,6 +5352,7 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/snyk-paket-parser/-/snyk-paket-parser-1.6.0.tgz", "integrity": "sha512-6htFynjBe/nakclEHUZ1A3j5Eu32/0pNve5Qm4MFn3YQmJgj7UcAO8hdyK3QfzEY29/kAv/rkJQg+SKshn+N9Q==", + "dev": true, "requires": { "tslib": "^1.9.3" } @@ -3615,6 +5361,7 @@ "version": "1.9.0", "resolved": "https://registry.npmjs.org/snyk-php-plugin/-/snyk-php-plugin-1.9.0.tgz", "integrity": "sha512-uORrEoC47dw0ITZYu5vKqQtmXnbbQs+ZkWeo5bRHGdf10W8e4rNr1S1R4bReiLrSbSisYhVHeFMkdOAiLIPJVQ==", + "dev": true, "requires": { "@snyk/cli-interface": "2.3.2", "@snyk/composer-lockfile-parser": "1.4.0", @@ -3625,6 +5372,7 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/@snyk/cli-interface/-/cli-interface-2.3.2.tgz", "integrity": "sha512-jmZyxVHqzYU1GfdnWCGdd68WY/lAzpPVyqalHazPj4tFJehrSfEFc82RMTYAMgXEJuvFRFIwhsvXh3sWUhIQmg==", + "dev": true, "requires": { "tslib": "^1.9.3" } @@ -3635,6 +5383,7 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/snyk-policy/-/snyk-policy-1.14.1.tgz", "integrity": "sha512-C5vSkoBYxPnaqb218sm4m6N5s1BhIXlldpIX5xRNnZ0QkDwVj3dy/PfgwxRgVQh7QFGa1ajbvKmsGmm4RRsN8g==", + "dev": true, "requires": { "debug": "^4.1.1", "email-validator": "^2.0.4", @@ -3650,17 +5399,26 @@ "@types/node": { "version": "6.14.10", "resolved": "https://registry.npmjs.org/@types/node/-/node-6.14.10.tgz", - "integrity": "sha512-pF4HjZGSog75kGq7B1InK/wt/N08BuPATo+7HRfv7gZUzccebwv/fmWVGs/j6LvSiLWpCuGGhql51M/wcQsNzA==" + "integrity": "sha512-pF4HjZGSog75kGq7B1InK/wt/N08BuPATo+7HRfv7gZUzccebwv/fmWVGs/j6LvSiLWpCuGGhql51M/wcQsNzA==", + "dev": true + }, + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "dev": true }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true }, "snyk-module": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/snyk-module/-/snyk-module-2.1.0.tgz", "integrity": "sha512-K5xeA39vLbm23Y/29wFEhKGvo7FwV4x9XhCP5gB22dBPyYiCCNiDERX4ofHQvtM6q96cL0hIroMdlbctv/0nPw==", + "dev": true, "requires": { "@types/hosted-git-info": "^2.7.0", "@types/node": "^6.14.7", @@ -3672,6 +5430,7 @@ "version": "3.2.6", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, "requires": { "ms": "^2.1.1" } @@ -3684,6 +5443,7 @@ "version": "1.17.1", "resolved": "https://registry.npmjs.org/snyk-python-plugin/-/snyk-python-plugin-1.17.1.tgz", "integrity": "sha512-KKklat9Hfbj4hw2y63LRhgmziYzmyRt+cSuzN5KDmBSAGYck0EAoPDtNpJXjrIs1kPNz28EXnE6NDnadXnOjiQ==", + "dev": true, "requires": { "@snyk/cli-interface": "^2.0.3", "tmp": "0.0.33" @@ -3693,6 +5453,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/snyk-resolve/-/snyk-resolve-1.0.1.tgz", "integrity": "sha512-7+i+LLhtBo1Pkth01xv+RYJU8a67zmJ8WFFPvSxyCjdlKIcsps4hPQFebhz+0gC5rMemlaeIV6cqwqUf9PEDpw==", + "dev": true, "requires": { "debug": "^3.1.0", "then-fs": "^2.0.0" @@ -3702,6 +5463,7 @@ "version": "3.2.6", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, "requires": { "ms": "^2.1.1" } @@ -3712,6 +5474,7 @@ "version": "4.4.0", "resolved": "https://registry.npmjs.org/snyk-resolve-deps/-/snyk-resolve-deps-4.4.0.tgz", "integrity": "sha512-aFPtN8WLqIk4E1ulMyzvV5reY1Iksz+3oPnUVib1jKdyTHymmOIYF7z8QZ4UUr52UsgmrD9EA/dq7jpytwFoOQ==", + "dev": true, "requires": { "@types/node": "^6.14.4", "@types/semver": "^5.5.0", @@ -3735,15 +5498,49 @@ "@types/node": { "version": "6.14.10", "resolved": "https://registry.npmjs.org/@types/node/-/node-6.14.10.tgz", - "integrity": "sha512-pF4HjZGSog75kGq7B1InK/wt/N08BuPATo+7HRfv7gZUzccebwv/fmWVGs/j6LvSiLWpCuGGhql51M/wcQsNzA==" + "integrity": "sha512-pF4HjZGSog75kGq7B1InK/wt/N08BuPATo+7HRfv7gZUzccebwv/fmWVGs/j6LvSiLWpCuGGhql51M/wcQsNzA==", + "dev": true }, "debug": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, "requires": { "ms": "^2.1.1" } + }, + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "dev": true + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "snyk-module": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/snyk-module/-/snyk-module-1.9.1.tgz", + "integrity": "sha512-A+CCyBSa4IKok5uEhqT+hV/35RO6APFNLqk9DRRHg7xW2/j//nPX8wTSZUPF8QeRNEk/sX+6df7M1y6PBHGSHA==", + "dev": true, + "requires": { + "debug": "^3.1.0", + "hosted-git-info": "^2.7.1" + } + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true } } }, @@ -3751,6 +5548,7 @@ "version": "2.11.0", "resolved": "https://registry.npmjs.org/snyk-sbt-plugin/-/snyk-sbt-plugin-2.11.0.tgz", "integrity": "sha512-wUqHLAa3MzV6sVO+05MnV+lwc+T6o87FZZaY+43tQPytBI2Wq23O3j4POREM4fa2iFfiQJoEYD6c7xmhiEUsSA==", + "dev": true, "requires": { "debug": "^4.1.1", "semver": "^6.1.2", @@ -3762,12 +5560,14 @@ "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true }, "tmp": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz", "integrity": "sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==", + "dev": true, "requires": { "rimraf": "^2.6.3" } @@ -3778,6 +5578,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/snyk-tree/-/snyk-tree-1.0.0.tgz", "integrity": "sha1-D7cxdtvzLngvGRAClBYESPkRHMg=", + "dev": true, "requires": { "archy": "^1.0.0" } @@ -3786,6 +5587,7 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/snyk-try-require/-/snyk-try-require-1.3.1.tgz", "integrity": "sha1-bgJvkuZK9/zM6h7lPVJIQeQYohI=", + "dev": true, "requires": { "debug": "^3.1.0", "lodash.clonedeep": "^4.3.0", @@ -3797,9 +5599,26 @@ "version": "3.2.6", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, "requires": { "ms": "^2.1.1" } + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true } } }, @@ -3840,26 +5659,112 @@ "version": "0.5.19", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, "requires": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, + "split-ca": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz", + "integrity": "sha1-bIOv82kvphJW4M0ZfgXp3hV2kaY=", + "dev": true + }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true }, "sqlstring": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz", "integrity": "sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A=" }, + "ssh2": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-0.8.9.tgz", + "integrity": "sha512-GmoNPxWDMkVpMFa9LVVzQZHF6EW3WKmBwL+4/GeILf2hFmix5Isxm7Amamo8o7bHiU0tC+wXsGcUXOxp8ChPaw==", + "dev": true, + "requires": { + "ssh2-streams": "~0.4.10" + } + }, + "ssh2-streams": { + "version": "0.4.10", + "resolved": "https://registry.npmjs.org/ssh2-streams/-/ssh2-streams-0.4.10.tgz", + "integrity": "sha512-8pnlMjvnIZJvmTzUIIA5nT4jr2ZWNNVHwyXfMGdRJbug9TpI3kd99ffglgfSWqujVv/0gxwMsDn9j9RVst8yhQ==", + "dev": true, + "requires": { + "asn1": "~0.2.0", + "bcrypt-pbkdf": "^1.0.2", + "streamsearch": "~0.1.2" + } + }, "statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" }, + "stream-buffers": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-3.0.2.tgz", + "integrity": "sha512-DQi1h8VEBA/lURbSwFtEHnSTb9s2/pwLEaFuNhXwy1Dx3Sa0lOuYT2yNUr4/j2fs8oCAMANtrZ5OrPZtyVs3MQ==", + "dev": true + }, + "stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", + "dev": true + }, + "stream-to-array": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/stream-to-array/-/stream-to-array-2.3.0.tgz", + "integrity": "sha1-u/azn19D7DC8cbq8s3VXrOzzQ1M=", + "dev": true, + "requires": { + "any-promise": "^1.1.0" + } + }, + "stream-to-promise": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/stream-to-promise/-/stream-to-promise-2.2.0.tgz", + "integrity": "sha1-se2y4cjLESidG1A8CNPyrvUeZQ8=", + "dev": true, + "requires": { + "any-promise": "~1.3.0", + "end-of-stream": "~1.1.0", + "stream-to-array": "~2.3.0" + }, + "dependencies": { + "end-of-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.1.0.tgz", + "integrity": "sha1-6TUyWLqpEIll78QcsO+K3i88+wc=", + "dev": true, + "requires": { + "once": "~1.3.0" + } + }, + "once": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", + "integrity": "sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA=", + "dev": true, + "requires": { + "wrappy": "1" + } + } + } + }, + "streamsearch": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", + "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=", + "dev": true + }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", @@ -3928,7 +5833,8 @@ "strip-eof": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true }, "strip-json-comments": { "version": "2.0.1", @@ -3957,6 +5863,7 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, "requires": { "has-flag": "^3.0.0" } @@ -4018,6 +5925,29 @@ "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", "dev": true }, + "tar": { + "version": "4.4.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", + "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", + "dev": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.8.6", + "minizlib": "^1.2.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.3" + }, + "dependencies": { + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + } + } + }, "tar-fs": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.1.tgz", @@ -4062,12 +5992,14 @@ "temp-dir": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", - "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==" + "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", + "dev": true }, "tempfile": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/tempfile/-/tempfile-2.0.0.tgz", "integrity": "sha1-awRGhWqbERTRhW/8vlCczLCXcmU=", + "dev": true, "requires": { "temp-dir": "^1.0.0", "uuid": "^3.0.1" @@ -4076,17 +6008,16 @@ "temp-dir": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz", - "integrity": "sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0=" + "integrity": "sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0=", + "dev": true } } }, "term-size": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", - "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", - "requires": { - "execa": "^0.7.0" - } + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.0.tgz", + "integrity": "sha512-a6sumDlzyHVJWb8+YofY4TW112G6p2FCPEAFk+59gIYHv3XHRhm9ltVQ9kli4hNWeQBwSpe8cRN25x0ROunMOw==", + "dev": true }, "text-table": { "version": "0.2.0", @@ -4098,6 +6029,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/then-fs/-/then-fs-2.0.0.tgz", "integrity": "sha1-cveS3Z0xcFqRrhnr/Piz+WjIHaI=", + "dev": true, "requires": { "promise": ">=3.2 <8" } @@ -4105,26 +6037,48 @@ "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } }, "thunkify": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/thunkify/-/thunkify-2.1.2.tgz", "integrity": "sha1-+qDp0jDFGsyVyhOjYawFyn4EVT0=" }, - "timed-out": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", - "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=" - }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, "requires": { "os-tmpdir": "~1.0.2" } }, + "to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, "toidentifier": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", @@ -4133,7 +6087,8 @@ "toml": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz", - "integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==" + "integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==", + "dev": true }, "traverse-chain": { "version": "0.1.0", @@ -4144,12 +6099,14 @@ "tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==" + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true }, "tslib": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz", - "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==" + "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==", + "dev": true }, "tsutils": { "version": "3.17.1", @@ -4160,6 +6117,12 @@ "tslib": "^1.8.1" } }, + "tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "dev": true + }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -4168,6 +6131,12 @@ "safe-buffer": "^5.0.1" } }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", @@ -4182,10 +6151,26 @@ "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", "dev": true }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "requires": { + "is-typedarray": "^1.0.0" + } + }, "typescript": { "version": "3.8.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz", - "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==" + "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==", + "dev": true + }, + "underscore": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.10.2.tgz", + "integrity": "sha512-N4P+Q/BuyuEKFJ43B9gYuOj4TQUHXX+j2FqguVOpjkssLUUrnJofCcBccJSCoeturDoZU6GorDTHSvUDlSQbTg==", + "dev": true }, "uniq": { "version": "1.0.1", @@ -4194,22 +6179,96 @@ "dev": true }, "unique-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", - "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dev": true, "requires": { - "crypto-random-string": "^1.0.0" + "crypto-random-string": "^2.0.0" } }, + "universalify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", + "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==" + }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, - "unzip-response": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz", - "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=" + "update-notifier": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-4.1.0.tgz", + "integrity": "sha512-w3doE1qtI0/ZmgeoDoARmI5fjDoT93IfKgEGqm26dGUOh8oNpaSTsGNdYRN/SjOuo10jcJGwkEL3mroKzktkew==", + "dev": true, + "requires": { + "boxen": "^4.2.0", + "chalk": "^3.0.0", + "configstore": "^5.0.1", + "has-yarn": "^2.1.0", + "import-lazy": "^2.1.0", + "is-ci": "^2.0.0", + "is-installed-globally": "^0.3.1", + "is-npm": "^4.0.0", + "is-yarn-global": "^0.3.0", + "latest-version": "^5.0.0", + "pupa": "^2.0.1", + "semver-diff": "^3.1.1", + "xdg-basedir": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } }, "uri-js": { "version": "4.2.2", @@ -4220,12 +6279,22 @@ "punycode": "^2.1.0" } }, + "url-parse": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", + "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==", + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, "url-parse-lax": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", - "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "dev": true, "requires": { - "prepend-http": "^1.0.1" + "prepend-http": "^2.0.0" } }, "util-deprecate": { @@ -4247,7 +6316,8 @@ "vscode-languageserver-types": { "version": "3.15.1", "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.15.1.tgz", - "integrity": "sha512-+a9MPUQrNGRrGU630OGbYVQ+11iOIovjCkqxajPa9w57Sd5ruK8WQNsslzpa0x/QJqC8kRc2DUxWjIFwoNm4ZQ==" + "integrity": "sha512-+a9MPUQrNGRrGU630OGbYVQ+11iOIovjCkqxajPa9w57Sd5ruK8WQNsslzpa0x/QJqC8kRc2DUxWjIFwoNm4ZQ==", + "dev": true }, "walkdir": { "version": "0.4.1", @@ -4268,6 +6338,7 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, "requires": { "isexe": "^2.0.0" } @@ -4286,60 +6357,52 @@ } }, "widest-line": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz", - "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "dev": true, "requires": { - "string-width": "^2.1.1" + "string-width": "^4.0.0" + }, + "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + } } }, "window-size": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", - "integrity": "sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY=" + "integrity": "sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY=", + "dev": true }, "windows-release": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-3.3.0.tgz", - "integrity": "sha512-2HetyTg1Y+R+rUgrKeUEhAG/ZuOmTrI1NBb3ZyAGQMYmOJjBBPe4MTodghRkmLJZHwkuPi02anbeGP+Zf401LQ==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-3.3.1.tgz", + "integrity": "sha512-Pngk/RDCaI/DkuHPlGTdIkDiTAnAkyMjoQMZqRsxydNl1qGXNIoZrB7RK8g53F2tEgQBMqQJHQdYZuQEEAu54A==", + "dev": true, "requires": { "execa": "^1.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "requires": { - "pump": "^3.0.0" - } - } } }, "word-wrap": { @@ -4351,6 +6414,7 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, "requires": { "ansi-styles": "^3.2.0", "string-width": "^3.0.0", @@ -4360,22 +6424,20 @@ "ansi-regex": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true }, "string-width": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, "requires": { "emoji-regex": "^7.0.1", "is-fullwidth-code-point": "^2.0.0", @@ -4386,6 +6448,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, "requires": { "ansi-regex": "^4.1.0" } @@ -4407,24 +6470,36 @@ } }, "write-file-atomic": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", - "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, "requires": { - "graceful-fs": "^4.1.11", "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "ws": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", + "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "requires": { + "async-limiter": "~1.0.0" } }, "xdg-basedir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", - "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", + "dev": true }, "xml2js": { "version": "0.4.23", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", + "dev": true, "requires": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" @@ -4433,7 +6508,8 @@ "xmlbuilder": { "version": "11.0.1", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true }, "xregexp": { "version": "2.0.0", @@ -4456,20 +6532,35 @@ } } }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, "y18n": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true }, "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "yaml": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz", + "integrity": "sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==", + "dev": true }, "yargs": { "version": "3.32.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", "integrity": "sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU=", + "dev": true, "requires": { "camelcase": "^2.0.1", "cliui": "^3.0.3", @@ -4480,15 +6571,11 @@ "y18n": "^3.2.0" }, "dependencies": { - "camelcase": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", - "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=" - }, "string-width": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -4499,6 +6586,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, "requires": { "ansi-regex": "^2.0.0" } diff --git a/package.json b/package.json index 98d66ba4..6a9ea55a 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,12 @@ { "name": "@sv443/jokeapi", - "version": "2.1.5", + "version": "2.2.0", "description": "A RESTful API that serves jokes from many categories while also offering a lot of filtering methods", "main": "JokeAPI.js", "homepage": "https://sv443.net/jokeapi/v2", "scripts": { "start": "node . && exit $?", - "test": "echo \"No test script specified\" && exit 0", + "test": "snyk test && node tools/test", "reformat": "node tools/reformat", "reassign-ids": "node tools/reassign-ids", "validate-jokes": "node tools/validate-jokes", @@ -33,7 +33,7 @@ ], "author": { "name": "Sv443", - "email": "sven.fehler@web.de", + "email": "contact@sv443.net", "url": "https://sv443.net/" }, "contributors": [ @@ -50,11 +50,14 @@ "license": "MIT", "bugs": { "url": "https://github.com/Sv443/JokeAPI/issues/new/choose", - "email": "sven.fehler@web.de" + "email": "contact@sv443.net" }, "dependencies": { + "@pm2/io": "^4.3.5", "dotenv": "^8.2.0", "farmhash": "^3.1.0", + "fs-extra": "^9.0.1", + "fuse.js": "^5.2.3", "http-ratelimit": "^0.2.3", "js2xmlparser": "^4.0.1", "json-to-pretty-yaml": "^1.2.2", @@ -63,13 +66,16 @@ "promise-all-sequential": "^1.0.0", "rate-limiter-flexible": "^2.1.9", "request-ip": "^2.1.3", - "snyk": "^1.321.0", "svjsl": "^1.9.4", + "url-parse": "^1.4.7", "xss": "^1.0.8" }, "devDependencies": { "eslint": "^7.5.0", - "madge": "^3.9.0" + "lodash": "^4.17.15", + "madge": "^3.9.0", + "snyk": "^1.361.3", + "table": "^5.4.6" }, "snyk": true } diff --git a/settings.js b/settings.js old mode 100755 new mode 100644 index f3239129..5d996a03 --- a/settings.js +++ b/settings.js @@ -5,7 +5,7 @@ const bgc = jsl.colors.bg; const settings = { debug: { - verboseLogging: false, // set to true to enable extra debug output + verboseLogging: true, // set to true to enable extra debug output progressBarDisabled: true, // set to true to disable the progress bar - greatly improves readability of verbose debug output onlyLogErrors: true, // set to true to disable sending any console logs but error messages }, @@ -22,7 +22,6 @@ const settings = { website: packageJSON.author.url, // author website github: `https://github.com/${packageJSON.author.name}`, // author github page }, - infoMsg: "If you want to be updated on the status and future updates of JokeAPI or need some help, please consider joining my Discord server: https://sv443.net/discord", privacyPolicyUrl: "https://sv443.net/privacypolicy/en" }, wrapper: { @@ -42,6 +41,7 @@ const settings = { "./data/logs", "./data/submissions", "./docs/compiled", + "./data/lists" ], exitSignals: [ // all signals that should cause a soft exit "SIGINT", @@ -55,16 +55,23 @@ const settings = { blacklistLoggingEnabled: true, // whether or not to log the character when an IP is on the blacklist }, jokes: { - jokesFormatVersion: 2, // current joke format version - jokesFilePath: "./data/jokes.json", // path to the jokes file + jokesFormatVersion: 3, // current joke format version + jokesFolderPath: "./data/jokes/", // path to the jokes folder - needs trailing slash jokeSubmissionURL: `${packageJSON.homepage}#submit`, // joke submission url jokeSubmissionPath: "./data/submissions/", // path to a directory where joke submissions should be saved to - needs trailing slash + submissions: { + timeFrame: 60, // time frame of submission rate limiter (in seconds) + rateLimiting: 3, // how many requests per timeframe should be allowed + invalidCharRegex: /(?![\u0000-\u0fff])./gm, // eslint-disable-line no-control-regex + }, + jokesTemplateFile: "template.json", // relative to "jokes.jokesFolderPath" possible: { anyCategoryName: "Any", // the name of the "Any" category categories: [ // all categories (excluding "Any") - case insensitive "Miscellaneous", "Programming", - "Dark" + "Dark", + "Pun" ], flags: [ // all flags - HAS TO BE LOWER CASE! "nsfw", @@ -89,16 +96,18 @@ const settings = { fileFormat: "json", // the default file format string mimeType: "application/json", // the default file format mime type }, - lastIDsMaxLength: 10, // the maximum amount of joke IDs that get saved to the blacklist-array - jokeRandomizationAttempts: 20, // after how many attempts of selecting a random joke to stop trying + lastIDsMaxLength: 15, // the maximum amount of joke IDs that get saved to the blacklist-array + jokeRandomizationAttempts: 25, // after how many attempts of selecting a random joke to stop trying splitChars: [",", "+", "-"], // which characters should separate the values of parameters with support for multiple values splitCharRegex: /[,+-]/gm, // which characters should separate the values of parameters with support for multiple values + maxAmount: 10, // the maximum amount of jokes that can be fetched with a single call to the get jokes endpoint + encodeAmount: 5, // if more than this number of jokes is requested, encode them }, httpServer: { port: 8076, // http server port allowCORS: true, // whether or not to allow Cross Origin Resource Sharing rateLimiting: 60, // amount of allowed requests per below defined timeframe - timeFrame: 60, // timeframe in seconds - also supports floating point numbers + timeFrame: 60, // timeframe in seconds urlPathOffset: 0, // example: "/jokeapi/info" with an offset of 1 will only start parsing the path beginning at "info" - an Apache reverse proxy will do this automatically though maxPayloadSize: 5120, // max size (in bytes) that will be accepted in a PUT request - if payload exceeds this size, it will abort with status 413 maxUrlLength: 250, // max amount of characters of the URL - if the URL is longer than this, the request will abort with status 414 @@ -124,8 +133,8 @@ const settings = { ], }, errors: { - errorRegistryIncludePath: "./data/errorRegistry", // path to the error registry - errorLogDir: "./data/logs/", // path to the error log directory - needs trailing slash + errorLogDir: "./data/logs/", // path to the error log directory - needs trailing slash + errorMessagesPath: "./data/errorMessages", // path to error messages file }, lists: { blacklistPath: "./data/lists/ipBlacklist.json", // path to the IP blacklist @@ -159,12 +168,12 @@ const settings = { success: col.green, // when request was successful error: col.red, // when request was errored ratelimit: col.magenta, // when request was rate limited - docs: col.yellow, // when docs were requested - blacklisted: bgc.red + col.yellow, // when a request IP is blacklisted + docs: col.yellow, // when docs were requested + blacklisted: bgc.red + col.yellow, // when a request IP is blacklisted docsrecompiled: bgc.yellow + col.blue, // when the docs were recompiled }, analytics: { - enabled: true, // whether or not the analytics module should be enabled + enabled: false, // whether or not the analytics module should be enabled dirPath: "./data/analytics/", // path to the analytics directory - needs trailing slash sqlTableName: "analytics", // name of the SQL table }, @@ -175,8 +184,17 @@ const settings = { }, auth: { tokenListFile: "./data/tokens.json", // path to the token list file - tokenHeaderName: "authorization", // the name of the token header (in lower case) + tokenHeaderName: "authorization", // the name of the token header (lower case) tokenValidHeader: "Token-Valid", // the name of the token validity response header (normal case, not lower case) + daemonInterval: 20, // after how many seconds the auth tokens should be refreshed + }, + languages: { + langFilePath: "./data/languages.json", // file containing all language codes and corresponding language information + defaultLanguage: "en", // default language (two character code, lowercase) + translationsFile: "./data/translations.json", // translations file + }, + tests: { // unit tests + location: "./tests/", // folder where unit tests are located - requires trailing slash } } diff --git a/src/analytics.js b/src/analytics.js index 5b648597..925ca2b4 100755 --- a/src/analytics.js +++ b/src/analytics.js @@ -1,7 +1,7 @@ const http = require("http"); const jsl = require("svjsl"); const sql = require("mysql"); -const fs = require("fs"); +const fs = require("fs-extra"); const logger = require("./logger"); const settings = require("../settings"); const debug = require("./verboseLogging"); diff --git a/src/auth.js b/src/auth.js index 9ca10c9d..2de2424f 100755 --- a/src/auth.js +++ b/src/auth.js @@ -1,35 +1,65 @@ const http = require("http"); const jsl = require("svjsl"); -const fs = require("fs"); +const fs = require("fs-extra"); +const crypto = require("crypto"); const settings = require("../settings"); jsl.unused([http]); +var previousDaemonHash; +var tokenList; - +/** + * Initializes the auth module + */ const init = () => { return new Promise(resolve => { fs.exists(settings.auth.tokenListFile, exists => { if(!exists) fs.writeFileSync(settings.auth.tokenListFile, JSON.stringify([], null, 4)); - try - { - let tokens = JSON.parse(fs.readFileSync(settings.auth.tokenListFile).toString()); - process._tokenList = tokens; - return resolve(); - } - catch(err) - { - process._tokenList = []; - fs.writeFileSync(settings.auth.tokenListFile, JSON.stringify([], null, 4)); - return resolve(); - } + refreshTokens(); + setInterval(() => daemonInterval(), settings.auth.daemonInterval); + return resolve(); }); }); }; +/** + * To be called on interval to check if the tokens should be refreshed + */ +function daemonInterval() +{ + let tokenFileRaw = fs.readFileSync(settings.auth.tokenListFile).toString(); + let tokenHash = crypto.createHash("md5").update(tokenFileRaw).digest("hex"); + + if(previousDaemonHash == undefined) + return; + else if(previousDaemonHash != tokenHash) + { + previousDaemonHash = tokenHash; + refreshTokens(); + } +} + +/** + * Refreshes the auth tokens in memory + */ +function refreshTokens() +{ + try + { + let tokens = JSON.parse(fs.readFileSync(settings.auth.tokenListFile).toString()); + tokenList = tokens; + } + catch(err) + { + tokenList = []; + fs.writeFileSync(settings.auth.tokenListFile, JSON.stringify([], null, 4)); + } +} + /** * @typedef {Object} Authorization * @prop {Boolean} isAuthorized @@ -48,9 +78,9 @@ const authByHeader = (req, res) => { if(req.headers && req.headers[settings.auth.tokenHeaderName]) { - if(Array.isArray(process._tokenList) && process._tokenList.length > 0) + if(Array.isArray(tokenList) && tokenList.length > 0) { - process._tokenList.forEach(tokenObj => { + tokenList.forEach(tokenObj => { if(tokenObj.token == req.headers[settings.auth.tokenHeaderName].toString()) { requestersToken = req.headers[settings.auth.tokenHeaderName].toString(); diff --git a/src/classes/AllJokes.js b/src/classes/AllJokes.js index 25a15fac..6e16a806 100755 --- a/src/classes/AllJokes.js +++ b/src/classes/AllJokes.js @@ -1,74 +1,118 @@ -/** - * @typedef {Object} SingleJoke A joke of type single - * @prop {String} category The category of the joke - * @prop {("single")} type The type of the joke - * @prop {String} joke The joke itself - * @prop {Object} flags - * @prop {Boolean} flags.nsfw Whether the joke is NSFW or not - * @prop {Boolean} flags.racist Whether the joke is racist or not - * @prop {Boolean} flags.sexist Whether the joke is sexist or not - * @prop {Boolean} flags.religious Whether the joke is religiously offensive or not - * @prop {Boolean} flags.political Whether the joke is politically offensive or not - * @prop {Number} id The ID of the joke - */ +const jsl = require("svjsl"); + +const parseJokes = require("../parseJokes"); +const languages = require("../languages"); + +const settings = require("../../settings"); -/** - * @typedef {Object} TwopartJoke A joke of type twopart - * @prop {String} category The category of the joke - * @prop {("twopart")} type The type of the joke - * @prop {String} setup The setup of the joke - * @prop {String} delivery The delivery of the joke - * @prop {Object} flags - * @prop {Boolean} flags.nsfw Whether the joke is NSFW or not - * @prop {Boolean} flags.racist Whether the joke is racist or not - * @prop {Boolean} flags.sexist Whether the joke is sexist or not - * @prop {Boolean} flags.religious Whether the joke is religiously offensive or not - * @prop {Boolean} flags.political Whether the joke is politically offensive or not - * @prop {Number} id The ID of the joke - */ + +jsl.unused(parseJokes); // only used for typedefs + +// expected format: +/* +{ + "en": { + info: { + formatVersion: 2 + }, + jokes: [ + { + (joke) + }, + ... + ] + }, + ... +} +*/ /** - * @typedef {Object} JokeArray - * @prop {Object} info - * @prop {Number} info.formatVersion - * @prop {Array} jokes + * @typedef {Object} CountPerLangObj + * @prop {Number} [en] + * @prop {Number} [de] */ class AllJokes { /** * Constructs a new AllJokes object. This object contains all methods to get certain jokes - * @param {JokeArray} jokeArray + * @param {Object} jokeArray */ constructor(jokeArray) { - if(typeof jokeArray != "object" || Array.isArray(jokeArray) || !Array.isArray(jokeArray.jokes)) + this.jokes = {}; + let jokeCount = 0; + let formatVersions = []; + let jokeCountPerLang = {}; + + //#SECTION check validity, get joke count and get format versions + Object.keys(jokeArray).forEach(key => { + if(!languages.isValidLang(key)) + throw new Error(`Error: invalid language code in construction of an AllJokes object. Expected valid two character language code - got "${key}"`); + + if(!jokeCountPerLang[key]) + jokeCountPerLang[key] = 0; + + jokeCount += jokeArray[key].jokes.length; + jokeCountPerLang[key] += jokeArray[key].jokes.length; + + let fv = jokeArray[key].info.formatVersion; + + jokeArray[key].jokes.forEach((j, i) => { + jsl.unused(j); + + jokeArray[key].jokes[i].lang = key; + }); + + if(fv != settings.jokes.jokesFormatVersion) + throw new Error(`Error: Jokes file with language ${key} has the wrong format version. Expected ${settings.jokes.jokesFormatVersion} but got ${fv}`); + + formatVersions.push(fv); + }); + + formatVersions.push(settings.jokes.jokesFormatVersion); + + if(!jsl.allEqual(formatVersions)) + throw new Error(`Error: One or more of the jokes-xy.json files contain(s) a wrong formatVersion parameter`); + + if(typeof jokeArray != "object" || Array.isArray(jokeArray)) throw new Error(`Error while constructing a new AllJokes object: parameter "jokeArray" is invalid`); - this.info = jokeArray["info"]; - this.jokes = jokeArray["jokes"]; - this._jokeCount = jokeArray["jokes"].length; - this._formatVersion = this.info.formatVersion; + this.jokes = jokeArray; + this._jokeCount = jokeCount; + this._jokeCountPerLang = jokeCountPerLang; + this._formatVersions = formatVersions; + + return this; } /** - * Returns an array of all jokes - * @returns {Array} + * Returns an array of all jokes of the specified language + * @param {String} [langCode="en"] Two character language code + * @returns {Array} */ - getJokeArray() + getJokeArray(langCode) { - return this.jokes; + if(!languages.isValidLang(langCode)) + langCode = settings.languages.defaultLanguage; + + return (typeof this.jokes[langCode] == "object" ? this.jokes[langCode].jokes : []); } /** * Returns the joke format version - * @returns {(Number|undefined)} Returns a number, if the format version was set, returns undefined, if not + * @param {String} [langCode="en"] Two character language code + * @returns {Number|undefined} Returns a number if the format version was set, returns undefined, if not */ - getFormatVersion() + getFormatVersion(langCode) { - if(this.info == undefined) + if(!languages.isValidLang(langCode)) + langCode = settings.languages.defaultLanguage; + + if(typeof this.jokes[langCode] != "object") return undefined; - return this.info.formatVersion; + + return this.jokes[langCode].info ? this.jokes[langCode].info.formatVersion : undefined; } /** @@ -81,13 +125,13 @@ class AllJokes } /** - * Returns the joke format version - * @returns {Number} + * Returns an object containing joke counts for every lang code + * @returns {CountPerLangObj} */ - getJokeFormatVersion() + getJokeCountPerLang() { - return this._formatVersion; + return this._jokeCountPerLang; } } -module.exports = AllJokes; \ No newline at end of file +module.exports = AllJokes; diff --git a/src/classes/FilteredJoke.js b/src/classes/FilteredJoke.js index f6a0fd99..f5854d6a 100755 --- a/src/classes/FilteredJoke.js +++ b/src/classes/FilteredJoke.js @@ -2,41 +2,18 @@ // filters can be applied with setter methods // final getter method returns one or multiple jokes that match all filters -/** - * @typedef {Object} SingleJoke A joke of type single - * @prop {String} category The category of the joke - * @prop {("single")} type The type of the joke - * @prop {String} joke The joke itself - * @prop {Object} flags - * @prop {Boolean} flags.nsfw Whether the joke is NSFW or not - * @prop {Boolean} flags.racist Whether the joke is racist or not - * @prop {Boolean} flags.sexist Whether the joke is sexist or not - * @prop {Boolean} flags.religious Whether the joke is religiously offensive or not - * @prop {Boolean} flags.political Whether the joke is politically offensive or not - * @prop {Number} id The ID of the joke - */ - -/** - * @typedef {Object} TwopartJoke A joke of type twopart - * @prop {String} category The category of the joke - * @prop {("twopart")} type The type of the joke - * @prop {String} setup The setup of the joke - * @prop {String} delivery The delivery of the joke - * @prop {Object} flags - * @prop {Boolean} flags.nsfw Whether the joke is NSFW or not - * @prop {Boolean} flags.racist Whether the joke is racist or not - * @prop {Boolean} flags.sexist Whether the joke is sexist or not - * @prop {Boolean} flags.religious Whether the joke is religiously offensive or not - * @prop {Boolean} flags.political Whether the joke is politically offensive or not - * @prop {Number} id The ID of the joke - */ - const AllJokes = require("./AllJokes"); const parseJokes = require("../parseJokes"); +const languages = require("../languages"); +const tr = require("../translate"); const jsl = require("svjsl"); const settings = require("../../settings"); -jsl.unused([AllJokes]); + +jsl.unused(AllJokes); + +var _lastIDs = []; +var _selectionAttempts = 0; class FilteredJoke { @@ -53,18 +30,29 @@ class FilteredJoke this._allJokes = allJokes; this._filteredJokes = null; + let idRangePerLang = {}; + + Object.keys(parseJokes.jokeCountPerLang).forEach(lc => { + idRangePerLang[lc] = [ 0, (parseJokes.jokeCountPerLang[lc] - 1) ]; + }); + this._allowedCategories = [ settings.jokes.possible.anyCategoryName.toLowerCase(), ...settings.jokes.possible.categories.map(c => c.toLowerCase()) ]; this._allowedTypes = [...settings.jokes.possible.types]; this._searchString = null; - this._idRange = [0, (parseJokes.jokeCount - 1)]; + this._idRange = [0, (parseJokes.jokeCountPerLang[settings.languages.defaultLanguage] - 1)]; + this._idRangePerLang = idRangePerLang; this._flags = []; this._errors = []; + this._lang = settings.languages.defaultLanguage; + this._amount = 1; + + if(!_lastIDs || !Array.isArray(_lastIDs)) + _lastIDs = []; - if(!global._lastIDs || !Array.isArray(global._lastIDs)) - global._lastIDs = []; + return this; } //#MARKER categories @@ -182,12 +170,16 @@ class FilteredJoke * The IDs a joke can be of * @param {Number} start * @param {Number} [end] If this is not set, it will default to the same value the param `start` has + * @param {String} [lang] Lang code * @returns {Boolean} Returns false if the parameter(s) is/are not of type `number`, else returns true */ - setIdRange(start, end = null) + setIdRange(start, end = null, lang = null) { if(jsl.isEmpty(end)) end = start; + + if(jsl.isEmpty(lang)) + lang = this.getLanguage() || settings.languages.defaultLanguage; if(isNaN(parseInt(start)) || isNaN(parseInt(end)) || typeof start != "number" || typeof end != "number" || jsl.isEmpty(start) || jsl.isEmpty(end)) { @@ -195,7 +187,7 @@ class FilteredJoke return false; } - if(start < 0 || end > (parseJokes.jokeCount - 1)) + if(start < 0 || end > this._idRangePerLang[lang][1]) { this._errors.push("The \"idRange\" parameter values are out of range"); return false; @@ -247,23 +239,81 @@ class FilteredJoke return this._flags; } + //#MARKER language + /** + * Sets the language + * @param {String} code + * @returns {Boolean} Returns true if the language was set, false if it is invalid + */ + setLanguage(code) + { + if(languages.isValidLang(code) === true) + { + this._lang = code; + return true; + } + + return false; + } + + /** + * Returns the set language code + * @returns {String} + */ + getLanguage() + { + return this._lang || settings.languages.defaultLanguage; + } + + //#MARKER amount + /** + * Sets the amount of jokes + * @param {Number} num + * @returns {Boolean|String} Returns true if the amount was set, string containing error if it is invalid + */ + setAmount(num) + { + num = parseInt(num); + + if(isNaN(num) || num < 1 || num > settings.jokes.maxAmount) + return `"num" parameter in FilteredJoke.setAmount() couldn't be resolved to an integer or it is less than 0 or greater than ${settings.jokes.maxAmount}`; + + this._amount = num; + return true; + } + + /** + * Returns the set joke amount or `1` if not yet set + * @returns {Number} + */ + getAmount() + { + return this._amount || 1; + } + //#MARKER apply filters /** * Applies the previously set filters and modifies the `this._filteredJokes` property with the applied filters * @private + * @param {String} lang * @returns {Promise} */ - _applyFilters() + _applyFilters(lang) { return new Promise((resolve, reject) => { try { this._filteredJokes = []; - this._allJokes.getJokeArray().forEach(joke => { // iterate over each joke, reading all set filters and thereby checking if it suits the request + if(!lang) + lang = settings.languages.defaultLanguage; + + this._allJokes.getJokeArray(lang).forEach(joke => { + // iterate over each joke, reading all set filters and thereby checking if it suits the request + // to deny a joke from being served, just return from this callback function //#SECTION id range - let idRange = this.getIdRange(); + let idRange = this.getIdRange(lang); if(joke.id < idRange[0] || joke.id > idRange[1]) // if the joke is return; @@ -298,109 +348,156 @@ class FilteredJoke let searchMatches = false; if(!jsl.isEmpty(this.getSearchString())) { - if(joke.type == "single" - && joke.joke.toLowerCase().includes(this.getSearchString())) + if(joke.type == "single" && joke.joke.toLowerCase().includes(this.getSearchString())) searchMatches = true; - else if (joke.type == "twopart" - && (joke.setup + joke.delivery).toLowerCase().includes(this.getSearchString())) + else if (joke.type == "twopart" && (joke.setup + joke.delivery).toLowerCase().includes(this.getSearchString())) searchMatches = true; } else searchMatches = true; if(!searchMatches) // if the provided search string doesn't match the joke, the joke is invalid return; + + //#SECTION language + let langCode = this.getLanguage(); + if(!languages.isValidLang(langCode)) + return; // invalid lang code + if(joke.lang.toLowerCase() != langCode.toLowerCase()) + return; // lang code doesn't match + + // amount param is used in getJokes() //#SECTION done this._filteredJokes.push(joke); // joke is valid, push it to the array that gets passed in the resolve() }); - resolve(this._filteredJokes); + + return resolve(this._filteredJokes); } catch(err) { - reject(err); + return reject(err); } }); } - //#MARKER get joke(s) + //#MARKER get joke /** * Applies all filters and returns the final joke - * @returns {Promise} Returns a promise containing a single, randomly selected joke that matches the previously set filters. If the filters didn't match, rejects promise. + * @param {Number} [amount=1] The amount of jokes to return + * @returns {Promise>} Returns a promise containing an array, which in turn contains a single or multiple randomly selected joke/s that match/es the previously set filters. If the filters didn't match, rejects promise. */ - getJoke() + getJokes(amount = 1) { + amount = parseInt(amount); + if(isNaN(amount) || jsl.isEmpty(amount)) + amount = 1; + return new Promise((resolve, reject) => { - this._applyFilters().then(filteredJokes => { + let retJokes = []; + let multiSelectLastIDs = []; + + this._applyFilters(this._lang || settings.languages.defaultLanguage).then(filteredJokes => { if(filteredJokes.length == 0 || typeof filteredJokes != "object") { - if(this._errors) + if(this._errors && this._errors.length > 0) return reject(this._errors); else - return reject("No jokes were found that match your provided filter(s)"); + return reject(tr(this.getLanguage(), "foundNoMatchingJokes")); } - if(!global._lastIDs || !Array.isArray(global._lastIDs)) - global._lastIDs = []; + if(!_lastIDs || !Array.isArray(_lastIDs)) + _lastIDs = []; - if(typeof global._selectionAttempts != "number") - global._selectionAttempts = 0; + if(typeof _selectionAttempts != "number") + _selectionAttempts = 0; /** - * @param {Array} jokes + * @param {Array} jokes */ let selectRandomJoke = jokes => { - let selectedJoke = filteredJokes[jsl.randRange(0, (filteredJokes.length - 1))]; + let idx = jsl.randRange(0, (jokes.length - 1)); + let selectedJoke = jokes[idx]; - if(jokes.length > settings.jokes.lastIDsMaxLength && global._lastIDs.includes(selectedJoke.id)) + if(jokes.length > settings.jokes.lastIDsMaxLength && _lastIDs.includes(selectedJoke.id)) { - if(global._selectionAttempts > settings.jokes.jokeRandomizationAttempts) + if(_selectionAttempts > settings.jokes.jokeRandomizationAttempts) return reject(); - global._selectionAttempts++; - let reducedJokeArray = []; + _selectionAttempts++; - jokes.forEach(j => { - if(!global._lastIDs.includes(j.id)) - reducedJokeArray.push(j); - }); - - return selectRandomJoke(reducedJokeArray); + jokes.splice(idx, 1); // remove joke that is already contained in _lastIDs + + return selectRandomJoke(jokes); } else { - global._lastIDs.push(selectedJoke.id); + _lastIDs.push(selectedJoke.id); + + if(_lastIDs.length > settings.jokes.lastIDsMaxLength) + _lastIDs.shift(); + + _selectionAttempts = 0; + + if(!multiSelectLastIDs.includes(selectedJoke.id)) + { + multiSelectLastIDs.push(selectedJoke.id); + return selectedJoke; + } + else + { + if(_selectionAttempts > settings.jokes.jokeRandomizationAttempts) + return reject(); - if(global._lastIDs.length > settings.jokes.lastIDsMaxLength) - global._lastIDs.shift(); + _selectionAttempts++; - global._selectionAttempts = 0; + jokes.splice(idx, 1); // remove joke that is already contained in _lastIDs - return selectedJoke; + return selectRandomJoke(jokes); + } } }; - let randJoke = selectRandomJoke(filteredJokes); - return resolve(randJoke); + if(amount < filteredJokes.length) + { + for(let i = 0; i < amount; i++) + { + let rJoke = selectRandomJoke(filteredJokes); + if(rJoke != null) + retJokes.push(rJoke); + } + } + else retJokes = filteredJokes; + + // Sort jokes by ID + // retJokes.sort((a, b) => { + // if(b.id > a.id) + // return -1; + // else + // return 1; + // }); + + return resolve(retJokes); }).catch(err => { return reject(err); }); }); } + //#MARKER get all jokes /** * Applies all filters and returns an array of all jokes that are viable - * @returns {Promise>} Returns a promise containing a single, randomly selected joke that matches the previously set filters. If the filters didn't match, rejects promise. + * @returns {Promise>} Returns a promise containing a single, randomly selected joke that matches the previously set filters. If the filters didn't match, rejects promise. */ getAllJokes() { return new Promise((resolve, reject) => { - this._applyFilters().then(filteredJokes => { - resolve(filteredJokes); + this._applyFilters(this._lang || settings.languages.defaultLanguage).then(filteredJokes => { + return resolve(filteredJokes); }).catch(err => { - reject(err); + return reject(err); }); }); } } -module.exports = FilteredJoke; \ No newline at end of file +module.exports = FilteredJoke; diff --git a/src/docs.js b/src/docs.js index 47524acf..45984ca7 100755 --- a/src/docs.js +++ b/src/docs.js @@ -2,7 +2,7 @@ const jsl = require("svjsl"); const farmhash = require("farmhash"); -const fs = require("fs"); +const fs = require("fs-extra"); const settings = require("../settings"); const debug = require("./verboseLogging"); const packageJSON = require("../package.json"); @@ -12,6 +12,7 @@ const zlib = require("zlib"); const xss = require("xss"); const semver = require("semver"); const analytics = require("./analytics"); +const languages = require("./languages"); /** @@ -109,17 +110,21 @@ const recompileDocs = () => { process.brCompErrOnce = false; if(settings.httpServer.encodings.gzip) - saveEncoded("gzip", injectedFileNames[i], injected).catch(err => void(err)); + saveEncoded("gzip", injectedFileNames[i], injected).catch(err => jsl.unused(err)); if(settings.httpServer.encodings.deflate) - saveEncoded("deflate", injectedFileNames[i], injected).catch(err => void(err)); + saveEncoded("deflate", injectedFileNames[i], injected).catch(err => jsl.unused(err)); if(settings.httpServer.encodings.brotli) - saveEncoded("brotli", injectedFileNames[i], injected).catch(() => { + { + saveEncoded("brotli", injectedFileNames[i], injected).catch(err => { + jsl.unused(err); + if(!process.brCompErrOnce) { process.brCompErrOnce = true; injectError(`Brotli compression is only supported since Node.js version 11.7.0 - current Node.js version is ${semver.clean(process.version)}`, false); } }); + } fs.writeFile(injectedFileNames[i], injected, err => { if(err) @@ -127,7 +132,7 @@ const recompileDocs = () => { return resolve(); }); - }) + }); })); }); @@ -262,6 +267,10 @@ const inject = filePath => { "": settings.jokes.jokesFormatVersion.toString(), "": settings.httpServer.maxPayloadSize.toString(), "": settings.httpServer.maxUrlLength.toString(), + "": languages.jokeLangs().length.toString(), + "": languages.systemLangs().length.toString(), + "": settings.jokes.maxAmount.toString(), + "": settings.jokes.encodeAmount.toString() }; let allMatches = 0; @@ -303,4 +312,4 @@ const sanitize = str => { const minify = input => input.toString().replace(/(\n|\r\n|\t)/gm, ""); -module.exports = { init, recompileDocs, minify, sanitize }; \ No newline at end of file +module.exports = { init, recompileDocs, minify, sanitize }; diff --git a/src/fileFormatConverter.js b/src/fileFormatConverter.js index 84314301..5f745b0c 100755 --- a/src/fileFormatConverter.js +++ b/src/fileFormatConverter.js @@ -4,14 +4,20 @@ const jsl = require("svjsl"); const jsonToYaml = require("json-to-pretty-yaml"); const jsonToXml = require("js2xmlparser"); +const languages = require("./languages"); +const tr = require("./translate"); +const systemLangs = tr.systemLangs; + +const settings = require("../settings"); /** * Converts a JSON object to a string representation of a XML, YAML, plain text or JSON (as fallback) object - based on a passed format string * @param {("xml"|"yaml"|"json"|"txt")} format Can be "xml", "yaml" or "txt", everything else will default to JSON - * @param {Object} jsonInput + * @param {Object} jsonInput + * @param {String} [lang] Needed for converting to "txt" - TODO: implement everywhere * @returns {String} String representation of the converted object */ -const auto = (format, jsonInput) => { +const auto = (format, jsonInput, lang) => { format = format.toLowerCase(); switch(format) { @@ -20,7 +26,7 @@ const auto = (format, jsonInput) => { case "xml": return toXML(jsonInput); case "txt": - return toTXT(jsonInput); + return toTXT(jsonInput, lang); case "json": default: return JSON.stringify(jsonInput, null, 4); @@ -39,16 +45,26 @@ const toXML = jsonInput => { return jsonToXml.parse("data", jsonInput); }; -const toTXT = jsonInput => { - let returnText = `Internal Error - no conversion mapping for data with keys "${Object.keys(jsonInput).join(", ")}" - ERR_NO_CONV_MAPPING @ FF_CONV`; +/** + * Converts a JSON object to plain text, according to the set conversion mapping + * @param {Object} jsonInput + * @param {String} lang + */ +const toTXT = (jsonInput, lang) => { + let returnText = tr(lang, "noConversionMapping", Object.keys(jsonInput).join(", "), "ERR_NO_CONV_MAPPING @ FFCONV"); if(!jsonInput) - returnText = "Internal Error - could not convert data to plain text - ERR_NO_JSON_INPUT @ FF_CONV" + returnText = tr(lang, "cantConvertToPlainText", "ERR_NO_JSON_INPUT @ FFCONV"); if(jsonInput) { if(jsonInput.error === true) - returnText = `${jsonInput.internalError === true ? "Internal " : ""}Error ${jsonInput.code || 100} - ${jsonInput.message}\nThis error is caused by:\n- ${jsonInput.causedBy.join("\n- ")}${jsonInput.additionalInfo ? `\n\nAdditional Information: ${jsonInput.additionalInfo}` : ""}`; + { + if(jsonInput.internalError) + returnText = tr(lang, "conversionInternalError", (jsonInput.code || 100), jsonInput.message, jsonInput.causedBy.join("\n- "), (jsonInput.additionalInfo ? jsonInput.additionalInfo : "X")); + else + returnText = tr(lang, "conversionGeneralError", (jsonInput.code || 100), jsonInput.message, jsonInput.causedBy.join("\n- "), (jsonInput.additionalInfo ? jsonInput.additionalInfo : "X")); + } else { if(jsonInput.joke || (jsonInput.setup && jsonInput.delivery)) // endpoint: /joke @@ -58,27 +74,67 @@ const toTXT = jsonInput => { else if(jsonInput.type == "twopart") returnText = `${jsonInput.setup}\n\n${jsonInput.delivery}`; } - - else if(jsonInput.formats) // endpoint: /formats - returnText = `Available formats are: "${jsonInput.formats.join('", "')}"`; else if(jsonInput.categories) // endpoint: /categories - returnText = `Available categories are: "${jsonInput.categories.join('", "')}"`; + returnText = tr(lang, "availableCategories", jsonInput.categories.join('", "')); else if(jsonInput.flags) // endpoint: /flags - returnText = `Available flags are: "${jsonInput.flags.join('", "')}"`; + returnText = tr(lang, "availableFlags", jsonInput.flags.join('", "')); else if(jsonInput.ping) // endpoint: /ping - returnText = `${jsonInput.ping}\nTimestamp: ${jsonInput.timestamp}`; + returnText = `${jsonInput.ping}\n${tr(lang, "timestamp", jsonInput.timestamp)}`; + + else if(jsonInput.code) // endpoint: /langcode + returnText = `${jsonInput.error ? tr(lang, "genericError", jsonInput.message) : tr(lang, "languageCode", jsonInput.code)}`; + + else if(jsonInput.defaultLanguage) // endpoint: /languages + { + let suppLangs = []; + languages.jokeLangs().forEach(lang => { + suppLangs.push(`${lang.name} [${lang.code}]`); + }); + + let sysLangs = systemLangs().map(lc => `${languages.codeToLanguage(lc)} [${lc}]`); + + let possLangs = []; + + jsonInput.possibleLanguages.forEach(pl => { + possLangs.push(`${pl.name} [${pl.code}]`); + }); + + returnText = tr(lang, "languagesEndpoint", languages.codeToLanguage(jsonInput.defaultLanguage), jsonInput.defaultLanguage, languages.jokeLangs().length, suppLangs.sort().join(", "), sysLangs.length, sysLangs.sort().join(", "), possLangs.sort().join("\n")); + } else if(jsonInput.version) // endpoint: /info - returnText = `Version: ${jsonInput.version}\nJoke Count: ${jsonInput.jokes.totalCount}\nCategories: "${jsonInput.jokes.categories.join('", "')}"\nFlags: "${jsonInput.jokes.flags.join('", "')}"\nSubmission URL: ${jsonInput.jokes.submissionURL}\n\n${jsonInput.info}`; + { + let suppLangs = []; + languages.jokeLangs().forEach(lang => { + suppLangs.push(`${lang.name} [${lang.code}]`); + }); + + let sysLangs = systemLangs().map(lc => `${languages.codeToLanguage(lc)} [${lc}]`); + + let idRanges = []; + Object.keys(jsonInput.jokes.idRange).forEach(lc => { + let lcIr = jsonInput.jokes.idRange[lc]; + idRanges.push(`${languages.codeToLanguage(lc)} [${lc}]: ${lcIr[0]}-${lcIr[1]}`); + }); + + returnText = tr(lang, "infoEndpoint", + settings.info.name, jsonInput.version, jsonInput.jokes.totalCount, jsonInput.jokes.categories.join(`", "`), jsonInput.jokes.flags.join('", "'), + jsonInput.formats.join('", "'), jsonInput.jokes.types.join('", "'), jsonInput.jokes.submissionURL, idRanges.join("\n"), languages.jokeLangs().length, + suppLangs.sort().join(", "), sysLangs.length, sysLangs.sort().join(", "), jsonInput.info + ); + } + + else if(jsonInput.formats) // endpoint: /formats + returnText = tr(lang, "availableFormats", jsonInput.formats.join('", "')); else if(Array.isArray(jsonInput) && jsonInput[0].usage && jsonInput[0].usage.method) // endpoint: /endpoints { - returnText = "Endpoints:\n\n\n"; + returnText = `${tr(lang, "endpointsWord")}:\n\n\n`; jsonInput.forEach(ep => { - returnText += `${ep.name} - ${ep.description}\n Usage: ${ep.usage.method} ${ep.usage.url}\n Supported parameters: ${ep.usage.supportedParams.length > 0 ? `"${ep.usage.supportedParams.join('", "')}"` : "none"}\n\n\n` + returnText += `${tr(lang, "endpointDetails", ep.name, ep.description, ep.usage.method, ep.usage.url, (ep.usage.supportedParams.length > 0 ? `"${ep.usage.supportedParams.join('", "')}"` : "X"))}\n\n`; }); } } diff --git a/src/httpServer.js b/src/httpServer.js old mode 100755 new mode 100644 index 5d04a143..c2b9aabe --- a/src/httpServer.js +++ b/src/httpServer.js @@ -2,9 +2,10 @@ const jsl = require("svjsl"); const http = require("http"); -const { RateLimiterMemory, RateLimiterRes } = require("rate-limiter-flexible"); const Readable = require("stream").Readable; -const fs = require("fs"); +const fs = require("fs-extra"); +const zlib = require("zlib"); +const semver = require("semver"); const settings = require("../settings"); const debug = require("./verboseLogging"); @@ -17,6 +18,10 @@ const lists = require("./lists"); const analytics = require("./analytics"); const jokeSubmission = require("./jokeSubmission"); const auth = require("./auth"); +const meter = require("./meter"); +const languages = require("./languages"); +const { RateLimiterMemory, RateLimiterRes } = require("rate-limiter-flexible"); +const tr = require("./translate"); jsl.unused(RateLimiterRes); // typedef only @@ -30,12 +35,17 @@ const init = () => { * Initializes the HTTP server - should only be called once */ let initHttpServer = () => { - //#SECTION set up rate limiter + //#SECTION set up rate limiters let rl = new RateLimiterMemory({ points: settings.httpServer.rateLimiting, duration: settings.httpServer.timeFrame }); + let rlSubm = new RateLimiterMemory({ + points: settings.jokes.submissions.rateLimiting, + duration: settings.jokes.submissions.timeFrame + }); + //#SECTION create HTTP server let httpServer = http.createServer(async (req, res) => { let parsedURL = parseURL(req.url); @@ -45,17 +55,21 @@ const init = () => { let analyticsObject = { ipAddress: ip, urlPath: parsedURL.pathArray, - urlParameters: parsedURL.queryParams + urlParameters: parsedURL.queryParams }; + let lang = parsedURL.queryParams ? parsedURL.queryParams.lang : "invalid-lang-code"; + + if(languages.isValidLang(lang) !== true) + lang = settings.languages.defaultLanguage; - debug("HTTP", `Incoming ${req.method} request from "${ip.substring(0, 8)}${localhostIP ? `..." ${jsl.colors.fg.blue}(local)${jsl.colors.rst}` : "...\""}`); + debug("HTTP", `Incoming ${req.method} request from "${lang}-${ip.substring(0, 8)}${localhostIP ? `..." ${jsl.colors.fg.blue}(local)${jsl.colors.rst}` : "...\""} to ${req.url}`); let fileFormat = settings.jokes.defaultFileFormat.fileFormat; if(!jsl.isEmpty(parsedURL.queryParams) && !jsl.isEmpty(parsedURL.queryParams.format)) fileFormat = parseURL.getFileFormatFromQString(parsedURL.queryParams); if(req.url.length > settings.httpServer.maxUrlLength) - return respondWithError(res, 108, 414, fileFormat, `The length of the URL (${req.url.length} characters) exceeds the maximum accepted length of ${settings.httpServer.maxUrlLength} characters`); + return respondWithError(res, 108, 414, fileFormat, tr(lang, "uriTooLong", req.url.length, settings.httpServer.maxUrlLength), lang, req.url.length); //#SECTION check lists try @@ -63,7 +77,7 @@ const init = () => { if(lists.isBlacklisted(ip)) { logRequest("blacklisted", null, analyticsObject); - return respondWithError(res, 103, 403, fileFormat); + return respondWithError(res, 103, 403, fileFormat, tr(lang, "ipBlacklisted", settings.info.author.website), lang); } debug("HTTP", `Requested URL: ${parsedURL.initialURL}`); @@ -106,9 +120,13 @@ const init = () => { urlPath: parsedURL.pathArray } }); - return respondWithError(res, 500, 100, fileFormat, err); + return respondWithError(res, 500, 100, fileFormat, tr(lang, "errSetupHttpResponse", err), lang); } + meter.update("reqtotal", 1); + meter.update("req1min", 1); + meter.update("req10min", 1); + //#SECTION GET if(req.method === "GET") { @@ -120,7 +138,7 @@ const init = () => { let lowerCaseEndpoints = []; endpoints.forEach(ep => lowerCaseEndpoints.push(ep.name.toLowerCase())); - if(!jsl.isEmpty(urlPath)) + if(!jsl.isArrayEmpty(urlPath)) requestedEndpoint = urlPath[0]; else { @@ -133,7 +151,7 @@ const init = () => { setRateLimitedHeaders(res, rlRes); analytics.rateLimited(ip); logRequest("ratelimited", `IP: ${ip}`, analyticsObject); - return respondWithError(res, 101, 429, fileFormat); + return respondWithError(res, 101, 429, fileFormat, tr(lang, "rateLimited", settings.httpServer.rateLimiting, settings.httpServer.timeFrame), lang); } else return serveDocumentation(req, res); @@ -143,7 +161,7 @@ const init = () => { // setRateLimitedHeaders(res, rlRes); analytics.rateLimited(ip); logRequest("ratelimited", `IP: ${ip}`, analyticsObject); - return respondWithError(res, 101, 429, fileFormat); + return respondWithError(res, 101, 429, fileFormat, tr(lang, "rateLimited", settings.httpServer.rateLimiting, settings.httpServer.timeFrame), lang); } } @@ -211,7 +229,7 @@ const init = () => { } catch(err) { - return respondWithError(res, 104, 500, fileFormat); + return respondWithError(res, 104, 500, fileFormat, tr(lang, "endpointInternalError", err), lang); } } else @@ -220,12 +238,12 @@ const init = () => { { let rlRes = await rl.get(ip); - if((rlRes &&rlRes._remainingPoints < 0) && !lists.isWhitelisted(ip) && !isAuthorized) + if((rlRes && rlRes._remainingPoints < 0) && !lists.isWhitelisted(ip) && !isAuthorized) { setRateLimitedHeaders(res, rlRes); logRequest("ratelimited", `IP: ${ip}`, analyticsObject); analytics.rateLimited(ip); - return respondWithError(res, 101, 429, fileFormat); + return respondWithError(res, 101, 429, fileFormat, tr(lang, "rateLimited", settings.httpServer.rateLimiting, settings.httpServer.timeFrame), lang); } else { @@ -243,7 +261,7 @@ const init = () => { // setRateLimitedHeaders(res, rlRes); logRequest("ratelimited", `IP: ${ip}`, analyticsObject); analytics.rateLimited(ip); - return respondWithError(res, 101, 429, fileFormat); + return respondWithError(res, 100, 500, fileFormat, tr(lang, "generalInternalError", err), lang); } } } @@ -253,9 +271,9 @@ const init = () => { if(!foundEndpoint) { if(!jsl.isEmpty(fileFormat) && req.url.toLowerCase().includes("format")) - return respondWithError(res, 102, 404, fileFormat, `Endpoint "${!jsl.isEmpty(requestedEndpoint) ? requestedEndpoint : "/"}" not found - Please read the documentation at ${settings.info.docsURL}#endpoints to see all available endpoints`); + return respondWithError(res, 102, 404, fileFormat, tr(lang, "endpointNotFound", (!jsl.isEmpty(requestedEndpoint) ? requestedEndpoint : "/")), lang); else - return respondWithErrorPage(res, 404, `Endpoint "${!jsl.isEmpty(requestedEndpoint) ? requestedEndpoint : "/"}" not found - Please read the documentation at ${settings.info.docsURL}#endpoints to see all available endpoints`); + return respondWithErrorPage(res, 404, tr(lang, "endpointNotFound", (!jsl.isEmpty(requestedEndpoint) ? requestedEndpoint : "/"))); } }, 5000); } @@ -264,8 +282,12 @@ const init = () => { else if(req.method === "PUT") { //#MARKER Joke submission - if(!jsl.isEmpty(parsedURL.pathArray) && parsedURL.pathArray[0] == "submit") + let submissionsRateLimited = await rlSubm.get(ip); + + if(!jsl.isEmpty(parsedURL.pathArray) && parsedURL.pathArray[0] == "submit" && !(submissionsRateLimited && submissionsRateLimited._remainingPoints <= 0 && !headerAuth.isAuthorized)) { + rlSubm.consume(ip, 1); + let data = ""; let dataGotten = false; req.on("data", chunk => { @@ -273,8 +295,8 @@ const init = () => { let payloadLength = byteLength(data); if(payloadLength > settings.httpServer.maxPayloadSize) - return respondWithError(res, 107, 413, fileFormat, `The provided payload data is too large (${payloadLength} bytes of ${settings.httpServer.maxPayloadSize})`); - + return respondWithError(res, 107, 413, fileFormat, tr(lang, "payloadTooLarge", payloadLength, settings.httpServer.maxPayloadSize), lang); + if(!jsl.isEmpty(data)) dataGotten = true; @@ -285,13 +307,17 @@ const init = () => { if(!dataGotten) { debug("HTTP", "PUT request timed out"); - return respondWithError(res, 105, 400, fileFormat, "Request body is empty"); + return respondWithError(res, 105, 400, fileFormat, tr(lang, "requestEmptyOrTimedOut"), lang); } }, 3000); } else { //#MARKER Restart / invalid PUT + + if(submissionsRateLimited && submissionsRateLimited._remainingPoints <= 0 && !headerAuth.isAuthorized) + return respondWithError(res, 110, 429, fileFormat, tr(lang, "rateLimitedShort"), lang); + let data = ""; let dataGotten = false; req.on("data", chunk => { @@ -307,18 +333,18 @@ const init = () => { "error": false, "message": `Restarting ${settings.info.name}`, "timestamp": new Date().getTime() - })); + }, lang)); console.log(`\n\n[${logger.getTimestamp(" | ")}] ${jsl.colors.fg.red}IP ${jsl.colors.fg.yellow}${ip.substr(0, 8)}[...]${jsl.colors.fg.red} sent a restart command\n\n\n${jsl.colors.rst}`); process.exit(2); // if the process is exited with status 2, the package node-wrap will restart the process } - else return respondWithErrorPage(res, 400, `Request body is invalid or was sent to the wrong endpoint "${parsedURL.pathArray != null ? parsedURL.pathArray[0] : "/"}", please refer to the documentation at ${settings.info.docsURL}#submit-joke to see how to correctly structure a joke submission.`); + else return respondWithErrorPage(res, 400, tr(lang, "invalidSubmissionOrWrongEndpoint", (parsedURL.pathArray != null ? parsedURL.pathArray[0] : "/"))); }); setTimeout(() => { if(!dataGotten) { debug("HTTP", "PUT request timed out"); - return respondWithErrorPage(res, 400, "Request body is empty"); + return respondWithErrorPage(res, 400, tr(lang, "requestBodyIsInvalid")); } }, 3000); } @@ -335,7 +361,7 @@ const init = () => { "internalError": false, "message": `Wrong method "${req.method}" used. Expected "GET", "OPTIONS" or "HEAD"`, "timestamp": new Date().getTime() - })); + }, lang)); } }); @@ -412,21 +438,45 @@ function setRateLimitedHeaders(res, rlRes) * @param {Number} responseCode The HTTP response code to end the request with * @param {String} fileFormat The file format to respond with - automatically gets converted to MIME type * @param {String} errorMessage Additional error info + * @param {String} lang Language code of the request + * @param {...any} args Arguments to replace numbered %-placeholders with. Only use objects that are strings or convertable to them with `.toString()`! */ -const respondWithError = (res, errorCode, responseCode, fileFormat, errorMessage) => { +const respondWithError = (res, errorCode, responseCode, fileFormat, errorMessage, lang, ...args) => { try { - let errFromRegistry = require(`.${settings.errors.errorRegistryIncludePath}`)[errorCode.toString()]; + errorCode = errorCode.toString(); + let errFromRegistry = require("../data/errorMessages")[errorCode]; let errObj = {}; + if(errFromRegistry == undefined) + throw new Error(`Couldn't find errorMessages module or Node is using an outdated, cached version`); + + if(!lang || !languages.isValidLang(lang)) + lang = settings.languages.defaultLanguage; + + let insArgs = (texts, insertions) => { + if(!Array.isArray(insertions) || insertions.length <= 0) + return texts; + + insertions.forEach((ins, i) => { + + if(Array.isArray(texts)) + texts = texts.map(tx => tx.replace(`%${i + 1}`, ins)); + else if(typeof texts == "string") + texts = texts.replace(`%${i + 1}`, ins); + }); + + return texts; + }; + if(fileFormat != "xml") { errObj = { "error": true, "internalError": errFromRegistry.errorInternal, "code": errorCode, - "message": errFromRegistry.errorMessage, - "causedBy": errFromRegistry.causedBy, + "message": insArgs(errFromRegistry.errorMessage[lang], args) || insArgs(errFromRegistry.errorMessage[settings.languages.defaultLanguage], args), + "causedBy": insArgs(errFromRegistry.causedBy[lang], args) || insArgs(errFromRegistry.causedBy[settings.languages.defaultLanguage], args), "timestamp": new Date().getTime() } } @@ -436,8 +486,8 @@ const respondWithError = (res, errorCode, responseCode, fileFormat, errorMessage "error": true, "internalError": errFromRegistry.errorInternal, "code": errorCode, - "message": errFromRegistry.errorMessage, - "causedBy": {"cause": errFromRegistry.causedBy}, + "message": insArgs(errFromRegistry.errorMessage[lang], args) || insArgs(errFromRegistry.errorMessage[settings.languages.defaultLanguage], args), + "causedBy": {"cause": insArgs(errFromRegistry.causedBy[lang], args) || insArgs(errFromRegistry.causedBy[settings.languages.defaultLanguage], args)}, "timestamp": new Date().getTime() } } @@ -445,7 +495,9 @@ const respondWithError = (res, errorCode, responseCode, fileFormat, errorMessage if(!jsl.isEmpty(errorMessage)) errObj.additionalInfo = errorMessage; - return pipeString(res, convertFileFormat.auto(fileFormat, errObj), parseURL.getMimeTypeFromFileFormatString(fileFormat), responseCode); + let converted = convertFileFormat.auto(fileFormat, errObj, lang).toString(); + + return pipeString(res, converted, parseURL.getMimeTypeFromFileFormatString(fileFormat), responseCode); } catch(err) { @@ -492,7 +544,8 @@ const pipeString = (res, text, mimeType, statusCode = 200) => { try { statusCode = parseInt(statusCode); - if(isNaN(statusCode)) throw new Error(""); + if(isNaN(statusCode)) + throw new Error("Invalid status code"); } catch(err) { @@ -506,16 +559,18 @@ const pipeString = (res, text, mimeType, statusCode = 200) => { s.push(text); s.push(null); - if(!res.headersSent) - { - res.writeHead(statusCode, { - "Content-Type": `${mimeType}; charset=UTF-8`, - "Content-Length": text.length - }); - } - if(!res.writableEnded) + { s.pipe(res); + + if(!res.headersSent) + { + res.writeHead(statusCode, { + "Content-Type": `${mimeType}; charset=UTF-8`, + "Content-Length": byteLength(text) // Content-Length needs the byte length, not the char length + }); + } + } } /** @@ -538,14 +593,17 @@ const pipeFile = (res, filePath, mimeType, statusCode = 200) => { } if(!fs.existsSync(filePath)) - return respondWithErrorPage(res, 404, `File at "${filePath}" not found.`); + return respondWithErrorPage(res, 404, `Internal error: file at "${filePath}" not found.`); try { - res.writeHead(statusCode, { - "Content-Type": `${mimeType}; charset=UTF-8`, - "Content-Length": fs.statSync(filePath).size - }); + if(!res.headersSent) + { + res.writeHead(statusCode, { + "Content-Type": `${mimeType}; charset=UTF-8`, + "Content-Length": fs.statSync(filePath).size + }); + } let readStream = fs.createReadStream(filePath); readStream.pipe(res); @@ -605,7 +663,7 @@ const serveDocumentation = (req, res) => { /** * Returns the name of the client's accepted encoding with the highest priority * @param {http.IncomingMessage} req The HTTP req object - * @returns {null|"gzip"|"deflate"|"brotli"} Returns null if no encodings are supported, else returns the encoding name + * @returns {null|"gzip"|"deflate"|"br"} Returns null if no encodings are supported, else returns the encoding name */ const getAcceptedEncoding = req => { let selectedEncoding = null; @@ -632,24 +690,15 @@ const getAcceptedEncoding = req => { } /** - * Returns the length of a string in bytes - [Source of code](https://gist.github.com/lovasoa/11357947) + * Returns the length of a string in bytes * @param {String} str * @returns {Number} */ -const byteLength = str => { - let s = str.length; - for (let i = str.length - 1; i >= 0; i--) - { - let code = str.charCodeAt(i); - - if (code > 0x7f && code <= 0x7ff) - s++; - else if (code > 0x7ff && code <= 0xffff) - s+=2; - if (code >= 0xDC00 && code <= 0xDFFF) - i--; - } - return s; +function byteLength(str) +{ + if(!str) + return 0; + return Buffer.byteLength(str, "utf8"); } /** @@ -665,10 +714,71 @@ const getFileExtensionFromEncoding = encoding => { case "deflate": return "zz"; case "br": + case "brotli": return "br"; default: return ""; } } -module.exports = { init, respondWithError, respondWithErrorPage, pipeString, pipeFile, serveDocumentation, getAcceptedEncoding, getFileExtensionFromEncoding }; +/** + * Tries to serve data with an encoding supported by the client, else just serves the raw data + * @param {http.IncomingMessage} req The HTTP req object + * @param {http.ServerResponse} res The HTTP res object + * @param {String} data The data to send to the client + * @param {String} mimeType The MIME type to respond with + */ +function tryServeEncoded(req, res, data, mimeType) +{ + let selectedEncoding = getAcceptedEncoding(req); + + debug("HTTP", `Trying to serve with encoding ${selectedEncoding}`); + + if(selectedEncoding) + res.setHeader("Content-Encoding", selectedEncoding); + else + res.setHeader("Content-Encoding", "identity"); + + switch(selectedEncoding) + { + case "br": + if(!semver.lt(process.version, "v11.7.0")) // Brotli was added in Node v11.7.0 + { + zlib.brotliCompress(data, (err, encRes) => { + if(!err) + return pipeString(res, encRes, mimeType); + else + return pipeString(res, `Internal error while encoding text into ${selectedEncoding}: ${err}`, mimeType); + }); + } + else + { + res.setHeader("Content-Encoding", "identity"); + + return pipeString(res, data, mimeType); + } + break; + case "gzip": + zlib.gzip(data, (err, encRes) => { + if(!err) + return pipeString(res, encRes, mimeType); + else + return pipeString(res, `Internal error while encoding text into ${selectedEncoding}: ${err}`, mimeType); + }); + break; + case "deflate": + zlib.deflate(data, (err, encRes) => { + if(!err) + return pipeString(res, encRes, mimeType); + else + return pipeString(res, `Internal error while encoding text into ${selectedEncoding}: ${err}`, mimeType); + }); + break; + default: + res.setHeader("Content-Encoding", "identity"); + + return pipeString(res, data, mimeType); + } +} + +module.exports = { init, respondWithError, respondWithErrorPage, pipeString, pipeFile, serveDocumentation, getAcceptedEncoding, getFileExtensionFromEncoding, tryServeEncoded }; diff --git a/src/jokeSubmission.js b/src/jokeSubmission.js index 20797d4e..aa2dde8d 100755 --- a/src/jokeSubmission.js +++ b/src/jokeSubmission.js @@ -1,5 +1,5 @@ const jsl = require("svjsl"); -const fs = require("fs"); +const fs = require("fs-extra"); const http = require("http"); var httpServer = require("./httpServer"); // module loading order is a bit fucked, so this module has to be loaded multiple times @@ -8,9 +8,12 @@ const logRequest = require("./logRequest"); const convertFileFormat = require("./fileFormatConverter"); const analytics = require("./analytics"); const parseURL = require("./parseURL"); +const meter = require("./meter"); +const tr = require("./translate"); + const settings = require("../settings"); -jsl.unused([http, analytics]); +jsl.unused(http, analytics, tr); /** @@ -26,10 +29,18 @@ const jokeSubmission = (res, data, fileFormat, ip, analyticsObject) => { { if(typeof httpServer == "object" && Object.keys(httpServer).length <= 0) httpServer = require("./httpServer"); - + let submittedJoke = JSON.parse(data); + + let langCode = submittedJoke.lang || settings.languages.defaultLanguage; + if(jsl.isEmpty(submittedJoke)) - return httpServer.respondWithError(res, 105, 400, fileFormat, "Request body is empty"); + return httpServer.respondWithError(res, 105, 400, fileFormat, tr(langCode, "requestBodyIsInvalid"), langCode); + + let invalidChars = data.match(settings.jokes.submissions.invalidCharRegex); + let invalidCharsStr = invalidChars ? invalidChars.map(ch => `0x${ch.charCodeAt(0).toString(16)}`).join(", ") : null; + if(invalidCharsStr && invalidChars.length > 0) + return httpServer.respondWithError(res, 109, 400, fileFormat, tr(langCode, "invalidChars", invalidCharsStr), langCode); if(submittedJoke.formatVersion == parseJokes.jokeFormatVersion && submittedJoke.formatVersion == settings.jokes.jokesFormatVersion) { @@ -37,14 +48,14 @@ const jokeSubmission = (res, data, fileFormat, ip, analyticsObject) => { let validationResult = parseJokes.validateSingle(submittedJoke); if(typeof validationResult === "object") - return httpServer.respondWithError(res, 105, 400, fileFormat, `Submitted joke format is incorrect - encountered error${validationResult.length == 1 ? ": " : "s:\n"}${validationResult.join("\n")}`); + return httpServer.respondWithError(res, 105, 400, fileFormat, tr(langCode, "submittedJokeFormatInvalid", validationResult.join("\n")), langCode); else if(validationResult === true) { // joke is valid, find file name and then write to file let sanitizedIP = ip.replace(settings.httpServer.ipSanitization.regex, settings.httpServer.ipSanitization.replaceChar).substring(0, 8); let curUnix = new Date().getTime(); - let fileName = `${settings.jokes.jokeSubmissionPath}submission_${sanitizedIP}_0_${curUnix}.json`; + let fileName = `${settings.jokes.jokeSubmissionPath}${langCode}/submission_${sanitizedIP}_0_${curUnix}.json`; let iter = 0; let findNextNum = currentNum => { @@ -52,7 +63,7 @@ const jokeSubmission = (res, data, fileFormat, ip, analyticsObject) => { if(iter >= settings.httpServer.rateLimiting) { logRequest("ratelimited", `IP: ${ip}`, analyticsObject); - return httpServer.respondWithError(res, 101, 429, fileFormat); + return httpServer.respondWithError(res, 101, 429, fileFormat, tr(langCode, "rateLimited", settings.httpServer.rateLimiting, settings.httpServer.timeFrame)); } if(fs.existsSync(`${settings.jokes.jokeSubmissionPath}submission_${sanitizedIP}_${currentNum}_${curUnix}.json`)) @@ -60,28 +71,30 @@ const jokeSubmission = (res, data, fileFormat, ip, analyticsObject) => { else return currentNum; }; + fs.ensureDirSync(`${settings.jokes.jokeSubmissionPath}${langCode}`); + if(fs.existsSync(`${settings.jokes.jokeSubmissionPath}${fileName}`)) - fileName = `${settings.jokes.jokeSubmissionPath}submission_${sanitizedIP}_${findNextNum()}_${curUnix}.json`; + fileName = `${settings.jokes.jokeSubmissionPath}${langCode}/submission_${sanitizedIP}_${findNextNum()}_${curUnix}.json`; try { // file name was found, write to file now: - return writeJokeToFile(res, fileName, submittedJoke, fileFormat, ip, analyticsObject); + return writeJokeToFile(res, fileName, submittedJoke, fileFormat, ip, analyticsObject, langCode); } catch(err) { - return httpServer.respondWithError(res, 100, 500, fileFormat, `Internal error while saving joke: ${err}`); + return httpServer.respondWithError(res, 100, 500, fileFormat, tr(langCode, "errWhileSavingSubmission", err), langCode); } } } else { - return httpServer.respondWithError(res, 105, 400, fileFormat, `Joke format version is incorrect - expected "${parseJokes.jokeFormatVersion}" - got "${submittedJoke.formatVersion}"`); + return httpServer.respondWithError(res, 105, 400, fileFormat, tr(langCode, "wrongFormatVersion", parseJokes.jokeFormatVersion, submittedJoke.formatVersion), langCode); } } catch(err) { - return httpServer.respondWithError(res, 105, 400, fileFormat, `Request body contains invalid JSON: ${err}`); + return httpServer.respondWithError(res, 105, 400, fileFormat, tr(settings.languages.defaultLanguage, "invalidJSON", err), settings.languages.defaultLanguage); } } @@ -93,31 +106,82 @@ const jokeSubmission = (res, data, fileFormat, ip, analyticsObject) => { * @param {String} fileFormat * @param {String} ip * @param {(analytics.AnalyticsDocsRequest|analytics.AnalyticsSuccessfulRequest|analytics.AnalyticsRateLimited|analytics.AnalyticsError|analytics.AnalyticsSubmission)} analyticsObject + * @param {String} [langCode] */ -const writeJokeToFile = (res, filePath, submittedJoke, fileFormat, ip, analyticsObject) => { +const writeJokeToFile = (res, filePath, submittedJoke, fileFormat, ip, analyticsObject, langCode) => { if(typeof httpServer == "object" && Object.keys(httpServer).length <= 0) httpServer = require("./httpServer"); - fs.writeFile(filePath, JSON.stringify(submittedJoke, null, 4), err => { + let reformattedJoke = reformatJoke(submittedJoke); + + fs.writeFile(filePath, JSON.stringify(reformattedJoke, null, 4), err => { if(!err) { // successfully wrote to file let responseObj = { "error": false, - "message": "Joke submission was successfully saved. It will soon be checked out by the author.", - "submission": submittedJoke, + "message": tr(langCode, "submissionSaved"), + "submission": reformattedJoke, "timestamp": new Date().getTime() }; + meter.update("submission", 1); + let submissionObject = analyticsObject; - submissionObject.submission = submittedJoke; + submissionObject.submission = reformattedJoke; logRequest("submission", ip, submissionObject); - return httpServer.pipeString(res, convertFileFormat.auto(fileFormat, responseObj), parseURL.getMimeTypeFromFileFormatString(fileFormat), 201); + return httpServer.pipeString(res, convertFileFormat.auto(fileFormat, responseObj, langCode), parseURL.getMimeTypeFromFileFormatString(fileFormat), 201); } // error while writing to file - else return httpServer.respondWithError(res, 100, 500, fileFormat, `Internal error while saving joke: ${err}`); + else return httpServer.respondWithError(res, 100, 500, fileFormat, tr(langCode, "errWhileSavingSubmission", err), langCode); }); } -module.exports = jokeSubmission; \ No newline at end of file +/** + * Ensures that a joke is formatted as expected + * @param {parseJokes.SingleJoke|parseJokes.TwopartJoke} joke + * @returns {parseJokes.SingleJoke|parseJokes.TwopartJoke} + */ +function reformatJoke(joke) +{ + let retJoke = {}; + + if(joke.formatVersion) + retJoke.formatVersion = joke.formatVersion; + + retJoke = { + ...retJoke, + category: joke.category, + type: joke.type + }; + + if(joke.type == "single") + { + retJoke.joke = joke.joke; + } + else if(joke.type == "twopart") + { + retJoke.setup = joke.setup; + retJoke.delivery = joke.delivery; + } + + retJoke.flags = { + nsfw: joke.flags.nsfw, + religious: joke.flags.religious, + political: joke.flags.political, + racist: joke.flags.racist, + sexist: joke.flags.sexist, + }; + + if(joke.lang) + retJoke.lang = joke.lang; + + if(joke.id) + retJoke.id = joke.id; + + return retJoke; +} + +module.exports = jokeSubmission; +module.exports.reformatJoke = reformatJoke; diff --git a/src/languages.js b/src/languages.js new file mode 100644 index 00000000..0fd5b399 --- /dev/null +++ b/src/languages.js @@ -0,0 +1,175 @@ +const fs = require("fs-extra"); +const jsl = require("svjsl"); +const Fuse = require("fuse.js"); + +const debug = require("./verboseLogging"); +const tr = require("./translate"); + +const settings = require("../settings"); + +var langs; + +/** + * Initializes the language module + */ +function init() +{ + debug("Languages", `Initializing - loading languages from "${settings.languages.langFilePath}"`); + return new Promise((resolve, reject) => { + fs.readFile(settings.languages.langFilePath, (err, data) => { + if(err) + return reject(err); + else + { + let languages = JSON.parse(data.toString()); + debug("Languages", `Found ${languages.length} languages`); + langs = languages; + return resolve(languages); + } + }); + }); +} + +/** + * Checks whether or not a provided language code is ISO 639-1 or ISO 639-2 compatible + * @param {String} langCode Two-character language code + * @returns {Boolean|String} Returns `true` if code exists, string with error message if not + */ +function isValidLang(langCode) +{ + if(langs == undefined) + return "INTERNAL_ERROR: Language module was not correctly initialized (yet)"; + + if(typeof langCode !== "string" || langCode.length !== 2) + return "Language code is not a string or not two characters in length"; + + let requested = langs[langCode.toLowerCase()]; + + if(typeof requested === "string") + return true; + else + return "Language code doesn't exist"; +} + +/** + * Converts a language name (fuzzy) into an ISO 639-1 or ISO 639-2 compatible lang code + * @param {String} language + * @returns {Boolean|String} Returns `false` if no matching language code was found, else returns string with language code + */ +function languageToCode(language) +{ + if(langs == undefined) + throw new Error("INTERNAL_ERROR: Language module was not correctly initialized (yet)"); + + if(typeof language !== "string" || language.length < 1) + throw new TypeError("Language is not a string or not two characters in length"); + + let searchObj = []; + + Object.keys(langs).forEach(key => { + searchObj.push({ + code: key, + lang: langs[key].toLowerCase() + }); + }); + + let fuzzy = new Fuse(searchObj, { + includeScore: true, + keys: ["code", "lang"], + threshold: 0.4 + }); + + let result = fuzzy.search(language)[0]; + + if(result) + return result.item.code; + else + return false; +} + +/** + * Converts an ISO 639-1 or ISO 639-2 compatible lang code into a language name + * @param {String} code + * @returns {Boolean|String} Returns `false` if no matching language was found, else returns string with language name + */ +function codeToLanguage(code) +{ + try + { + let jsonObj = JSON.parse(fs.readFileSync(settings.languages.langFilePath).toString()); + + return jsonObj[code] || false; + } + catch(err) + { + jsl.unused(err); + return false; + } +} + +/** + * @typedef {Object} SupLangObj + * @prop {String} code + * @prop {String} name + */ + +/** + * Returns a list of languages that jokes are available from + * @returns {Array} + */ +function jokeLangs() +{ + let retLangs = []; + + fs.readdirSync(settings.jokes.jokesFolderPath).forEach(f => { + if(f == settings.jokes.jokesTemplateFile) + return; + + let langCode = f.split("-")[1].substr(0, 2); + + retLangs.push({ + code: langCode, + name: codeToLanguage(langCode) + }); + }); + + return retLangs; +} + +/** + * Returns a list of languages that error messages and maybe other stuff are available as + * @returns {Array} + */ +function systemLangs() +{ + return tr.systemLangs(); +} + +/** + * Returns all possible language codes + * @returns {Array} + */ +function getPossibleCodes() +{ + return Object.keys(langs); +} + +/** + * Returns all possible languages, mapped as an object where keys are codes and values are language names + * @returns {Object} + */ +function getPossibleLanguages() +{ + return langs; +} + +module.exports = { + init, + isValidLang, + languageToCode, + codeToLanguage, + jokeLangs, + systemLangs, + getPossibleCodes, + getPossibleLanguages +}; diff --git a/src/lists.js b/src/lists.js index 81a3ed66..e53845bc 100755 --- a/src/lists.js +++ b/src/lists.js @@ -1,7 +1,7 @@ // this module initializes the blacklist, whitelist and console blacklist const jsl = require("svjsl"); -const fs = require("fs"); +const fs = require("fs-extra"); const settings = require("../settings"); const debug = require("./verboseLogging"); const logger = require("./logger"); @@ -12,7 +12,8 @@ const resolveIP = require("./resolveIP"); * Initializes all lists (blacklist, whitelist and console blacklist) * @returns {Promise} */ -const init = () => { +function init() +{ return new Promise((resolve, reject) => { //#SECTION read list files debug("Lists", "Reading blacklist..."); @@ -21,7 +22,8 @@ const init = () => { return reject(err1); else if(!jsl.isEmpty(err1) && err1.toString().includes("ENOENT")) { - debug("Lists", `${jsl.colors.fg.red}No blacklist file found! Defaulting to empty list.${jsl.colors.rst}`); + fs.writeFileSync(settings.lists.blacklistPath, "[\n\t\n]"); + debug("Lists", `${jsl.colors.fg.red}No blacklist file found! Created empty list.${jsl.colors.rst}`); blacklist = "[\n\t\n]"; } @@ -71,14 +73,15 @@ const init = () => { }); }); }); -}; +} /** * Checks whether a provided IP address is in the blacklist * @param {String} ip * @returns {Bool} true if blacklisted, false if not */ -const isBlacklisted = ip => { +function isBlacklisted(ip) +{ if(jsl.isEmpty(process.jokeapi) || jsl.isEmpty(process.jokeapi.lists) || !(process.jokeapi.lists.blacklist instanceof Array)) { logger("fatal", `Blacklist was not initialized when calling lists.isBlacklisted()`, true); @@ -107,7 +110,8 @@ const isBlacklisted = ip => { * @returns {Bool} Returns true if whitelisted, false if not * @throws Throws an error if the lists module was not previously initialized */ -const isWhitelisted = ip => { +function isWhitelisted(ip) +{ let whitelisted = false; if(jsl.isEmpty(process.jokeapi) || jsl.isEmpty(process.jokeapi.lists) || !(process.jokeapi.lists.whitelist instanceof Array)) @@ -131,7 +135,8 @@ const isWhitelisted = ip => { * @param {String} ip * @returns {Bool} true if console blacklisted, false if not */ -const isConsoleBlacklisted = ip => { +function isConsoleBlacklisted(ip) +{ if(jsl.isEmpty(process.jokeapi) || jsl.isEmpty(process.jokeapi.lists) || !(process.jokeapi.lists.consoleBlacklist instanceof Array)) { logger("fatal", `Console blacklist was not initialized when calling lists.isConsoleBlacklisted()`, true); diff --git a/src/logRequest.js b/src/logRequest.js old mode 100755 new mode 100644 index db987318..aa28a078 --- a/src/logRequest.js +++ b/src/logRequest.js @@ -124,15 +124,18 @@ const logRequest = (type, additionalInfo, analyticsData) => { if(!settings.logging.blacklistLoggingEnabled) logDisabled = true; - // analytics({ - // type: "Error", - // data: { - // ipAddress: analyticsData.ipAddress, - // urlPath: analyticsData.urlPath, - // urlParameters: analyticsData.urlParameters, - // errorMessage: `Blacklisted - ${additionalInfo}` - // } - // }); + if(!jsl.isEmpty(analyticsData)) + { + analytics({ + type: "Blacklisted", + data: { + ipAddress: analyticsData.ipAddress, + urlPath: analyticsData.urlPath, + urlParameters: analyticsData.urlParameters, + submission: analyticsData.submission + } + }); + } break; } @@ -154,13 +157,12 @@ const logRequest = (type, additionalInfo, analyticsData) => { * @param {Number} initTimestamp The timestamp of when JokeAPI was initialized */ const initMsg = (initTimestamp) => { - console.log("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); - console.log(`${jsl.colors.fg.blue}[${logger.getTimestamp(" | ")}] ${jsl.colors.fg.green}Started ${settings.info.name} v${settings.info.version}${jsl.colors.rst}`); + console.log(` \n \n${jsl.colors.fg.blue}[${logger.getTimestamp(" | ")}] ${jsl.colors.fg.green}Started ${settings.info.name} v${settings.info.version}${jsl.colors.rst}`); console.log(` ├─ Registered and validated ${jsl.colors.fg.green}${parseJokes.jokeCount}${jsl.colors.rst} jokes`); if(analytics.connectionInfo && analytics.connectionInfo.connected) console.log(` ├─ Connected to analytics database at ${jsl.colors.fg.green}${analytics.connectionInfo.info}${jsl.colors.rst}`); else - console.log(` ├─ Analytics database ${jsl.colors.fg.red}not connected${jsl.colors.rst}`); + console.log(` ├─ Analytics database ${settings.analytics.enabled ? jsl.colors.fg.red : jsl.colors.fg.yellow}not connected${settings.analytics.enabled ? "" : " (disabled)"}${jsl.colors.rst}`); console.log(` ├─ ${settings.info.name} is listening at ${jsl.colors.fg.green}0.0.0.0:${settings.httpServer.port}${jsl.colors.rst}`); console.log(` └─ Initialization took ${jsl.colors.fg.green}${(new Date().getTime() - initTimestamp).toFixed(0)}ms${jsl.colors.rst}`); process.stdout.write("\n"); diff --git a/src/logger.js b/src/logger.js index e361b744..fe1f6e07 100755 --- a/src/logger.js +++ b/src/logger.js @@ -1,4 +1,4 @@ -const fs = require("fs"); +const fs = require("fs-extra"); const jsl = require("svjsl"); const settings = require("../settings"); @@ -76,4 +76,4 @@ const getTimestamp = (separator) => { } module.exports = logger; -module.exports.getTimestamp = getTimestamp; \ No newline at end of file +module.exports.getTimestamp = getTimestamp; diff --git a/src/main.js b/src/main.js old mode 100755 new mode 100644 index 66790377..942728d1 --- a/src/main.js +++ b/src/main.js @@ -5,7 +5,7 @@ const jsl = require("svjsl"); -const fs = require("fs"); +const fs = require("fs-extra"); require("dotenv").config(); const settings = require("../settings"); @@ -17,6 +17,9 @@ const docs = require("./docs"); const analytics = require("./analytics"); const logRequest = require("./logRequest"); const auth = require("./auth"); +const languages = require("./languages"); +const translate = require("./translate"); +const meter = require("./meter"); const promiseAllSequential = require("promise-all-sequential"); const col = jsl.colors.fg; @@ -31,12 +34,20 @@ settings.init.exitSignals.forEach(sig => { const initAll = () => { let initTimestamp = new Date().getTime(); debug("Init", "Initializing all modules - calling joke parser..."); - + process.jokeapi = {}; initializeDirs(); let initPromises = []; let initStages = [ + { + name: "Initializing languages", + fn: languages.init + }, + { + name: "Initializing translations", + fn: translate.init + }, { name: "Initializing joke parser", fn: parseJokes.init @@ -60,6 +71,10 @@ const initAll = () => { { name: "Initializing analytics module", fn: analytics.init + }, + { + name: "Initializing pm2 meter", + fn: meter.init } ]; diff --git a/src/meter.js b/src/meter.js new file mode 100644 index 00000000..0b554a82 --- /dev/null +++ b/src/meter.js @@ -0,0 +1,130 @@ +// handles custom metering / monitoring values in pm2 + +const io = require("@pm2/io"); +const fs = require("fs-extra"); + +const debug = require("./verboseLogging"); +const settings = require("../settings"); + +var req1mMeter = null; +var req10mMeter = null; +var req1hMeter = null; +var reqtotalMeter = null; +var submissionMeter = null; +var m1 = 0; +var m10 = 0; +var h1 = 0; +var tot = 0; +var subms = 0; + +/** + * Initializes the meter module + * @returns {Promise} + */ +function init() +{ + return new Promise((resolve, reject) => { + try + { + req1mMeter = io.metric({ + name: "Reqs / 1m", + unit: "req" + }); + req1mMeter.set(-1); + setInterval(() => { + req1mMeter.set(m1); + m1 = 0; + }, 1000 * 60); + + + req10mMeter = io.metric({ + name: "Reqs / 10m", + unit: "req" + }); + req10mMeter.set(-1); + setInterval(() => { + req10mMeter.set(m10); + m10 = 0; + }, 1000 * 60 * 10); + + + req1hMeter = io.metric({ + name: "Reqs / 1h", + unit: "req" + }); + req1hMeter.set(-1); + setInterval(() => { + req1hMeter.set(h1); + h1 = 0; + }, 1000 * 60 * 60); + + + reqtotalMeter = io.metric({ + name: "Total Reqs", + unit: "req" + }); + reqtotalMeter.set(-1); + + + submissionMeter = io.metric({ + name: "Submissions", + unit: "sub" + }); + subms = fs.readdirSync(settings.jokes.jokeSubmissionPath).length; + submissionMeter.set(subms); + setInterval(() => { + subms = fs.readdirSync(settings.jokes.jokeSubmissionPath).length; + submissionMeter.set(subms); + }, 1000 * 60 * 10); + } + catch(err) + { + return reject(err); + } + + return resolve(); + }); +} + +/** + * Adds a number to a meter + * @param {"req1min"|"req10mins"|"req1hour"|"reqtotal"|"submission"} meterName + * @param {Number|undefined} addValue + */ +function update(meterName, addValue) +{ + if(typeof addValue == "undefined") + addValue = 1; + + debug("Meter", `Updating meter ${meterName} - adding value ${addValue}`); + + if(typeof addValue != "number") + throw new TypeError(`meter.update(): "addValue" has wrong type "${typeof addValue}" - expected "number"`); + + switch(meterName) + { + case "req1min": + m1 += addValue; + break; + case "req10min": + m10 += addValue; + break; + case "req1hour": + h1 += addValue; + break; + case "reqtotal": + tot += addValue; + reqtotalMeter.set(tot); + break; + case "submission": + subms += addValue; + submissionMeter.set(subms); + break; + default: + throw new Error(`meter.update(): "meterName" has incorrect value`); + } + + return; +} + +module.exports = { init, update }; diff --git a/src/parseJokes.js b/src/parseJokes.js index 16241fc3..4bb8c3d0 100755 --- a/src/parseJokes.js +++ b/src/parseJokes.js @@ -1,10 +1,11 @@ // this module parses all the jokes to verify that they are valid and that their structure is not messed up -const fs = require("fs"); +const fs = require("fs-extra"); const jsl = require("svjsl"); const settings = require("../settings"); const debug = require("./verboseLogging"); +const languages = require("./languages"); const AllJokes = require("./classes/AllJokes"); /** @@ -13,77 +14,118 @@ const AllJokes = require("./classes/AllJokes"); */ const init = () => { return new Promise((resolve, reject) => { - fs.readFile(settings.jokes.jokesFilePath, (err, jokesFile) => { - if(err) - return reject(err); - - let result = []; - - try - { - jokesFile = JSON.parse(jokesFile.toString()); - } - catch(err) - { - return reject(`Error while parsing file "${settings.jokes.jokesFilePath}" as JSON: ${err}`); - } - - //#MARKER format version - if(jokesFile.info.formatVersion == settings.jokes.jokesFormatVersion) - result.push(true); - else result.push(`Joke file format version is set to "${jokesFile.info.formatVersion}" - Expected: "${settings.jokes.jokesFormatVersion}"`); - - jokesFile.jokes.forEach((joke, i) => { - //#MARKER joke ID - if(!jsl.isEmpty(joke.id) && !isNaN(parseInt(joke.id))) - result.push(true); - else result.push(`Joke with index/ID ${i} doesn't have an "id" property or it is invalid`); - - //#MARKER type and actual joke - if(joke.type == "single") - { - if(!jsl.isEmpty(joke.joke)) - result.push(true); - else result.push(`Joke with index/ID ${i} doesn't have a "joke" property`); - } - else if(joke.type == "twopart") - { - if(!jsl.isEmpty(joke.setup)) - result.push(true); - else result.push(`Joke with index/ID ${i} doesn't have a "setup" property`); - - if(!jsl.isEmpty(joke.delivery)) - result.push(true); - else result.push(`Joke with index/ID ${i} doesn't have a "delivery" property`); - } - else result.push(`Joke with index/ID ${i} doesn't have a "type" property or it is invalid`); - - //#MARKER flags - if(!jsl.isEmpty(joke.flags)) - { - if(!jsl.isEmpty(joke.flags.nsfw) || (joke.flags.nsfw !== false && joke.flags.nsfw !== true)) - result.push(true); - else result.push(`Joke with index/ID ${i} has an invalid "NSFW" flag`); - - if(!jsl.isEmpty(joke.flags.racist) || (joke.flags.racist !== false && joke.flags.racist !== true)) - result.push(true); - else result.push(`Joke with index/ID ${i} has an invalid "racist" flag`); - - if(!jsl.isEmpty(joke.flags.sexist) || (joke.flags.sexist !== false && joke.flags.sexist !== true)) - result.push(true); - else result.push(`Joke with index/ID ${i} has an invalid "sexist" flag`); - - if(!jsl.isEmpty(joke.flags.political) || (joke.flags.political !== false && joke.flags.political !== true)) - result.push(true); - else result.push(`Joke with index/ID ${i} has an invalid "political" flag`); - - if(!jsl.isEmpty(joke.flags.religious) || (joke.flags.religious !== false && joke.flags.religious !== true)) + let jokesFiles = fs.readdirSync(settings.jokes.jokesFolderPath); + let result = []; + let allJokesFilesObj = {}; + + let outerPromises = []; + + jokesFiles.forEach(jf => { + if(jf == settings.jokes.jokesTemplateFile) + return; + + outerPromises.push(new Promise((resolveOuter, rejectOuter) => { + jsl.unused(rejectOuter); + + let fileNameValid = (fileName) => { + if(!fileName.endsWith(".json")) + return false; + let spl1 = fileName.split(".json")[0]; + if(spl1.includes("-") && languages.isValidLang(spl1.split("-")[1]) && spl1.split("-")[0] == "jokes") + return true; + return false; + }; + + let getLangCode = (fileName) => { + if(!fileName.endsWith(".json")) + return false; + let spl1 = fileName.split(".json")[0]; + if(spl1.includes("-") && languages.isValidLang(spl1.split("-")[1])) + return spl1.split("-")[1].toLowerCase(); + }; + + let langCode = getLangCode(jf); + + if(!jf.endsWith(".json") || !fileNameValid(jf)) + result.push(`${jsl.colors.fg.red}Error: Invalid file "${settings.jokes.jokesFolderPath}${jf}" found. It has to follow this pattern: "jokes-xy.json"`); + + + fs.readFile(`${settings.jokes.jokesFolderPath}${jf}`, (err, jokesFile) => { + if(err) + return reject(err); + + try + { + jokesFile = JSON.parse(jokesFile.toString()); + } + catch(err) + { + return reject(`Error while parsing file "${settings.jokes.jokesFilePath}" as JSON: ${err}`); + } + + //#MARKER format version + if(jokesFile.info.formatVersion == settings.jokes.jokesFormatVersion) result.push(true); - else result.push(`Joke with index/ID ${i} has an invalid "religious" flag`); - } - else result.push(`Joke with index/ID ${i} doesn't have a "flags" object or it is invalid`); - }); + else result.push(`Joke file format version is set to "${jokesFile.info.formatVersion}" - Expected: "${settings.jokes.jokesFormatVersion}"`); + + jokesFile.jokes.forEach((joke, i) => { + //#MARKER joke ID + if(!jsl.isEmpty(joke.id) && !isNaN(parseInt(joke.id))) + result.push(true); + else result.push(`Joke with index/ID ${i} doesn't have an "id" property or it is invalid`); + + //#MARKER type and actual joke + if(joke.type == "single") + { + if(!jsl.isEmpty(joke.joke)) + result.push(true); + else result.push(`Joke with index/ID ${i} doesn't have a "joke" property`); + } + else if(joke.type == "twopart") + { + if(!jsl.isEmpty(joke.setup)) + result.push(true); + else result.push(`Joke with index/ID ${i} doesn't have a "setup" property`); + + if(!jsl.isEmpty(joke.delivery)) + result.push(true); + else result.push(`Joke with index/ID ${i} doesn't have a "delivery" property`); + } + else result.push(`Joke with index/ID ${i} doesn't have a "type" property or it is invalid`); + + //#MARKER flags + if(!jsl.isEmpty(joke.flags)) + { + if(!jsl.isEmpty(joke.flags.nsfw) || (joke.flags.nsfw !== false && joke.flags.nsfw !== true)) + result.push(true); + else result.push(`Joke with index/ID ${i} has an invalid "NSFW" flag`); + + if(!jsl.isEmpty(joke.flags.racist) || (joke.flags.racist !== false && joke.flags.racist !== true)) + result.push(true); + else result.push(`Joke with index/ID ${i} has an invalid "racist" flag`); + + if(!jsl.isEmpty(joke.flags.sexist) || (joke.flags.sexist !== false && joke.flags.sexist !== true)) + result.push(true); + else result.push(`Joke with index/ID ${i} has an invalid "sexist" flag`); + + if(!jsl.isEmpty(joke.flags.political) || (joke.flags.political !== false && joke.flags.political !== true)) + result.push(true); + else result.push(`Joke with index/ID ${i} has an invalid "political" flag`); + + if(!jsl.isEmpty(joke.flags.religious) || (joke.flags.religious !== false && joke.flags.religious !== true)) + result.push(true); + else result.push(`Joke with index/ID ${i} has an invalid "religious" flag`); + } + else result.push(`Joke with index/ID ${i} doesn't have a "flags" object or it is invalid`); + }); + + allJokesFilesObj[langCode] = jokesFile; + return resolveOuter(); + }); + })); + }); + Promise.all(outerPromises).then(() => { let errors = []; result.forEach(res => { @@ -91,11 +133,22 @@ const init = () => { errors.push(res); }); - let allJokesObj = new AllJokes(jokesFile); + let allJokesObj = new AllJokes(allJokesFilesObj); + + let formatVersions = [settings.jokes.jokesFormatVersion]; + languages.jokeLangs().map(jl => jl.code).sort().forEach(lang => { + formatVersions.push(allJokesObj.getFormatVersion(lang)); + }); + + if(!jsl.allEqual(formatVersions)) + errors.push(`One or more of the jokes files has an invalid format version`); + module.exports.allJokes = allJokesObj; module.exports.jokeCount = allJokesObj.getJokeCount(); - module.exports.jokeFormatVersion = allJokesObj.getFormatVersion(); - this.jokeFormatVersion = allJokesObj.getFormatVersion(); + module.exports.jokeCountPerLang = allJokesObj.getJokeCountPerLang(); + let fmtVer = allJokesObj.getFormatVersion("en"); + module.exports.jokeFormatVersion = fmtVer; + this.jokeFormatVersion = fmtVer; debug("JokeParser", `Done parsing jokes. Errors: ${errors.length === 0 ? jsl.colors.fg.green : jsl.colors.fg.red}${errors.length}${jsl.colors.rst}`); @@ -104,6 +157,8 @@ const init = () => { return resolve(); return reject(`Errors:\n- ${errors.join("\n- ")}`); + }).catch(err => { + return reject(err); }); }); } @@ -119,6 +174,7 @@ const init = () => { * @prop {Boolean} flags.religious Whether the joke is religiously offensive or not * @prop {Boolean} flags.political Whether the joke is politically offensive or not * @prop {Number} id The ID of the joke + * @prop {String} lang The language of the joke */ /** @@ -133,6 +189,7 @@ const init = () => { * @prop {Boolean} flags.religious Whether the joke is religiously offensive or not * @prop {Boolean} flags.political Whether the joke is politically offensive or not * @prop {Number} id The ID of the joke + * @prop {String} lang The language of the joke */ /** @@ -208,6 +265,16 @@ const validateSingle = joke => { jokeErrors.push(`Joke doesn't have the "religious" flag or it is invalid`); } else jokeErrors.push(`Joke doesn't have a "flags" object or it is invalid`); + + //#MARKER lang + if(jsl.isEmpty(joke.lang)) + jokeErrors.push(`Joke doesn't have a "lang" property or it is empty or of the wrong type`); + + let langV = languages.isValidLang(joke.lang); + if(typeof langV === "string") + jokeErrors.push(`"lang" parameter: ${langV}`); + else if(langV !== true) + jokeErrors.push(`Joke doesn't have a "lang" property or it is empty or of the wrong type`); } catch(err) { @@ -219,4 +286,4 @@ const validateSingle = joke => { else return jokeErrors; } -module.exports = { init, validateSingle } \ No newline at end of file +module.exports = { init, validateSingle } diff --git a/src/parseURL.js b/src/parseURL.js index 1a68e142..7192fd47 100755 --- a/src/parseURL.js +++ b/src/parseURL.js @@ -1,7 +1,8 @@ // this module parses the passed URL, returning an object that is uniform and easy to use +const urlParse = require("url-parse"); const jsl = require("svjsl"); -const fs = require("fs"); +const fs = require("fs-extra"); const settings = require("../settings"); @@ -24,47 +25,25 @@ const settings = require("../settings"); * @param {String} url * @returns {(ParsedUrl|ErroredParsedUrl)} */ -const parseURL = url => { - let error = null; - - let pathArr = []; - let qstrObj = {}; - +function parseURL(url) +{ try { - let rawPath = url.split("?")[0]; - let rawQstr = url.split("?")[1]; - - - if(rawPath.includes("/")) - pathArr = rawPath.split("/"); - else pathArr = [rawQstr]; - - if(pathArr.includes("v2")) - { - pathArr.forEach((itm, i) => { - if(itm == "v2") - pathArr.splice(i, 1); - }); - } + let trimFirstSlash = u2 => { + if(u2[0] == "") + u2.shift(); + return u2; + }; - pathArr.forEach((pathSection, i) => { - if(jsl.isEmpty(pathSection)) - pathArr.splice(i, 1); - }); + let parsed = urlParse(url); - // if a URL path offset was set in the settings, remove the first n elements from the path array - if(settings.httpServer.urlPathOffset > 0) - { - for(let i = 0; i < settings.httpServer.urlPathOffset; i++) - { - if(pathArr.length > 0) - pathArr.shift(); - } - } + let qstrObj = {}; + let qstrArr = []; + let rawQstr = (parsed.query == "" ? null : parsed.query); + if(rawQstr && rawQstr.startsWith("?")) + rawQstr = rawQstr.substr(1); - let qstrArr = []; if(!jsl.isEmpty(rawQstr) && rawQstr.includes("&")) qstrArr = rawQstr.split("&"); else if(!jsl.isEmpty(rawQstr)) @@ -72,6 +51,7 @@ const parseURL = url => { if(qstrArr.length > 0) + { qstrArr.forEach(qstrEntry => { if(qstrEntry.includes("=")) { @@ -79,30 +59,109 @@ const parseURL = url => { qstrObj[decodeURIComponent(splitEntry[0])] = decodeURIComponent(splitEntry[1].toLowerCase()); } }); - else qstrObj = null; - } - catch(err) - { - error = err; - } - - if(jsl.isArrayEmpty(pathArr)) - pathArr = null; + } + else + qstrObj = null; - if(!error) - return { + let retObj = { error: null, initialURL: url, - pathArray: pathArr, + pathArray: trimFirstSlash(parsed.pathname.split("/")), queryParams: qstrObj - } - else + }; + + return retObj; + } + catch(err) + { return { - error: error, + error: err.toString(), initialURL: url - } + }; + } } +// *** *** *** Still leaving legacy code in, in case something goes wrong and I need to quickly switch back *** *** *** + +// const parseURL = url => { +// let error = null; + +// let pathArr = []; +// let qstrObj = {}; + +// try +// { +// let rawPath = url.split("?")[0]; +// let rawQstr = url.split("?")[1]; + + +// if(rawPath.includes("/")) +// pathArr = rawPath.split("/"); +// else pathArr = [rawQstr]; + +// if(pathArr.includes("v2")) +// { +// pathArr.forEach((itm, i) => { +// if(itm == "v2") +// pathArr.splice(i, 1); +// }); +// } + +// pathArr.forEach((pathSection, i) => { +// if(jsl.isEmpty(pathSection)) +// pathArr.splice(i, 1); +// }); + +// // if a URL path offset was set in the settings, remove the first n elements from the path array +// if(settings.httpServer.urlPathOffset > 0) +// { +// for(let i = 0; i < settings.httpServer.urlPathOffset; i++) +// { +// if(pathArr.length > 0) +// pathArr.shift(); +// } +// } + + +// let qstrArr = []; +// if(!jsl.isEmpty(rawQstr) && rawQstr.includes("&")) +// qstrArr = rawQstr.split("&"); +// else if(!jsl.isEmpty(rawQstr)) +// qstrArr = [rawQstr]; + + +// if(qstrArr.length > 0) +// qstrArr.forEach(qstrEntry => { +// if(qstrEntry.includes("=")) +// { +// let splitEntry = qstrEntry.split("="); +// qstrObj[decodeURIComponent(splitEntry[0])] = decodeURIComponent(splitEntry[1].toLowerCase()); +// } +// }); +// else qstrObj = null; +// } +// catch(err) +// { +// error = err; +// } + +// if(jsl.isArrayEmpty(pathArr)) +// pathArr = null; + +// if(!error) +// return { +// error: null, +// initialURL: url, +// pathArray: pathArr, +// queryParams: qstrObj +// } +// else +// return { +// error: error, +// initialURL: url +// } +// } + const getFileFormatFromQString = qstrObj => { if(!jsl.isEmpty(qstrObj.format)) { @@ -130,4 +189,4 @@ const getMimeTypeFromFileFormatString = fileFormatString => { module.exports = parseURL; module.exports.getFileFormatFromQString = getFileFormatFromQString; -module.exports.getMimeTypeFromFileFormatString = getMimeTypeFromFileFormatString; \ No newline at end of file +module.exports.getMimeTypeFromFileFormatString = getMimeTypeFromFileFormatString; diff --git a/src/translate.js b/src/translate.js new file mode 100644 index 00000000..2c032d01 --- /dev/null +++ b/src/translate.js @@ -0,0 +1,85 @@ +const fs = require("fs-extra"); +const jsl = require("svjsl"); + +const debug = require("./verboseLogging") +const settings = require("../settings"); + + +var trFile = {}; + +/** + * Initializes the translation module by caching the translations so they only need to be read from disk once + * @returns {Promise} + */ +function init() +{ + debug("Translate", `Initializing - loading translations from "${settings.languages.translationsFile}"`); + return new Promise((resolve, reject) => { + fs.readFile(settings.languages.translationsFile, (err, res) => { + if(err) + return reject(`Error while reading translations file: ${err}`); + else + { + trFile = JSON.parse(res.toString()); + debug("Translate", `Found ${Object.keys(trFile.tr).length} translations`); + return resolve(); + } + }); + }); +} + +/** + * Returns the translation of a sentence of a specified language. + * @param {String} lang Language code + * @param {String} id The name of the translation node + * @param {...any} args Arguments to replace numbered %-placeholders with. Only use objects that are strings or convertable to them with `.toString()`! + * @returns {String|null} Returns `null` if no translation is available. Else returns a string + */ +function translate(lang, id, ...args) +{ + if(!lang) + lang = settings.languages.defaultLanguage; + + let langTr = trFile.tr[id]; + if(!langTr) + return null; + + let translation = langTr[lang.toString().toLowerCase()].toString(); + if(!translation) + return null; + + if(Array.isArray(args) && translation.includes("%")) + { + args.forEach((arg, i) => { + let rex = new RegExp(`%${i + 1}`); + if(translation.match(rex)) + { + try + { + translation = translation.replace(rex, arg.toString()); + } + catch(err) + { + jsl.unused(err); + } + } + }); + } + + debug("Translate", `Translating ${id} into ${lang} - result: ${translation}`); + + return translation; +} + +/** + * Returns a list of system languages (2 char code) + * @returns {Array} + */ +function systemLangs() +{ + return trFile.languages; +} + +module.exports = translate; +module.exports.init = init; +module.exports.systemLangs = systemLangs; diff --git a/src/verboseLogging.js b/src/verboseLogging.js index 50f16b39..3cb823b1 100755 --- a/src/verboseLogging.js +++ b/src/verboseLogging.js @@ -10,11 +10,12 @@ const col = jsl.colors; * @param {String} section * @param {String} message */ -const verboseLogging = (section, message) => { - if(jsl.isEmpty(settings.debug.verboseLogging) || settings.debug.verboseLogging === false) +function verboseLogging(section, message) +{ + if(settings.debug.verboseLogging !== true) return; console.log(`${col.fg.yellow}[DBG/${col.rst}${col.fg.blue}${section}${col.rst}${col.fg.yellow}]${col.rst} - ${message}`); } -module.exports = verboseLogging; \ No newline at end of file +module.exports = verboseLogging; diff --git a/tests/joke-endpoint.js b/tests/joke-endpoint.js new file mode 100644 index 00000000..61f91ceb --- /dev/null +++ b/tests/joke-endpoint.js @@ -0,0 +1,28 @@ +/* eslint-disable */ + +const settings = require("../settings"); + +const meta = { + name: "Joke", + category: "Endpoints", + endpointURL: "/joke" +}; + +/** + * @typedef {Object} UnitTestResult + * @prop {String} name + * @prop {Boolean} success + */ + +/** + * Runs this unit test + * @returns {Promise, String>} + */ +function run() +{ + return new Promise((resolve, reject) => { + + }); +} + +module.exports = { meta, run }; diff --git a/tools/add-joke.js b/tools/add-joke.js index 9efd751d..7a5dcc9c 100755 --- a/tools/add-joke.js +++ b/tools/add-joke.js @@ -1,121 +1,137 @@ const jsl = require("svjsl"); const readline = require("readline"); const settings = require("../settings"); -const fs = require("fs"); +const fs = require("fs-extra"); +const { join } = require("path"); +const jokeSubmission = require("../src/jokeSubmission"); +const languages = require("../src/languages"); -const init = () => { +const init = async () => { let joke = {}; + if(!process.stdin.isTTY) + { + console.log(`${jsl.colors.fg.red}Error: process doesn't have a stdin to read from${jsl.colors.rst}`); + process.exit(1); + } + process.stdin.setRawMode(true); - getJokeCategory().then(cat => { - joke["category"] = cat; - getJokeType().then(type => { - joke["type"] = type; + await languages.init(); - let rl = readline.createInterface(process.stdin, process.stdout); - rl.pause(); + joke["category"] = await getJokeCategory(); + joke["type"] = await getJokeType(); + let jokeLang = await getJokeLang(); - let contFlags = () => { - process.stdout.write("\n"); - - joke["flags"] = {}; - let allFlags = settings.jokes.possible.flags; - - let flagIteration = idx => { - if(idx >= allFlags.length) - return flagIterFinished(); - else - { - jsl.pause(`Is this joke ${allFlags[idx]}? (y/N):`).then(key => { - if(key.toLowerCase() == "y") - joke["flags"][allFlags[idx]] = true; - else joke["flags"][allFlags[idx]] = false; - - return flagIteration(++idx); - }).catch(err => { - console.error(`Error: ${err}`); - return process.exit(1); - }); - } - }; - - let flagIterFinished = () => { - - fs.readFile(settings.jokes.jokesFilePath, (err, res) => { - if(!err) - { - let jokeFile = JSON.parse(res.toString()); + let rl = readline.createInterface(process.stdin, process.stdout); + rl.pause(); - joke["id"] = jokeFile.jokes.length; + let contFlags = () => { + process.stdout.write("\n"); - jokeFile.jokes.push(joke); + joke["flags"] = {}; + let allFlags = settings.jokes.possible.flags; - fs.writeFile(settings.jokes.jokesFilePath, JSON.stringify(jokeFile, null, 4), (err) => { - if(err) - { - console.log(`${jsl.colors.fg.red}\n${err}${jsl.colors.rst}\n\n`); - process.exit(1); - } - else + let flagIteration = idx => { + if(idx >= allFlags.length) + return flagIterFinished(); + else + { + jsl.pause(`Is this joke ${allFlags[idx]}? (y/N):`).then(key => { + if(key.toLowerCase() == "y") + joke["flags"][allFlags[idx]] = true; + else joke["flags"][allFlags[idx]] = false; + + return flagIteration(++idx); + }).catch(err => { + console.error(`Error: ${err}`); + return process.exit(1); + }); + } + }; + + let jokesFileName = `jokes-${jokeLang}.json`; + + let flagIterFinished = () => { + let fPath = join(settings.jokes.jokesFolderPath, jokesFileName); + + if(!fs.existsSync(fPath)) + fs.copySync(join(settings.jokes.jokesFolderPath, settings.jokes.jokesTemplateFile), fPath); + + fs.readFile(fPath, (err, res) => { + if(!err) + { + let jokeFile = JSON.parse(res.toString()); + + joke = jokeSubmission.reformatJoke(joke); + + joke["id"] = jokeFile.jokes.length || 0; + + jokeFile.jokes.push(joke); + + fs.writeFile(fPath, JSON.stringify(jokeFile, null, 4), (err) => { + if(err) + { + console.log(`${jsl.colors.fg.red}\n${err}${jsl.colors.rst}\n\n`); + process.exit(1); + } + else + { + console.clear(); + console.log(`${jsl.colors.fg.green}\nJoke was successfully added to file "${jokesFileName}":${jsl.colors.rst}\n\n${JSON.stringify(joke, null, 4)}\n\n\n`); + + jsl.pause("Add another joke? (y/N): ").then(key => { + if(key.toLowerCase() === "y") { console.clear(); - console.log(`${jsl.colors.fg.green}\nJoke was successfully added:${jsl.colors.rst}\n\n${JSON.stringify(joke, null, 4)}\n\n\n`); - - jsl.pause("Add another joke? (y/N): ").then(key => { - if(key.toLowerCase() === "y") - { - console.clear(); - return init(); - } - else return process.exit(0); - }).catch(err => { - console.error(`Error: ${err}`); - return process.exit(1); - }); + return init(); } + else return process.exit(0); + }).catch(err => { + console.error(`Error: ${err}`); + return process.exit(1); }); } - else - { - console.log(`${jsl.colors.fg.red}\n${err}${jsl.colors.rst}\n\n`); - process.exit(1); - } }); } + else + { + console.log(`${jsl.colors.fg.red}\n${err}${jsl.colors.rst}\n\n`); + process.exit(1); + } + }); + } - return flagIteration(0); - }; + return flagIteration(0); + }; - console.log(`Use "\\n" for a line break. Special characters like double quotes do not need to be escaped.\n`); + console.log(`Use "\\n" for a line break. Special characters like double quotes do not need to be escaped.\n`); - if(type != "twopart") - { - rl.resume(); - rl.question("Enter Joke: ", jokeText => { - rl.pause(); - joke["joke"] = jokeText.replace(/\\n/gm, "\n"); + if(joke["type"] != "twopart") + { + rl.resume(); + rl.question("Enter Joke: ", jokeText => { + rl.pause(); + joke["joke"] = jokeText.replace(/\\n/gm, "\n"); - return contFlags(); - }); - } - else - { - rl.resume(); - rl.question("Enter Joke Setup: ", jokeSetup => { - rl.question("Enter Joke Delivery: ", jokeDelivery => { - rl.pause(); - joke["setup"] = jokeSetup.replace(/\\n/gm, "\n"); - joke["delivery"] = jokeDelivery.replace(/\\n/gm, "\n"); - - return contFlags(); - }); - }); - } + return contFlags(); }); - }); + } + else + { + rl.resume(); + rl.question("Enter Joke Setup: ", jokeSetup => { + rl.question("Enter Joke Delivery: ", jokeDelivery => { + rl.pause(); + joke["setup"] = jokeSetup.replace(/\\n/gm, "\n"); + joke["delivery"] = jokeDelivery.replace(/\\n/gm, "\n"); + + return contFlags(); + }); + }); + } }; const getJokeCategory = () => { @@ -166,4 +182,34 @@ const getJokeType = () => { }); }; -init(); \ No newline at end of file +function getJokeLang() +{ + return new Promise(resolve => { + let langRL = readline.createInterface(process.stdin, process.stdout); + + let tryGetLang = () => { + langRL.resume(); + langRL.question("Enter two-character language code (en): ", ans => { + langRL.pause(); + + if(!ans) + ans = settings.languages.defaultLanguage; + + ans = ans.toString().toLowerCase(); + + if(languages.isValidLang(ans) === true) + return resolve(ans); + else + { + console.clear(); + console.log(`\n${jsl.colors.fg.red}Invalid lang code!${jsl.colors.rst}\n\n`); + return tryGetLang(); + } + }); + } + + return tryGetLang(); + }); +} + +init(); diff --git a/tools/add-token.js b/tools/add-token.js index a78c06a0..d049dc84 100755 --- a/tools/add-token.js +++ b/tools/add-token.js @@ -1,5 +1,5 @@ const jsl = require("svjsl"); -const fs = require("fs"); +const fs = require("fs-extra"); const settings = require("../settings"); try diff --git a/tools/reassign-ids.js b/tools/reassign-ids.js index b5b96885..80058dfd 100755 --- a/tools/reassign-ids.js +++ b/tools/reassign-ids.js @@ -1,40 +1,54 @@ // this reassigns all jokes' IDs. Always run this after something changes in the joke's order // run this with the command "npm run reassign-ids" +const { resolve, join } = require("path"); +const fs = require("fs-extra"); +const settings = require("../settings"); + try { - const fs = require("fs"); - const settings = require("../settings"); + console.log(`\nReassigning joke IDs in files in path "${settings.jokes.jokesFolderPath}"...`); - console.log(`\nReassigning joke IDs in file "${settings.jokes.jokesFilePath}"...`); + let totalReassignedFiles = 0; + let totalReassignedIDs = 0; - let initialJokes = JSON.parse(fs.readFileSync(settings.jokes.jokesFilePath).toString()).jokes; - let initialInfo = JSON.parse(fs.readFileSync(settings.jokes.jokesFilePath).toString()).info; + fs.readdirSync(settings.jokes.jokesFolderPath).forEach(fName => { + if(fName.startsWith("template")) + return; - let reassignedJokes = []; + totalReassignedFiles++; - if(initialInfo.formatVersion != 2) - initialInfo.formatVersion = 2; + let fPath = resolve(join(settings.jokes.jokesFolderPath, fName)); + let jokeFileObj = JSON.parse(fs.readFileSync(fPath).toString()); - initialJokes.forEach((joke, i) => { - let rJoke = joke; - rJoke.id = i; - reassignedJokes.push(rJoke); - }); + let initialJokes = jokeFileObj.jokes; + let initialInfo = jokeFileObj.info; + + let reassignedJokes = []; - let doneFile = { - info: initialInfo, - jokes: reassignedJokes - }; + if(initialInfo.formatVersion != settings.jokes.jokesFormatVersion) + initialInfo.formatVersion = settings.jokes.jokesFormatVersion; - fs.writeFileSync(settings.jokes.jokesFilePath, JSON.stringify(doneFile, null, 4)); + initialJokes.forEach((joke, i) => { + let rJoke = joke; + rJoke.id = i; + reassignedJokes.push(rJoke); + totalReassignedIDs++; + }); + let doneFile = { + info: initialInfo, + jokes: reassignedJokes + }; - console.log(`\x1b[32m\x1b[1mDone reassigning IDs of all ${reassignedJokes.length} jokes.\n\x1b[0m`); + fs.writeFileSync(fPath, JSON.stringify(doneFile, null, 4)); + }); + + console.log(`\x1b[32m\x1b[1mDone reassigning IDs of all ${totalReassignedFiles} files (${totalReassignedIDs} reassigned joke IDs).\n\x1b[0m`); process.exit(0); } catch(err) { console.log(`\n\n\x1b[31m\x1b[1m>> Error while reassigning joke IDs:\n${err}\n\n\x1b[0m`); process.exit(1); -} \ No newline at end of file +} diff --git a/tools/reformat.js b/tools/reformat.js index 7e7f4905..d98f3460 100755 --- a/tools/reformat.js +++ b/tools/reformat.js @@ -1,12 +1,11 @@ // this reformats jokes from the old <1.1.3 format to the new 2.0.0 format // run this with the command "npm run reformat" +const fs = require("fs-extra"); +const isEmpty = require("svjsl").isEmpty; + try { - const fs = require("fs"); - const isEmpty = require("svjsl").isEmpty; - - console.log(`\nReformatting jokes from file "./data/jokes.json" to new format and putting it in file "./data/jokes_new.json"...`); let initialJokes = JSON.parse(fs.readFileSync("./data/jokes.json").toString()); @@ -62,4 +61,4 @@ catch(err) { console.log(`\n\n\x1b[31m\x1b[1m>> Error while reformatting jokes:\n${err}\n\n\x1b[0m`); process.exit(1); -} \ No newline at end of file +} diff --git a/tools/submissions.js b/tools/submissions.js index 718560c4..d19db9e6 100755 --- a/tools/submissions.js +++ b/tools/submissions.js @@ -1,10 +1,14 @@ const settings = require("../settings"); -const fs = require("fs"); -const path = require("path"); +const fs = require("fs-extra"); +const { resolve, join } = require("path"); const jsl = require("svjsl"); +const parseJokes = require("../src/parseJokes"); +const jokeSubmission = require("../src/jokeSubmission"); +jsl.unused(parseJokes); let addedCount = 0; -let jokesFile = getAllJokes(); +let jokesFiles = getAllJokes(); + const run = () => { let submissions = getSubmissions(); @@ -26,17 +30,15 @@ const run = () => { let goThroughSubmission = (idx) => { if(!submissions[idx]) - { - finishAdding(); - console.log(`${jsl.colors.fg.green}Successfully added ${jsl.colors.fg.yellow}${addedCount}${jsl.colors.fg.green} joke${addedCount != 1 ? "s" : ""}${jsl.colors.rst}.\nExiting.\n\n`); - return process.exit(0); - } + return finishAdding(); let submission = submissions[idx]; if(submission.formatVersion != settings.jokes.jokesFormatVersion) console.error(`${jsl.colors.fg.red}Error: Format version is incorrect${jsl.colors.rst}`); + console.log(`${jsl.colors.fg.yellow}Language:${jsl.colors.rst} ${submission.lang}`); + if(submission.type == "single") { console.log(`${jsl.colors.fg.yellow}Joke:${jsl.colors.rst} ${submission.joke}`); @@ -73,12 +75,31 @@ const run = () => { }; /** - * Reads the jokes file and returns it as an object - * @returns {Object} + * @typedef {Object} AllJokesObj + * @prop {Object} [en] + * @prop {Object} en.info + * @prop {String} en.info.formatVersion + * @prop {Array|Array} en.jokes + */ + +/** + * Reads the jokes files and returns it as an object + * @returns {AllJokesObj} */ function getAllJokes() { - return JSON.parse(fs.readFileSync(settings.jokes.jokesFilePath).toString()); + let retObj = {}; + fs.readdirSync(settings.jokes.jokesFolderPath).forEach(jokesFile => { + if(jokesFile.startsWith("template")) + return; + + let langCode = jokesFile.split("-")[1].substr(0, 2); + let filePath = resolve(join(settings.jokes.jokesFolderPath, jokesFile)); + + retObj[langCode] = JSON.parse(fs.readFileSync(filePath).toString()); + }); + + return retObj; } /** @@ -86,22 +107,42 @@ function getAllJokes() * @param {Object} joke */ const addJoke = joke => { - let fJoke = new Object(joke); + let fJoke = JSON.parse(JSON.stringify(joke)); // reserialize because call by reference :( delete fJoke.formatVersion; + delete fJoke.lang; - jokesFile.jokes.push(fJoke); - addedCount++; + // jokesFile.jokes.push(fJoke); + if(!jokesFiles[joke.lang]) + jokesFiles[joke.lang] = JSON.parse(fs.readFileSync(resolve(join(settings.jokes.jokesFolderPath, settings.jokes.jokesTemplateFile))).toString()); + + Object.keys(jokesFiles).forEach(langCode => { + if(joke.lang == langCode) + { + jokesFiles[langCode].jokes.push(jokeSubmission.reformatJoke(fJoke)); + addedCount++; + } + }); }; /** - * Writes the `jokesFile` object to the jokes file + * Writes the `jokesFiles` object to the jokes file */ const finishAdding = () => { - fs.writeFileSync(settings.jokes.jokesFilePath, JSON.stringify(jokesFile, null, 4)); + Object.keys(jokesFiles).forEach(langCode => { + fs.writeFileSync(resolve(join(settings.jokes.jokesFolderPath, `jokes-${langCode}.json`)), JSON.stringify(jokesFiles[langCode], null, 4)); + }); - fs.readdirSync(settings.jokes.jokeSubmissionPath).forEach(file => { - fs.unlinkSync(path.join(settings.jokes.jokeSubmissionPath, file)); + jsl.pause(`Delete all submissions? (Y/n):`).then(val => { + if(val.toLowerCase() != "n") + { + fs.readdirSync(settings.jokes.jokeSubmissionPath).forEach(folder => { + fs.removeSync(join(settings.jokes.jokeSubmissionPath, folder)); + }); + } + + console.log(`${jsl.colors.fg.green}Successfully added ${jsl.colors.fg.yellow}${addedCount}${jsl.colors.fg.green} joke${addedCount != 1 ? "s" : ""}${jsl.colors.rst}.\nExiting.\n\n`); + return process.exit(0); }); }; @@ -130,10 +171,17 @@ const getFlags = joke => { */ const getSubmissions = () => { let submissions = []; - fs.readdirSync(settings.jokes.jokeSubmissionPath).forEach(file => { - submissions.push(JSON.parse(fs.readFileSync(path.resolve(`${settings.jokes.jokeSubmissionPath}/${file}`)).toString())); + fs.readdirSync(settings.jokes.jokeSubmissionPath).forEach(lang => { + fs.readdirSync(resolve(join(settings.jokes.jokeSubmissionPath, lang))).forEach(file => { + submissions.push(JSON.parse(fs.readFileSync(resolve(`${settings.jokes.jokeSubmissionPath}/${lang}/${file}`)).toString())); + }); }); return submissions; }; -run(); \ No newline at end of file +if(!process.stdin.isTTY) +{ + console.log(`${jsl.colors.fg.red}Error: process doesn't have a stdin to read from${jsl.colors.rst}`); + process.exit(1); +} +else run(); diff --git a/tools/test.js b/tools/test.js new file mode 100644 index 00000000..adadfd59 --- /dev/null +++ b/tools/test.js @@ -0,0 +1,16 @@ +/* eslint-disable */ + +const settings = require("../settings"); + + +function getAllTests() +{ + +} + +function runAllTests() +{ + let tests = getAllTests(); +} + +runAllTests(); diff --git a/tools/validate-ids.js b/tools/validate-ids.js index 9920ecd2..9e18ef91 100755 --- a/tools/validate-ids.js +++ b/tools/validate-ids.js @@ -1,6 +1,10 @@ // this validates all jokes' IDs. This will be run through the CI to make sure the IDs are correct // run this with the command "npm run reassign-ids" +const { resolve, join } = require("path"); +const fs = require("fs-extra"); +const settings = require("../settings"); + const exitWithError = (msg, err) => { console.log(`\n\n\x1b[31m\x1b[1m>> ${msg}:\n${err}\n\n\x1b[0m`); process.exit(1); @@ -8,46 +12,63 @@ const exitWithError = (msg, err) => { try { - const fs = require("fs"); - const settings = require("../settings"); + console.log(`\nValidating joke IDs in files in "${settings.jokes.jokesFolderPath}"...`); + + let validatedFiles = 0; + let notOk = 0; + + fs.readdirSync(settings.jokes.jokesFolderPath).forEach(fName => { + if(fName.startsWith("template")) + return; + + let langCode = fName.split("-")[1].substr(0, 2); - console.log(`\nValidating joke IDs in file "${settings.jokes.jokesFilePath}"...`); + let filePath = resolve(join(settings.jokes.jokesFolderPath, fName)); - let initialJokes = JSON.parse(fs.readFileSync(settings.jokes.jokesFilePath).toString()).jokes; - let initialInfo = JSON.parse(fs.readFileSync(settings.jokes.jokesFilePath).toString()).info; + let jokeFileObj = JSON.parse(fs.readFileSync(filePath).toString()); + let initialJokes = jokeFileObj.jokes; + let initialInfo = jokeFileObj.info; + + if(initialInfo.formatVersion != settings.jokes.jokesFormatVersion) + return exitWithError("Error while checking format version", `Format version in file "${filePath}" (version ${initialInfo.formatVersion}) is different from the one being currently used in JokeAPI (${settings.jokes.jokesFormatVersion})`); + + let erroredJokes = []; + + initialJokes.forEach((joke, i) => { + if(joke.id != i) + erroredJokes.push({joke: joke, idx: i}); + }); - if(initialInfo.formatVersion != settings.jokes.jokesFormatVersion) - return exitWithError("Error while checking format version", `Format version in file "${settings.jokes.jokesFilePath}" (version ${initialInfo.formatVersion}) is different from the one being currently used in JokeAPI (${settings.jokes.jokesFormatVersion})`); + validatedFiles++; - let erroredJokes = []; + if(erroredJokes.length != 0) + { + console.log(`\n\n\x1b[31m\x1b[1mInvalid joke ID${erroredJokes.length > 1 ? "s" : ""} found:\x1b[0m\n`); + erroredJokes.forEach(errjoke => { + let jokeContent = ""; + if(errjoke.joke.type == "single") + jokeContent = errjoke.joke.joke.replace(/\n/gm, "\\n"); + else if(errjoke.joke.type == "twopart") + jokeContent = `${errjoke.joke.setup.replace(/\n/gm, "\\n")} - ${errjoke.joke.delivery.replace(/\n/gm, "\\n")}`; - initialJokes.forEach((joke, i) => { - if(joke.id != i) - erroredJokes.push({joke: joke, idx: i}); + console.log(`#${errjoke.joke.id} | ${langCode} | ${errjoke.joke.category} | ${jokeContent} (Expected ID #${errjoke.idx} - joke instead has #${errjoke.joke.id})`); + notOk++; + }); + } }); - if(erroredJokes.length == 0) + if(notOk > 0) { - console.log(`\x1b[32m\x1b[1mDone validating IDs of all ${initialJokes.length} jokes.\n\x1b[0m`); - process.exit(0); + console.log(`\n\x1b[33m\x1b[1mYou can run the command "npm run reassign-ids" to correct all joke IDs\n\x1b[0m`); + process.exit(1); } else { - console.log(`\n\n\x1b[31m\x1b[1mInvalid joke ID${erroredJokes.length > 1 ? "s" : ""} found:\x1b[0m\n`); - erroredJokes.forEach(errjoke => { - let jokeContent = ""; - if(errjoke.joke.type == "single") - jokeContent = errjoke.joke.joke.replace(/\n/gm, "\\n"); - else if(errjoke.joke.type == "twopart") - jokeContent = `${errjoke.joke.setup.replace(/\n/gm, "\\n")} - ${errjoke.joke.delivery.replace(/\n/gm, "\\n")}`; - - console.log(`> #${errjoke.joke.id} | ${errjoke.joke.category} | ${jokeContent} (Expected ID #${errjoke.idx} - joke instead has #${errjoke.joke.id})`); - }); - console.log(`\n\x1b[33m\x1b[1mYou can run the command "npm run reassign-ids" to correct all joke IDs\n\x1b[0m`); - process.exit(1); + console.log(`\x1b[32m\x1b[1mDone validating IDs of all ${validatedFiles} files.\n\x1b[0m`); + process.exit(0); } } catch(err) { return exitWithError("General error while validating joke IDs", err); -} \ No newline at end of file +} diff --git a/tools/validate-jokes.js b/tools/validate-jokes.js index d2f3c591..876be881 100755 --- a/tools/validate-jokes.js +++ b/tools/validate-jokes.js @@ -1,12 +1,16 @@ const jsl = require("svjsl"); const settings = require("../settings"); -console.log(`\nValidating jokes in "${settings.jokes.jokesFilePath}"...\n`); - -require("../src/parseJokes").init().then(() => { - console.log(`\n${jsl.colors.fg.green}All jokes are valid.${jsl.colors.rst}\n`); - process.exit(0); -}).catch(err => { - console.log(`\n${jsl.colors.fg.red}${err}${jsl.colors.rst}\n`); - process.exit(1); -}); \ No newline at end of file + + +console.log(`\nValidating jokes-xy.json files in "${settings.jokes.jokesFolderPath}"...\n`); + +require("../src/translate").init().then(() => { + require("../src/parseJokes").init().then(() => { + console.log(`${jsl.colors.fg.green}All jokes are valid.${jsl.colors.rst}\n`); + process.exit(0); + }).catch(err => { + console.log(`${jsl.colors.fg.red}${err}${jsl.colors.rst}\n`); + process.exit(1); + }); +});

Code Name