From d7930dd1d3aff36a9c3b1dc4d955be078a7006d3 Mon Sep 17 00:00:00 2001 From: ADAMJR Date: Sun, 22 Aug 2021 19:25:02 +0100 Subject: [PATCH] Migrate Old Dashboard to New --- CREDITS.md | 3 + backend/.gitignore | 6 +- backend/.vscode/launch.json | 20 + backend/.vscode/settings.json | 12 + backend/.vscode/tasks.json | 18 + backend/README.md | 73 +- backend/assets/avatars/avatar_aqua.png | Bin backend/assets/avatars/avatar_coffee.png | Bin backend/assets/avatars/avatar_fire.png | Bin backend/assets/avatars/avatar_gold.png | Bin backend/assets/avatars/avatar_grey.png | Bin backend/assets/avatars/avatar_rainbow.png | Bin backend/assets/avatars/avatar_sky.png | Bin backend/assets/avatars/avatar_tree.png | Bin backend/assets/avatars/bot.png | Bin backend/assets/avatars/unknown.png | Bin backend/package-lock.json | 12738 ++++++++++++++-- backend/package.json | 82 +- backend/src/api/modules/api-error.ts | 16 + .../src/api/modules/email/email-functions.ts | 36 + backend/src/api/modules/email/email.ts | 56 + .../src/api/modules/email/templates/email.css | 101 + .../email/templates/forgot-password.pug | 32 + .../api/modules/email/templates/includes.pug | 2 + .../modules/email/templates/verify-email.pug | 24 + .../api/modules/email/templates/verify.pug | 23 + backend/src/api/modules/email/verification.ts | 45 + backend/src/api/modules/middleware.ts | 82 + backend/src/api/modules/rate-limiter.ts | 9 + backend/src/api/modules/ws-guard.ts | 97 + backend/src/api/routes/api-routes.ts | 9 + backend/src/api/routes/auth-routes.ts | 107 + backend/src/api/routes/channel-routes.ts | 66 + backend/src/api/routes/dev-routes.ts | 81 + backend/src/api/routes/guilds-routes.ts | 47 + backend/src/api/routes/invites-routes.ts | 11 + backend/src/api/routes/users-routes.ts | 82 + backend/src/api/server.ts | 89 + .../api/websocket/modules/session-manager.ts | 18 + .../src/api/websocket/modules/ws-cooldowns.ts | 49 + backend/src/api/websocket/modules/ws-rooms.ts | 54 + backend/src/api/websocket/websocket.ts | 72 + .../src/api/websocket/ws-events/add-friend.ts | 81 + .../api/websocket/ws-events/channel-create.ts | 42 + .../api/websocket/ws-events/channel-delete.ts | 32 + .../src/api/websocket/ws-events/disconnect.ts | 44 + .../api/websocket/ws-events/guild-create.ts | 31 + .../api/websocket/ws-events/guild-delete.ts | 42 + .../websocket/ws-events/guild-member-add.ts | 60 + .../ws-events/guild-member-remove.ts | 56 + .../ws-events/guild-member-update.ts | 57 + .../websocket/ws-events/guild-role-create.ts | 34 + .../websocket/ws-events/guild-role-delete.ts | 31 + .../websocket/ws-events/guild-role-update.ts | 30 + .../api/websocket/ws-events/guild-update.ts | 56 + .../api/websocket/ws-events/invite-create.ts | 29 + .../api/websocket/ws-events/invite-delete.ts | 31 + .../api/websocket/ws-events/message-create.ts | 49 + .../api/websocket/ws-events/message-delete.ts | 46 + .../api/websocket/ws-events/message-update.ts | 50 + backend/src/api/websocket/ws-events/ready.ts | 47 + .../api/websocket/ws-events/remove-friend.ts | 50 + .../api/websocket/ws-events/typing-start.ts | 19 + .../api/websocket/ws-events/user-update.ts | 32 + .../src/api/websocket/ws-events/ws-event.ts | 12 + backend/src/app.ts | 35 +- backend/src/data/channels.ts | 70 + backend/src/data/data-utils.ts | 10 - backend/src/data/db-wrapper.ts | 9 + backend/src/data/guild-members.ts | 50 + backend/src/data/guilds.ts | 65 + backend/src/data/invites.ts | 23 + backend/src/data/messages.ts | 54 + backend/src/data/models/application.ts | 47 + backend/src/data/models/channel.ts | 94 +- backend/src/data/models/guild-member.ts | 37 + backend/src/data/models/guild.ts | 80 +- backend/src/data/models/invite.ts | 63 +- backend/src/data/models/message.ts | 52 +- backend/src/data/models/role.ts | 62 + backend/src/data/models/user.ts | 144 +- backend/src/data/pings.ts | 16 + backend/src/data/roles.ts | 45 + backend/src/data/snowflake-entity.ts | 49 + backend/src/data/types/entity-types.ts | 206 + backend/src/data/types/env.ts | 15 + backend/src/data/types/ws-types.ts | 441 + backend/src/data/users.ts | 180 + backend/src/rest/apply-middleware.ts | 19 - backend/src/rest/apply-routes.ts | 85 - backend/src/rest/middleware.ts | 25 - backend/src/rest/routes/auth-routes.ts | 43 - backend/src/rest/server.ts | 15 - backend/src/system/bot.ts | 46 + backend/src/system/ws-service.ts | 29 + backend/src/types | 1 - backend/src/utils/deps.ts | 26 +- backend/src/utils/generate-invite.ts | 7 - backend/src/utils/log.ts | 25 + backend/src/utils/snowflake.ts | 23 - backend/src/utils/utils.ts | 52 + backend/src/ws/events/channel-create.ts | 37 - backend/src/ws/events/channel-delete.ts | 24 - backend/src/ws/events/disconnect.ts | 11 - backend/src/ws/events/guild-create.ts | 37 - backend/src/ws/events/guild-delete.ts | 43 - backend/src/ws/events/guild-member-add.ts | 59 - backend/src/ws/events/guild-member-remove.ts | 42 - backend/src/ws/events/guild-update.ts | 27 - backend/src/ws/events/invite-create.ts | 30 - backend/src/ws/events/message-create.ts | 20 - backend/src/ws/events/message-delete.ts | 31 - backend/src/ws/events/message-update.ts | 35 - backend/src/ws/events/ready.ts | 30 - backend/src/ws/events/typing-start.ts | 16 - backend/src/ws/events/user-delete.ts | 29 - backend/src/ws/events/user-update.ts | 29 - backend/src/ws/events/ws-event.ts | 8 - backend/src/ws/session-manager.ts | 9 - backend/src/ws/websocket.ts | 61 - backend/test/integration/_test-template.ts | 44 + .../integration/routes/auth-routes.tests.ts | 210 + .../routes/channel-routes.tests.ts | 82 + .../integration/routes/guilds-routes.tests.ts | 99 + .../routes/invites-routes.tests.ts | 49 + .../test/integration/ws/add-friend.tests.ts | 119 + .../integration/ws/channel-create.tests.ts | 68 + .../test/integration/ws/guild-create.tests.ts | 45 + .../test/integration/ws/guild-delete.tests.ts | 96 + .../integration/ws/guild-member-add.tests.ts | 105 + .../ws/guild-member-remove.tests.ts | 79 + .../ws/guild-member-update.tests.ts | 107 + .../test/integration/ws/guild-update.tests.ts | 125 + .../integration/ws/invite-create.tests.ts | 41 + .../integration/ws/invite-delete.tests.ts | 65 + .../integration/ws/message-create.tests.ts | 72 + .../integration/ws/message-delete.tests.ts | 93 + .../integration/ws/message-update.tests.ts | 64 + backend/test/integration/ws/ready.tests.ts | 124 + .../integration/ws/remove-friend.tests.ts | 62 + .../test/integration/ws/user-update.tests.ts | 84 + backend/test/integration/ws/ws-guard.tests.ts | 231 + backend/test/mock/email-mock.ts | 9 + backend/test/mock/mock.ts | 207 + backend/test/test-utils.ts | 15 + backend/test/test.ts | 63 + backend/test/unit/models/application.tests.ts | 35 + backend/test/unit/models/channel.tests.ts | 43 + .../test/unit/models/guild-member.tests.ts | 29 + backend/test/unit/models/guild.tests.ts | 34 + backend/test/unit/models/invite.tests.ts | 37 + backend/test/unit/models/message.tests.ts | 41 + backend/test/unit/models/role.tests.ts | 37 + backend/test/unit/models/user.tests.ts | 78 + backend/test/unit/snowflake-entity.tests.ts | 31 + backend/test/unit/ws/ws-cooldowns.tests.ts | 44 + backend/tsconfig.json | 93 +- .../src/components/pages/page-wrapper.tsx | 3 +- frontend/src/store/services/ws.ts | 8 +- types/entity.d.ts | 1 + types/patterns.ts | 9 + 161 files changed, 18807 insertions(+), 2016 deletions(-) create mode 100644 CREDITS.md create mode 100644 backend/.vscode/launch.json create mode 100644 backend/.vscode/settings.json create mode 100644 backend/.vscode/tasks.json mode change 100755 => 100644 backend/assets/avatars/avatar_aqua.png mode change 100755 => 100644 backend/assets/avatars/avatar_coffee.png mode change 100755 => 100644 backend/assets/avatars/avatar_fire.png mode change 100755 => 100644 backend/assets/avatars/avatar_gold.png mode change 100755 => 100644 backend/assets/avatars/avatar_grey.png mode change 100755 => 100644 backend/assets/avatars/avatar_rainbow.png mode change 100755 => 100644 backend/assets/avatars/avatar_sky.png mode change 100755 => 100644 backend/assets/avatars/avatar_tree.png mode change 100755 => 100644 backend/assets/avatars/bot.png mode change 100755 => 100644 backend/assets/avatars/unknown.png create mode 100644 backend/src/api/modules/api-error.ts create mode 100644 backend/src/api/modules/email/email-functions.ts create mode 100644 backend/src/api/modules/email/email.ts create mode 100644 backend/src/api/modules/email/templates/email.css create mode 100644 backend/src/api/modules/email/templates/forgot-password.pug create mode 100644 backend/src/api/modules/email/templates/includes.pug create mode 100644 backend/src/api/modules/email/templates/verify-email.pug create mode 100644 backend/src/api/modules/email/templates/verify.pug create mode 100644 backend/src/api/modules/email/verification.ts create mode 100644 backend/src/api/modules/middleware.ts create mode 100644 backend/src/api/modules/rate-limiter.ts create mode 100644 backend/src/api/modules/ws-guard.ts create mode 100644 backend/src/api/routes/api-routes.ts create mode 100644 backend/src/api/routes/auth-routes.ts create mode 100644 backend/src/api/routes/channel-routes.ts create mode 100644 backend/src/api/routes/dev-routes.ts create mode 100644 backend/src/api/routes/guilds-routes.ts create mode 100644 backend/src/api/routes/invites-routes.ts create mode 100644 backend/src/api/routes/users-routes.ts create mode 100644 backend/src/api/server.ts create mode 100644 backend/src/api/websocket/modules/session-manager.ts create mode 100644 backend/src/api/websocket/modules/ws-cooldowns.ts create mode 100644 backend/src/api/websocket/modules/ws-rooms.ts create mode 100644 backend/src/api/websocket/websocket.ts create mode 100644 backend/src/api/websocket/ws-events/add-friend.ts create mode 100644 backend/src/api/websocket/ws-events/channel-create.ts create mode 100644 backend/src/api/websocket/ws-events/channel-delete.ts create mode 100644 backend/src/api/websocket/ws-events/disconnect.ts create mode 100644 backend/src/api/websocket/ws-events/guild-create.ts create mode 100644 backend/src/api/websocket/ws-events/guild-delete.ts create mode 100644 backend/src/api/websocket/ws-events/guild-member-add.ts create mode 100644 backend/src/api/websocket/ws-events/guild-member-remove.ts create mode 100644 backend/src/api/websocket/ws-events/guild-member-update.ts create mode 100644 backend/src/api/websocket/ws-events/guild-role-create.ts create mode 100644 backend/src/api/websocket/ws-events/guild-role-delete.ts create mode 100644 backend/src/api/websocket/ws-events/guild-role-update.ts create mode 100644 backend/src/api/websocket/ws-events/guild-update.ts create mode 100644 backend/src/api/websocket/ws-events/invite-create.ts create mode 100644 backend/src/api/websocket/ws-events/invite-delete.ts create mode 100644 backend/src/api/websocket/ws-events/message-create.ts create mode 100644 backend/src/api/websocket/ws-events/message-delete.ts create mode 100644 backend/src/api/websocket/ws-events/message-update.ts create mode 100644 backend/src/api/websocket/ws-events/ready.ts create mode 100644 backend/src/api/websocket/ws-events/remove-friend.ts create mode 100644 backend/src/api/websocket/ws-events/typing-start.ts create mode 100644 backend/src/api/websocket/ws-events/user-update.ts create mode 100644 backend/src/api/websocket/ws-events/ws-event.ts create mode 100644 backend/src/data/channels.ts delete mode 100644 backend/src/data/data-utils.ts create mode 100644 backend/src/data/db-wrapper.ts create mode 100644 backend/src/data/guild-members.ts create mode 100644 backend/src/data/guilds.ts create mode 100644 backend/src/data/invites.ts create mode 100644 backend/src/data/messages.ts create mode 100644 backend/src/data/models/application.ts create mode 100644 backend/src/data/models/guild-member.ts create mode 100644 backend/src/data/models/role.ts create mode 100644 backend/src/data/pings.ts create mode 100644 backend/src/data/roles.ts create mode 100644 backend/src/data/snowflake-entity.ts create mode 100644 backend/src/data/types/entity-types.ts create mode 100644 backend/src/data/types/env.ts create mode 100644 backend/src/data/types/ws-types.ts create mode 100644 backend/src/data/users.ts delete mode 100644 backend/src/rest/apply-middleware.ts delete mode 100644 backend/src/rest/apply-routes.ts delete mode 100644 backend/src/rest/middleware.ts delete mode 100644 backend/src/rest/routes/auth-routes.ts delete mode 100644 backend/src/rest/server.ts create mode 100644 backend/src/system/bot.ts create mode 100644 backend/src/system/ws-service.ts delete mode 120000 backend/src/types delete mode 100644 backend/src/utils/generate-invite.ts create mode 100644 backend/src/utils/log.ts delete mode 100644 backend/src/utils/snowflake.ts create mode 100644 backend/src/utils/utils.ts delete mode 100644 backend/src/ws/events/channel-create.ts delete mode 100644 backend/src/ws/events/channel-delete.ts delete mode 100644 backend/src/ws/events/disconnect.ts delete mode 100644 backend/src/ws/events/guild-create.ts delete mode 100644 backend/src/ws/events/guild-delete.ts delete mode 100644 backend/src/ws/events/guild-member-add.ts delete mode 100644 backend/src/ws/events/guild-member-remove.ts delete mode 100644 backend/src/ws/events/guild-update.ts delete mode 100644 backend/src/ws/events/invite-create.ts delete mode 100644 backend/src/ws/events/message-create.ts delete mode 100644 backend/src/ws/events/message-delete.ts delete mode 100644 backend/src/ws/events/message-update.ts delete mode 100644 backend/src/ws/events/ready.ts delete mode 100644 backend/src/ws/events/typing-start.ts delete mode 100644 backend/src/ws/events/user-delete.ts delete mode 100644 backend/src/ws/events/user-update.ts delete mode 100644 backend/src/ws/events/ws-event.ts delete mode 100644 backend/src/ws/session-manager.ts delete mode 100644 backend/src/ws/websocket.ts create mode 100644 backend/test/integration/_test-template.ts create mode 100644 backend/test/integration/routes/auth-routes.tests.ts create mode 100644 backend/test/integration/routes/channel-routes.tests.ts create mode 100644 backend/test/integration/routes/guilds-routes.tests.ts create mode 100644 backend/test/integration/routes/invites-routes.tests.ts create mode 100644 backend/test/integration/ws/add-friend.tests.ts create mode 100644 backend/test/integration/ws/channel-create.tests.ts create mode 100644 backend/test/integration/ws/guild-create.tests.ts create mode 100644 backend/test/integration/ws/guild-delete.tests.ts create mode 100644 backend/test/integration/ws/guild-member-add.tests.ts create mode 100644 backend/test/integration/ws/guild-member-remove.tests.ts create mode 100644 backend/test/integration/ws/guild-member-update.tests.ts create mode 100644 backend/test/integration/ws/guild-update.tests.ts create mode 100644 backend/test/integration/ws/invite-create.tests.ts create mode 100644 backend/test/integration/ws/invite-delete.tests.ts create mode 100644 backend/test/integration/ws/message-create.tests.ts create mode 100644 backend/test/integration/ws/message-delete.tests.ts create mode 100644 backend/test/integration/ws/message-update.tests.ts create mode 100644 backend/test/integration/ws/ready.tests.ts create mode 100644 backend/test/integration/ws/remove-friend.tests.ts create mode 100644 backend/test/integration/ws/user-update.tests.ts create mode 100644 backend/test/integration/ws/ws-guard.tests.ts create mode 100644 backend/test/mock/email-mock.ts create mode 100644 backend/test/mock/mock.ts create mode 100644 backend/test/test-utils.ts create mode 100644 backend/test/test.ts create mode 100644 backend/test/unit/models/application.tests.ts create mode 100644 backend/test/unit/models/channel.tests.ts create mode 100644 backend/test/unit/models/guild-member.tests.ts create mode 100644 backend/test/unit/models/guild.tests.ts create mode 100644 backend/test/unit/models/invite.tests.ts create mode 100644 backend/test/unit/models/message.tests.ts create mode 100644 backend/test/unit/models/role.tests.ts create mode 100644 backend/test/unit/models/user.tests.ts create mode 100644 backend/test/unit/snowflake-entity.tests.ts create mode 100644 backend/test/unit/ws/ws-cooldowns.tests.ts create mode 100644 types/patterns.ts diff --git a/CREDITS.md b/CREDITS.md new file mode 100644 index 00000000..e4966ce9 --- /dev/null +++ b/CREDITS.md @@ -0,0 +1,3 @@ +# Credits + +## DClone Icon - @nwlandas diff --git a/backend/.gitignore b/backend/.gitignore index 13dfa363..90733dd3 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -1,2 +1,4 @@ -.env -node_modules/ \ No newline at end of file +.env + +node_modules/ +lib/ \ No newline at end of file diff --git a/backend/.vscode/launch.json b/backend/.vscode/launch.json new file mode 100644 index 00000000..d9f22e85 --- /dev/null +++ b/backend/.vscode/launch.json @@ -0,0 +1,20 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Launch Program", + "skipFiles": [ + "/**" + ], + "program": "${workspaceFolder}/lib/app.js", + "outFiles": [ + "${workspaceFolder}/**/*.js" + ] + } + ] +} \ No newline at end of file diff --git a/backend/.vscode/settings.json b/backend/.vscode/settings.json new file mode 100644 index 00000000..e52b1e43 --- /dev/null +++ b/backend/.vscode/settings.json @@ -0,0 +1,12 @@ +{ + "mochaExplorer.files": "test/unit/*.tests.ts", + "mochaExplorer.require": "ts-node/register", + "mochaExplorer.logpanel": true, + "editor.tabSize": 2, + "debug.javascript.warnOnLongPrediction": false, + "cSpell.words": [ + "cooldown", + "metascraper" + ], + "typescript.tsdk": "node_modules/typescript/lib" +} \ No newline at end of file diff --git a/backend/.vscode/tasks.json b/backend/.vscode/tasks.json new file mode 100644 index 00000000..98111d03 --- /dev/null +++ b/backend/.vscode/tasks.json @@ -0,0 +1,18 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "typescript", + "tsconfig": "tsconfig.json", + "option": "watch", + "problemMatcher": [ + "$tsc-watch" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "label": "tsc: watch - tsconfig.json" + } + ] +} \ No newline at end of file diff --git a/backend/README.md b/backend/README.md index 45c44866..c0b811bc 100644 --- a/backend/README.md +++ b/backend/README.md @@ -1,54 +1,19 @@ -### There are 2 main components in this API. - -REST refers to the REST API (uses HTTP). -WS refers to the WebSocket API (uses WS). - -### Dependency Injection - -Due to the nature of the DI used in this project, dependencies cannot be circular: - -> rest/server.ts - -```ts -import { WS } from '...'; -... -Deps.get(WS); -``` - -> ws/websocket.ts - -```ts -import { REST } from '...'; -... -Deps.get(REST); -``` - -`Deps.add` -> create dep with custom object. -`Deps.get` -> get dep, or create with no args constructor. - ---- - -# WS - -SNAKE_CASE is used as lowercase to avoid possible conflict with socket.io event names. - -## Events - -| NAME | DESCRIPTION | -| :------------- | :--------------------------------- | -| READY | When a user connects with browser. | -| MESSAGE_CREATE | When user sends message. | -| MESSAGE_DELETE | When user deletes message. | - ---- - -## PORTS - -`3000` -> REST API and WS -`4200` -> React website - ---- - -## Different Folders, One Big Repo - --> Branch versions will be used (e.g. v1,v2,...,v12) +# Accord - API +Tested code that brings Accord to life. + +![Lines of Code](https://img.shields.io/tokei/lines/github/d-clone/API?color=46828d&style=for-the-badge) + +> © All rights reserved. This repo is not (yet) open source, and is awaiting completion. + +## `.env` +```.env +API_URL="http://localhost:3000/api" +EMAIL_ADDRESS="example@gmail.com" +EMAIL_PASSWORD="google_account_password" +MONGO_URI="mongodb://localhost/accord" +PORT=3000 +ROOT_ENDPOINT="http://localhost:3000" +WEBSITE_URL="http://localhost:4200" +``` + +> For help on setting up gmail mailing, go here - https://nodemailer.com/usage/using-gmail. \ No newline at end of file diff --git a/backend/assets/avatars/avatar_aqua.png b/backend/assets/avatars/avatar_aqua.png old mode 100755 new mode 100644 diff --git a/backend/assets/avatars/avatar_coffee.png b/backend/assets/avatars/avatar_coffee.png old mode 100755 new mode 100644 diff --git a/backend/assets/avatars/avatar_fire.png b/backend/assets/avatars/avatar_fire.png old mode 100755 new mode 100644 diff --git a/backend/assets/avatars/avatar_gold.png b/backend/assets/avatars/avatar_gold.png old mode 100755 new mode 100644 diff --git a/backend/assets/avatars/avatar_grey.png b/backend/assets/avatars/avatar_grey.png old mode 100755 new mode 100644 diff --git a/backend/assets/avatars/avatar_rainbow.png b/backend/assets/avatars/avatar_rainbow.png old mode 100755 new mode 100644 diff --git a/backend/assets/avatars/avatar_sky.png b/backend/assets/avatars/avatar_sky.png old mode 100755 new mode 100644 diff --git a/backend/assets/avatars/avatar_tree.png b/backend/assets/avatars/avatar_tree.png old mode 100755 new mode 100644 diff --git a/backend/assets/avatars/bot.png b/backend/assets/avatars/bot.png old mode 100755 new mode 100644 diff --git a/backend/assets/avatars/unknown.png b/backend/assets/avatars/unknown.png old mode 100755 new mode 100644 diff --git a/backend/package-lock.json b/backend/package-lock.json index 1d01d59b..57057b02 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -10,40 +10,168 @@ "license": "ISC", "dependencies": { "body-parser": "^1.19.0", + "chai-things": "^0.2.0", + "colors": "^1.4.0", "cors": "^2.8.5", - "dotenv": "^10.0.0", + "dotenv": "^8.2.0", "express": "^4.17.1", - "faker": "^5.5.3", - "http-errors": "^1.7.2", + "express-async-errors": "^3.1.1", + "express-rate-limit": "^5.2.6", + "faker": "^5.4.0", + "got": "^11.7.0", + "helmet": "^4.4.1", "jsonwebtoken": "^8.5.1", - "mongoose": "^5.13.3", + "metascraper": "^5.14.14", + "metascraper-description": "^5.14.14", + "metascraper-image": "^5.14.14", + "metascraper-title": "^5.14.14", + "metascraper-url": "^5.14.14", + "mongoose": "^5.10.7", + "mongoose-unique-validator": "^2.0.3", + "node-fetch": "^2.6.1", + "nodemailer": "^6.5.0", + "nodemailer-pug-engine": "^2.0.0", "passport": "^0.4.1", "passport-local": "^1.0.0", - "passport-local-mongoose": "^6.1.0", - "socket.io": "^4.1.3", - "uuid": "^8.3.2" + "passport-local-mongoose": "^6.0.1", + "rate-limit-mongo": "^2.3.1", + "re2": "^1.16.0", + "socket.io": "^4.0.0", + "socket.io-client": "^4.0.0", + "ts-node": "^9.1.1", + "typescript": "^4.2.3" }, "devDependencies": { + "@types/chai": "^4.2.14", + "@types/chai-as-promised": "^7.1.3", + "@types/chai-spies": "^1.0.3", + "@types/chai-things": "^0.0.34", + "@types/colors": "^1.2.1", + "@types/cors": "^2.8.7", "@types/dotenv": "^8.2.0", - "@types/express": "^4.17.13", - "@types/faker": "^5.5.7", - "@types/http-errors": "^1.8.1", - "@types/jsonwebtoken": "^8.5.4", - "@types/mongoose": "^5.11.97", - "@types/passport": "^1.0.7", - "@types/passport-local": "^1.0.34", - "@types/passport-local-mongoose": "^4.0.15", - "@types/socket.io": "^3.0.2", - "@types/uuid": "^8.3.1", - "ts-node": "^9.1.1", - "ts-node-dev": "^1.1.8", - "typescript": "^4.3.5" + "@types/express": "^4.17.11", + "@types/express-rate-limit": "^5.1.1", + "@types/faker": "^5.1.6", + "@types/jsonwebtoken": "^8.5.0", + "@types/mocha": "^8.2.0", + "@types/mongoose": "^5.7.36", + "@types/node": "^14.11.2", + "@types/node-fetch": "^2.5.7", + "@types/nodemailer": "^6.4.1", + "@types/passport": "^1.0.4", + "@types/passport-local": "^1.0.33", + "@types/socket.io": "^2.1.13", + "@types/socket.io-client": "^1.4.36", + "@types/supertest": "^2.0.10", + "chai": "^4.2.0", + "chai-as-promised": "^7.1.1", + "chai-spies": "^1.0.0", + "i": "^0.3.6", + "mocha": "^8.2.1", + "npm": "^7.6.3", + "sazerac": "^2.0.0", + "supertest": "^6.1.3" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", + "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==" + }, + "node_modules/@babel/parser": { + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.12.tgz", + "integrity": "sha512-4T7Pb244rxH24yR116LAuJ+adxXXnHhZaLJjegJVKSdoNCe4x1eDBaud5YIcQFcqzsaD5BHvJw5BQ0AZapdCRw==", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.12.tgz", + "integrity": "sha512-K4nY2xFN4QMvQwkQ+zmBDp6ANMbVNw6BbxWmYA4qNjhR9W+Lj/8ky5MEY2Me5r+B2c6/v6F53oMndG+f9s3IiA==", + "dependencies": { + "@babel/helper-validator-identifier": "^7.12.11", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "node_modules/@metascraper/helpers": { + "version": "5.14.14", + "resolved": "https://registry.npmjs.org/@metascraper/helpers/-/helpers-5.14.14.tgz", + "integrity": "sha512-sznekODvL+k5wTaU48Y8prdrPy/xTp3WxSAQrApBPeKPGEJTyfQWglkp+1W7xEDDrnJYzjOJDuq6EX3l9WYv1g==", + "dependencies": { + "audio-extensions": "0.0.0", + "chrono-node": "2.1.8", + "condense-whitespace": "~2.0.0", + "entities": "~2.0.3", + "file-extension": "~4.0.5", + "has-values": "~2.0.1", + "image-extensions": "~1.1.0", + "is-relative-url": "~3.0.0", + "is-uri": "~1.2.0", + "iso-639-3": "~2.1.0", + "isostring": "0.0.1", + "lodash": "~4.17.20", + "memoize-one": "~5.1.1", + "mime-types": "~2.1.27", + "normalize-url": "~5.2.0", + "smartquotes": "~2.3.1", + "title": "~3.4.2", + "truncate": "~2.1.0", + "url-regex-safe": "~1.0.2", + "video-extensions": "~1.1.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@sindresorhus/is": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-3.1.2.tgz", + "integrity": "sha512-JiX9vxoKMmu8Y3Zr2RVathBL1Cdu4Nt4MuNWemt1Nc06A0RAin9c5FArkhGsyMBWfCu4zj+9b+GxtjAnE4qqLQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/@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==", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "engines": { + "node": ">= 6" } }, "node_modules/@types/body-parser": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.1.tgz", - "integrity": "sha512-a6bTJ21vFOGIkwM0kzh9Yr89ziVxq4vYH2fQ6N8AeipEzai/cFK6aGMArIkUeIdRIgpwQa+2bXiLuUJCpSf2Cg==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==", "dev": true, "dependencies": { "@types/connect": "*", @@ -51,51 +179,119 @@ } }, "node_modules/@types/bson": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.4.tgz", - "integrity": "sha512-awqorHvQS0DqxkHQ/FxcPX9E+H7Du51Qw/2F+5TBMSaE3G0hm+8D3eXJ6MAzFw75nE8V7xF0QvzUSdxIjJb/GA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.2.tgz", + "integrity": "sha512-+uWmsejEHfmSjyyM/LkrP0orfE2m5Mx9Xel4tXNeqi1ldK5XMQcDsFkBmLDtuyKUbxj2jGDo0H240fbCRJZo7Q==", + "dev": true, "dependencies": { "@types/node": "*" } }, + "node_modules/@types/cacheable-request": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz", + "integrity": "sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ==", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "*", + "@types/node": "*", + "@types/responselike": "*" + } + }, + "node_modules/@types/chai": { + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.14.tgz", + "integrity": "sha512-G+ITQPXkwTrslfG5L/BksmbLUA0M1iybEsmCWPqzSxsRRhJZimBKJkoMi8fr/CPygPTj4zO5pJH7I2/cm9M7SQ==", + "dev": true + }, + "node_modules/@types/chai-as-promised": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.3.tgz", + "integrity": "sha512-FQnh1ohPXJELpKhzjuDkPLR2BZCAqed+a6xV4MI/T3XzHfd2FlarfUGUdZYgqYe8oxkYn0fchHEeHfHqdZ96sg==", + "dev": true, + "dependencies": { + "@types/chai": "*" + } + }, + "node_modules/@types/chai-spies": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/chai-spies/-/chai-spies-1.0.3.tgz", + "integrity": "sha512-RBZjhVuK7vrg4rWMt04UF5zHYwfHnpk5mIWu3nQvU3AKGDixXzSjZ6v0zke6pBcaJqMv3IBZ5ibLWPMRDL0sLw==", + "dev": true, + "dependencies": { + "@types/chai": "*" + } + }, + "node_modules/@types/chai-things": { + "version": "0.0.34", + "resolved": "https://registry.npmjs.org/@types/chai-things/-/chai-things-0.0.34.tgz", + "integrity": "sha512-vcpFz782jq7FpEnE9Yq0cfgP8NwRPQgQ2271Q+7hEldOHttTQaYuVj0S9ViQXkM+sYSgUh/2OF5vTq8iei8Ljg==", + "dev": true, + "dependencies": { + "@types/chai": "*" + } + }, + "node_modules/@types/colors": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/colors/-/colors-1.2.1.tgz", + "integrity": "sha512-7jNkpfN2lVO07nJ1RWzyMnNhH/I5N9iWuMPx9pedptxJ4MODf8rRV0lbJi6RakQ4sKQk231Fw4e2W9n3D7gZ3w==", + "dev": true, + "dependencies": { + "colors": "*" + } + }, "node_modules/@types/component-emitter": { "version": "1.2.10", "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.10.tgz", "integrity": "sha512-bsjleuRKWmGqajMerkzox19aGbscQX5rmmvvXl3wlIp5gMG1HgkiwPxsN5p070fBDKTNSPgojVbuY1+HWMbFhg==" }, "node_modules/@types/connect": { - "version": "3.4.35", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "version": "3.4.33", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz", + "integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-y7mImlc/rNkvCRmg8gC3/lj87S7pTUIJ6QGjwHR9WQJcFs+ZMTOaoPrkdFA/YdbuqVEmEbb5RdhVxMkAcgOnpg==" + }, + "node_modules/@types/cookiejar": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.2.tgz", + "integrity": "sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==", + "dev": true }, "node_modules/@types/cors": { - "version": "2.8.12", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", - "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==" + "version": "2.8.10", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.10.tgz", + "integrity": "sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ==" }, "node_modules/@types/dotenv": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/@types/dotenv/-/dotenv-8.2.0.tgz", "integrity": "sha512-ylSC9GhfRH7m1EUXBXofhgx4lUWmFeQDINW5oLuS+gxWdfUeW4zJdeVTYVkexEW+e2VUvlZR2kGnGGipAWR7kw==", - "deprecated": "This is a stub types definition. dotenv provides its own type definitions, so you do not need this installed.", "dev": true, "dependencies": { "dotenv": "*" } }, + "node_modules/@types/engine.io": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/engine.io/-/engine.io-3.1.4.tgz", + "integrity": "sha512-98rXVukLD6/ozrQ2O80NAlWDGA4INg+tqsEReWJldqyi2fulC9V7Use/n28SWgROXKm6003ycWV4gZHoF8GA6w==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/express": { - "version": "4.17.13", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", - "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.11.tgz", + "integrity": "sha512-no+R6rW60JEc59977wIxreQVsIEOAYwgCqldrA/vkpCnbD7MqTefO97lmoBe4WE0F156bC4uLSP1XHDOySnChg==", "dev": true, "dependencies": { "@types/body-parser": "*", @@ -104,10 +300,19 @@ "@types/serve-static": "*" } }, + "node_modules/@types/express-rate-limit": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/express-rate-limit/-/express-rate-limit-5.1.1.tgz", + "integrity": "sha512-6oMYZBLlhxC5sdcRXXz528QyfGz3zTy9YdHwqlxLfgx5Cd3zwYaUjjPpJcaTtHmRefLi9P8kLBPz2wB7yz4JtQ==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, "node_modules/@types/express-serve-static-core": { - "version": "4.17.24", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.24.tgz", - "integrity": "sha512-3UJuW+Qxhzwjq3xhwXm2onQcFHn76frIYVbTu+kn24LFxI+dEhdfISDFovPB8VpEgW8oQCTpRuCe+0zJxB7NEA==", + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.19.tgz", + "integrity": "sha512-DJOSHzX7pCiSElWaGR8kCprwibCB/3yW6vcT8VG3P0SJjnv19gnWG/AZMfM60Xj/YJIp/YCaDHyvzsFVeniARA==", "dev": true, "dependencies": { "@types/node": "*", @@ -116,69 +321,116 @@ } }, "node_modules/@types/faker": { - "version": "5.5.7", - "resolved": "https://registry.npmjs.org/@types/faker/-/faker-5.5.7.tgz", - "integrity": "sha512-ejzb61Q5zQTtS0ZIafgQ7ahO5ACzmGhG5PfX2hxWyth3k0/aysb4ZOxKQB8DbzwSPppA5jmFBwqnBxjv5hqI5Q==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/@types/faker/-/faker-5.1.6.tgz", + "integrity": "sha512-D+gfFWR/YCvlrYL8lgNZO1jKgIUW+cfhxsgMOqUMYwCI+tl0htD7vCCXp/oJsIxJpxuI7zqmo3gpVQBkFCM4iA==", "dev": true }, - "node_modules/@types/http-errors": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-1.8.1.tgz", - "integrity": "sha512-e+2rjEwK6KDaNOm5Aa9wNGgyS9oSZU/4pfSMMPYNOfjvFI0WVXm29+ITRFr6aKDvvKo7uU1jV68MW4ScsfDi7Q==", - "dev": true + "node_modules/@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==" }, "node_modules/@types/jsonwebtoken": { - "version": "8.5.4", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.4.tgz", - "integrity": "sha512-4L8msWK31oXwdtC81RmRBAULd0ShnAHjBuKT9MRQpjP0piNrZdXyTRcKY9/UIfhGeKIT4PvF5amOOUbbT/9Wpg==", + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.0.tgz", + "integrity": "sha512-9bVao7LvyorRGZCw0VmH/dr7Og+NdjYSsKAxB43OQoComFbBgsEpoR9JW6+qSq/ogwVBg8GI2MfAlk4SYI4OLg==", "dev": true, "dependencies": { "@types/node": "*" } }, + "node_modules/@types/keyv": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz", + "integrity": "sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/mime": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", - "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.3.tgz", + "integrity": "sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q==", + "dev": true + }, + "node_modules/@types/mocha": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.0.tgz", + "integrity": "sha512-/Sge3BymXo4lKc31C8OINJgXLaw+7vL1/L1pGiBNpGrBiT8FQiaFpSYV0uhTaG4y78vcMBTMFsWaHDvuD+xGzQ==", "dev": true }, "node_modules/@types/mongodb": { - "version": "3.6.20", - "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.6.20.tgz", - "integrity": "sha512-WcdpPJCakFzcWWD9juKoZbRtQxKIMYF/JIAM4JrNHrMcnJL6/a2NWjXxW7fo9hxboxxkg+icff8d7+WIEvKgYQ==", + "version": "3.5.27", + "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.5.27.tgz", + "integrity": "sha512-1jxKDgdfJEOO9zp+lv43p8jOqRs02xPrdUTzAZIVK9tVEySfCEmktL2jEu9A3wOBEOs18yKzpVIKUh8b8ALk3w==", + "dev": true, "dependencies": { "@types/bson": "*", "@types/node": "*" } }, "node_modules/@types/mongoose": { - "version": "5.11.97", - "resolved": "https://registry.npmjs.org/@types/mongoose/-/mongoose-5.11.97.tgz", - "integrity": "sha512-cqwOVYT3qXyLiGw7ueU2kX9noE8DPGRY6z8eUxudhXY8NZ7DMKYAxyZkLSevGfhCX3dO/AoX5/SO9lAzfjon0Q==", - "deprecated": "Mongoose publishes its own types, so you do not need to install this package.", + "version": "5.7.36", + "resolved": "https://registry.npmjs.org/@types/mongoose/-/mongoose-5.7.36.tgz", + "integrity": "sha512-ggFXgvkHgCNlT35B9d/heDYfSqOSwTmQjkRoR32sObGV5Xjd0N0WWuYlLzqeCg94j4hYN/OZxZ1VNNLltX/IVQ==", "dev": true, "dependencies": { - "mongoose": "*" + "@types/mongodb": "*", + "@types/node": "*" } }, "node_modules/@types/node": { - "version": "16.3.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.3.3.tgz", - "integrity": "sha512-8h7k1YgQKxKXWckzFCMfsIwn0Y61UK6tlD6y2lOb3hTOIMlK3t9/QwHOhc81TwU+RMf0As5fj7NPjroERCnejQ==" + "version": "14.11.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.11.2.tgz", + "integrity": "sha512-jiE3QIxJ8JLNcb1Ps6rDbysDhN4xa8DJJvuC9prr6w+1tIh+QAbYyNF3tyiZNLDBIuBCf4KEcV2UvQm/V60xfA==" + }, + "node_modules/@types/node-fetch": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.7.tgz", + "integrity": "sha512-o2WVNf5UhWRkxlf6eq+jMZDu7kjgpgJfl4xVNlvryc95O/6F2ld8ztKX+qu+Rjyet93WAWm5LjeX9H5FGkODvw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "form-data": "^3.0.0" + } + }, + "node_modules/@types/node-fetch/node_modules/form-data": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", + "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/nodemailer": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.1.tgz", + "integrity": "sha512-8081UY/0XTTDpuGqCnDc8IY+Q3DSg604wB3dBH0CaZlj4nZWHWuxtZ3NRZ9c9WUrz1Vfm6wioAUnqL3bsh49uQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } }, "node_modules/@types/passport": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.7.tgz", - "integrity": "sha512-JtswU8N3kxBYgo+n9of7C97YQBT+AYPP2aBfNGTzABqPAZnK/WOAaKfh3XesUYMZRrXFuoPc2Hv0/G/nQFveHw==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.4.tgz", + "integrity": "sha512-h5OfAbfBBYSzjeU0GTuuqYEk9McTgWeGQql9g3gUw2/NNCfD7VgExVRYJVVeU13Twn202Mvk9BT0bUrl30sEgA==", "dev": true, "dependencies": { "@types/express": "*" } }, "node_modules/@types/passport-local": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/@types/passport-local/-/passport-local-1.0.34.tgz", - "integrity": "sha512-PSc07UdYx+jhadySxxIYWuv6sAnY5e+gesn/5lkPKfBeGuIYn9OPR+AAEDq73VRUh6NBTpvE/iPE62rzZUslog==", + "version": "1.0.33", + "resolved": "https://registry.npmjs.org/@types/passport-local/-/passport-local-1.0.33.tgz", + "integrity": "sha512-+rn6ZIxje0jZ2+DAiWFI8vGG7ZFKB0hXx2cUdMmudSWsigSq6ES7Emso46r4HJk0qCgrZVfI8sJiM7HIYf4SbA==", "dev": true, "dependencies": { "@types/express": "*", @@ -186,26 +438,6 @@ "@types/passport-strategy": "*" } }, - "node_modules/@types/passport-local-mongoose": { - "version": "4.0.15", - "resolved": "https://registry.npmjs.org/@types/passport-local-mongoose/-/passport-local-mongoose-4.0.15.tgz", - "integrity": "sha512-wg1ri5Tyl8c/FLzm57efHlHJVwr7RcbsqORcQBOI7XWSw6PEtt75qX0OQept3/4k0vV/GYTdeTWnTzSFyZtwEA==", - "dev": true, - "dependencies": { - "@types/mongoose": "4.*", - "@types/passport-local": "*" - } - }, - "node_modules/@types/passport-local-mongoose/node_modules/@types/mongoose": { - "version": "4.7.56", - "resolved": "https://registry.npmjs.org/@types/mongoose/-/mongoose-4.7.56.tgz", - "integrity": "sha512-c4bQmM/WUMQvOpEJFJbNjwLU72bzFOtKJ+4HtqdA+wtMhWpwIBoKdL7KsNt7BRjelK0dcSrgymvz/miQuPjikw==", - "dev": true, - "dependencies": { - "@types/mongodb": "*", - "@types/node": "*" - } - }, "node_modules/@types/passport-strategy": { "version": "0.2.35", "resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.35.tgz", @@ -217,54 +449,90 @@ } }, "node_modules/@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "version": "6.9.5", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.5.tgz", + "integrity": "sha512-/JHkVHtx/REVG0VVToGRGH2+23hsYLHdyG+GrvoUGlGAd0ErauXDyvHtRI/7H7mzLm+tBCKA7pfcpkQ1lf58iQ==", "dev": true }, "node_modules/@types/range-parser": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", - "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", + "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==", "dev": true }, - "node_modules/@types/serve-static": { - "version": "1.13.10", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", - "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", - "dev": true, + "node_modules/@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", "dependencies": { - "@types/mime": "^1", "@types/node": "*" } }, - "node_modules/@types/socket.io": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/socket.io/-/socket.io-3.0.2.tgz", - "integrity": "sha512-pu0sN9m5VjCxBZVK8hW37ZcMe8rjn4HHggBN5CbaRTvFwv5jOmuIRZEuddsBPa9Th0ts0SIo3Niukq+95cMBbQ==", - "deprecated": "This is a stub types definition. socket.io provides its own type definitions, so you do not need this installed.", + "node_modules/@types/serve-static": { + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.5.tgz", + "integrity": "sha512-6M64P58N+OXjU432WoLLBQxbA0LRGBCRm7aAGQJ+SMC1IMl0dgRVi9EFfoDcS2a7Xogygk/eGN94CfwU9UF7UQ==", "dev": true, "dependencies": { - "socket.io": "*" + "@types/express-serve-static-core": "*", + "@types/mime": "*" } }, - "node_modules/@types/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I=", + "node_modules/@types/socket.io": { + "version": "2.1.13", + "resolved": "https://registry.npmjs.org/@types/socket.io/-/socket.io-2.1.13.tgz", + "integrity": "sha512-JRgH3nCgsWel4OPANkhH8TelpXvacAJ9VeryjuqCDiaVDMpLysd6sbt0dr6Z15pqH3p2YpOT3T1C5vQ+O/7uyg==", + "dev": true, + "dependencies": { + "@types/engine.io": "*", + "@types/node": "*", + "@types/socket.io-parser": "*" + } + }, + "node_modules/@types/socket.io-client": { + "version": "1.4.36", + "resolved": "https://registry.npmjs.org/@types/socket.io-client/-/socket.io-client-1.4.36.tgz", + "integrity": "sha512-ZJWjtFBeBy1kRSYpVbeGYTElf6BqPQUkXDlHHD4k/42byCN5Rh027f4yARHCink9sKAkbtGZXEAmR0ZCnc2/Ag==", "dev": true }, - "node_modules/@types/strip-json-comments": { - "version": "0.0.30", - "resolved": "https://registry.npmjs.org/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz", - "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==", + "node_modules/@types/socket.io-parser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@types/socket.io-parser/-/socket.io-parser-2.2.1.tgz", + "integrity": "sha512-+JNb+7N7tSINyXPxAJb62+NcpC1x/fPn7z818W4xeNCdPTp6VsO/X8fCsg6+ug4a56m1v9sEiTIIUKVupcHOFQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/superagent": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.10.tgz", + "integrity": "sha512-xAgkb2CMWUMCyVc/3+7iQfOEBE75NvuZeezvmixbUw3nmENf2tCnQkW5yQLTYqvXUQ+R6EXxdqKKbal2zM5V/g==", + "dev": true, + "dependencies": { + "@types/cookiejar": "*", + "@types/node": "*" + } + }, + "node_modules/@types/supertest": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-2.0.10.tgz", + "integrity": "sha512-Xt8TbEyZTnD5Xulw95GLMOkmjGICrOQyJ2jqgkSjAUR3mm7pAIzSR0NFBaMcwlzVvlpCjNwbATcWWwjNiZiFrQ==", + "dev": true, + "dependencies": { + "@types/superagent": "*" + } + }, + "node_modules/@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", "dev": true }, - "node_modules/@types/uuid": { - "version": "8.3.1", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.1.tgz", - "integrity": "sha512-Y2mHTRAbqfFkpjldbkHGY8JIzRN6XqYRliG8/24FcHm2D2PwW24fl5xMRTVGdrb7iMrwCaIEbLWerGIkXuFWVg==", - "dev": true + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, "node_modules/accepts": { "version": "1.3.7", @@ -278,10 +546,127 @@ "node": ">= 0.6" } }, + "node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agent-base/node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/agent-base/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/agentkeepalive": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.1.4.tgz", + "integrity": "sha512-+V/rGa3EuU74H6wR04plBb7Ks10FbtUQgRj/FQOG7uUIEuaINI+AiqJR1k6t3SVNs7o7ZjIdus6706qqzVq8jQ==", + "dependencies": { + "debug": "^4.1.0", + "depd": "^1.1.2", + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/agentkeepalive/node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/agentkeepalive/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-styles": { + "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==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", "dev": true, "dependencies": { "normalize-path": "^3.0.0", @@ -291,23 +676,98 @@ "node": ">= 8" } }, + "node_modules/aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "node_modules/arch": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/arch/-/arch-2.1.2.tgz", + "integrity": "sha512-NTBIIbAfkJeIletyABbVtdPgeKfDafR+1mZV/AyyfC1UkVkp9iUjV+wwmqtUgphHYajbI86jejBJp5e+jkGTiQ==" + }, + "node_modules/are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } }, "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + }, + "node_modules/assert-never": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/assert-never/-/assert-never-1.2.1.tgz", + "integrity": "sha512-TaTivMB6pYI1kXwrFlEhLeGfOqoDNdTxjCdwRfFFkEA30Eu+k48W34nlok2EYWJfFFzqaEmichdNM7th6M5HNw==" + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", "dev": true }, + "node_modules/audio-extensions": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/audio-extensions/-/audio-extensions-0.0.0.tgz", + "integrity": "sha1-0O7+B3+562JYmO7ZmFiQVIzx+NI=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/babel-walk": { + "version": "3.0.0-canary-5", + "resolved": "https://registry.npmjs.org/babel-walk/-/babel-walk-3.0.0-canary-5.tgz", + "integrity": "sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==", + "dependencies": { + "@babel/types": "^7.9.6" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/backo2": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", + "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" + }, + "node_modules/balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, "node_modules/base64-arraybuffer": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz", @@ -367,24 +827,15 @@ "node": ">= 0.8" } }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" }, "node_modules/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, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -402,10 +853,16 @@ "node": ">=8" } }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, "node_modules/bson": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.6.tgz", - "integrity": "sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.5.tgz", + "integrity": "sha512-kDuEzldR21lHciPQAIulLs1LZlCXdLziXI6Mb/TDkwXhb//UORJNPXgcRs2CuO4H0DcMkpfT3/ySsP3unoZjBg==", "engines": { "node": ">=0.6.19" } @@ -418,8 +875,7 @@ "node_modules/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==", - "dev": true + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" }, "node_modules/bytes": { "version": "3.1.0", @@ -429,25 +885,366 @@ "node": ">= 0.8" } }, - "node_modules/chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "node_modules/cacache": { + "version": "15.0.6", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.0.6.tgz", + "integrity": "sha512-g1WYDMct/jzW+JdWEyjaX2zoBkZ6ZT9VpOyp2I/VMtDsNLffNat3kqPFfi1eDRSK9/SuKGyORDHcQMcPF8sQ/w==", + "dependencies": { + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cacache/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cacache/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/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==", + "engines": { + "node": ">=10" + } + }, + "node_modules/cacheable-request": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.1.tgz", + "integrity": "sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw==", + "dependencies": { + "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" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cacheable-request/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cacheable-request/node_modules/normalize-url": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", + "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/chai": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", + "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", "dev": true, "dependencies": { - "anymatch": "~3.1.2", + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "pathval": "^1.1.0", + "type-detect": "^4.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chai-as-promised": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz", + "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==", + "dev": true, + "dependencies": { + "check-error": "^1.0.2" + } + }, + "node_modules/chai-spies": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/chai-spies/-/chai-spies-1.0.0.tgz", + "integrity": "sha512-elF2ZUczBsFoP07qCfMO/zeggs8pqCf3fZGyK5+2X4AndS8jycZYID91ztD9oQ7d/0tnS963dPkd0frQEThDsg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/chai-things": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/chai-things/-/chai-things-0.2.0.tgz", + "integrity": "sha1-xVEoN4+bs5nplPAAUhUZhO1uvnA=" + }, + "node_modules/chalk": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", + "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", + "dependencies": { + "ansi-styles": "^3.1.0", + "escape-string-regexp": "^1.0.5", + "supports-color": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/character-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", + "integrity": "sha1-x84o821LzZdE5f/CxfzeHHMmH8A=", + "dependencies": { + "is-regex": "^1.0.3" + } + }, + "node_modules/check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/cheerio": { + "version": "1.0.0-rc.3", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.3.tgz", + "integrity": "sha512-0td5ijfUPuubwLUu0OBoe98gZj8C/AA+RW3v67GPlGOrvxWjZmBXiBCRU+I8VEiNyJzjth40POfHiz2RB3gImA==", + "dependencies": { + "css-select": "~1.2.0", + "dom-serializer": "~0.1.1", + "entities": "~1.1.1", + "htmlparser2": "^3.9.1", + "lodash": "^4.15.0", + "parse5": "^3.0.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cheerio-advanced-selectors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/cheerio-advanced-selectors/-/cheerio-advanced-selectors-2.0.1.tgz", + "integrity": "sha512-5wHR8bpiD5pdUtaS81A6hnJezzoDzL1TLWfK6bxnLkIgEKPV26BlOdMCcvuj3fTE7JSalsTUeNU7AOD/u6bYhw==" + }, + "node_modules/cheerio/node_modules/entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" + }, + "node_modules/chokidar": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", + "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.1", "braces": "~3.0.2", - "glob-parent": "~5.1.2", + "glob-parent": "~5.1.0", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "readdirp": "~3.5.0" }, "engines": { "node": ">= 8.10.0" }, "optionalDependencies": { - "fsevents": "~2.3.2" + "fsevents": "~2.1.2" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/chrono-node": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chrono-node/-/chrono-node-2.1.8.tgz", + "integrity": "sha512-Bn54l+yEX4yA+/8dYIz/tITSWcPFwy1N5KpUEndPngwUCr7hTDKqVAdBjiqPrbvgOeSvcOhe3yIBH+HsmzM3GA==", + "dependencies": { + "dayjs": "^1.8.29" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/clipboardy": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-1.2.2.tgz", + "integrity": "sha512-16KrBOV7bHmHdxcQiCvfUFYVFyEah4FI8vYT1Fr7CGSA4G+xBWMEfUEQJS1hxeHGtI9ju1Bzs9uXSbj5HZKArw==", + "dependencies": { + "arch": "^2.1.0", + "execa": "^0.8.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "dependencies": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "node_modules/cliui/node_modules/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, + "engines": { + "node": ">=6" + } + }, + "node_modules/cliui/node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/cliui/node_modules/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, + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/cliui/node_modules/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, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "dependencies": { + "mimic-response": "^1.0.0" + } + }, + "node_modules/code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" } }, "node_modules/component-emitter": { @@ -458,8 +1255,29 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "node_modules/condense-whitespace": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/condense-whitespace/-/condense-whitespace-2.0.0.tgz", + "integrity": "sha512-Ath9o58/0rxZXbyoy3zZgrVMoIemi30sukG/btuMKCLyqfQt3dNOWc9N3EHEMa2Q3i0tXQPDJluYFLwy7pJuQw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/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=" + }, + "node_modules/constantinople": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-4.0.1.tgz", + "integrity": "sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==", + "dependencies": { + "@babel/parser": "^7.6.0", + "@babel/types": "^7.6.1" + } }, "node_modules/content-disposition": { "version": "0.5.3", @@ -481,9 +1299,9 @@ } }, "node_modules/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", "engines": { "node": ">= 0.6" } @@ -493,6 +1311,12 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, + "node_modules/cookiejar": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", + "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==", + "dev": true + }, "node_modules/core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -513,29 +1337,116 @@ "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" }, - "node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "node_modules/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=", "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" } }, + "node_modules/css-select": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", + "dependencies": { + "boolbase": "~1.0.0", + "css-what": "2.1", + "domutils": "1.5.1", + "nth-check": "~1.0.1" + } + }, + "node_modules/css-what": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", + "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", + "engines": { + "node": "*" + } + }, + "node_modules/dayjs": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.9.1.tgz", + "integrity": "sha512-01NCTBg8cuMJG1OQc6PR7T66+AFYiPwgDvdJmvJBn29NGzIG+DIFxPLNjHzwz3cpFIvG+NcwIjP9hSaPVoOaDg==" + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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==", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/decompress-response/node_modules/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==", + "engines": { + "node": ">=10" + } + }, + "node_modules/deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/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==", + "engines": { + "node": ">=10" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + }, "node_modules/denque": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.0.tgz", - "integrity": "sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz", + "integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ==", "engines": { "node": ">=0.10" } @@ -557,26 +1468,57 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, "engines": { "node": ">=0.3.1" } }, - "node_modules/dotenv": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", - "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", - "engines": { - "node": ">=10" + "node_modules/doctypes": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", + "integrity": "sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk=" + }, + "node_modules/dom-serializer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", + "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", + "dependencies": { + "domelementtype": "^1.3.0", + "entities": "^1.1.1" } }, - "node_modules/dynamic-dedupe": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz", - "integrity": "sha1-BuRMIj9eTpTXjvnbI6ZRXOL5YqE=", - "dev": true, + "node_modules/dom-serializer/node_modules/entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" + }, + "node_modules/domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + }, + "node_modules/domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", "dependencies": { - "xtend": "^4.0.0" + "domelementtype": "1" + } + }, + "node_modules/domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "dependencies": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "node_modules/dotenv": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", + "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==", + "engines": { + "node": ">=8" } }, "node_modules/ecdsa-sig-formatter": { @@ -592,6 +1534,12 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, + "node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, "node_modules/encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", @@ -600,10 +1548,39 @@ "node": ">= 0.8" } }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/encoding/node_modules/iconv-lite": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz", + "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/engine.io": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-5.1.1.tgz", - "integrity": "sha512-aMWot7H5aC8L4/T8qMYbLdvKlZOdJTH54FxfdFunTGvhMx1BHkJOntWArsVfgAZVwAO9LC2sryPWRcEeUzCe5w==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-5.0.0.tgz", + "integrity": "sha512-BATIdDV3H1SrE9/u2BAotvsmjJg0t1P4+vGedImSs1lkFAtQdvk4Ev1y4LDiPF7BPWgXWEG+NDY+nLvW3UrMWw==", "dependencies": { "accepts": "~1.3.4", "base64id": "2.0.0", @@ -617,6 +1594,38 @@ "node": ">=10.0.0" } }, + "node_modules/engine.io-client": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-5.0.0.tgz", + "integrity": "sha512-e6GK0Fqvq45Nu/j7YdIVqXtDPvlsggAcfml3QiEiGdJ1qeh7IQU6knxSN3+yy9BmbnXtIfjo1hK4MFyHKdc9mQ==", + "dependencies": { + "base64-arraybuffer": "0.1.4", + "component-emitter": "~1.3.0", + "debug": "~4.3.1", + "engine.io-parser": "~4.0.1", + "has-cors": "1.1.0", + "parseqs": "0.0.6", + "parseuri": "0.0.6", + "ws": "~7.4.2", + "yeast": "0.1.2" + } + }, + "node_modules/engine.io-client/node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/engine.io-client/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "node_modules/engine.io-parser": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-4.0.2.tgz", @@ -628,11 +1637,74 @@ "node": ">=8.0.0" } }, + "node_modules/engine.io/node_modules/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/engine.io/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/entities": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz", + "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==" + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==" + }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" }, + "node_modules/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=", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -641,6 +1713,23 @@ "node": ">= 0.6" } }, + "node_modules/execa": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.8.0.tgz", + "integrity": "sha1-2NdrvBtVIX7RkP1t1J08d07PyNo=", + "dependencies": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.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" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/express": { "version": "4.17.1", "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", @@ -681,31 +1770,34 @@ "node": ">= 0.10.0" } }, - "node_modules/express/node_modules/cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", - "engines": { - "node": ">= 0.6" - } + "node_modules/express-async-errors": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/express-async-errors/-/express-async-errors-3.1.1.tgz", + "integrity": "sha512-h6aK1da4tpqWSbyCa3FxB/V6Ehd4EEB15zyQq9qe75OZBp0krinNKuH4rAY+S/U/2I36vdLAUFSjQJ+TFmODng==" }, - "node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "node_modules/express-rate-limit": { + "version": "5.2.6", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-5.2.6.tgz", + "integrity": "sha512-nE96xaxGfxiS5jP3tD3kIW1Jg9yQgX0rXCs3rCkZtmbWHEGyotwaezkLj7bnB41Z0uaOLM8W4AX6qHao4IZ2YA==" }, "node_modules/faker": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/faker/-/faker-5.5.3.tgz", - "integrity": "sha512-wLTv2a28wjUyWkbnX7u/ABZBkUkIF2fCd73V6P2oFqEGEktDfzWx4UxrSqtPRw0xPRAcjeAOIiJWqZm3pP4u3g==" + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/faker/-/faker-5.4.0.tgz", + "integrity": "sha512-Y9n/Ky/xZx/Bj8DePvXspUYRtHl/rGQytoIT5LaxmNwSe3wWyOeOXb3lT6Dpipq240PVpeFaGKzScz/5fvff2g==" + }, + "node_modules/fast-safe-stringify": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", + "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==", + "dev": true + }, + "node_modules/file-extension": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/file-extension/-/file-extension-4.0.5.tgz", + "integrity": "sha512-l0rOL3aKkoi6ea7MNZe6OHgqYYpn48Qfflr8Pe9G9JPPTx5A+sfboK91ZufzIs59/lPqh351l0eb6iKU9J5oGg==", + "engines": { + "node": ">=4" + } }, "node_modules/fill-range": { "version": "7.0.1", @@ -736,23 +1828,38 @@ "node": ">= 0.8" } }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, "dependencies": { - "ms": "2.0.0" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" } }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/formidable": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.2.tgz", + "integrity": "sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q==", + "dev": true }, "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", "engines": { "node": ">= 0.6" } @@ -765,18 +1872,27 @@ "node": ">= 0.6" } }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", "dev": true, - "hasInstallScript": true, "optional": true, "os": [ "darwin" @@ -788,19 +1904,68 @@ "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "dependencies": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } }, "node_modules/generaterr": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/generaterr/-/generaterr-1.5.0.tgz", "integrity": "sha1-sM62zFFk3yoGEzjMNAqGFTlcUvw=" }, - "node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, + "node_modules/get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "engines": { + "node": ">=4" + } + }, + "node_modules/glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -811,15 +1976,12 @@ }, "engines": { "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" } }, "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", "dev": true, "dependencies": { "is-glob": "^4.0.1" @@ -828,11 +1990,45 @@ "node": ">= 6" } }, + "node_modules/got": { + "version": "11.7.0", + "resolved": "https://registry.npmjs.org/got/-/got-11.7.0.tgz", + "integrity": "sha512-7en2XwH2MEqOsrK0xaKhbWibBoZqy+f1RSUoIeF1BLcnf+pyQdDsljWMfmOh+QKJwuvDIiKx38GtPh5wFdGGjg==", + "dependencies": { + "@sindresorhus/is": "^3.1.1", + "@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.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" + }, + "node_modules/growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true, + "engines": { + "node": ">=4.x" + } + }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, "dependencies": { "function-bind": "^1.1.1" }, @@ -840,6 +2036,96 @@ "node": ">= 0.4.0" } }, + "node_modules/has-cors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", + "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=" + }, + "node_modules/has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + }, + "node_modules/has-values": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-2.0.1.tgz", + "integrity": "sha512-+QdH3jOmq9P8GfdjFg0eJudqx1FqU62NQJ4P16rOEHeRdl7ckgwn6uqQjzYE0ZoHVV/e5E2esuJ5Gl5+HUW19w==", + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/helmet": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-4.4.1.tgz", + "integrity": "sha512-G8tp0wUMI7i8wkMk2xLcEvESg5PiCitFMYgGRc/PwULB0RVhTP5GFdxOwvJwp9XVha8CuS8mnhmE8I/8dx/pbw==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "dependencies": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" + }, + "node_modules/htmlparser2/node_modules/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==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/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==" + }, "node_modules/http-errors": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", @@ -855,6 +2141,102 @@ "node": ">= 0.6" } }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-agent/node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/http-proxy-agent/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/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==", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/i": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/i/-/i-0.3.6.tgz", + "integrity": "sha1-2WyScyB28HJxG2sQ/X1PZa2O4j0=", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -866,11 +2248,39 @@ "node": ">=0.10.0" } }, + "node_modules/image-extensions": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/image-extensions/-/image-extensions-1.1.0.tgz", + "integrity": "sha1-uOa/YDnfAFbjM1AqALZjejEF2JQ=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==" + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -881,6 +2291,28 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, + "node_modules/install-artifact-from-github": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/install-artifact-from-github/-/install-artifact-from-github-1.2.0.tgz", + "integrity": "sha512-3OxCPcY55XlVM3kkfIpeCgmoSKnMsz2A3Dbhsq0RXpIknKQmrX1YiznCeW9cD2ItFmDxziA3w6Eg8d80AoL3oA==", + "bin": { + "install-from-cache": "bin/install-from-cache.js", + "save-to-github-cache": "bin/save-to-github-cache.js" + } + }, + "node_modules/ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" + }, + "node_modules/ip-regex": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.2.0.tgz", + "integrity": "sha512-n5cDDeTWWRwK1EBoWwRti+8nP4NbytBBY0pldmnIkq6Z55KNFmWofh4rl9dPZpj+U/nVq7gweR3ylrvMt4YZ5A==", + "engines": { + "node": ">=8" + } + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -889,6 +2321,14 @@ "node": ">= 0.10" } }, + "node_modules/is-absolute-url": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", + "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", + "engines": { + "node": ">=8" + } + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -902,15 +2342,20 @@ } }, "node_modules/is-core-module": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.5.0.tgz", - "integrity": "sha512-TXCMSDsEHMEEZ6eCA8rwRDbLu55MRGmrctljsBX/2v1d9/GzqHOxW5c5oPSgrUt2vBFXebu9rGqckXGPWOlYpg==", - "dev": true, + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", + "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", "dependencies": { "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-expression": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-4.0.0.tgz", + "integrity": "sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==", + "dependencies": { + "acorn": "^7.1.1", + "object-assign": "^4.1.1" } }, "node_modules/is-extglob": { @@ -922,6 +2367,14 @@ "node": ">=0.10.0" } }, + "node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dependencies": { + "number-is-nan": "^1.0.0" + } + }, "node_modules/is-glob": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", @@ -934,6 +2387,11 @@ "node": ">=0.10.0" } }, + "node_modules/is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU=" + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -943,10 +2401,100 @@ "node": ">=0.12.0" } }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" + }, + "node_modules/is-regex": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.2.tgz", + "integrity": "sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg==", + "dependencies": { + "call-bind": "^1.0.2", + "has-symbols": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-relative-url": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-relative-url/-/is-relative-url-3.0.0.tgz", + "integrity": "sha512-U1iSYRlY2GIMGuZx7gezlB5dp1Kheaym7zKzO1PV06mOihiWTXejLwm4poEJysPyXF+HtK/BEd0DVlcCh30pEA==", + "dependencies": { + "is-absolute-url": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-uri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-uri/-/is-uri-1.2.0.tgz", + "integrity": "sha1-uS/yNK9owO2X0u7UZJLQF5O31CA=", + "dependencies": { + "parse-uri": "~1.0.0", + "punycode2": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "node_modules/iso-639-3": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/iso-639-3/-/iso-639-3-2.1.0.tgz", + "integrity": "sha512-NYcq+YfrCFVGw/xWhRB9mhCSWxlOxYv3eK3WzWzc86P8huEZ7UDQq8Bu0zKqpZFOdq221Gy8VWWLr1aaYc+FJA==" + }, + "node_modules/isostring": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isostring/-/isostring-0.0.1.tgz", + "integrity": "sha1-3bYI77/InNqG25yxa+CQp4gTTH8=" + }, + "node_modules/js-stringify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", + "integrity": "sha1-Fzb939lyTyijaCrcYjCufk6Weds=" + }, + "node_modules/js-yaml": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/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==" }, "node_modules/jsonwebtoken": { "version": "8.5.1", @@ -969,6 +2517,20 @@ "npm": ">=1.4.28" } }, + "node_modules/jsonwebtoken/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/jstransformer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", + "integrity": "sha1-7Yvwkh4vPx7U1cGkT2hwntJHIsM=", + "dependencies": { + "is-promise": "^2.0.0", + "promise": "^7.0.1" + } + }, "node_modules/jwa": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", @@ -989,9 +2551,70 @@ } }, "node_modules/kareem": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.2.tgz", - "integrity": "sha512-STHz9P7X2L4Kwn72fA4rGyqyXdmrMSdxqHx9IXon/FXluXieaFA6KJ2upcHAHxQPQ0LeM/OjLrhFxifHewOALQ==" + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.1.tgz", + "integrity": "sha512-l3hLhffs9zqoDe8zjmb/mAN4B8VT3L56EUvKNqLFVs9YlFA+zx7ke1DO8STAdDyYNkeSo1nKmjuvQeI12So8Xw==" + }, + "node_modules/keyv": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz", + "integrity": "sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" + }, + "node_modules/lodash.at": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.at/-/lodash.at-4.6.0.tgz", + "integrity": "sha1-k83OZk8KGZTqM9181A4jr9EbD/g=", + "dev": true + }, + "node_modules/lodash.concat": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.concat/-/lodash.concat-4.5.0.tgz", + "integrity": "sha1-sFOuAuSoAIWC5yVrnQK9ptA4A5U=", + "dev": true + }, + "node_modules/lodash.filter": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz", + "integrity": "sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4=", + "dev": true + }, + "node_modules/lodash.foreach": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", + "integrity": "sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM=" + }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" }, "node_modules/lodash.includes": { "version": "4.3.0", @@ -1003,6 +2626,12 @@ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" }, + "node_modules/lodash.isfunction": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", + "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==", + "dev": true + }, "node_modules/lodash.isinteger": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", @@ -1013,6 +2642,12 @@ "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" }, + "node_modules/lodash.isobject": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz", + "integrity": "sha1-PI+41bW/S/kK4G4U8qUwpO2TXh0=", + "dev": true + }, "node_modules/lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", @@ -1023,16 +2658,167 @@ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" }, + "node_modules/lodash.isundefined": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz", + "integrity": "sha1-I+89lTVWUgOmbO/VuDD4SJEa+0g=", + "dev": true + }, + "node_modules/lodash.keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-4.2.0.tgz", + "integrity": "sha1-oIYCrBLk+4P5H8H7ejYKTZujUgU=", + "dev": true + }, "node_modules/lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" }, + "node_modules/lodash.toarray": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz", + "integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE=", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", + "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/log-symbols/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/log-symbols/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/log-symbols/node_modules/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 + }, + "node_modules/log-symbols/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-symbols/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/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==", + "dependencies": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + }, + "node_modules/make-fetch-happen": { + "version": "8.0.14", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-8.0.14.tgz", + "integrity": "sha512-EsS89h6l4vbfJEtBZnENTOFk8mCRpY5ru36Xe5bcX1KYIli2mkSHqoFsp5O1wMDvTJJzxe/4THpCTtygjeeGWQ==", + "dependencies": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.0.5", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^5.0.0", + "ssri": "^8.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/make-fetch-happen/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-fetch-happen/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/media-typer": { "version": "0.3.0", @@ -1042,6 +2828,11 @@ "node": ">= 0.6" } }, + "node_modules/memoize-one": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.1.1.tgz", + "integrity": "sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA==" + }, "node_modules/memory-pager": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", @@ -1053,6 +2844,66 @@ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" }, + "node_modules/metascraper": { + "version": "5.14.14", + "resolved": "https://registry.npmjs.org/metascraper/-/metascraper-5.14.14.tgz", + "integrity": "sha512-jKG4jOFLF+6uaP9fjZsEOih5sk7Ru5HDr1pc2R4D9DhTqfq8VJ8a28/QL55U+XMGWHuT4I7lfOo36GO7kqadcw==", + "dependencies": { + "@metascraper/helpers": "^5.14.14", + "cheerio": "~1.0.0-rc.3", + "cheerio-advanced-selectors": "~2.0.1", + "lodash": "~4.17.20", + "whoops": "~4.1.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/metascraper-description": { + "version": "5.14.14", + "resolved": "https://registry.npmjs.org/metascraper-description/-/metascraper-description-5.14.14.tgz", + "integrity": "sha512-d/tqcrJbcv0l+R89WTJmjH95XQlVXETS15VIelnEYRYqOdnZbOz3ZOPgs+SKzo4fz8LwRyQ6aKqFocnGvjv2wA==", + "dependencies": { + "@metascraper/helpers": "^5.14.14" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/metascraper-image": { + "version": "5.14.14", + "resolved": "https://registry.npmjs.org/metascraper-image/-/metascraper-image-5.14.14.tgz", + "integrity": "sha512-vkza91FrDQH/JFuqJLzBSUcjXNbPXvvA5HfaoAealQUQyIaF6AyxwPGzGe9aJo9h5Iw5njysLWNwxZMrtOq1kw==", + "dependencies": { + "@metascraper/helpers": "^5.14.14" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/metascraper-title": { + "version": "5.14.14", + "resolved": "https://registry.npmjs.org/metascraper-title/-/metascraper-title-5.14.14.tgz", + "integrity": "sha512-2uiAJmG+buADSmM1BTI499tBcmLGyGFkpQSGwqc2wqn1K87r1yuqHRSbhNqDNkqYhfT9px3DIek5daOrzxe1ew==", + "dependencies": { + "@metascraper/helpers": "^5.14.14", + "lodash": "~4.17.20" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/metascraper-url": { + "version": "5.14.14", + "resolved": "https://registry.npmjs.org/metascraper-url/-/metascraper-url-5.14.14.tgz", + "integrity": "sha512-BJccdsdVymwbxDqFRZkndYn3rGJ9BFgnF87ilMkPiLg0aP+2n7028Kl2KnNGP0eFBe1m/yUXHivQ27xcCBdBmw==", + "dependencies": { + "@metascraper/helpers": "^5.14.14" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -1073,29 +2924,44 @@ } }, "node_modules/mime-db": { - "version": "1.48.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz", - "integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==", + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.31", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz", - "integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==", + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", "dependencies": { - "mime-db": "1.48.0" + "mime-db": "1.44.0" }, "engines": { "node": ">= 0.6" } }, + "node_modules/mimic-fn": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.0.0.tgz", + "integrity": "sha512-PiVO95TKvhiwgSwg1IdLYlCTdul38yZxZMIcnDSFIBUm4BNZha2qpQ4GpJ++15bHoKDtrW2D69lMfFwdFYtNZQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "engines": { + "node": ">=4" + } + }, "node_modules/minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -1103,17 +2969,103 @@ "node": "*" } }, - "node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true + "node_modules/minipass": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", + "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-fetch": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.3.3.tgz", + "integrity": "sha512-akCrLDWfbdAWkMLBxJEeWTdNsjML+dt5YgOI4gJ53vuO0vrmYQkUPxa6j6V65s9CcePIr2SSWqjT2EcrNseryQ==", + "dependencies": { + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "optionalDependencies": { + "encoding": "^0.1.12" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, "bin": { "mkdirp": "bin/cmd.js" }, @@ -1121,15 +3073,118 @@ "node": ">=10" } }, + "node_modules/mocha": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.2.1.tgz", + "integrity": "sha512-cuLBVfyFfFqbNR0uUKbDGXKGk+UDFe6aR4os78XIrMQpZl/nv7JYHcvP5MFIAb374b2zFXsdgEGwmzMtP0Xg8w==", + "dev": true, + "dependencies": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.4.3", + "debug": "4.2.0", + "diff": "4.0.2", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.1.6", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "3.14.0", + "log-symbols": "4.0.0", + "minimatch": "3.0.4", + "ms": "2.1.2", + "nanoid": "3.1.12", + "serialize-javascript": "5.0.1", + "strip-json-comments": "3.1.1", + "supports-color": "7.2.0", + "which": "2.0.2", + "wide-align": "1.1.3", + "workerpool": "6.0.2", + "yargs": "13.3.2", + "yargs-parser": "13.1.2", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 10.12.0" + } + }, + "node_modules/mocha/node_modules/debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/mocha/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/mongodb": { - "version": "3.6.10", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.10.tgz", - "integrity": "sha512-fvIBQBF7KwCJnDZUnFFy4WqEFP8ibdXeFANnylW19+vOwdjOAvqIzPdsNCEMT6VKTHnYu4K64AWRih0mkFms6Q==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.2.tgz", + "integrity": "sha512-sSZOb04w3HcnrrXC82NEh/YGCmBuRgR+C1hZgmmv4L6dBz4BkRse6Y8/q/neXer9i95fKUBbFi4KgeceXmbsOA==", "dependencies": { "bl": "^2.2.1", "bson": "^1.1.4", "denque": "^1.4.1", - "optional-require": "^1.0.3", + "require_optional": "^1.0.1", "safe-buffer": "^5.1.2" }, "engines": { @@ -1137,99 +3192,65 @@ }, "optionalDependencies": { "saslprep": "^1.0.0" - }, - "peerDependenciesMeta": { - "aws4": { - "optional": true - }, - "bson-ext": { - "optional": true - }, - "kerberos": { - "optional": true - }, - "mongodb-client-encryption": { - "optional": true - }, - "mongodb-extjson": { - "optional": true - }, - "snappy": { - "optional": true - } } }, "node_modules/mongoose": { - "version": "5.13.3", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.13.3.tgz", - "integrity": "sha512-q+zX6kqHAvwxf5speMWhq6qF4vdj+x6/kfD5RSKdZKNm52yGmaUygN+zgrtQjBZPFEzG0B3vF6GP0PoAGadE+w==", + "version": "5.10.7", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.10.7.tgz", + "integrity": "sha512-oiofFrD4I5p3PhJXn49QyrU1nX5CY01qhPkfMMrXYPhkfGLEJVwFVO+0PsCxD91A2kQP+d/iFyk5U8e86KI8eQ==", "dependencies": { - "@types/mongodb": "^3.5.27", - "@types/node": "14.x || 15.x", "bson": "^1.1.4", - "kareem": "2.3.2", - "mongodb": "3.6.10", + "kareem": "2.3.1", + "mongodb": "3.6.2", "mongoose-legacy-pluralize": "1.0.2", - "mpath": "0.8.3", - "mquery": "3.2.5", + "mpath": "0.7.0", + "mquery": "3.2.2", "ms": "2.1.2", "regexp-clone": "1.0.0", "safe-buffer": "5.2.1", - "sift": "13.5.2", + "sift": "7.0.1", "sliced": "1.0.1" }, "engines": { "node": ">=4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mongoose" } }, "node_modules/mongoose-legacy-pluralize": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz", - "integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==", - "peerDependencies": { - "mongoose": "*" + "integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==" + }, + "node_modules/mongoose-unique-validator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/mongoose-unique-validator/-/mongoose-unique-validator-2.0.3.tgz", + "integrity": "sha512-3/8pmvAC1acBZS6eWKAWQUiZBlARE1wyWtjga4iQ2wDJeOfRlIKmAvTNHSZXKaAf7RCRUd7wh7as6yWAOrjpQg==", + "dependencies": { + "lodash.foreach": "^4.1.0", + "lodash.get": "^4.0.2" } }, - "node_modules/mongoose/node_modules/@types/node": { - "version": "15.14.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-15.14.3.tgz", - "integrity": "sha512-gliNP92vLGGha1nioYHIIT2WrZ450sxpRgyPCEyog2hMVi6LEbhY/Pkj+EDiGWrCXntZ9lrnE2+lTIlyYtaxCg==" + "node_modules/mongoose/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/mongoose/node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" }, "node_modules/mpath": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.8.3.tgz", - "integrity": "sha512-eb9rRvhDltXVNL6Fxd2zM9D4vKBxjVVQNLNijlj7uoXUy19zNDsIif5zR+pWmPCWNKwAtqyo4JveQm4nfD5+eA==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.7.0.tgz", + "integrity": "sha512-Aiq04hILxhz1L+f7sjGyn7IxYzWm1zLNNXcfhDtx04kZ2Gk7uvFdgZ8ts1cWa/6d0TQmag2yR8zSGZUmp0tFNg==", "engines": { "node": ">=4.0.0" } }, "node_modules/mquery": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/mquery/-/mquery-3.2.5.tgz", - "integrity": "sha512-VjOKHHgU84wij7IUoZzFRU07IAxd5kWJaDmyUzQlbjHjyoeK5TNeeo8ZsFDtTYnSgpW6n/nMNIHvE3u8Lbrf4A==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-3.2.2.tgz", + "integrity": "sha512-XB52992COp0KP230I3qloVUbkLUxJIu328HBP2t2EsxSFtf4W1HPSOBWOXf1bqxK4Xbb66lfMJ+Bpfd9/yZE1Q==", "dependencies": { "bluebird": "3.5.1", "debug": "3.1.0", @@ -1249,15 +3270,27 @@ "ms": "2.0.0" } }, - "node_modules/mquery/node_modules/ms": { + "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "node_modules/nan": { + "version": "2.14.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", + "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==" + }, + "node_modules/nanoid": { + "version": "3.1.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.12.tgz", + "integrity": "sha512-1qstj9z5+x491jfiC4Nelk+f8XBad7LN20PmyWINJEMRSf3wcAjAWysw1qaA8z6NSKe2sjq1hRSDpBH5paCb6A==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || >=13.7" + } }, "node_modules/negotiator": { "version": "0.6.2", @@ -1267,6 +3300,111 @@ "node": ">= 0.6" } }, + "node_modules/node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "engines": { + "node": "4.x || >=6.0.0" + } + }, + "node_modules/node-gyp": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.0.0.tgz", + "integrity": "sha512-Jod6NxyWtcwrpAQe0O/aXOpC5QfncotgtG73dg65z6VW/C6g/G4jiajXQUBIJ8pk/VfM6mBYE9BN/HvudTunUQ==", + "dependencies": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^8.0.14", + "nopt": "^5.0.0", + "npmlog": "^4.1.2", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.0", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": ">= 10.12.0" + } + }, + "node_modules/node-gyp/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-gyp/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-gyp/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/node-gyp/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/nodemailer": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.5.0.tgz", + "integrity": "sha512-Tm4RPrrIZbnqDKAvX+/4M+zovEReiKlEXWDzG4iwtpL9X34MJY+D5LnQPH/+eghe8DLlAVshHAJZAZWBGhkguw==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/nodemailer-pug-engine": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/nodemailer-pug-engine/-/nodemailer-pug-engine-2.0.0.tgz", + "integrity": "sha512-hslY4VdaDrLLYosC7Q7D+dK4FwahJJWqYWvWaW3isNIQ4wrUperjr31FrEye1ZPsq9giPMVQ2O0q/EwOPx166Q==", + "dependencies": { + "pug": "^3.0.0" + } + }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -1276,6 +3414,2861 @@ "node": ">=0.10.0" } }, + "node_modules/normalize-url": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-5.2.1.tgz", + "integrity": "sha512-bFT2ilr7p37ZPEQ9LO9HP/tdFIAE7Q4UoeojXNKeLjs0vXxZetM+C2K9jdbVS7b6ut66CflVLgk1yqHJVrXmiw==", + "engines": { + "node": ">=10" + } + }, + "node_modules/npm": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/npm/-/npm-7.6.3.tgz", + "integrity": "sha512-+Cs8TEtkfdQGTIPw8AeqVtNNHyo1Zw8HATzAFFWYnK7jQYgT/CatEy85+BlEoEpqvga2uaKqVrXsTAYj28emjg==", + "bundleDependencies": [ + "@npmcli/arborist", + "@npmcli/ci-detect", + "@npmcli/config", + "@npmcli/run-script", + "abbrev", + "ansicolors", + "ansistyles", + "archy", + "byte-size", + "cacache", + "chalk", + "chownr", + "cli-columns", + "cli-table3", + "columnify", + "glob", + "graceful-fs", + "hosted-git-info", + "ini", + "init-package-json", + "is-cidr", + "json-parse-even-better-errors", + "leven", + "libnpmaccess", + "libnpmdiff", + "libnpmfund", + "libnpmhook", + "libnpmorg", + "libnpmpack", + "libnpmpublish", + "libnpmsearch", + "libnpmteam", + "libnpmversion", + "make-fetch-happen", + "minipass", + "minipass-pipeline", + "mkdirp", + "mkdirp-infer-owner", + "ms", + "node-gyp", + "nopt", + "npm-audit-report", + "npm-package-arg", + "npm-pick-manifest", + "npm-profile", + "npm-registry-fetch", + "npm-user-validate", + "npmlog", + "opener", + "pacote", + "parse-conflict-json", + "qrcode-terminal", + "read", + "read-package-json", + "read-package-json-fast", + "readdir-scoped-modules", + "rimraf", + "semver", + "ssri", + "tar", + "text-table", + "tiny-relative-date", + "treeverse", + "validate-npm-package-name", + "which", + "write-file-atomic" + ], + "dev": true, + "dependencies": { + "@npmcli/arborist": "^2.2.8", + "@npmcli/ci-detect": "^1.2.0", + "@npmcli/config": "^1.2.9", + "@npmcli/run-script": "^1.8.3", + "abbrev": "~1.1.1", + "ansicolors": "~0.3.2", + "ansistyles": "~0.1.3", + "archy": "~1.0.0", + "byte-size": "^7.0.1", + "cacache": "^15.0.5", + "chalk": "^4.1.0", + "chownr": "^2.0.0", + "cli-columns": "^3.1.2", + "cli-table3": "^0.6.0", + "columnify": "~1.5.4", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "hosted-git-info": "^3.0.8", + "ini": "^2.0.0", + "init-package-json": "^2.0.2", + "is-cidr": "^4.0.2", + "json-parse-even-better-errors": "^2.3.1", + "leven": "^3.1.0", + "libnpmaccess": "^4.0.1", + "libnpmdiff": "^2.0.4", + "libnpmfund": "^1.0.2", + "libnpmhook": "^6.0.1", + "libnpmorg": "^2.0.1", + "libnpmpack": "^2.0.1", + "libnpmpublish": "^4.0.0", + "libnpmsearch": "^3.1.0", + "libnpmteam": "^2.0.2", + "libnpmversion": "^1.0.11", + "make-fetch-happen": "^8.0.14", + "minipass": "^3.1.3", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "mkdirp-infer-owner": "^2.0.0", + "ms": "^2.1.2", + "node-gyp": "^7.1.2", + "nopt": "^5.0.0", + "npm-audit-report": "^2.1.4", + "npm-package-arg": "^8.1.1", + "npm-pick-manifest": "^6.1.0", + "npm-profile": "^5.0.2", + "npm-registry-fetch": "^9.0.0", + "npm-user-validate": "^1.0.1", + "npmlog": "~4.1.2", + "opener": "^1.5.2", + "pacote": "^11.3.0", + "parse-conflict-json": "^1.1.1", + "qrcode-terminal": "^0.12.0", + "read": "~1.0.7", + "read-package-json": "^3.0.1", + "read-package-json-fast": "^2.0.2", + "readdir-scoped-modules": "^1.1.0", + "rimraf": "^3.0.2", + "semver": "^7.3.4", + "ssri": "^8.0.1", + "tar": "^6.1.0", + "text-table": "~0.2.0", + "tiny-relative-date": "^1.3.0", + "treeverse": "^1.0.4", + "validate-npm-package-name": "~3.0.0", + "which": "^2.0.2", + "write-file-atomic": "^3.0.3" + }, + "bin": { + "npm": "bin/npm-cli.js", + "npx": "bin/npx-cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/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=", + "dependencies": { + "path-key": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/@npmcli/arborist": { + "version": "2.2.8", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/installed-package-contents": "^1.0.7", + "@npmcli/map-workspaces": "^1.0.2", + "@npmcli/metavuln-calculator": "^1.1.0", + "@npmcli/move-file": "^1.1.0", + "@npmcli/name-from-folder": "^1.0.1", + "@npmcli/node-gyp": "^1.0.1", + "@npmcli/run-script": "^1.8.2", + "bin-links": "^2.2.1", + "cacache": "^15.0.3", + "common-ancestor-path": "^1.0.1", + "json-parse-even-better-errors": "^2.3.1", + "json-stringify-nice": "^1.1.1", + "mkdirp-infer-owner": "^2.0.0", + "npm-install-checks": "^4.0.0", + "npm-package-arg": "^8.1.0", + "npm-pick-manifest": "^6.1.0", + "npm-registry-fetch": "^9.0.0", + "pacote": "^11.2.6", + "parse-conflict-json": "^1.1.1", + "promise-all-reject-late": "^1.0.0", + "promise-call-limit": "^1.0.1", + "read-package-json-fast": "^2.0.2", + "readdir-scoped-modules": "^1.1.0", + "semver": "^7.3.4", + "tar": "^6.1.0", + "treeverse": "^1.0.4", + "walk-up-path": "^1.0.0" + }, + "bin": { + "arborist": "bin/index.js" + } + }, + "node_modules/npm/node_modules/@npmcli/ci-detect": { + "version": "1.3.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/@npmcli/config": { + "version": "1.2.9", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "ini": "^2.0.0", + "mkdirp-infer-owner": "^2.0.0", + "nopt": "^5.0.0", + "semver": "^7.3.4", + "walk-up-path": "^1.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/@npmcli/disparity-colors": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "ansi-styles": "^4.3.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/@npmcli/git": { + "version": "2.0.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/promise-spawn": "^1.1.0", + "lru-cache": "^6.0.0", + "mkdirp": "^1.0.3", + "npm-pick-manifest": "^6.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.2", + "unique-filename": "^1.1.1", + "which": "^2.0.2" + } + }, + "node_modules/npm/node_modules/@npmcli/installed-package-contents": { + "version": "1.0.7", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + }, + "bin": { + "installed-package-contents": "index.js" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm/node_modules/@npmcli/map-workspaces": { + "version": "1.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/name-from-folder": "^1.0.1", + "glob": "^7.1.6", + "minimatch": "^3.0.4", + "read-package-json-fast": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "cacache": "^15.0.5", + "pacote": "^11.1.11", + "semver": "^7.3.2" + } + }, + "node_modules/npm/node_modules/@npmcli/move-file": { + "version": "1.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/@npmcli/name-from-folder": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/@npmcli/node-gyp": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/@npmcli/promise-spawn": { + "version": "1.3.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "infer-owner": "^1.0.4" + } + }, + "node_modules/npm/node_modules/@npmcli/run-script": { + "version": "1.8.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/node-gyp": "^1.0.2", + "@npmcli/promise-spawn": "^1.3.2", + "infer-owner": "^1.0.4", + "node-gyp": "^7.1.0", + "puka": "^1.0.1", + "read-package-json-fast": "^2.0.1" + } + }, + "node_modules/npm/node_modules/@tootallnate/once": { + "version": "1.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/npm/node_modules/abbrev": { + "version": "1.1.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/agent-base": { + "version": "6.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/npm/node_modules/agentkeepalive": { + "version": "4.1.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.0", + "depd": "^1.1.2", + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/npm/node_modules/aggregate-error": { + "version": "3.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/ajv": { + "version": "6.12.6", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/npm/node_modules/ansi-regex": { + "version": "2.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/ansicolors": { + "version": "0.3.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/ansistyles": { + "version": "0.1.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/aproba": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/archy": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/are-we-there-yet": { + "version": "1.1.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "node_modules/npm/node_modules/asap": { + "version": "2.0.6", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/asn1": { + "version": "0.2.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/npm/node_modules/assert-plus": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/npm/node_modules/asynckit": { + "version": "0.4.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/aws-sign2": { + "version": "0.7.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/npm/node_modules/aws4": { + "version": "1.11.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/balanced-match": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "BSD-3-Clause", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/npm/node_modules/bin-links": { + "version": "2.2.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "cmd-shim": "^4.0.1", + "mkdirp": "^1.0.3", + "npm-normalize-package-bin": "^1.0.0", + "read-cmd-shim": "^2.0.0", + "rimraf": "^3.0.0", + "write-file-atomic": "^3.0.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/binary-extensions": { + "version": "2.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/brace-expansion": { + "version": "1.1.11", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/npm/node_modules/builtins": { + "version": "1.0.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/byte-size": { + "version": "7.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/cacache": { + "version": "15.0.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.0", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm/node_modules/caseless": { + "version": "0.12.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0" + }, + "node_modules/npm/node_modules/chalk": { + "version": "4.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/npm/node_modules/chownr": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/cidr-regex": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "ip-regex": "^4.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/clean-stack": { + "version": "2.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/cli-columns": { + "version": "3.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "string-width": "^2.0.0", + "strip-ansi": "^3.0.1" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm/node_modules/cli-table3": { + "version": "0.6.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "object-assign": "^4.1.0", + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "colors": "^1.1.2" + } + }, + "node_modules/npm/node_modules/cli-table3/node_modules/ansi-regex": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/cli-table3/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/cli-table3/node_modules/string-width": { + "version": "4.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/cli-table3/node_modules/strip-ansi": { + "version": "6.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/clone": { + "version": "1.0.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/npm/node_modules/cmd-shim": { + "version": "4.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "mkdirp-infer-owner": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/code-point-at": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/npm/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/colors": { + "version": "1.4.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/npm/node_modules/columnify": { + "version": "1.5.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "strip-ansi": "^3.0.0", + "wcwidth": "^1.0.0" + } + }, + "node_modules/npm/node_modules/combined-stream": { + "version": "1.0.8", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/npm/node_modules/common-ancestor-path": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/concat-map": { + "version": "0.0.1", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/console-control-strings": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/core-util-is": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/dashdash": { + "version": "1.14.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/npm/node_modules/debug": { + "version": "4.3.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/npm/node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/debuglog": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/npm/node_modules/defaults": { + "version": "1.0.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + } + }, + "node_modules/npm/node_modules/delayed-stream": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/npm/node_modules/delegates": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/depd": { + "version": "1.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/npm/node_modules/dezalgo": { + "version": "1.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "node_modules/npm/node_modules/diff": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/npm/node_modules/ecc-jsbn": { + "version": "0.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/npm/node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/encoding": { + "version": "0.1.13", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/npm/node_modules/env-paths": { + "version": "2.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/err-code": { + "version": "2.0.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/extend": { + "version": "3.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/extsprintf": { + "version": "1.3.0", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/fast-deep-equal": { + "version": "3.1.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/forever-agent": { + "version": "0.6.1", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/npm/node_modules/form-data": { + "version": "2.3.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/npm/node_modules/fs-minipass": { + "version": "2.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/fs.realpath": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/function-bind": { + "version": "1.1.1", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/gauge": { + "version": "2.7.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "node_modules/npm/node_modules/gauge/node_modules/aproba": { + "version": "1.2.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/gauge/node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/gauge/node_modules/string-width": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/getpass": { + "version": "0.1.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/npm/node_modules/glob": { + "version": "7.1.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/graceful-fs": { + "version": "4.2.6", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/har-schema": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/har-validator": { + "version": "5.1.5", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/has": { + "version": "1.0.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/npm/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/has-unicode": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/hosted-git-info": { + "version": "3.0.8", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/http-cache-semantics": { + "version": "4.1.0", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause" + }, + "node_modules/npm/node_modules/http-proxy-agent": { + "version": "4.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/npm/node_modules/http-signature": { + "version": "1.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/npm/node_modules/https-proxy-agent": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/npm/node_modules/humanize-ms": { + "version": "1.2.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/npm/node_modules/iconv-lite": { + "version": "0.6.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/ignore-walk": { + "version": "3.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minimatch": "^3.0.4" + } + }, + "node_modules/npm/node_modules/imurmurhash": { + "version": "0.1.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/npm/node_modules/indent-string": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/infer-owner": { + "version": "1.0.4", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/inflight": { + "version": "1.0.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/npm/node_modules/inherits": { + "version": "2.0.4", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/ini": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/init-package-json": { + "version": "2.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.1", + "npm-package-arg": "^8.1.0", + "promzard": "^0.3.0", + "read": "~1.0.1", + "read-package-json": "^3.0.0", + "semver": "^7.3.2", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/ip": { + "version": "1.1.5", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/ip-regex": { + "version": "4.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/is-cidr": { + "version": "4.0.2", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "cidr-regex": "^3.1.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/is-core-module": { + "version": "2.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/npm/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/is-lambda": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/is-typedarray": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/isarray": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/isexe": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/isstream": { + "version": "0.1.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/jsbn": { + "version": "0.1.1", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/json-schema": { + "version": "0.2.3", + "dev": true, + "inBundle": true + }, + "node_modules/npm/node_modules/json-schema-traverse": { + "version": "0.4.1", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/json-stringify-nice": { + "version": "1.1.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/json-stringify-safe": { + "version": "5.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/jsonparse": { + "version": "1.3.1", + "dev": true, + "engines": [ + "node >= 0.2.0" + ], + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/jsprim": { + "version": "1.4.1", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "inBundle": true, + "license": "MIT", + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "node_modules/npm/node_modules/just-diff": { + "version": "3.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff-apply": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/leven": { + "version": "3.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/libnpmaccess": { + "version": "4.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "minipass": "^3.1.1", + "npm-package-arg": "^8.0.0", + "npm-registry-fetch": "^9.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/libnpmdiff": { + "version": "2.0.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/disparity-colors": "^1.0.1", + "@npmcli/installed-package-contents": "^1.0.7", + "binary-extensions": "^2.2.0", + "diff": "^5.0.0", + "minimatch": "^3.0.4", + "npm-package-arg": "^8.1.1", + "pacote": "^11.3.0", + "tar": "^6.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/libnpmfund": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^2.0.0" + } + }, + "node_modules/npm/node_modules/libnpmhook": { + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^9.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/libnpmorg": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^9.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/libnpmpack": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/run-script": "^1.8.3", + "npm-package-arg": "^8.1.0", + "pacote": "^11.2.6" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/libnpmpublish": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "normalize-package-data": "^3.0.0", + "npm-package-arg": "^8.1.0", + "npm-registry-fetch": "^9.0.0", + "semver": "^7.1.3", + "ssri": "^8.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/libnpmsearch": { + "version": "3.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^9.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/libnpmteam": { + "version": "2.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^9.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/libnpmversion": { + "version": "1.0.11", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^2.0.6", + "@npmcli/run-script": "^1.8.3", + "read-package-json-fast": "^2.0.1", + "semver": "^7.3.4", + "stringify-package": "^1.0.1" + } + }, + "node_modules/npm/node_modules/lru-cache": { + "version": "6.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/make-fetch-happen": { + "version": "8.0.14", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.0.5", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^5.0.0", + "ssri": "^8.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm/node_modules/mime-db": { + "version": "1.45.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/npm/node_modules/mime-types": { + "version": "2.1.28", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.45.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/npm/node_modules/minimatch": { + "version": "3.0.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/npm/node_modules/minipass": { + "version": "3.1.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-collect": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/minipass-fetch": { + "version": "1.3.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "optionalDependencies": { + "encoding": "^0.1.12" + } + }, + "node_modules/npm/node_modules/minipass-flush": { + "version": "1.0.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/minipass-json-stream": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "jsonparse": "^1.3.1", + "minipass": "^3.0.0" + } + }, + "node_modules/npm/node_modules/minipass-pipeline": { + "version": "1.2.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-sized": { + "version": "1.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minizlib": { + "version": "2.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/mkdirp": { + "version": "1.0.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/mkdirp-infer-owner": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "infer-owner": "^1.0.4", + "mkdirp": "^1.0.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/ms": { + "version": "2.1.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/mute-stream": { + "version": "0.0.8", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/node-gyp": { + "version": "7.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.3", + "nopt": "^5.0.0", + "npmlog": "^4.1.2", + "request": "^2.88.2", + "rimraf": "^3.0.2", + "semver": "^7.3.2", + "tar": "^6.0.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": ">= 10.12.0" + } + }, + "node_modules/npm/node_modules/nopt": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/normalize-package-data": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^3.0.6", + "resolve": "^1.17.0", + "semver": "^7.3.2", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/npm-audit-report": { + "version": "2.1.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "chalk": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/npm-bundled": { + "version": "1.1.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "node_modules/npm/node_modules/npm-install-checks": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/npm-normalize-package-bin": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/npm-package-arg": { + "version": "8.1.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^3.0.6", + "semver": "^7.0.0", + "validate-npm-package-name": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/npm-packlist": { + "version": "2.1.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.6", + "ignore-walk": "^3.0.3", + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + }, + "bin": { + "npm-packlist": "bin/index.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/npm-pick-manifest": { + "version": "6.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-install-checks": "^4.0.0", + "npm-package-arg": "^8.0.0", + "semver": "^7.0.0" + } + }, + "node_modules/npm/node_modules/npm-profile": { + "version": "5.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^9.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/npm-registry-fetch": { + "version": "9.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/ci-detect": "^1.0.0", + "lru-cache": "^6.0.0", + "make-fetch-happen": "^8.0.9", + "minipass": "^3.1.3", + "minipass-fetch": "^1.3.0", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.0.0", + "npm-package-arg": "^8.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/npm-user-validate": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause" + }, + "node_modules/npm/node_modules/npmlog": { + "version": "4.1.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "node_modules/npm/node_modules/number-is-nan": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/oauth-sign": { + "version": "0.9.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/npm/node_modules/object-assign": { + "version": "4.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/once": { + "version": "1.4.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/npm/node_modules/opener": { + "version": "1.5.2", + "dev": true, + "inBundle": true, + "license": "(WTFPL OR MIT)", + "bin": { + "opener": "bin/opener-bin.js" + } + }, + "node_modules/npm/node_modules/p-map": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/pacote": { + "version": "11.3.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^2.0.1", + "@npmcli/installed-package-contents": "^1.0.6", + "@npmcli/promise-spawn": "^1.2.0", + "@npmcli/run-script": "^1.8.2", + "cacache": "^15.0.5", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "infer-owner": "^1.0.4", + "minipass": "^3.1.3", + "mkdirp": "^1.0.3", + "npm-package-arg": "^8.0.1", + "npm-packlist": "^2.1.4", + "npm-pick-manifest": "^6.0.0", + "npm-registry-fetch": "^9.0.0", + "promise-retry": "^2.0.1", + "read-package-json-fast": "^2.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.1.0" + }, + "bin": { + "pacote": "lib/bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/parse-conflict-json": { + "version": "1.1.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^2.3.0", + "just-diff": "^3.0.1", + "just-diff-apply": "^3.0.0" + } + }, + "node_modules/npm/node_modules/path-is-absolute": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/path-parse": { + "version": "1.0.6", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/performance-now": { + "version": "2.1.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/process-nextick-args": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/promise-all-reject-late": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-call-limit": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-inflight": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/promise-retry": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/promzard": { + "version": "0.3.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "read": "1" + } + }, + "node_modules/npm/node_modules/psl": { + "version": "1.8.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/puka": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/punycode": { + "version": "2.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/qrcode-terminal": { + "version": "0.12.0", + "dev": true, + "inBundle": true, + "bin": { + "qrcode-terminal": "bin/qrcode-terminal.js" + } + }, + "node_modules/npm/node_modules/qs": { + "version": "6.5.2", + "dev": true, + "inBundle": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/npm/node_modules/read": { + "version": "1.0.7", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "mute-stream": "~0.0.4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/npm/node_modules/read-cmd-shim": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/read-package-json": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.1", + "json-parse-even-better-errors": "^2.3.0", + "normalize-package-data": "^3.0.0", + "npm-normalize-package-bin": "^1.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/read-package-json-fast": { + "version": "2.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^2.3.0", + "npm-normalize-package-bin": "^1.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/readable-stream": { + "version": "2.3.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/npm/node_modules/readdir-scoped-modules": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "debuglog": "^1.0.1", + "dezalgo": "^1.0.0", + "graceful-fs": "^4.1.2", + "once": "^1.3.0" + } + }, + "node_modules/npm/node_modules/request": { + "version": "2.88.2", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/npm/node_modules/request/node_modules/tough-cookie": { + "version": "2.5.0", + "dev": true, + "inBundle": true, + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/npm/node_modules/resolve": { + "version": "1.20.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/npm/node_modules/retry": { + "version": "0.12.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm/node_modules/rimraf": { + "version": "3.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/safer-buffer": { + "version": "2.1.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/semver": { + "version": "7.3.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/set-blocking": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/signal-exit": { + "version": "3.0.3", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/smart-buffer": { + "version": "4.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks": { + "version": "2.5.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ip": "^1.1.5", + "smart-buffer": "^4.1.0" + }, + "engines": { + "node": ">= 10.13.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks-proxy-agent": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4", + "socks": "^2.3.3" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/npm/node_modules/spdx-correct": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-exceptions": { + "version": "2.3.0", + "dev": true, + "inBundle": true, + "license": "CC-BY-3.0" + }, + "node_modules/npm/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-license-ids": { + "version": "3.0.7", + "dev": true, + "inBundle": true, + "license": "CC0-1.0" + }, + "node_modules/npm/node_modules/sshpk": { + "version": "1.16.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/ssri": { + "version": "8.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/npm/node_modules/string-width": { + "version": "2.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/string-width/node_modules/ansi-regex": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/string-width/node_modules/strip-ansi": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/stringify-package": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/strip-ansi": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/tar": { + "version": "6.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm/node_modules/text-table": { + "version": "0.2.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/tiny-relative-date": { + "version": "1.3.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/treeverse": { + "version": "1.0.4", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/tunnel-agent": { + "version": "0.6.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/npm/node_modules/tweetnacl": { + "version": "0.14.5", + "dev": true, + "inBundle": true, + "license": "Unlicense" + }, + "node_modules/npm/node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/npm/node_modules/unique-filename": { + "version": "1.1.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/npm/node_modules/unique-slug": { + "version": "2.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, + "node_modules/npm/node_modules/uri-js": { + "version": "4.4.1", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/npm/node_modules/util-deprecate": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/uuid": { + "version": "3.4.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/npm/node_modules/validate-npm-package-license": { + "version": "3.0.4", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/npm/node_modules/validate-npm-package-name": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "builtins": "^1.0.3" + } + }, + "node_modules/npm/node_modules/verror": { + "version": "1.10.0", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "inBundle": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/npm/node_modules/walk-up-path": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/wcwidth": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/npm/node_modules/which": { + "version": "2.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/wide-align": { + "version": "1.1.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "string-width": "^1.0.2 || 2" + } + }, + "node_modules/npm/node_modules/wrappy": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/write-file-atomic": { + "version": "3.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/npm/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "dependencies": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "node_modules/nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "dependencies": { + "boolbase": "~1.0.0" + } + }, + "node_modules/number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -1284,6 +6277,12 @@ "node": ">=0.10.0" } }, + "node_modules/object-inspect": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", + "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==", + "dev": true + }, "node_modules/on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -1299,22 +6298,99 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "dependencies": { "wrappy": "1" } }, - "node_modules/optional-require": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.1.0.tgz", - "integrity": "sha512-5/7ee3eTFg1P+F9usTubuNCLfWRK6DjV0EFHLlbp7MmV5UlWqpWIVSnH6xo4u+fc5WHXaJuvJi6iuYnfDyj6oQ==", - "dependencies": { - "require-at": "^1.0.6" - }, + "node_modules/p-cancelable": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.0.0.tgz", + "integrity": "sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", "engines": { "node": ">=4" } }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/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, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-uri": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/parse-uri/-/parse-uri-1.0.3.tgz", + "integrity": "sha512-upMnGxNcm+45So85HoguwZTVZI9u11i36DdxJfGF2HYWS2eh3TIx7+/tTi7qrEq15qzGkVhsKjesau+kCk48pA==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/parse5": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz", + "integrity": "sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/parseqs": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz", + "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==" + }, + "node_modules/parseuri": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz", + "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==" + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -1347,16 +6423,28 @@ } }, "node_modules/passport-local-mongoose": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/passport-local-mongoose/-/passport-local-mongoose-6.1.0.tgz", - "integrity": "sha512-kxRDejpBXoPmWau1RCrmEeNYEXGG9ec4aDYjd0pFAHIEAzZ0RXKn581ISfjpHZ1zZLoCCM2pWUo4SfGHNJNwnw==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/passport-local-mongoose/-/passport-local-mongoose-6.0.1.tgz", + "integrity": "sha512-Jc/ImrVnG7o7aTIqrAznt2CxVxy5M2gAxc3erVZyPVUVfiwgvrhlzA9c5fswValA12H2C9mm1AjqGDz+491TDg==", "dependencies": { "generaterr": "^1.5.0", "passport-local": "^1.0.0", - "scmp": "^2.1.0" + "scmp": "^2.1.0", + "semver": "^7.1.1" }, "engines": { - "node": ">= 8.0.0" + "node": ">= 6.0.0" + } + }, + "node_modules/passport-local-mongoose/node_modules/semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/passport-strategy": { @@ -1367,41 +6455,62 @@ "node": ">= 0.4.0" } }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true, "engines": { "node": ">=0.10.0" } }, + "node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "engines": { + "node": ">=4" + } + }, "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" }, "node_modules/path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/pause": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" }, "node_modules/picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", "dev": true, "engines": { "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/process-nextick-args": { @@ -1409,18 +6518,174 @@ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "node_modules/promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", "dependencies": { - "forwarded": "0.2.0", + "asap": "~2.0.3" + } + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=" + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "dependencies": { + "forwarded": "~0.1.2", "ipaddr.js": "1.9.1" }, "engines": { "node": ">= 0.10" } }, + "node_modules/pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" + }, + "node_modules/pug": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pug/-/pug-3.0.2.tgz", + "integrity": "sha512-bp0I/hiK1D1vChHh6EfDxtndHji55XP/ZJKwsRqrz6lRia6ZC2OZbdAymlxdVFwd1L70ebrVJw4/eZ79skrIaw==", + "dependencies": { + "pug-code-gen": "^3.0.2", + "pug-filters": "^4.0.0", + "pug-lexer": "^5.0.1", + "pug-linker": "^4.0.0", + "pug-load": "^3.0.0", + "pug-parser": "^6.0.0", + "pug-runtime": "^3.0.1", + "pug-strip-comments": "^2.0.0" + } + }, + "node_modules/pug-attrs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-3.0.0.tgz", + "integrity": "sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA==", + "dependencies": { + "constantinople": "^4.0.1", + "js-stringify": "^1.0.2", + "pug-runtime": "^3.0.0" + } + }, + "node_modules/pug-code-gen": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-3.0.2.tgz", + "integrity": "sha512-nJMhW16MbiGRiyR4miDTQMRWDgKplnHyeLvioEJYbk1RsPI3FuA3saEP8uwnTb2nTJEKBU90NFVWJBk4OU5qyg==", + "dependencies": { + "constantinople": "^4.0.1", + "doctypes": "^1.1.0", + "js-stringify": "^1.0.2", + "pug-attrs": "^3.0.0", + "pug-error": "^2.0.0", + "pug-runtime": "^3.0.0", + "void-elements": "^3.1.0", + "with": "^7.0.0" + } + }, + "node_modules/pug-error": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-2.0.0.tgz", + "integrity": "sha512-sjiUsi9M4RAGHktC1drQfCr5C5eriu24Lfbt4s+7SykztEOwVZtbFk1RRq0tzLxcMxMYTBR+zMQaG07J/btayQ==" + }, + "node_modules/pug-filters": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-4.0.0.tgz", + "integrity": "sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A==", + "dependencies": { + "constantinople": "^4.0.1", + "jstransformer": "1.0.0", + "pug-error": "^2.0.0", + "pug-walk": "^2.0.0", + "resolve": "^1.15.1" + } + }, + "node_modules/pug-lexer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-5.0.1.tgz", + "integrity": "sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w==", + "dependencies": { + "character-parser": "^2.2.0", + "is-expression": "^4.0.0", + "pug-error": "^2.0.0" + } + }, + "node_modules/pug-linker": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-4.0.0.tgz", + "integrity": "sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw==", + "dependencies": { + "pug-error": "^2.0.0", + "pug-walk": "^2.0.0" + } + }, + "node_modules/pug-load": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-3.0.0.tgz", + "integrity": "sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ==", + "dependencies": { + "object-assign": "^4.1.1", + "pug-walk": "^2.0.0" + } + }, + "node_modules/pug-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-6.0.0.tgz", + "integrity": "sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw==", + "dependencies": { + "pug-error": "^2.0.0", + "token-stream": "1.0.0" + } + }, + "node_modules/pug-runtime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-3.0.1.tgz", + "integrity": "sha512-L50zbvrQ35TkpHwv0G6aLSuueDRwc/97XdY8kL3tOT0FmhgG7UypU3VztfV/LATAvmUfYi4wNxSajhSAeNN+Kg==" + }, + "node_modules/pug-strip-comments": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-2.0.0.tgz", + "integrity": "sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ==", + "dependencies": { + "pug-error": "^2.0.0" + } + }, + "node_modules/pug-walk": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-2.0.0.tgz", + "integrity": "sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==" + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode2": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/punycode2/-/punycode2-1.0.0.tgz", + "integrity": "sha1-4rS5qaj/FX0LhEOOIDGB7niS39g=" + }, "node_modules/qs": { "version": "6.7.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", @@ -1429,6 +6694,23 @@ "node": ">=0.6" } }, + "node_modules/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==", + "engines": { + "node": ">=10" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -1437,6 +6719,16 @@ "node": ">= 0.6" } }, + "node_modules/rate-limit-mongo": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/rate-limit-mongo/-/rate-limit-mongo-2.3.1.tgz", + "integrity": "sha512-sl0T3G6aB6hgawZldDlvDZ8vi2tIxbcpk/N1WdI4jsS1/FqdDF92jKeCQiE8Oq7kDSsU+jFT142GE6HfKWKrUw==", + "dependencies": { + "mongodb": ">= 3.3.4 < 4.0.0", + "twostep": "0.4.2", + "underscore": "1.12.1" + } + }, "node_modules/raw-body": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", @@ -1451,6 +6743,17 @@ "node": ">= 0.8" } }, + "node_modules/re2": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/re2/-/re2-1.16.0.tgz", + "integrity": "sha512-eizTZL2ZO0ZseLqfD4t3Qd0M3b3Nr0MBWpX81EbPMIud/1d/CSfUIx2GQK8fWiAeHoSekO5EOeFib2udTZLwYw==", + "hasInstallScript": true, + "dependencies": { + "install-artifact-from-github": "^1.2.0", + "nan": "^2.14.2", + "node-gyp": "^8.0.0" + } + }, "node_modules/readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", @@ -1465,10 +6768,15 @@ "util-deprecate": "~1.0.1" } }, + "node_modules/readable-stream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", "dev": true, "dependencies": { "picomatch": "^2.2.1" @@ -1482,37 +6790,80 @@ "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-1.0.0.tgz", "integrity": "sha512-TuAasHQNamyyJ2hb97IuBEif4qBHGjPHBS64sZwytpLEqtBQ1gPJTnOaQ6qmpET16cK14kkjbazl6+p0RRv0yw==" }, - "node_modules/require-at": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/require-at/-/require-at-1.0.6.tgz", - "integrity": "sha512-7i1auJbMUrXEAZCOQ0VNJgmcT2VOKPRl2YGJwgpHpC9CE91Mv4/4UYIUm4chGJaI381ZDq1JUicFii64Hapd8g==", - "engines": { - "node": ">=4" + "node_modules/require_optional": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", + "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", + "dependencies": { + "resolve-from": "^2.0.0", + "semver": "^5.1.0" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, "node_modules/resolve": { "version": "1.20.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", - "dev": true, "dependencies": { "is-core-module": "^2.2.0", "path-parse": "^1.0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-alpn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.0.0.tgz", + "integrity": "sha512-rTuiIEqFmGxne4IovivKSDzld2lWW9QCjqv80SYjPgf+gS35eaCAjaP54CCwGAwBtnCsvNLYtqxe1Nw+i6JEmA==" + }, + "node_modules/resolve-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", + "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/responselike": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", + "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", + "dependencies": { + "lowercase-keys": "^2.0.0" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "engines": { + "node": ">= 4" } }, "node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/safe-buffer": { @@ -1537,6 +6888,43 @@ "node": ">=6" } }, + "node_modules/sazerac": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/sazerac/-/sazerac-2.0.0.tgz", + "integrity": "sha512-+oVUuqwbHf9aNJiT2WBoCYWmQy02AiBcbW1jxZUDBrrXleYRBat4qwbnwTXD3lyOVUk4F27cR8ArHZkCvjuSzw==", + "dev": true, + "dependencies": { + "deep-eql": "^4.0.0", + "lodash.at": "^4.6.0", + "lodash.concat": "^4.5.0", + "lodash.filter": "^4.6.0", + "lodash.isfunction": "^3.0.9", + "lodash.isobject": "^3.0.2", + "lodash.isstring": "^4.0.1", + "lodash.isundefined": "^3.0.1", + "lodash.keys": "^4.2.0", + "lodash.toarray": "^4.4.0", + "sprintf-js": "^1.1.2" + } + }, + "node_modules/sazerac/node_modules/deep-eql": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.0.0.tgz", + "integrity": "sha512-GxJC5MOg2KyQlv6WiUF/VAnMj4MWnYiXo4oLgeptOELVoknyErb4Z8+5F/IM/K4g9/80YzzatxmWcyRwUseH0A==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/sazerac/node_modules/sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", + "dev": true + }, "node_modules/scmp": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/scmp/-/scmp-2.1.0.tgz", @@ -1573,24 +6961,20 @@ "node": ">= 0.8.0" } }, - "node_modules/send/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, "node_modules/send/node_modules/ms": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" }, + "node_modules/serialize-javascript": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", + "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, "node_modules/serve-static": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", @@ -1605,44 +6989,134 @@ "node": ">= 0.8.0" } }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, "node_modules/setprototypeof": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" }, + "node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, "node_modules/sift": { - "version": "13.5.2", - "resolved": "https://registry.npmjs.org/sift/-/sift-13.5.2.tgz", - "integrity": "sha512-+gxdEOMA2J+AI+fVsCqeNn7Tgx3M9ZN9jdi95939l1IJ8cZsqS8sqpJyOkic2SJk+1+98Uwryt/gL6XDaV+UZA==" + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/sift/-/sift-7.0.1.tgz", + "integrity": "sha512-oqD7PMJ+uO6jV9EQCl0LrRw1OwsiPsiFQR5AR30heR+4Dl7jBBbDLnNvWiak20tzZlSE1H7RB30SX/1j/YYT7g==" + }, + "node_modules/signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" }, "node_modules/sliced": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=" }, + "node_modules/smart-buffer": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.1.0.tgz", + "integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw==", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/smartquotes": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/smartquotes/-/smartquotes-2.3.1.tgz", + "integrity": "sha1-Aeu1ldbHqeJNkOjLlcF9Dhr0lAc=", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/socket.io": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.1.3.tgz", - "integrity": "sha512-tLkaY13RcO4nIRh1K2hT5iuotfTaIQw7cVIe0FUykN3SuQi0cm7ALxuyT5/CtDswOMWUzMGTibxYNx/gU7In+Q==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.0.0.tgz", + "integrity": "sha512-/c1riZMV/4yz7KEpaMhDQbwhJDIoO55whXaRKgyEBQrLU9zUHXo9rzeTMvTOqwL9mbKfHKdrXcMoCeQ/1YtMsg==", "dependencies": { "@types/cookie": "^0.4.0", - "@types/cors": "^2.8.10", + "@types/cors": "^2.8.8", "@types/node": ">=10.0.0", "accepts": "~1.3.4", "base64id": "~2.0.0", "debug": "~4.3.1", - "engine.io": "~5.1.1", - "socket.io-adapter": "~2.3.1", - "socket.io-parser": "~4.0.4" + "engine.io": "~5.0.0", + "socket.io-adapter": "~2.2.0", + "socket.io-parser": "~4.0.3" }, "engines": { "node": ">=10.0.0" } }, "node_modules/socket.io-adapter": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.3.1.tgz", - "integrity": "sha512-8cVkRxI8Nt2wadkY6u60Y4rpW3ejA1rxgcK2JuyIhmF+RMNpTy1QRtkHIDUOf3B4HlQwakMsWbKftMv/71VMmw==" + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.2.0.tgz", + "integrity": "sha512-rG49L+FwaVEwuAdeBRq49M97YI3ElVabJPzvHT9S6a2CWhDKnjSFasvwAwSYPRhQzfn4NtDIbCaGYgOCOU/rlg==" + }, + "node_modules/socket.io-client": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.0.0.tgz", + "integrity": "sha512-27yQxmXJAEYF19Ygyl8FPJ0if0wegpSmkIIbrWJeI7n7ST1JyH8bbD5v3fjjGY5cfCanACJ3dARUAyiVFNrlTQ==", + "dependencies": { + "@types/component-emitter": "^1.2.10", + "backo2": "~1.0.2", + "component-emitter": "~1.3.0", + "debug": "~4.3.1", + "engine.io-client": "~5.0.0", + "parseuri": "0.0.6", + "socket.io-parser": "~4.0.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-client/node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/socket.io-client/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/socket.io-parser": { "version": "4.0.4", @@ -1657,11 +7131,89 @@ "node": ">=10.0.0" } }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/socket.io-parser/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/socket.io/node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/socket.io/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/socks": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.1.tgz", + "integrity": "sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA==", + "dependencies": { + "ip": "^1.1.5", + "smart-buffer": "^4.1.0" + }, + "engines": { + "node": ">= 10.13.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-5.0.0.tgz", + "integrity": "sha512-lEpa1zsWCChxiynk+lCycKuC502RxDWLKJZoIhnxrWNjLSDGYRFflHA1/228VkRcnv9TIb8w98derGbpKxJRgA==", + "dependencies": { + "agent-base": "6", + "debug": "4", + "socks": "^2.3.3" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/socks-proxy-agent/node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socks-proxy-agent/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -1670,7 +7222,6 @@ "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, "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -1685,6 +7236,23 @@ "memory-pager": "^1.0.2" } }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "node_modules/ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", @@ -1701,24 +7269,260 @@ "safe-buffer": "~5.1.0" } }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/superagent": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-6.1.0.tgz", + "integrity": "sha512-OUDHEssirmplo3F+1HWKUrUjvnQuA+nZI6i/JJBdXb5eq9IyEQwPyPpqND+SSsxf6TygpBEkUjISVRN4/VOpeg==", + "dev": true, + "dependencies": { + "component-emitter": "^1.3.0", + "cookiejar": "^2.1.2", + "debug": "^4.1.1", + "fast-safe-stringify": "^2.0.7", + "form-data": "^3.0.0", + "formidable": "^1.2.2", + "methods": "^1.1.2", + "mime": "^2.4.6", + "qs": "^6.9.4", + "readable-stream": "^3.6.0", + "semver": "^7.3.2" + }, + "engines": { + "node": ">= 7.0.0" + } + }, + "node_modules/superagent/node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/superagent/node_modules/form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/superagent/node_modules/lru-cache": { + "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, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/superagent/node_modules/mime": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", + "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/superagent/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/superagent/node_modules/qs": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz", + "integrity": "sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/superagent/node_modules/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, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/superagent/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/superagent/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/supertest": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-6.1.3.tgz", + "integrity": "sha512-v2NVRyP73XDewKb65adz+yug1XMtmvij63qIWHZzSX8tp6wiq6xBLUy4SUAd2NII6wIipOmHT/FD9eicpJwdgQ==", + "dev": true, + "dependencies": { + "methods": "^1.1.2", + "superagent": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/supports-color": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", + "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "dependencies": { + "has-flag": "^2.0.0" + }, "engines": { "node": ">=4" } }, - "node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true, + "node_modules/tar": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz", + "integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/title": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/title/-/title-3.4.2.tgz", + "integrity": "sha512-cSNFZ/ChKlX2SfF+k9XvvXkjVa1JrzdGt6v/hoxVig5VaDGRmNHANfawcAdW2mfkd7y+uBHH6n9EHAxJmkO5Hw==", + "dependencies": { + "arg": "1.0.0", + "chalk": "2.3.0", + "clipboardy": "1.2.2", + "titleize": "1.0.0" + }, + "bin": { + "title": "bin/title.js" + } + }, + "node_modules/title/node_modules/arg": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/arg/-/arg-1.0.0.tgz", + "integrity": "sha512-Wk7TEzl1KqvTGs/uyhmHO/3XLd3t1UeU4IstvPXVzGPM522cTjqjNZ99esCkcL52sjqjo8e8CTBcWhkxvGzoAw==" + }, + "node_modules/titleize": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/titleize/-/titleize-1.0.0.tgz", + "integrity": "sha1-fTUHIgYYMLpmF2MeDP0+oIOY2Vo=", "engines": { "node": ">=0.10.0" } }, + "node_modules/tlds": { + "version": "1.210.0", + "resolved": "https://registry.npmjs.org/tlds/-/tlds-1.210.0.tgz", + "integrity": "sha512-5bzt4JE+NlnwiKpVW9yzWxuc44m+t2opmPG+eSKDp5V5qdyGvjMngKgBb5ZK8GiheQMbRTCKpRwFJeIEO6pV7Q==", + "bin": { + "tlds": "bin.js" + } + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "engines": { + "node": ">=4" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -1739,20 +7543,23 @@ "node": ">=0.6" } }, - "node_modules/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==", - "dev": true, - "bin": { - "tree-kill": "cli.js" + "node_modules/token-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-1.0.0.tgz", + "integrity": "sha1-zCAOqyYT9BZtJ/+a/HylbUnfbrQ=" + }, + "node_modules/truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/truncate/-/truncate-2.1.0.tgz", + "integrity": "sha512-em3E3SUDONOjTBcZ36DTm3RvDded3IRU9rX32oHwwXNt3rJD5MVaFlJTQvs8tJoHRoeYP36OuQ1eL/Q7bNEWIQ==", + "engines": { + "node": "*" } }, "node_modules/ts-node": { "version": "9.1.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", - "dev": true, "dependencies": { "arg": "^4.1.0", "create-require": "^1.1.0", @@ -1769,55 +7576,20 @@ }, "engines": { "node": ">=10.0.0" - }, - "peerDependencies": { - "typescript": ">=2.7" } }, - "node_modules/ts-node-dev": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-1.1.8.tgz", - "integrity": "sha512-Q/m3vEwzYwLZKmV6/0VlFxcZzVV/xcgOt+Tx/VjaaRHyiBcFlV0541yrT09QjzzCxlDZ34OzKjrFAynlmtflEg==", + "node_modules/twostep": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/twostep/-/twostep-0.4.2.tgz", + "integrity": "sha1-hLxQh6hxV00ev3vjB54D4SLUFbY=" + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true, - "dependencies": { - "chokidar": "^3.5.1", - "dynamic-dedupe": "^0.3.0", - "minimist": "^1.2.5", - "mkdirp": "^1.0.4", - "resolve": "^1.0.0", - "rimraf": "^2.6.1", - "source-map-support": "^0.5.12", - "tree-kill": "^1.2.2", - "ts-node": "^9.0.0", - "tsconfig": "^7.0.0" - }, - "bin": { - "ts-node-dev": "lib/bin.js", - "tsnd": "lib/bin.js" - }, "engines": { - "node": ">=0.8.0" - }, - "peerDependencies": { - "node-notifier": "*", - "typescript": "*" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/tsconfig": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz", - "integrity": "sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==", - "dev": true, - "dependencies": { - "@types/strip-bom": "^3.0.0", - "@types/strip-json-comments": "0.0.30", - "strip-bom": "^3.0.0", - "strip-json-comments": "^2.0.0" + "node": ">=4" } }, "node_modules/type-is": { @@ -1833,10 +7605,9 @@ } }, "node_modules/typescript": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", - "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", - "dev": true, + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.3.tgz", + "integrity": "sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -1845,6 +7616,27 @@ "node": ">=4.2.0" } }, + "node_modules/underscore": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz", + "integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==" + }, + "node_modules/unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -1853,6 +7645,19 @@ "node": ">= 0.8" } }, + "node_modules/url-regex-safe": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/url-regex-safe/-/url-regex-safe-1.0.2.tgz", + "integrity": "sha512-t1doIKbYDBRyqXZz7A98AXH/zimKmYapxFjBZwcz3BmqjB921rUPEUokAaShKyenaX3McOM8FbkWLvFvX3Jsyw==", + "dependencies": { + "ip-regex": "^4.1.0", + "re2": "^1.15.4", + "tlds": "^1.209.0" + }, + "engines": { + "node": ">= 10.12.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -1866,14 +7671,6 @@ "node": ">= 0.4.0" } }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -1882,56 +7679,426 @@ "node": ">= 0.8" } }, + "node_modules/video-extensions": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/video-extensions/-/video-extensions-1.1.0.tgz", + "integrity": "sha1-6qhrRfKahTwrhz6djiO1E3Epl9Y=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha1-YU9/v42AHwu18GYfWy9XhXUOTwk=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "node_modules/whoops": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/whoops/-/whoops-4.1.0.tgz", + "integrity": "sha512-42soctqvFs9FaU1r4ZadCy2F6A9dUc4SN3ud+tbDEdmyZDTeYBgKKqtIdo6NiQlnZnJegWRCyKLk2edYH9DsHA==", + "dependencies": { + "clean-stack": "~2.2.0", + "mimic-fn": "~3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dependencies": { + "string-width": "^1.0.2 || 2" + } + }, + "node_modules/with": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/with/-/with-7.0.2.tgz", + "integrity": "sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==", + "dependencies": { + "@babel/parser": "^7.9.6", + "@babel/types": "^7.9.6", + "assert-never": "^1.2.1", + "babel-walk": "3.0.0-canary-5" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/workerpool": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.2.tgz", + "integrity": "sha512-DSNyvOpFKrNusaaUwk+ej6cBj1bmhLcBfj80elGk+ZIo5JSkq+unB1dLKEOcNfJDZgjGICfhQ0Q5TbP0PvF4+Q==", + "dev": true + }, + "node_modules/wrap-ansi": { + "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, + "dependencies": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/wrap-ansi/node_modules/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, + "engines": { + "node": ">=6" + } + }, + "node_modules/wrap-ansi/node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/wrap-ansi/node_modules/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, + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/wrap-ansi/node_modules/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, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "node_modules/ws": { - "version": "7.4.6", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", - "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.4.tgz", + "integrity": "sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw==", "engines": { "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } } }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "node_modules/y18n": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", + "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", + "dev": true + }, + "node_modules/yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + }, + "node_modules/yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "dependencies": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "node_modules/yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser/node_modules/camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", "dev": true, "engines": { - "node": ">=0.4" + "node": ">=10" } }, + "node_modules/yargs-unparser/node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/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, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/yargs/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/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, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/yargs/node_modules/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, + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/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, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yeast": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", + "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=" + }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, "engines": { "node": ">=6" } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + } } }, "dependencies": { + "@babel/helper-validator-identifier": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", + "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==" + }, + "@babel/parser": { + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.12.tgz", + "integrity": "sha512-4T7Pb244rxH24yR116LAuJ+adxXXnHhZaLJjegJVKSdoNCe4x1eDBaud5YIcQFcqzsaD5BHvJw5BQ0AZapdCRw==" + }, + "@babel/types": { + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.12.tgz", + "integrity": "sha512-K4nY2xFN4QMvQwkQ+zmBDp6ANMbVNw6BbxWmYA4qNjhR9W+Lj/8ky5MEY2Me5r+B2c6/v6F53oMndG+f9s3IiA==", + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "@metascraper/helpers": { + "version": "5.14.14", + "resolved": "https://registry.npmjs.org/@metascraper/helpers/-/helpers-5.14.14.tgz", + "integrity": "sha512-sznekODvL+k5wTaU48Y8prdrPy/xTp3WxSAQrApBPeKPGEJTyfQWglkp+1W7xEDDrnJYzjOJDuq6EX3l9WYv1g==", + "requires": { + "audio-extensions": "0.0.0", + "chrono-node": "2.1.8", + "condense-whitespace": "~2.0.0", + "entities": "~2.0.3", + "file-extension": "~4.0.5", + "has-values": "~2.0.1", + "image-extensions": "~1.1.0", + "is-relative-url": "~3.0.0", + "is-uri": "~1.2.0", + "iso-639-3": "~2.1.0", + "isostring": "0.0.1", + "lodash": "~4.17.20", + "memoize-one": "~5.1.1", + "mime-types": "~2.1.27", + "normalize-url": "~5.2.0", + "smartquotes": "~2.3.1", + "title": "~3.4.2", + "truncate": "~2.1.0", + "url-regex-safe": "~1.0.2", + "video-extensions": "~1.1.0" + } + }, + "@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "requires": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + } + }, + "@sindresorhus/is": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-3.1.2.tgz", + "integrity": "sha512-JiX9vxoKMmu8Y3Zr2RVathBL1Cdu4Nt4MuNWemt1Nc06A0RAin9c5FArkhGsyMBWfCu4zj+9b+GxtjAnE4qqLQ==" + }, + "@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==", + "requires": { + "defer-to-connect": "^2.0.0" + } + }, + "@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==" + }, "@types/body-parser": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.1.tgz", - "integrity": "sha512-a6bTJ21vFOGIkwM0kzh9Yr89ziVxq4vYH2fQ6N8AeipEzai/cFK6aGMArIkUeIdRIgpwQa+2bXiLuUJCpSf2Cg==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==", "dev": true, "requires": { "@types/connect": "*", @@ -1939,36 +8106,96 @@ } }, "@types/bson": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.4.tgz", - "integrity": "sha512-awqorHvQS0DqxkHQ/FxcPX9E+H7Du51Qw/2F+5TBMSaE3G0hm+8D3eXJ6MAzFw75nE8V7xF0QvzUSdxIjJb/GA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.2.tgz", + "integrity": "sha512-+uWmsejEHfmSjyyM/LkrP0orfE2m5Mx9Xel4tXNeqi1ldK5XMQcDsFkBmLDtuyKUbxj2jGDo0H240fbCRJZo7Q==", + "dev": true, "requires": { "@types/node": "*" } }, + "@types/cacheable-request": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz", + "integrity": "sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ==", + "requires": { + "@types/http-cache-semantics": "*", + "@types/keyv": "*", + "@types/node": "*", + "@types/responselike": "*" + } + }, + "@types/chai": { + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.14.tgz", + "integrity": "sha512-G+ITQPXkwTrslfG5L/BksmbLUA0M1iybEsmCWPqzSxsRRhJZimBKJkoMi8fr/CPygPTj4zO5pJH7I2/cm9M7SQ==", + "dev": true + }, + "@types/chai-as-promised": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.3.tgz", + "integrity": "sha512-FQnh1ohPXJELpKhzjuDkPLR2BZCAqed+a6xV4MI/T3XzHfd2FlarfUGUdZYgqYe8oxkYn0fchHEeHfHqdZ96sg==", + "dev": true, + "requires": { + "@types/chai": "*" + } + }, + "@types/chai-spies": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/chai-spies/-/chai-spies-1.0.3.tgz", + "integrity": "sha512-RBZjhVuK7vrg4rWMt04UF5zHYwfHnpk5mIWu3nQvU3AKGDixXzSjZ6v0zke6pBcaJqMv3IBZ5ibLWPMRDL0sLw==", + "dev": true, + "requires": { + "@types/chai": "*" + } + }, + "@types/chai-things": { + "version": "0.0.34", + "resolved": "https://registry.npmjs.org/@types/chai-things/-/chai-things-0.0.34.tgz", + "integrity": "sha512-vcpFz782jq7FpEnE9Yq0cfgP8NwRPQgQ2271Q+7hEldOHttTQaYuVj0S9ViQXkM+sYSgUh/2OF5vTq8iei8Ljg==", + "dev": true, + "requires": { + "@types/chai": "*" + } + }, + "@types/colors": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/colors/-/colors-1.2.1.tgz", + "integrity": "sha512-7jNkpfN2lVO07nJ1RWzyMnNhH/I5N9iWuMPx9pedptxJ4MODf8rRV0lbJi6RakQ4sKQk231Fw4e2W9n3D7gZ3w==", + "dev": true, + "requires": { + "colors": "*" + } + }, "@types/component-emitter": { "version": "1.2.10", "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.10.tgz", "integrity": "sha512-bsjleuRKWmGqajMerkzox19aGbscQX5rmmvvXl3wlIp5gMG1HgkiwPxsN5p070fBDKTNSPgojVbuY1+HWMbFhg==" }, "@types/connect": { - "version": "3.4.35", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "version": "3.4.33", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz", + "integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==", "dev": true, "requires": { "@types/node": "*" } }, "@types/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-y7mImlc/rNkvCRmg8gC3/lj87S7pTUIJ6QGjwHR9WQJcFs+ZMTOaoPrkdFA/YdbuqVEmEbb5RdhVxMkAcgOnpg==" + }, + "@types/cookiejar": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.2.tgz", + "integrity": "sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==", + "dev": true }, "@types/cors": { - "version": "2.8.12", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", - "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==" + "version": "2.8.10", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.10.tgz", + "integrity": "sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ==" }, "@types/dotenv": { "version": "8.2.0", @@ -1979,10 +8206,19 @@ "dotenv": "*" } }, + "@types/engine.io": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/engine.io/-/engine.io-3.1.4.tgz", + "integrity": "sha512-98rXVukLD6/ozrQ2O80NAlWDGA4INg+tqsEReWJldqyi2fulC9V7Use/n28SWgROXKm6003ycWV4gZHoF8GA6w==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/express": { - "version": "4.17.13", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", - "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.11.tgz", + "integrity": "sha512-no+R6rW60JEc59977wIxreQVsIEOAYwgCqldrA/vkpCnbD7MqTefO97lmoBe4WE0F156bC4uLSP1XHDOySnChg==", "dev": true, "requires": { "@types/body-parser": "*", @@ -1991,10 +8227,19 @@ "@types/serve-static": "*" } }, + "@types/express-rate-limit": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/express-rate-limit/-/express-rate-limit-5.1.1.tgz", + "integrity": "sha512-6oMYZBLlhxC5sdcRXXz528QyfGz3zTy9YdHwqlxLfgx5Cd3zwYaUjjPpJcaTtHmRefLi9P8kLBPz2wB7yz4JtQ==", + "dev": true, + "requires": { + "@types/express": "*" + } + }, "@types/express-serve-static-core": { - "version": "4.17.24", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.24.tgz", - "integrity": "sha512-3UJuW+Qxhzwjq3xhwXm2onQcFHn76frIYVbTu+kn24LFxI+dEhdfISDFovPB8VpEgW8oQCTpRuCe+0zJxB7NEA==", + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.19.tgz", + "integrity": "sha512-DJOSHzX7pCiSElWaGR8kCprwibCB/3yW6vcT8VG3P0SJjnv19gnWG/AZMfM60Xj/YJIp/YCaDHyvzsFVeniARA==", "dev": true, "requires": { "@types/node": "*", @@ -2003,68 +8248,115 @@ } }, "@types/faker": { - "version": "5.5.7", - "resolved": "https://registry.npmjs.org/@types/faker/-/faker-5.5.7.tgz", - "integrity": "sha512-ejzb61Q5zQTtS0ZIafgQ7ahO5ACzmGhG5PfX2hxWyth3k0/aysb4ZOxKQB8DbzwSPppA5jmFBwqnBxjv5hqI5Q==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/@types/faker/-/faker-5.1.6.tgz", + "integrity": "sha512-D+gfFWR/YCvlrYL8lgNZO1jKgIUW+cfhxsgMOqUMYwCI+tl0htD7vCCXp/oJsIxJpxuI7zqmo3gpVQBkFCM4iA==", "dev": true }, - "@types/http-errors": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-1.8.1.tgz", - "integrity": "sha512-e+2rjEwK6KDaNOm5Aa9wNGgyS9oSZU/4pfSMMPYNOfjvFI0WVXm29+ITRFr6aKDvvKo7uU1jV68MW4ScsfDi7Q==", - "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==" }, "@types/jsonwebtoken": { - "version": "8.5.4", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.4.tgz", - "integrity": "sha512-4L8msWK31oXwdtC81RmRBAULd0ShnAHjBuKT9MRQpjP0piNrZdXyTRcKY9/UIfhGeKIT4PvF5amOOUbbT/9Wpg==", + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.0.tgz", + "integrity": "sha512-9bVao7LvyorRGZCw0VmH/dr7Og+NdjYSsKAxB43OQoComFbBgsEpoR9JW6+qSq/ogwVBg8GI2MfAlk4SYI4OLg==", "dev": true, "requires": { "@types/node": "*" } }, + "@types/keyv": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz", + "integrity": "sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw==", + "requires": { + "@types/node": "*" + } + }, "@types/mime": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", - "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.3.tgz", + "integrity": "sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q==", + "dev": true + }, + "@types/mocha": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.0.tgz", + "integrity": "sha512-/Sge3BymXo4lKc31C8OINJgXLaw+7vL1/L1pGiBNpGrBiT8FQiaFpSYV0uhTaG4y78vcMBTMFsWaHDvuD+xGzQ==", "dev": true }, "@types/mongodb": { - "version": "3.6.20", - "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.6.20.tgz", - "integrity": "sha512-WcdpPJCakFzcWWD9juKoZbRtQxKIMYF/JIAM4JrNHrMcnJL6/a2NWjXxW7fo9hxboxxkg+icff8d7+WIEvKgYQ==", + "version": "3.5.27", + "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.5.27.tgz", + "integrity": "sha512-1jxKDgdfJEOO9zp+lv43p8jOqRs02xPrdUTzAZIVK9tVEySfCEmktL2jEu9A3wOBEOs18yKzpVIKUh8b8ALk3w==", + "dev": true, "requires": { "@types/bson": "*", "@types/node": "*" } }, "@types/mongoose": { - "version": "5.11.97", - "resolved": "https://registry.npmjs.org/@types/mongoose/-/mongoose-5.11.97.tgz", - "integrity": "sha512-cqwOVYT3qXyLiGw7ueU2kX9noE8DPGRY6z8eUxudhXY8NZ7DMKYAxyZkLSevGfhCX3dO/AoX5/SO9lAzfjon0Q==", + "version": "5.7.36", + "resolved": "https://registry.npmjs.org/@types/mongoose/-/mongoose-5.7.36.tgz", + "integrity": "sha512-ggFXgvkHgCNlT35B9d/heDYfSqOSwTmQjkRoR32sObGV5Xjd0N0WWuYlLzqeCg94j4hYN/OZxZ1VNNLltX/IVQ==", "dev": true, "requires": { - "mongoose": "*" + "@types/mongodb": "*", + "@types/node": "*" } }, "@types/node": { - "version": "16.3.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.3.3.tgz", - "integrity": "sha512-8h7k1YgQKxKXWckzFCMfsIwn0Y61UK6tlD6y2lOb3hTOIMlK3t9/QwHOhc81TwU+RMf0As5fj7NPjroERCnejQ==" + "version": "14.11.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.11.2.tgz", + "integrity": "sha512-jiE3QIxJ8JLNcb1Ps6rDbysDhN4xa8DJJvuC9prr6w+1tIh+QAbYyNF3tyiZNLDBIuBCf4KEcV2UvQm/V60xfA==" + }, + "@types/node-fetch": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.7.tgz", + "integrity": "sha512-o2WVNf5UhWRkxlf6eq+jMZDu7kjgpgJfl4xVNlvryc95O/6F2ld8ztKX+qu+Rjyet93WAWm5LjeX9H5FGkODvw==", + "dev": true, + "requires": { + "@types/node": "*", + "form-data": "^3.0.0" + }, + "dependencies": { + "form-data": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", + "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + } + } + }, + "@types/nodemailer": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.1.tgz", + "integrity": "sha512-8081UY/0XTTDpuGqCnDc8IY+Q3DSg604wB3dBH0CaZlj4nZWHWuxtZ3NRZ9c9WUrz1Vfm6wioAUnqL3bsh49uQ==", + "dev": true, + "requires": { + "@types/node": "*" + } }, "@types/passport": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.7.tgz", - "integrity": "sha512-JtswU8N3kxBYgo+n9of7C97YQBT+AYPP2aBfNGTzABqPAZnK/WOAaKfh3XesUYMZRrXFuoPc2Hv0/G/nQFveHw==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.4.tgz", + "integrity": "sha512-h5OfAbfBBYSzjeU0GTuuqYEk9McTgWeGQql9g3gUw2/NNCfD7VgExVRYJVVeU13Twn202Mvk9BT0bUrl30sEgA==", "dev": true, "requires": { "@types/express": "*" } }, "@types/passport-local": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/@types/passport-local/-/passport-local-1.0.34.tgz", - "integrity": "sha512-PSc07UdYx+jhadySxxIYWuv6sAnY5e+gesn/5lkPKfBeGuIYn9OPR+AAEDq73VRUh6NBTpvE/iPE62rzZUslog==", + "version": "1.0.33", + "resolved": "https://registry.npmjs.org/@types/passport-local/-/passport-local-1.0.33.tgz", + "integrity": "sha512-+rn6ZIxje0jZ2+DAiWFI8vGG7ZFKB0hXx2cUdMmudSWsigSq6ES7Emso46r4HJk0qCgrZVfI8sJiM7HIYf4SbA==", "dev": true, "requires": { "@types/express": "*", @@ -2072,28 +8364,6 @@ "@types/passport-strategy": "*" } }, - "@types/passport-local-mongoose": { - "version": "4.0.15", - "resolved": "https://registry.npmjs.org/@types/passport-local-mongoose/-/passport-local-mongoose-4.0.15.tgz", - "integrity": "sha512-wg1ri5Tyl8c/FLzm57efHlHJVwr7RcbsqORcQBOI7XWSw6PEtt75qX0OQept3/4k0vV/GYTdeTWnTzSFyZtwEA==", - "dev": true, - "requires": { - "@types/mongoose": "4.*", - "@types/passport-local": "*" - }, - "dependencies": { - "@types/mongoose": { - "version": "4.7.56", - "resolved": "https://registry.npmjs.org/@types/mongoose/-/mongoose-4.7.56.tgz", - "integrity": "sha512-c4bQmM/WUMQvOpEJFJbNjwLU72bzFOtKJ+4HtqdA+wtMhWpwIBoKdL7KsNt7BRjelK0dcSrgymvz/miQuPjikw==", - "dev": true, - "requires": { - "@types/mongodb": "*", - "@types/node": "*" - } - } - } - }, "@types/passport-strategy": { "version": "0.2.35", "resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.35.tgz", @@ -2105,53 +8375,90 @@ } }, "@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "version": "6.9.5", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.5.tgz", + "integrity": "sha512-/JHkVHtx/REVG0VVToGRGH2+23hsYLHdyG+GrvoUGlGAd0ErauXDyvHtRI/7H7mzLm+tBCKA7pfcpkQ1lf58iQ==", "dev": true }, "@types/range-parser": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", - "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", + "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==", "dev": true }, - "@types/serve-static": { - "version": "1.13.10", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", - "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", - "dev": true, + "@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", "requires": { - "@types/mime": "^1", "@types/node": "*" } }, - "@types/socket.io": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/socket.io/-/socket.io-3.0.2.tgz", - "integrity": "sha512-pu0sN9m5VjCxBZVK8hW37ZcMe8rjn4HHggBN5CbaRTvFwv5jOmuIRZEuddsBPa9Th0ts0SIo3Niukq+95cMBbQ==", + "@types/serve-static": { + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.5.tgz", + "integrity": "sha512-6M64P58N+OXjU432WoLLBQxbA0LRGBCRm7aAGQJ+SMC1IMl0dgRVi9EFfoDcS2a7Xogygk/eGN94CfwU9UF7UQ==", "dev": true, "requires": { - "socket.io": "*" + "@types/express-serve-static-core": "*", + "@types/mime": "*" } }, - "@types/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I=", + "@types/socket.io": { + "version": "2.1.13", + "resolved": "https://registry.npmjs.org/@types/socket.io/-/socket.io-2.1.13.tgz", + "integrity": "sha512-JRgH3nCgsWel4OPANkhH8TelpXvacAJ9VeryjuqCDiaVDMpLysd6sbt0dr6Z15pqH3p2YpOT3T1C5vQ+O/7uyg==", + "dev": true, + "requires": { + "@types/engine.io": "*", + "@types/node": "*", + "@types/socket.io-parser": "*" + } + }, + "@types/socket.io-client": { + "version": "1.4.36", + "resolved": "https://registry.npmjs.org/@types/socket.io-client/-/socket.io-client-1.4.36.tgz", + "integrity": "sha512-ZJWjtFBeBy1kRSYpVbeGYTElf6BqPQUkXDlHHD4k/42byCN5Rh027f4yARHCink9sKAkbtGZXEAmR0ZCnc2/Ag==", "dev": true }, - "@types/strip-json-comments": { - "version": "0.0.30", - "resolved": "https://registry.npmjs.org/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz", - "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==", + "@types/socket.io-parser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@types/socket.io-parser/-/socket.io-parser-2.2.1.tgz", + "integrity": "sha512-+JNb+7N7tSINyXPxAJb62+NcpC1x/fPn7z818W4xeNCdPTp6VsO/X8fCsg6+ug4a56m1v9sEiTIIUKVupcHOFQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/superagent": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.10.tgz", + "integrity": "sha512-xAgkb2CMWUMCyVc/3+7iQfOEBE75NvuZeezvmixbUw3nmENf2tCnQkW5yQLTYqvXUQ+R6EXxdqKKbal2zM5V/g==", + "dev": true, + "requires": { + "@types/cookiejar": "*", + "@types/node": "*" + } + }, + "@types/supertest": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-2.0.10.tgz", + "integrity": "sha512-Xt8TbEyZTnD5Xulw95GLMOkmjGICrOQyJ2jqgkSjAUR3mm7pAIzSR0NFBaMcwlzVvlpCjNwbATcWWwjNiZiFrQ==", + "dev": true, + "requires": { + "@types/superagent": "*" + } + }, + "@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", "dev": true }, - "@types/uuid": { - "version": "8.3.1", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.1.tgz", - "integrity": "sha512-Y2mHTRAbqfFkpjldbkHGY8JIzRN6XqYRliG8/24FcHm2D2PwW24fl5xMRTVGdrb7iMrwCaIEbLWerGIkXuFWVg==", - "dev": true + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, "accepts": { "version": "1.3.7", @@ -2162,33 +8469,180 @@ "negotiator": "0.6.2" } }, + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==" + }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "requires": { + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "agentkeepalive": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.1.4.tgz", + "integrity": "sha512-+V/rGa3EuU74H6wR04plBb7Ks10FbtUQgRj/FQOG7uUIEuaINI+AiqJR1k6t3SVNs7o7ZjIdus6706qqzVq8jQ==", + "requires": { + "debug": "^4.1.0", + "depd": "^1.1.2", + "humanize-ms": "^1.2.1" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", "dev": true, "requires": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "arch": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/arch/-/arch-2.1.2.tgz", + "integrity": "sha512-NTBIIbAfkJeIletyABbVtdPgeKfDafR+1mZV/AyyfC1UkVkp9iUjV+wwmqtUgphHYajbI86jejBJp5e+jkGTiQ==" + }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, "arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" + }, + "argparse": { + "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-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + }, + "assert-never": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/assert-never/-/assert-never-1.2.1.tgz", + "integrity": "sha512-TaTivMB6pYI1kXwrFlEhLeGfOqoDNdTxjCdwRfFFkEA30Eu+k48W34nlok2EYWJfFFzqaEmichdNM7th6M5HNw==" + }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", "dev": true }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "audio-extensions": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/audio-extensions/-/audio-extensions-0.0.0.tgz", + "integrity": "sha1-0O7+B3+562JYmO7ZmFiQVIzx+NI=" + }, + "babel-walk": { + "version": "3.0.0-canary-5", + "resolved": "https://registry.npmjs.org/babel-walk/-/babel-walk-3.0.0-canary-5.tgz", + "integrity": "sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==", + "requires": { + "@babel/types": "^7.9.6" + } + }, + "backo2": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", + "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, "base64-arraybuffer": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz", @@ -2234,28 +8688,17 @@ "qs": "6.7.0", "raw-body": "2.4.0", "type-is": "~1.6.17" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } } }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" + }, "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" @@ -2270,10 +8713,16 @@ "fill-range": "^7.0.1" } }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, "bson": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.6.tgz", - "integrity": "sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg==" + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.5.tgz", + "integrity": "sha512-kDuEzldR21lHciPQAIulLs1LZlCXdLziXI6Mb/TDkwXhb//UORJNPXgcRs2CuO4H0DcMkpfT3/ySsP3unoZjBg==" }, "buffer-equal-constant-time": { "version": "1.0.1", @@ -2283,28 +8732,310 @@ "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==", - "dev": true + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" }, "bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" }, - "chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "cacache": { + "version": "15.0.6", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.0.6.tgz", + "integrity": "sha512-g1WYDMct/jzW+JdWEyjaX2zoBkZ6ZT9VpOyp2I/VMtDsNLffNat3kqPFfi1eDRSK9/SuKGyORDHcQMcPF8sQ/w==", + "requires": { + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } + }, + "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==" + }, + "cacheable-request": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.1.tgz", + "integrity": "sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw==", + "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.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "requires": { + "pump": "^3.0.0" + } + }, + "normalize-url": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", + "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==" + } + } + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "chai": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", + "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", "dev": true, "requires": { - "anymatch": "~3.1.2", + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "pathval": "^1.1.0", + "type-detect": "^4.0.5" + } + }, + "chai-as-promised": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz", + "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==", + "dev": true, + "requires": { + "check-error": "^1.0.2" + } + }, + "chai-spies": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/chai-spies/-/chai-spies-1.0.0.tgz", + "integrity": "sha512-elF2ZUczBsFoP07qCfMO/zeggs8pqCf3fZGyK5+2X4AndS8jycZYID91ztD9oQ7d/0tnS963dPkd0frQEThDsg==", + "dev": true + }, + "chai-things": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/chai-things/-/chai-things-0.2.0.tgz", + "integrity": "sha1-xVEoN4+bs5nplPAAUhUZhO1uvnA=" + }, + "chalk": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", + "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", + "requires": { + "ansi-styles": "^3.1.0", + "escape-string-regexp": "^1.0.5", + "supports-color": "^4.0.0" + } + }, + "character-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", + "integrity": "sha1-x84o821LzZdE5f/CxfzeHHMmH8A=", + "requires": { + "is-regex": "^1.0.3" + } + }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "dev": true + }, + "cheerio": { + "version": "1.0.0-rc.3", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.3.tgz", + "integrity": "sha512-0td5ijfUPuubwLUu0OBoe98gZj8C/AA+RW3v67GPlGOrvxWjZmBXiBCRU+I8VEiNyJzjth40POfHiz2RB3gImA==", + "requires": { + "css-select": "~1.2.0", + "dom-serializer": "~0.1.1", + "entities": "~1.1.1", + "htmlparser2": "^3.9.1", + "lodash": "^4.15.0", + "parse5": "^3.0.1" + }, + "dependencies": { + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" + } + } + }, + "cheerio-advanced-selectors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/cheerio-advanced-selectors/-/cheerio-advanced-selectors-2.0.1.tgz", + "integrity": "sha512-5wHR8bpiD5pdUtaS81A6hnJezzoDzL1TLWfK6bxnLkIgEKPV26BlOdMCcvuj3fTE7JSalsTUeNU7AOD/u6bYhw==" + }, + "chokidar": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", + "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", + "dev": true, + "requires": { + "anymatch": "~3.1.1", "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", + "fsevents": "~2.1.2", + "glob-parent": "~5.1.0", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "readdirp": "~3.5.0" + } + }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" + }, + "chrono-node": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chrono-node/-/chrono-node-2.1.8.tgz", + "integrity": "sha512-Bn54l+yEX4yA+/8dYIz/tITSWcPFwy1N5KpUEndPngwUCr7hTDKqVAdBjiqPrbvgOeSvcOhe3yIBH+HsmzM3GA==", + "requires": { + "dayjs": "^1.8.29" + } + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==" + }, + "clipboardy": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-1.2.2.tgz", + "integrity": "sha512-16KrBOV7bHmHdxcQiCvfUFYVFyEah4FI8vYT1Fr7CGSA4G+xBWMEfUEQJS1hxeHGtI9ju1Bzs9uXSbj5HZKArw==", + "requires": { + "arch": "^2.1.0", + "execa": "^0.8.0" + } + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.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" + } + } + } + }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "requires": { + "mimic-response": "^1.0.0" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" } }, "component-emitter": { @@ -2315,8 +9046,26 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "condense-whitespace": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/condense-whitespace/-/condense-whitespace-2.0.0.tgz", + "integrity": "sha512-Ath9o58/0rxZXbyoy3zZgrVMoIemi30sukG/btuMKCLyqfQt3dNOWc9N3EHEMa2Q3i0tXQPDJluYFLwy7pJuQw==" + }, + "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=" + }, + "constantinople": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-4.0.1.tgz", + "integrity": "sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==", + "requires": { + "@babel/parser": "^7.6.0", + "@babel/types": "^7.6.1" + } }, "content-disposition": { "version": "0.5.3", @@ -2332,15 +9081,21 @@ "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" }, "cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" }, "cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, + "cookiejar": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", + "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==", + "dev": true + }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -2358,21 +9113,97 @@ "create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" }, - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "requires": { - "ms": "2.1.2" + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" } }, + "css-select": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", + "requires": { + "boolbase": "~1.0.0", + "css-what": "2.1", + "domutils": "1.5.1", + "nth-check": "~1.0.1" + } + }, + "css-what": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", + "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==" + }, + "dayjs": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.9.1.tgz", + "integrity": "sha512-01NCTBg8cuMJG1OQc6PR7T66+AFYiPwgDvdJmvJBn29NGzIG+DIFxPLNjHzwz3cpFIvG+NcwIjP9hSaPVoOaDg==" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "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==", + "requires": { + "mimic-response": "^3.1.0" + }, + "dependencies": { + "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==" + } + } + }, + "deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dev": true, + "requires": { + "type-detect": "^4.0.0" + } + }, + "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==" + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + }, "denque": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.0.tgz", - "integrity": "sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ==" + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz", + "integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ==" }, "depd": { "version": "1.1.2", @@ -2387,22 +9218,55 @@ "diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==" + }, + "doctypes": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", + "integrity": "sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk=" + }, + "dom-serializer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", + "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", + "requires": { + "domelementtype": "^1.3.0", + "entities": "^1.1.1" + }, + "dependencies": { + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" + } + } + }, + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + }, + "domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "requires": { + "domelementtype": "1" + } + }, + "domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } }, "dotenv": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", - "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==" - }, - "dynamic-dedupe": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz", - "integrity": "sha1-BuRMIj9eTpTXjvnbI6ZRXOL5YqE=", - "dev": true, - "requires": { - "xtend": "^4.0.0" - } + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", + "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" }, "ecdsa-sig-formatter": { "version": "1.0.11", @@ -2417,15 +9281,49 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, + "encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "optional": true, + "requires": { + "iconv-lite": "^0.6.2" + }, + "dependencies": { + "iconv-lite": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz", + "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==", + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + } + } + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, "engine.io": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-5.1.1.tgz", - "integrity": "sha512-aMWot7H5aC8L4/T8qMYbLdvKlZOdJTH54FxfdFunTGvhMx1BHkJOntWArsVfgAZVwAO9LC2sryPWRcEeUzCe5w==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-5.0.0.tgz", + "integrity": "sha512-BATIdDV3H1SrE9/u2BAotvsmjJg0t1P4+vGedImSs1lkFAtQdvk4Ev1y4LDiPF7BPWgXWEG+NDY+nLvW3UrMWw==", "requires": { "accepts": "~1.3.4", "base64id": "2.0.0", @@ -2434,6 +9332,57 @@ "debug": "~4.3.1", "engine.io-parser": "~4.0.0", "ws": "~7.4.2" + }, + "dependencies": { + "cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" + }, + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "engine.io-client": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-5.0.0.tgz", + "integrity": "sha512-e6GK0Fqvq45Nu/j7YdIVqXtDPvlsggAcfml3QiEiGdJ1qeh7IQU6knxSN3+yy9BmbnXtIfjo1hK4MFyHKdc9mQ==", + "requires": { + "base64-arraybuffer": "0.1.4", + "component-emitter": "~1.3.0", + "debug": "~4.3.1", + "engine.io-parser": "~4.0.1", + "has-cors": "1.1.0", + "parseqs": "0.0.6", + "parseuri": "0.0.6", + "ws": "~7.4.2", + "yeast": "0.1.2" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } } }, "engine.io-parser": { @@ -2444,16 +9393,56 @@ "base64-arraybuffer": "0.1.4" } }, + "entities": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz", + "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==" + }, + "env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==" + }, + "err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==" + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" }, + "execa": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.8.0.tgz", + "integrity": "sha1-2NdrvBtVIX7RkP1t1J08d07PyNo=", + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.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" + } + }, "express": { "version": "4.17.1", "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", @@ -2489,32 +9478,33 @@ "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" - }, - "dependencies": { - "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } } }, + "express-async-errors": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/express-async-errors/-/express-async-errors-3.1.1.tgz", + "integrity": "sha512-h6aK1da4tpqWSbyCa3FxB/V6Ehd4EEB15zyQq9qe75OZBp0krinNKuH4rAY+S/U/2I36vdLAUFSjQJ+TFmODng==" + }, + "express-rate-limit": { + "version": "5.2.6", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-5.2.6.tgz", + "integrity": "sha512-nE96xaxGfxiS5jP3tD3kIW1Jg9yQgX0rXCs3rCkZtmbWHEGyotwaezkLj7bnB41Z0uaOLM8W4AX6qHao4IZ2YA==" + }, "faker": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/faker/-/faker-5.5.3.tgz", - "integrity": "sha512-wLTv2a28wjUyWkbnX7u/ABZBkUkIF2fCd73V6P2oFqEGEktDfzWx4UxrSqtPRw0xPRAcjeAOIiJWqZm3pP4u3g==" + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/faker/-/faker-5.4.0.tgz", + "integrity": "sha512-Y9n/Ky/xZx/Bj8DePvXspUYRtHl/rGQytoIT5LaxmNwSe3wWyOeOXb3lT6Dpipq240PVpeFaGKzScz/5fvff2g==" + }, + "fast-safe-stringify": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", + "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==", + "dev": true + }, + "file-extension": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/file-extension/-/file-extension-4.0.5.tgz", + "integrity": "sha512-l0rOL3aKkoi6ea7MNZe6OHgqYYpn48Qfflr8Pe9G9JPPTx5A+sfboK91ZufzIs59/lPqh351l0eb6iKU9J5oGg==" }, "fill-range": { "version": "7.0.1", @@ -2537,62 +9527,116 @@ "parseurl": "~1.3.3", "statuses": "~1.5.0", "unpipe": "~1.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } } }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true + }, + "formidable": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.2.tgz", + "integrity": "sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q==", + "dev": true + }, "forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" }, "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "requires": { + "minipass": "^3.0.0" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", "dev": true, "optional": true }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } }, "generaterr": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/generaterr/-/generaterr-1.5.0.tgz", "integrity": "sha1-sM62zFFk3yoGEzjMNAqGFTlcUvw=" }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "dev": true + }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" + }, "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dev": true, + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -2603,23 +9647,125 @@ } }, "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", "dev": true, "requires": { "is-glob": "^4.0.1" } }, + "got": { + "version": "11.7.0", + "resolved": "https://registry.npmjs.org/got/-/got-11.7.0.tgz", + "integrity": "sha512-7en2XwH2MEqOsrK0xaKhbWibBoZqy+f1RSUoIeF1BLcnf+pyQdDsljWMfmOh+QKJwuvDIiKx38GtPh5wFdGGjg==", + "requires": { + "@sindresorhus/is": "^3.1.1", + "@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.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + } + }, + "graceful-fs": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" + }, + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true + }, "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-cors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", + "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=" + }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=" + }, + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + }, + "has-values": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-2.0.1.tgz", + "integrity": "sha512-+QdH3jOmq9P8GfdjFg0eJudqx1FqU62NQJ4P16rOEHeRdl7ckgwn6uqQjzYE0ZoHVV/e5E2esuJ5Gl5+HUW19w==", + "requires": { + "kind-of": "^6.0.2" + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "helmet": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-4.4.1.tgz", + "integrity": "sha512-G8tp0wUMI7i8wkMk2xLcEvESg5PiCitFMYgGRc/PwULB0RVhTP5GFdxOwvJwp9XVha8CuS8mnhmE8I/8dx/pbw==" + }, + "htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "requires": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + }, + "dependencies": { + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" + }, + "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==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "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==" + }, "http-errors": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", @@ -2632,6 +9778,78 @@ "toidentifier": "1.0.0" } }, + "http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "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==", + "requires": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + } + }, + "https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "requires": { + "agent-base": "6", + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", + "requires": { + "ms": "^2.0.0" + } + }, + "i": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/i/-/i-0.3.6.tgz", + "integrity": "sha1-2WyScyB28HJxG2sQ/X1PZa2O4j0=", + "dev": true + }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -2640,11 +9858,30 @@ "safer-buffer": ">= 2.1.2 < 3" } }, + "image-extensions": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/image-extensions/-/image-extensions-1.1.0.tgz", + "integrity": "sha1-uOa/YDnfAFbjM1AqALZjejEF2JQ=" + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==" + }, + "infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==" + }, "inflight": { "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" @@ -2655,11 +9892,31 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, + "install-artifact-from-github": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/install-artifact-from-github/-/install-artifact-from-github-1.2.0.tgz", + "integrity": "sha512-3OxCPcY55XlVM3kkfIpeCgmoSKnMsz2A3Dbhsq0RXpIknKQmrX1YiznCeW9cD2ItFmDxziA3w6Eg8d80AoL3oA==" + }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" + }, + "ip-regex": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.2.0.tgz", + "integrity": "sha512-n5cDDeTWWRwK1EBoWwRti+8nP4NbytBBY0pldmnIkq6Z55KNFmWofh4rl9dPZpj+U/nVq7gweR3ylrvMt4YZ5A==" + }, "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" }, + "is-absolute-url": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", + "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==" + }, "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -2670,20 +9927,36 @@ } }, "is-core-module": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.5.0.tgz", - "integrity": "sha512-TXCMSDsEHMEEZ6eCA8rwRDbLu55MRGmrctljsBX/2v1d9/GzqHOxW5c5oPSgrUt2vBFXebu9rGqckXGPWOlYpg==", - "dev": true, + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", + "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", "requires": { "has": "^1.0.3" } }, + "is-expression": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-4.0.0.tgz", + "integrity": "sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==", + "requires": { + "acorn": "^7.1.1", + "object-assign": "^4.1.1" + } + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", "dev": true }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, "is-glob": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", @@ -2693,16 +9966,93 @@ "is-extglob": "^2.1.1" } }, + "is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU=" + }, "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 }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true + }, + "is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" + }, + "is-regex": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.2.tgz", + "integrity": "sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg==", + "requires": { + "call-bind": "^1.0.2", + "has-symbols": "^1.0.1" + } + }, + "is-relative-url": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-relative-url/-/is-relative-url-3.0.0.tgz", + "integrity": "sha512-U1iSYRlY2GIMGuZx7gezlB5dp1Kheaym7zKzO1PV06mOihiWTXejLwm4poEJysPyXF+HtK/BEd0DVlcCh30pEA==", + "requires": { + "is-absolute-url": "^3.0.0" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "is-uri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-uri/-/is-uri-1.2.0.tgz", + "integrity": "sha1-uS/yNK9owO2X0u7UZJLQF5O31CA=", + "requires": { + "parse-uri": "~1.0.0", + "punycode2": "~1.0.0" + } + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "iso-639-3": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/iso-639-3/-/iso-639-3-2.1.0.tgz", + "integrity": "sha512-NYcq+YfrCFVGw/xWhRB9mhCSWxlOxYv3eK3WzWzc86P8huEZ7UDQq8Bu0zKqpZFOdq221Gy8VWWLr1aaYc+FJA==" + }, + "isostring": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isostring/-/isostring-0.0.1.tgz", + "integrity": "sha1-3bYI77/InNqG25yxa+CQp4gTTH8=" + }, + "js-stringify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", + "integrity": "sha1-Fzb939lyTyijaCrcYjCufk6Weds=" + }, + "js-yaml": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "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==" }, "jsonwebtoken": { "version": "8.5.1", @@ -2719,6 +10069,22 @@ "lodash.once": "^4.0.0", "ms": "^2.1.1", "semver": "^5.6.0" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "jstransformer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", + "integrity": "sha1-7Yvwkh4vPx7U1cGkT2hwntJHIsM=", + "requires": { + "is-promise": "^2.0.0", + "promise": "^7.0.1" } }, "jwa": { @@ -2741,9 +10107,64 @@ } }, "kareem": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.2.tgz", - "integrity": "sha512-STHz9P7X2L4Kwn72fA4rGyqyXdmrMSdxqHx9IXon/FXluXieaFA6KJ2upcHAHxQPQ0LeM/OjLrhFxifHewOALQ==" + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.1.tgz", + "integrity": "sha512-l3hLhffs9zqoDe8zjmb/mAN4B8VT3L56EUvKNqLFVs9YlFA+zx7ke1DO8STAdDyYNkeSo1nKmjuvQeI12So8Xw==" + }, + "keyv": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz", + "integrity": "sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==", + "requires": { + "json-buffer": "3.0.1" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" + }, + "lodash.at": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.at/-/lodash.at-4.6.0.tgz", + "integrity": "sha1-k83OZk8KGZTqM9181A4jr9EbD/g=", + "dev": true + }, + "lodash.concat": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.concat/-/lodash.concat-4.5.0.tgz", + "integrity": "sha1-sFOuAuSoAIWC5yVrnQK9ptA4A5U=", + "dev": true + }, + "lodash.filter": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz", + "integrity": "sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4=", + "dev": true + }, + "lodash.foreach": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", + "integrity": "sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM=" + }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" }, "lodash.includes": { "version": "4.3.0", @@ -2755,6 +10176,12 @@ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" }, + "lodash.isfunction": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", + "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==", + "dev": true + }, "lodash.isinteger": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", @@ -2765,6 +10192,12 @@ "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" }, + "lodash.isobject": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz", + "integrity": "sha1-PI+41bW/S/kK4G4U8qUwpO2TXh0=", + "dev": true + }, "lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", @@ -2775,22 +10208,155 @@ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" }, + "lodash.isundefined": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz", + "integrity": "sha1-I+89lTVWUgOmbO/VuDD4SJEa+0g=", + "dev": true + }, + "lodash.keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-4.2.0.tgz", + "integrity": "sha1-oIYCrBLk+4P5H8H7ejYKTZujUgU=", + "dev": true + }, "lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" }, + "lodash.toarray": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz", + "integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE=", + "dev": true + }, + "log-symbols": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", + "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", + "dev": true, + "requires": { + "chalk": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "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.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" + }, + "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==", + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, "make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + }, + "make-fetch-happen": { + "version": "8.0.14", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-8.0.14.tgz", + "integrity": "sha512-EsS89h6l4vbfJEtBZnENTOFk8mCRpY5ru36Xe5bcX1KYIli2mkSHqoFsp5O1wMDvTJJzxe/4THpCTtygjeeGWQ==", + "requires": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.0.5", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^5.0.0", + "ssri": "^8.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, + "memoize-one": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.1.1.tgz", + "integrity": "sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA==" + }, "memory-pager": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", @@ -2802,6 +10368,51 @@ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" }, + "metascraper": { + "version": "5.14.14", + "resolved": "https://registry.npmjs.org/metascraper/-/metascraper-5.14.14.tgz", + "integrity": "sha512-jKG4jOFLF+6uaP9fjZsEOih5sk7Ru5HDr1pc2R4D9DhTqfq8VJ8a28/QL55U+XMGWHuT4I7lfOo36GO7kqadcw==", + "requires": { + "@metascraper/helpers": "^5.14.14", + "cheerio": "~1.0.0-rc.3", + "cheerio-advanced-selectors": "~2.0.1", + "lodash": "~4.17.20", + "whoops": "~4.1.0" + } + }, + "metascraper-description": { + "version": "5.14.14", + "resolved": "https://registry.npmjs.org/metascraper-description/-/metascraper-description-5.14.14.tgz", + "integrity": "sha512-d/tqcrJbcv0l+R89WTJmjH95XQlVXETS15VIelnEYRYqOdnZbOz3ZOPgs+SKzo4fz8LwRyQ6aKqFocnGvjv2wA==", + "requires": { + "@metascraper/helpers": "^5.14.14" + } + }, + "metascraper-image": { + "version": "5.14.14", + "resolved": "https://registry.npmjs.org/metascraper-image/-/metascraper-image-5.14.14.tgz", + "integrity": "sha512-vkza91FrDQH/JFuqJLzBSUcjXNbPXvvA5HfaoAealQUQyIaF6AyxwPGzGe9aJo9h5Iw5njysLWNwxZMrtOq1kw==", + "requires": { + "@metascraper/helpers": "^5.14.14" + } + }, + "metascraper-title": { + "version": "5.14.14", + "resolved": "https://registry.npmjs.org/metascraper-title/-/metascraper-title-5.14.14.tgz", + "integrity": "sha512-2uiAJmG+buADSmM1BTI499tBcmLGyGFkpQSGwqc2wqn1K87r1yuqHRSbhNqDNkqYhfT9px3DIek5daOrzxe1ew==", + "requires": { + "@metascraper/helpers": "^5.14.14", + "lodash": "~4.17.20" + } + }, + "metascraper-url": { + "version": "5.14.14", + "resolved": "https://registry.npmjs.org/metascraper-url/-/metascraper-url-5.14.14.tgz", + "integrity": "sha512-BJccdsdVymwbxDqFRZkndYn3rGJ9BFgnF87ilMkPiLg0aP+2n7028Kl2KnNGP0eFBe1m/yUXHivQ27xcCBdBmw==", + "requires": { + "@metascraper/helpers": "^5.14.14" + } + }, "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -2813,76 +10424,230 @@ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" }, "mime-db": { - "version": "1.48.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz", - "integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==" + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" }, "mime-types": { - "version": "2.1.31", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz", - "integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==", + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", "requires": { - "mime-db": "1.48.0" + "mime-db": "1.44.0" } }, + "mimic-fn": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.0.0.tgz", + "integrity": "sha512-PiVO95TKvhiwgSwg1IdLYlCTdul38yZxZMIcnDSFIBUm4BNZha2qpQ4GpJ++15bHoKDtrW2D69lMfFwdFYtNZQ==" + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" + }, "minimatch": { "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" } }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true + "minipass": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", + "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", + "requires": { + "yallist": "^4.0.0" + }, + "dependencies": { + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } + }, + "minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-fetch": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.3.3.tgz", + "integrity": "sha512-akCrLDWfbdAWkMLBxJEeWTdNsjML+dt5YgOI4gJ53vuO0vrmYQkUPxa6j6V65s9CcePIr2SSWqjT2EcrNseryQ==", + "requires": { + "encoding": "^0.1.12", + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + } + }, + "minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "requires": { + "minipass": "^3.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "dependencies": { + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } }, "mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + }, + "mocha": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.2.1.tgz", + "integrity": "sha512-cuLBVfyFfFqbNR0uUKbDGXKGk+UDFe6aR4os78XIrMQpZl/nv7JYHcvP5MFIAb374b2zFXsdgEGwmzMtP0Xg8w==", + "dev": true, + "requires": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.4.3", + "debug": "4.2.0", + "diff": "4.0.2", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.1.6", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "3.14.0", + "log-symbols": "4.0.0", + "minimatch": "3.0.4", + "ms": "2.1.2", + "nanoid": "3.1.12", + "serialize-javascript": "5.0.1", + "strip-json-comments": "3.1.1", + "supports-color": "7.2.0", + "which": "2.0.2", + "wide-align": "1.1.3", + "workerpool": "6.0.2", + "yargs": "13.3.2", + "yargs-parser": "13.1.2", + "yargs-unparser": "2.0.0" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "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 + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "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" + } + } + } }, "mongodb": { - "version": "3.6.10", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.10.tgz", - "integrity": "sha512-fvIBQBF7KwCJnDZUnFFy4WqEFP8ibdXeFANnylW19+vOwdjOAvqIzPdsNCEMT6VKTHnYu4K64AWRih0mkFms6Q==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.2.tgz", + "integrity": "sha512-sSZOb04w3HcnrrXC82NEh/YGCmBuRgR+C1hZgmmv4L6dBz4BkRse6Y8/q/neXer9i95fKUBbFi4KgeceXmbsOA==", "requires": { "bl": "^2.2.1", "bson": "^1.1.4", "denque": "^1.4.1", - "optional-require": "^1.0.3", + "require_optional": "^1.0.1", "safe-buffer": "^5.1.2", "saslprep": "^1.0.0" } }, "mongoose": { - "version": "5.13.3", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.13.3.tgz", - "integrity": "sha512-q+zX6kqHAvwxf5speMWhq6qF4vdj+x6/kfD5RSKdZKNm52yGmaUygN+zgrtQjBZPFEzG0B3vF6GP0PoAGadE+w==", + "version": "5.10.7", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.10.7.tgz", + "integrity": "sha512-oiofFrD4I5p3PhJXn49QyrU1nX5CY01qhPkfMMrXYPhkfGLEJVwFVO+0PsCxD91A2kQP+d/iFyk5U8e86KI8eQ==", "requires": { - "@types/mongodb": "^3.5.27", - "@types/node": "14.x || 15.x", "bson": "^1.1.4", - "kareem": "2.3.2", - "mongodb": "3.6.10", + "kareem": "2.3.1", + "mongodb": "3.6.2", "mongoose-legacy-pluralize": "1.0.2", - "mpath": "0.8.3", - "mquery": "3.2.5", + "mpath": "0.7.0", + "mquery": "3.2.2", "ms": "2.1.2", "regexp-clone": "1.0.0", "safe-buffer": "5.2.1", - "sift": "13.5.2", + "sift": "7.0.1", "sliced": "1.0.1" }, "dependencies": { - "@types/node": { - "version": "15.14.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-15.14.3.tgz", - "integrity": "sha512-gliNP92vLGGha1nioYHIIT2WrZ450sxpRgyPCEyog2hMVi6LEbhY/Pkj+EDiGWrCXntZ9lrnE2+lTIlyYtaxCg==" + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "safe-buffer": { "version": "5.2.1", @@ -2894,18 +10659,26 @@ "mongoose-legacy-pluralize": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz", - "integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==", - "requires": {} + "integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==" + }, + "mongoose-unique-validator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/mongoose-unique-validator/-/mongoose-unique-validator-2.0.3.tgz", + "integrity": "sha512-3/8pmvAC1acBZS6eWKAWQUiZBlARE1wyWtjga4iQ2wDJeOfRlIKmAvTNHSZXKaAf7RCRUd7wh7as6yWAOrjpQg==", + "requires": { + "lodash.foreach": "^4.1.0", + "lodash.get": "^4.0.2" + } }, "mpath": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.8.3.tgz", - "integrity": "sha512-eb9rRvhDltXVNL6Fxd2zM9D4vKBxjVVQNLNijlj7uoXUy19zNDsIif5zR+pWmPCWNKwAtqyo4JveQm4nfD5+eA==" + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.7.0.tgz", + "integrity": "sha512-Aiq04hILxhz1L+f7sjGyn7IxYzWm1zLNNXcfhDtx04kZ2Gk7uvFdgZ8ts1cWa/6d0TQmag2yR8zSGZUmp0tFNg==" }, "mquery": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/mquery/-/mquery-3.2.5.tgz", - "integrity": "sha512-VjOKHHgU84wij7IUoZzFRU07IAxd5kWJaDmyUzQlbjHjyoeK5TNeeo8ZsFDtTYnSgpW6n/nMNIHvE3u8Lbrf4A==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-3.2.2.tgz", + "integrity": "sha512-XB52992COp0KP230I3qloVUbkLUxJIu328HBP2t2EsxSFtf4W1HPSOBWOXf1bqxK4Xbb66lfMJ+Bpfd9/yZE1Q==", "requires": { "bluebird": "3.5.1", "debug": "3.1.0", @@ -2921,35 +10694,2180 @@ "requires": { "ms": "2.0.0" } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" } } }, "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "nan": { + "version": "2.14.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", + "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==" + }, + "nanoid": { + "version": "3.1.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.12.tgz", + "integrity": "sha512-1qstj9z5+x491jfiC4Nelk+f8XBad7LN20PmyWINJEMRSf3wcAjAWysw1qaA8z6NSKe2sjq1hRSDpBH5paCb6A==", + "dev": true }, "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + }, + "node-gyp": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.0.0.tgz", + "integrity": "sha512-Jod6NxyWtcwrpAQe0O/aXOpC5QfncotgtG73dg65z6VW/C6g/G4jiajXQUBIJ8pk/VfM6mBYE9BN/HvudTunUQ==", + "requires": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^8.0.14", + "nopt": "^5.0.0", + "npmlog": "^4.1.2", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.0", + "which": "^2.0.2" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } + }, + "nodemailer": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.5.0.tgz", + "integrity": "sha512-Tm4RPrrIZbnqDKAvX+/4M+zovEReiKlEXWDzG4iwtpL9X34MJY+D5LnQPH/+eghe8DLlAVshHAJZAZWBGhkguw==" + }, + "nodemailer-pug-engine": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/nodemailer-pug-engine/-/nodemailer-pug-engine-2.0.0.tgz", + "integrity": "sha512-hslY4VdaDrLLYosC7Q7D+dK4FwahJJWqYWvWaW3isNIQ4wrUperjr31FrEye1ZPsq9giPMVQ2O0q/EwOPx166Q==", + "requires": { + "pug": "^3.0.0" + } + }, + "nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "requires": { + "abbrev": "1" + } + }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, + "normalize-url": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-5.2.1.tgz", + "integrity": "sha512-bFT2ilr7p37ZPEQ9LO9HP/tdFIAE7Q4UoeojXNKeLjs0vXxZetM+C2K9jdbVS7b6ut66CflVLgk1yqHJVrXmiw==" + }, + "npm": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/npm/-/npm-7.6.3.tgz", + "integrity": "sha512-+Cs8TEtkfdQGTIPw8AeqVtNNHyo1Zw8HATzAFFWYnK7jQYgT/CatEy85+BlEoEpqvga2uaKqVrXsTAYj28emjg==", + "dev": true, + "requires": { + "@npmcli/arborist": "^2.2.8", + "@npmcli/ci-detect": "^1.2.0", + "@npmcli/config": "^1.2.9", + "@npmcli/run-script": "^1.8.3", + "abbrev": "~1.1.1", + "ansicolors": "~0.3.2", + "ansistyles": "~0.1.3", + "archy": "~1.0.0", + "byte-size": "^7.0.1", + "cacache": "^15.0.5", + "chalk": "^4.1.0", + "chownr": "^2.0.0", + "cli-columns": "^3.1.2", + "cli-table3": "^0.6.0", + "columnify": "~1.5.4", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "hosted-git-info": "^3.0.8", + "ini": "^2.0.0", + "init-package-json": "^2.0.2", + "is-cidr": "^4.0.2", + "json-parse-even-better-errors": "^2.3.1", + "leven": "^3.1.0", + "libnpmaccess": "^4.0.1", + "libnpmdiff": "^2.0.4", + "libnpmfund": "^1.0.2", + "libnpmhook": "^6.0.1", + "libnpmorg": "^2.0.1", + "libnpmpack": "^2.0.1", + "libnpmpublish": "^4.0.0", + "libnpmsearch": "^3.1.0", + "libnpmteam": "^2.0.2", + "libnpmversion": "^1.0.11", + "make-fetch-happen": "^8.0.14", + "minipass": "^3.1.3", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "mkdirp-infer-owner": "^2.0.0", + "ms": "^2.1.2", + "node-gyp": "^7.1.2", + "nopt": "^5.0.0", + "npm-audit-report": "^2.1.4", + "npm-package-arg": "^8.1.1", + "npm-pick-manifest": "^6.1.0", + "npm-profile": "^5.0.2", + "npm-registry-fetch": "^9.0.0", + "npm-user-validate": "^1.0.1", + "npmlog": "~4.1.2", + "opener": "^1.5.2", + "pacote": "^11.3.0", + "parse-conflict-json": "^1.1.1", + "qrcode-terminal": "^0.12.0", + "read": "~1.0.7", + "read-package-json": "^3.0.1", + "read-package-json-fast": "^2.0.2", + "readdir-scoped-modules": "^1.1.0", + "rimraf": "^3.0.2", + "semver": "^7.3.4", + "ssri": "^8.0.1", + "tar": "^6.1.0", + "text-table": "~0.2.0", + "tiny-relative-date": "^1.3.0", + "treeverse": "^1.0.4", + "validate-npm-package-name": "~3.0.0", + "which": "^2.0.2", + "write-file-atomic": "^3.0.3" + }, + "dependencies": { + "@npmcli/arborist": { + "version": "2.2.8", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/installed-package-contents": "^1.0.7", + "@npmcli/map-workspaces": "^1.0.2", + "@npmcli/metavuln-calculator": "^1.1.0", + "@npmcli/move-file": "^1.1.0", + "@npmcli/name-from-folder": "^1.0.1", + "@npmcli/node-gyp": "^1.0.1", + "@npmcli/run-script": "^1.8.2", + "bin-links": "^2.2.1", + "cacache": "^15.0.3", + "common-ancestor-path": "^1.0.1", + "json-parse-even-better-errors": "^2.3.1", + "json-stringify-nice": "^1.1.1", + "mkdirp-infer-owner": "^2.0.0", + "npm-install-checks": "^4.0.0", + "npm-package-arg": "^8.1.0", + "npm-pick-manifest": "^6.1.0", + "npm-registry-fetch": "^9.0.0", + "pacote": "^11.2.6", + "parse-conflict-json": "^1.1.1", + "promise-all-reject-late": "^1.0.0", + "promise-call-limit": "^1.0.1", + "read-package-json-fast": "^2.0.2", + "readdir-scoped-modules": "^1.1.0", + "semver": "^7.3.4", + "tar": "^6.1.0", + "treeverse": "^1.0.4", + "walk-up-path": "^1.0.0" + } + }, + "@npmcli/ci-detect": { + "version": "1.3.0", + "bundled": true, + "dev": true + }, + "@npmcli/config": { + "version": "1.2.9", + "bundled": true, + "dev": true, + "requires": { + "ini": "^2.0.0", + "mkdirp-infer-owner": "^2.0.0", + "nopt": "^5.0.0", + "semver": "^7.3.4", + "walk-up-path": "^1.0.0" + } + }, + "@npmcli/disparity-colors": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-styles": "^4.3.0" + } + }, + "@npmcli/git": { + "version": "2.0.6", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/promise-spawn": "^1.1.0", + "lru-cache": "^6.0.0", + "mkdirp": "^1.0.3", + "npm-pick-manifest": "^6.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.2", + "unique-filename": "^1.1.1", + "which": "^2.0.2" + } + }, + "@npmcli/installed-package-contents": { + "version": "1.0.7", + "bundled": true, + "dev": true, + "requires": { + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "@npmcli/map-workspaces": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/name-from-folder": "^1.0.1", + "glob": "^7.1.6", + "minimatch": "^3.0.4", + "read-package-json-fast": "^2.0.1" + } + }, + "@npmcli/metavuln-calculator": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "requires": { + "cacache": "^15.0.5", + "pacote": "^11.1.11", + "semver": "^7.3.2" + } + }, + "@npmcli/move-file": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "requires": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + } + }, + "@npmcli/name-from-folder": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "@npmcli/node-gyp": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "@npmcli/promise-spawn": { + "version": "1.3.2", + "bundled": true, + "dev": true, + "requires": { + "infer-owner": "^1.0.4" + } + }, + "@npmcli/run-script": { + "version": "1.8.3", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/node-gyp": "^1.0.2", + "@npmcli/promise-spawn": "^1.3.2", + "infer-owner": "^1.0.4", + "node-gyp": "^7.1.0", + "puka": "^1.0.1", + "read-package-json-fast": "^2.0.1" + } + }, + "@tootallnate/once": { + "version": "1.1.2", + "bundled": true, + "dev": true + }, + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true + }, + "agent-base": { + "version": "6.0.2", + "bundled": true, + "dev": true, + "requires": { + "debug": "4" + } + }, + "agentkeepalive": { + "version": "4.1.4", + "bundled": true, + "dev": true, + "requires": { + "debug": "^4.1.0", + "depd": "^1.1.2", + "humanize-ms": "^1.2.1" + } + }, + "aggregate-error": { + "version": "3.1.0", + "bundled": true, + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ajv": { + "version": "6.12.6", + "bundled": true, + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "bundled": true, + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "ansicolors": { + "version": "0.3.2", + "bundled": true, + "dev": true + }, + "ansistyles": { + "version": "0.1.3", + "bundled": true, + "dev": true + }, + "aproba": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "archy": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "bundled": true, + "dev": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "asap": { + "version": "2.0.6", + "bundled": true, + "dev": true + }, + "asn1": { + "version": "0.2.4", + "bundled": true, + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "bundled": true, + "dev": true + }, + "aws-sign2": { + "version": "0.7.0", + "bundled": true, + "dev": true + }, + "aws4": { + "version": "1.11.0", + "bundled": true, + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "bin-links": { + "version": "2.2.1", + "bundled": true, + "dev": true, + "requires": { + "cmd-shim": "^4.0.1", + "mkdirp": "^1.0.3", + "npm-normalize-package-bin": "^1.0.0", + "read-cmd-shim": "^2.0.0", + "rimraf": "^3.0.0", + "write-file-atomic": "^3.0.3" + } + }, + "binary-extensions": { + "version": "2.2.0", + "bundled": true, + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "builtins": { + "version": "1.0.3", + "bundled": true, + "dev": true + }, + "byte-size": { + "version": "7.0.1", + "bundled": true, + "dev": true + }, + "cacache": { + "version": "15.0.5", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.0", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + } + }, + "caseless": { + "version": "0.12.0", + "bundled": true, + "dev": true + }, + "chalk": { + "version": "4.1.0", + "bundled": true, + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "chownr": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "cidr-regex": { + "version": "3.1.1", + "bundled": true, + "dev": true, + "requires": { + "ip-regex": "^4.1.0" + } + }, + "clean-stack": { + "version": "2.2.0", + "bundled": true, + "dev": true + }, + "cli-columns": { + "version": "3.1.2", + "bundled": true, + "dev": true, + "requires": { + "string-width": "^2.0.0", + "strip-ansi": "^3.0.1" + } + }, + "cli-table3": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "requires": { + "colors": "^1.1.2", + "object-assign": "^4.1.0", + "string-width": "^4.2.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "bundled": true, + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "string-width": { + "version": "4.2.0", + "bundled": true, + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, + "clone": { + "version": "1.0.4", + "bundled": true, + "dev": true + }, + "cmd-shim": { + "version": "4.1.0", + "bundled": true, + "dev": true, + "requires": { + "mkdirp-infer-owner": "^2.0.0" + } + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "color-convert": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "bundled": true, + "dev": true + }, + "colors": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "optional": true + }, + "columnify": { + "version": "1.5.4", + "bundled": true, + "dev": true, + "requires": { + "strip-ansi": "^3.0.0", + "wcwidth": "^1.0.0" + } + }, + "combined-stream": { + "version": "1.0.8", + "bundled": true, + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "common-ancestor-path": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "dashdash": { + "version": "1.14.1", + "bundled": true, + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "debug": { + "version": "4.3.1", + "bundled": true, + "dev": true, + "requires": { + "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "bundled": true, + "dev": true + } + } + }, + "debuglog": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "defaults": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "requires": { + "clone": "^1.0.2" + } + }, + "delayed-stream": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "depd": { + "version": "1.1.2", + "bundled": true, + "dev": true + }, + "dezalgo": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "requires": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "diff": { + "version": "5.0.0", + "bundled": true, + "dev": true + }, + "ecc-jsbn": { + "version": "0.1.2", + "bundled": true, + "dev": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "emoji-regex": { + "version": "8.0.0", + "bundled": true, + "dev": true + }, + "encoding": { + "version": "0.1.13", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "iconv-lite": "^0.6.2" + } + }, + "env-paths": { + "version": "2.2.0", + "bundled": true, + "dev": true + }, + "err-code": { + "version": "2.0.3", + "bundled": true, + "dev": true + }, + "extend": { + "version": "3.0.2", + "bundled": true, + "dev": true + }, + "extsprintf": { + "version": "1.3.0", + "bundled": true, + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "bundled": true, + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "bundled": true, + "dev": true + }, + "forever-agent": { + "version": "0.6.1", + "bundled": true, + "dev": true + }, + "form-data": { + "version": "2.3.3", + "bundled": true, + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "fs-minipass": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "bundled": true, + "dev": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + }, + "dependencies": { + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, + "getpass": { + "version": "0.1.7", + "bundled": true, + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.6", + "bundled": true, + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.2.6", + "bundled": true, + "dev": true + }, + "har-schema": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "har-validator": { + "version": "5.1.5", + "bundled": true, + "dev": true, + "requires": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + } + }, + "has": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "4.0.0", + "bundled": true, + "dev": true + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true + }, + "hosted-git-info": { + "version": "3.0.8", + "bundled": true, + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "http-cache-semantics": { + "version": "4.1.0", + "bundled": true, + "dev": true + }, + "http-proxy-agent": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + } + }, + "http-signature": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "https-proxy-agent": { + "version": "5.0.0", + "bundled": true, + "dev": true, + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "humanize-ms": { + "version": "1.2.1", + "bundled": true, + "dev": true, + "requires": { + "ms": "^2.0.0" + } + }, + "iconv-lite": { + "version": "0.6.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, + "ignore-walk": { + "version": "3.0.3", + "bundled": true, + "dev": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "imurmurhash": { + "version": "0.1.4", + "bundled": true, + "dev": true + }, + "indent-string": { + "version": "4.0.0", + "bundled": true, + "dev": true + }, + "infer-owner": { + "version": "1.0.4", + "bundled": true, + "dev": true + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "bundled": true, + "dev": true + }, + "ini": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "init-package-json": { + "version": "2.0.2", + "bundled": true, + "dev": true, + "requires": { + "glob": "^7.1.1", + "npm-package-arg": "^8.1.0", + "promzard": "^0.3.0", + "read": "~1.0.1", + "read-package-json": "^3.0.0", + "semver": "^7.3.2", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "^3.0.0" + } + }, + "ip": { + "version": "1.1.5", + "bundled": true, + "dev": true + }, + "ip-regex": { + "version": "4.3.0", + "bundled": true, + "dev": true + }, + "is-cidr": { + "version": "4.0.2", + "bundled": true, + "dev": true, + "requires": { + "cidr-regex": "^3.1.1" + } + }, + "is-core-module": { + "version": "2.2.0", + "bundled": true, + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "is-lambda": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "is-typedarray": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "isexe": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "isstream": { + "version": "0.1.2", + "bundled": true, + "dev": true + }, + "jsbn": { + "version": "0.1.1", + "bundled": true, + "dev": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "bundled": true, + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "bundled": true, + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "bundled": true, + "dev": true + }, + "json-stringify-nice": { + "version": "1.1.1", + "bundled": true, + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "bundled": true, + "dev": true + }, + "jsonparse": { + "version": "1.3.1", + "bundled": true, + "dev": true + }, + "jsprim": { + "version": "1.4.1", + "bundled": true, + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "just-diff": { + "version": "3.0.2", + "bundled": true, + "dev": true + }, + "just-diff-apply": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "leven": { + "version": "3.1.0", + "bundled": true, + "dev": true + }, + "libnpmaccess": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "requires": { + "aproba": "^2.0.0", + "minipass": "^3.1.1", + "npm-package-arg": "^8.0.0", + "npm-registry-fetch": "^9.0.0" + } + }, + "libnpmdiff": { + "version": "2.0.4", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/disparity-colors": "^1.0.1", + "@npmcli/installed-package-contents": "^1.0.7", + "binary-extensions": "^2.2.0", + "diff": "^5.0.0", + "minimatch": "^3.0.4", + "npm-package-arg": "^8.1.1", + "pacote": "^11.3.0", + "tar": "^6.1.0" + } + }, + "libnpmfund": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/arborist": "^2.0.0" + } + }, + "libnpmhook": { + "version": "6.0.1", + "bundled": true, + "dev": true, + "requires": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^9.0.0" + } + }, + "libnpmorg": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^9.0.0" + } + }, + "libnpmpack": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/run-script": "^1.8.3", + "npm-package-arg": "^8.1.0", + "pacote": "^11.2.6" + } + }, + "libnpmpublish": { + "version": "4.0.0", + "bundled": true, + "dev": true, + "requires": { + "normalize-package-data": "^3.0.0", + "npm-package-arg": "^8.1.0", + "npm-registry-fetch": "^9.0.0", + "semver": "^7.1.3", + "ssri": "^8.0.0" + } + }, + "libnpmsearch": { + "version": "3.1.0", + "bundled": true, + "dev": true, + "requires": { + "npm-registry-fetch": "^9.0.0" + } + }, + "libnpmteam": { + "version": "2.0.2", + "bundled": true, + "dev": true, + "requires": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^9.0.0" + } + }, + "libnpmversion": { + "version": "1.0.11", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/git": "^2.0.6", + "@npmcli/run-script": "^1.8.3", + "read-package-json-fast": "^2.0.1", + "semver": "^7.3.4", + "stringify-package": "^1.0.1" + } + }, + "lru-cache": { + "version": "6.0.0", + "bundled": true, + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "make-fetch-happen": { + "version": "8.0.14", + "bundled": true, + "dev": true, + "requires": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.0.5", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^5.0.0", + "ssri": "^8.0.0" + } + }, + "mime-db": { + "version": "1.45.0", + "bundled": true, + "dev": true + }, + "mime-types": { + "version": "2.1.28", + "bundled": true, + "dev": true, + "requires": { + "mime-db": "1.45.0" + } + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minipass": { + "version": "3.1.3", + "bundled": true, + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "minipass-collect": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-fetch": { + "version": "1.3.3", + "bundled": true, + "dev": true, + "requires": { + "encoding": "^0.1.12", + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + } + }, + "minipass-flush": { + "version": "1.0.5", + "bundled": true, + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-json-stream": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "jsonparse": "^1.3.1", + "minipass": "^3.0.0" + } + }, + "minipass-pipeline": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-sized": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + } + }, + "mkdirp": { + "version": "1.0.4", + "bundled": true, + "dev": true + }, + "mkdirp-infer-owner": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "chownr": "^2.0.0", + "infer-owner": "^1.0.4", + "mkdirp": "^1.0.3" + } + }, + "ms": { + "version": "2.1.3", + "bundled": true, + "dev": true + }, + "mute-stream": { + "version": "0.0.8", + "bundled": true, + "dev": true + }, + "node-gyp": { + "version": "7.1.2", + "bundled": true, + "dev": true, + "requires": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.3", + "nopt": "^5.0.0", + "npmlog": "^4.1.2", + "request": "^2.88.2", + "rimraf": "^3.0.2", + "semver": "^7.3.2", + "tar": "^6.0.2", + "which": "^2.0.2" + } + }, + "nopt": { + "version": "5.0.0", + "bundled": true, + "dev": true, + "requires": { + "abbrev": "1" + } + }, + "normalize-package-data": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "hosted-git-info": "^3.0.6", + "resolve": "^1.17.0", + "semver": "^7.3.2", + "validate-npm-package-license": "^3.0.1" + } + }, + "npm-audit-report": { + "version": "2.1.4", + "bundled": true, + "dev": true, + "requires": { + "chalk": "^4.0.0" + } + }, + "npm-bundled": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-install-checks": { + "version": "4.0.0", + "bundled": true, + "dev": true, + "requires": { + "semver": "^7.1.1" + } + }, + "npm-normalize-package-bin": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "npm-package-arg": { + "version": "8.1.1", + "bundled": true, + "dev": true, + "requires": { + "hosted-git-info": "^3.0.6", + "semver": "^7.0.0", + "validate-npm-package-name": "^3.0.0" + } + }, + "npm-packlist": { + "version": "2.1.4", + "bundled": true, + "dev": true, + "requires": { + "glob": "^7.1.6", + "ignore-walk": "^3.0.3", + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-pick-manifest": { + "version": "6.1.0", + "bundled": true, + "dev": true, + "requires": { + "npm-install-checks": "^4.0.0", + "npm-package-arg": "^8.0.0", + "semver": "^7.0.0" + } + }, + "npm-profile": { + "version": "5.0.2", + "bundled": true, + "dev": true, + "requires": { + "npm-registry-fetch": "^9.0.0" + } + }, + "npm-registry-fetch": { + "version": "9.0.0", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/ci-detect": "^1.0.0", + "lru-cache": "^6.0.0", + "make-fetch-happen": "^8.0.9", + "minipass": "^3.1.3", + "minipass-fetch": "^1.3.0", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.0.0", + "npm-package-arg": "^8.0.0" + } + }, + "npm-user-validate": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "oauth-sign": { + "version": "0.9.0", + "bundled": true, + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "opener": { + "version": "1.5.2", + "bundled": true, + "dev": true + }, + "p-map": { + "version": "4.0.0", + "bundled": true, + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "pacote": { + "version": "11.3.0", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/git": "^2.0.1", + "@npmcli/installed-package-contents": "^1.0.6", + "@npmcli/promise-spawn": "^1.2.0", + "@npmcli/run-script": "^1.8.2", + "cacache": "^15.0.5", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "infer-owner": "^1.0.4", + "minipass": "^3.1.3", + "mkdirp": "^1.0.3", + "npm-package-arg": "^8.0.1", + "npm-packlist": "^2.1.4", + "npm-pick-manifest": "^6.0.0", + "npm-registry-fetch": "^9.0.0", + "promise-retry": "^2.0.1", + "read-package-json-fast": "^2.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.1.0" + } + }, + "parse-conflict-json": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "requires": { + "json-parse-even-better-errors": "^2.3.0", + "just-diff": "^3.0.1", + "just-diff-apply": "^3.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "bundled": true, + "dev": true + }, + "performance-now": { + "version": "2.1.0", + "bundled": true, + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "bundled": true, + "dev": true + }, + "promise-all-reject-late": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "promise-call-limit": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "promise-inflight": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "promise-retry": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + } + }, + "promzard": { + "version": "0.3.0", + "bundled": true, + "dev": true, + "requires": { + "read": "1" + } + }, + "psl": { + "version": "1.8.0", + "bundled": true, + "dev": true + }, + "puka": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "punycode": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "qrcode-terminal": { + "version": "0.12.0", + "bundled": true, + "dev": true + }, + "qs": { + "version": "6.5.2", + "bundled": true, + "dev": true + }, + "read": { + "version": "1.0.7", + "bundled": true, + "dev": true, + "requires": { + "mute-stream": "~0.0.4" + } + }, + "read-cmd-shim": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "read-package-json": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "glob": "^7.1.1", + "json-parse-even-better-errors": "^2.3.0", + "normalize-package-data": "^3.0.0", + "npm-normalize-package-bin": "^1.0.0" + } + }, + "read-package-json-fast": { + "version": "2.0.2", + "bundled": true, + "dev": true, + "requires": { + "json-parse-even-better-errors": "^2.3.0", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "readable-stream": { + "version": "2.3.7", + "bundled": true, + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdir-scoped-modules": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "requires": { + "debuglog": "^1.0.1", + "dezalgo": "^1.0.0", + "graceful-fs": "^4.1.2", + "once": "^1.3.0" + } + }, + "request": { + "version": "2.88.2", + "bundled": true, + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "tough-cookie": { + "version": "2.5.0", + "bundled": true, + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + } + } + }, + "resolve": { + "version": "1.20.0", + "bundled": true, + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, + "retry": { + "version": "0.12.0", + "bundled": true, + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true, + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true + }, + "semver": { + "version": "7.3.4", + "bundled": true, + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "signal-exit": { + "version": "3.0.3", + "bundled": true, + "dev": true + }, + "smart-buffer": { + "version": "4.1.0", + "bundled": true, + "dev": true + }, + "socks": { + "version": "2.5.1", + "bundled": true, + "dev": true, + "requires": { + "ip": "^1.1.5", + "smart-buffer": "^4.1.0" + } + }, + "socks-proxy-agent": { + "version": "5.0.0", + "bundled": true, + "dev": true, + "requires": { + "agent-base": "6", + "debug": "4", + "socks": "^2.3.3" + } + }, + "spdx-correct": { + "version": "3.1.1", + "bundled": true, + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "bundled": true, + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.7", + "bundled": true, + "dev": true + }, + "sshpk": { + "version": "1.16.1", + "bundled": true, + "dev": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "ssri": { + "version": "8.0.1", + "bundled": true, + "dev": true, + "requires": { + "minipass": "^3.1.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "string-width": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "stringify-package": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "bundled": true, + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "tar": { + "version": "6.1.0", + "bundled": true, + "dev": true, + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + } + }, + "text-table": { + "version": "0.2.0", + "bundled": true, + "dev": true + }, + "tiny-relative-date": { + "version": "1.3.0", + "bundled": true, + "dev": true + }, + "treeverse": { + "version": "1.0.4", + "bundled": true, + "dev": true + }, + "tunnel-agent": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "bundled": true, + "dev": true + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "bundled": true, + "dev": true, + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "unique-filename": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "bundled": true, + "dev": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "uri-js": { + "version": "4.4.1", + "bundled": true, + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "uuid": { + "version": "3.4.0", + "bundled": true, + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "validate-npm-package-name": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "builtins": "^1.0.3" + } + }, + "verror": { + "version": "1.10.0", + "bundled": true, + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "walk-up-path": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "wcwidth": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "defaults": "^1.0.3" + } + }, + "which": { + "version": "2.0.2", + "bundled": true, + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "wide-align": { + "version": "1.1.3", + "bundled": true, + "dev": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "write-file-atomic": { + "version": "3.0.3", + "bundled": true, + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "yallist": { + "version": "4.0.0", + "bundled": true, + "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=", + "requires": { + "path-key": "^2.0.0" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "requires": { + "boolbase": "~1.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, + "object-inspect": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", + "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==", + "dev": true + }, "on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -2962,19 +12880,75 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1" } }, - "optional-require": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.1.0.tgz", - "integrity": "sha512-5/7ee3eTFg1P+F9usTubuNCLfWRK6DjV0EFHLlbp7MmV5UlWqpWIVSnH6xo4u+fc5WHXaJuvJi6iuYnfDyj6oQ==", + "p-cancelable": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.0.0.tgz", + "integrity": "sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg==" + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, "requires": { - "require-at": "^1.0.6" + "yocto-queue": "^0.1.0" } }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "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 + }, + "parse-uri": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/parse-uri/-/parse-uri-1.0.3.tgz", + "integrity": "sha512-upMnGxNcm+45So85HoguwZTVZI9u11i36DdxJfGF2HYWS2eh3TIx7+/tTi7qrEq15qzGkVhsKjesau+kCk48pA==" + }, + "parse5": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz", + "integrity": "sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==", + "requires": { + "@types/node": "*" + } + }, + "parseqs": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz", + "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==" + }, + "parseuri": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz", + "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==" + }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -2998,13 +12972,21 @@ } }, "passport-local-mongoose": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/passport-local-mongoose/-/passport-local-mongoose-6.1.0.tgz", - "integrity": "sha512-kxRDejpBXoPmWau1RCrmEeNYEXGG9ec4aDYjd0pFAHIEAzZ0RXKn581ISfjpHZ1zZLoCCM2pWUo4SfGHNJNwnw==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/passport-local-mongoose/-/passport-local-mongoose-6.0.1.tgz", + "integrity": "sha512-Jc/ImrVnG7o7aTIqrAznt2CxVxy5M2gAxc3erVZyPVUVfiwgvrhlzA9c5fswValA12H2C9mm1AjqGDz+491TDg==", "requires": { "generaterr": "^1.5.0", "passport-local": "^1.0.0", - "scmp": "^2.1.0" + "scmp": "^2.1.0", + "semver": "^7.1.1" + }, + "dependencies": { + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==" + } } }, "passport-strategy": { @@ -3012,32 +12994,47 @@ "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=" }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" }, "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" }, "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, + "pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true + }, "pause": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" }, "picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", "dev": true }, "process-nextick-args": { @@ -3045,25 +13042,202 @@ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, - "proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", "requires": { - "forwarded": "0.2.0", + "asap": "~2.0.3" + } + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=" + }, + "promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "requires": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + } + }, + "proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "requires": { + "forwarded": "~0.1.2", "ipaddr.js": "1.9.1" } }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" + }, + "pug": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pug/-/pug-3.0.2.tgz", + "integrity": "sha512-bp0I/hiK1D1vChHh6EfDxtndHji55XP/ZJKwsRqrz6lRia6ZC2OZbdAymlxdVFwd1L70ebrVJw4/eZ79skrIaw==", + "requires": { + "pug-code-gen": "^3.0.2", + "pug-filters": "^4.0.0", + "pug-lexer": "^5.0.1", + "pug-linker": "^4.0.0", + "pug-load": "^3.0.0", + "pug-parser": "^6.0.0", + "pug-runtime": "^3.0.1", + "pug-strip-comments": "^2.0.0" + } + }, + "pug-attrs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-3.0.0.tgz", + "integrity": "sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA==", + "requires": { + "constantinople": "^4.0.1", + "js-stringify": "^1.0.2", + "pug-runtime": "^3.0.0" + } + }, + "pug-code-gen": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-3.0.2.tgz", + "integrity": "sha512-nJMhW16MbiGRiyR4miDTQMRWDgKplnHyeLvioEJYbk1RsPI3FuA3saEP8uwnTb2nTJEKBU90NFVWJBk4OU5qyg==", + "requires": { + "constantinople": "^4.0.1", + "doctypes": "^1.1.0", + "js-stringify": "^1.0.2", + "pug-attrs": "^3.0.0", + "pug-error": "^2.0.0", + "pug-runtime": "^3.0.0", + "void-elements": "^3.1.0", + "with": "^7.0.0" + } + }, + "pug-error": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-2.0.0.tgz", + "integrity": "sha512-sjiUsi9M4RAGHktC1drQfCr5C5eriu24Lfbt4s+7SykztEOwVZtbFk1RRq0tzLxcMxMYTBR+zMQaG07J/btayQ==" + }, + "pug-filters": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-4.0.0.tgz", + "integrity": "sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A==", + "requires": { + "constantinople": "^4.0.1", + "jstransformer": "1.0.0", + "pug-error": "^2.0.0", + "pug-walk": "^2.0.0", + "resolve": "^1.15.1" + } + }, + "pug-lexer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-5.0.1.tgz", + "integrity": "sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w==", + "requires": { + "character-parser": "^2.2.0", + "is-expression": "^4.0.0", + "pug-error": "^2.0.0" + } + }, + "pug-linker": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-4.0.0.tgz", + "integrity": "sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw==", + "requires": { + "pug-error": "^2.0.0", + "pug-walk": "^2.0.0" + } + }, + "pug-load": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-3.0.0.tgz", + "integrity": "sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ==", + "requires": { + "object-assign": "^4.1.1", + "pug-walk": "^2.0.0" + } + }, + "pug-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-6.0.0.tgz", + "integrity": "sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw==", + "requires": { + "pug-error": "^2.0.0", + "token-stream": "1.0.0" + } + }, + "pug-runtime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-3.0.1.tgz", + "integrity": "sha512-L50zbvrQ35TkpHwv0G6aLSuueDRwc/97XdY8kL3tOT0FmhgG7UypU3VztfV/LATAvmUfYi4wNxSajhSAeNN+Kg==" + }, + "pug-strip-comments": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-2.0.0.tgz", + "integrity": "sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ==", + "requires": { + "pug-error": "^2.0.0" + } + }, + "pug-walk": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-2.0.0.tgz", + "integrity": "sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==" + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode2": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/punycode2/-/punycode2-1.0.0.tgz", + "integrity": "sha1-4rS5qaj/FX0LhEOOIDGB7niS39g=" + }, "qs": { "version": "6.7.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" }, + "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==" + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, "range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" }, + "rate-limit-mongo": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/rate-limit-mongo/-/rate-limit-mongo-2.3.1.tgz", + "integrity": "sha512-sl0T3G6aB6hgawZldDlvDZ8vi2tIxbcpk/N1WdI4jsS1/FqdDF92jKeCQiE8Oq7kDSsU+jFT142GE6HfKWKrUw==", + "requires": { + "mongodb": ">= 3.3.4 < 4.0.0", + "twostep": "0.4.2", + "underscore": "1.12.1" + } + }, "raw-body": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", @@ -3075,6 +13249,16 @@ "unpipe": "1.0.0" } }, + "re2": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/re2/-/re2-1.16.0.tgz", + "integrity": "sha512-eizTZL2ZO0ZseLqfD4t3Qd0M3b3Nr0MBWpX81EbPMIud/1d/CSfUIx2GQK8fWiAeHoSekO5EOeFib2udTZLwYw==", + "requires": { + "install-artifact-from-github": "^1.2.0", + "nan": "^2.14.2", + "node-gyp": "^8.0.0" + } + }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", @@ -3087,12 +13271,19 @@ "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + } } }, "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", "dev": true, "requires": { "picomatch": "^2.2.1" @@ -3103,26 +13294,63 @@ "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-1.0.0.tgz", "integrity": "sha512-TuAasHQNamyyJ2hb97IuBEif4qBHGjPHBS64sZwytpLEqtBQ1gPJTnOaQ6qmpET16cK14kkjbazl6+p0RRv0yw==" }, - "require-at": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/require-at/-/require-at-1.0.6.tgz", - "integrity": "sha512-7i1auJbMUrXEAZCOQ0VNJgmcT2VOKPRl2YGJwgpHpC9CE91Mv4/4UYIUm4chGJaI381ZDq1JUicFii64Hapd8g==" + "require_optional": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", + "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", + "requires": { + "resolve-from": "^2.0.0", + "semver": "^5.1.0" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true }, "resolve": { "version": "1.20.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", - "dev": true, "requires": { "is-core-module": "^2.2.0", "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==" + }, + "resolve-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", + "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" + }, + "responselike": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", + "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", + "requires": { + "lowercase-keys": "^2.0.0" + } + }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=" + }, "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "requires": { "glob": "^7.1.3" } @@ -3146,6 +13374,42 @@ "sparse-bitfield": "^3.0.3" } }, + "sazerac": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/sazerac/-/sazerac-2.0.0.tgz", + "integrity": "sha512-+oVUuqwbHf9aNJiT2WBoCYWmQy02AiBcbW1jxZUDBrrXleYRBat4qwbnwTXD3lyOVUk4F27cR8ArHZkCvjuSzw==", + "dev": true, + "requires": { + "deep-eql": "^4.0.0", + "lodash.at": "^4.6.0", + "lodash.concat": "^4.5.0", + "lodash.filter": "^4.6.0", + "lodash.isfunction": "^3.0.9", + "lodash.isobject": "^3.0.2", + "lodash.isstring": "^4.0.1", + "lodash.isundefined": "^3.0.1", + "lodash.keys": "^4.2.0", + "lodash.toarray": "^4.4.0", + "sprintf-js": "^1.1.2" + }, + "dependencies": { + "deep-eql": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.0.0.tgz", + "integrity": "sha512-GxJC5MOg2KyQlv6WiUF/VAnMj4MWnYiXo4oLgeptOELVoknyErb4Z8+5F/IM/K4g9/80YzzatxmWcyRwUseH0A==", + "dev": true, + "requires": { + "type-detect": "^4.0.0" + } + }, + "sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", + "dev": true + } + } + }, "scmp": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/scmp/-/scmp-2.1.0.tgz", @@ -3176,21 +13440,6 @@ "statuses": "~1.5.0" }, "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - }, - "dependencies": { - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, "ms": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", @@ -3198,6 +13447,15 @@ } } }, + "serialize-javascript": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", + "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, "serve-static": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", @@ -3209,41 +13467,129 @@ "send": "0.17.1" } }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, "setprototypeof": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, "sift": { - "version": "13.5.2", - "resolved": "https://registry.npmjs.org/sift/-/sift-13.5.2.tgz", - "integrity": "sha512-+gxdEOMA2J+AI+fVsCqeNn7Tgx3M9ZN9jdi95939l1IJ8cZsqS8sqpJyOkic2SJk+1+98Uwryt/gL6XDaV+UZA==" + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/sift/-/sift-7.0.1.tgz", + "integrity": "sha512-oqD7PMJ+uO6jV9EQCl0LrRw1OwsiPsiFQR5AR30heR+4Dl7jBBbDLnNvWiak20tzZlSE1H7RB30SX/1j/YYT7g==" + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" }, "sliced": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=" }, + "smart-buffer": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.1.0.tgz", + "integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw==" + }, + "smartquotes": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/smartquotes/-/smartquotes-2.3.1.tgz", + "integrity": "sha1-Aeu1ldbHqeJNkOjLlcF9Dhr0lAc=" + }, "socket.io": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.1.3.tgz", - "integrity": "sha512-tLkaY13RcO4nIRh1K2hT5iuotfTaIQw7cVIe0FUykN3SuQi0cm7ALxuyT5/CtDswOMWUzMGTibxYNx/gU7In+Q==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.0.0.tgz", + "integrity": "sha512-/c1riZMV/4yz7KEpaMhDQbwhJDIoO55whXaRKgyEBQrLU9zUHXo9rzeTMvTOqwL9mbKfHKdrXcMoCeQ/1YtMsg==", "requires": { "@types/cookie": "^0.4.0", - "@types/cors": "^2.8.10", + "@types/cors": "^2.8.8", "@types/node": ">=10.0.0", "accepts": "~1.3.4", "base64id": "~2.0.0", "debug": "~4.3.1", - "engine.io": "~5.1.1", - "socket.io-adapter": "~2.3.1", - "socket.io-parser": "~4.0.4" + "engine.io": "~5.0.0", + "socket.io-adapter": "~2.2.0", + "socket.io-parser": "~4.0.3" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } } }, "socket.io-adapter": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.3.1.tgz", - "integrity": "sha512-8cVkRxI8Nt2wadkY6u60Y4rpW3ejA1rxgcK2JuyIhmF+RMNpTy1QRtkHIDUOf3B4HlQwakMsWbKftMv/71VMmw==" + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.2.0.tgz", + "integrity": "sha512-rG49L+FwaVEwuAdeBRq49M97YI3ElVabJPzvHT9S6a2CWhDKnjSFasvwAwSYPRhQzfn4NtDIbCaGYgOCOU/rlg==" + }, + "socket.io-client": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.0.0.tgz", + "integrity": "sha512-27yQxmXJAEYF19Ygyl8FPJ0if0wegpSmkIIbrWJeI7n7ST1JyH8bbD5v3fjjGY5cfCanACJ3dARUAyiVFNrlTQ==", + "requires": { + "@types/component-emitter": "^1.2.10", + "backo2": "~1.0.2", + "component-emitter": "~1.3.0", + "debug": "~4.3.1", + "engine.io-client": "~5.0.0", + "parseuri": "0.0.6", + "socket.io-parser": "~4.0.4" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } }, "socket.io-parser": { "version": "4.0.4", @@ -3253,19 +13599,66 @@ "@types/component-emitter": "^1.2.10", "component-emitter": "~1.3.0", "debug": "~4.3.1" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "socks": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.1.tgz", + "integrity": "sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA==", + "requires": { + "ip": "^1.1.5", + "smart-buffer": "^4.1.0" + } + }, + "socks-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-5.0.0.tgz", + "integrity": "sha512-lEpa1zsWCChxiynk+lCycKuC502RxDWLKJZoIhnxrWNjLSDGYRFflHA1/228VkRcnv9TIb8w98derGbpKxJRgA==", + "requires": { + "agent-base": "6", + "debug": "4", + "socks": "^2.3.3" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } } }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" }, "source-map-support": { "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" @@ -3280,6 +13673,20 @@ "memory-pager": "^1.0.2" } }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "requires": { + "minipass": "^3.1.1" + } + }, "statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", @@ -3293,18 +13700,203 @@ "safe-buffer": "~5.1.0" } }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" }, "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, + "superagent": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-6.1.0.tgz", + "integrity": "sha512-OUDHEssirmplo3F+1HWKUrUjvnQuA+nZI6i/JJBdXb5eq9IyEQwPyPpqND+SSsxf6TygpBEkUjISVRN4/VOpeg==", + "dev": true, + "requires": { + "component-emitter": "^1.3.0", + "cookiejar": "^2.1.2", + "debug": "^4.1.1", + "fast-safe-stringify": "^2.0.7", + "form-data": "^3.0.0", + "formidable": "^1.2.2", + "methods": "^1.1.2", + "mime": "^2.4.6", + "qs": "^6.9.4", + "readable-stream": "^3.6.0", + "semver": "^7.3.2" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "lru-cache": { + "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": { + "yallist": "^4.0.0" + } + }, + "mime": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", + "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "qs": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz", + "integrity": "sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==", + "dev": true, + "requires": { + "side-channel": "^1.0.4" + } + }, + "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" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "supertest": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-6.1.3.tgz", + "integrity": "sha512-v2NVRyP73XDewKb65adz+yug1XMtmvij63qIWHZzSX8tp6wiq6xBLUy4SUAd2NII6wIipOmHT/FD9eicpJwdgQ==", + "dev": true, + "requires": { + "methods": "^1.1.2", + "superagent": "^6.1.0" + } + }, + "supports-color": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", + "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "requires": { + "has-flag": "^2.0.0" + } + }, + "tar": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz", + "integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==", + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "dependencies": { + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } + }, + "title": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/title/-/title-3.4.2.tgz", + "integrity": "sha512-cSNFZ/ChKlX2SfF+k9XvvXkjVa1JrzdGt6v/hoxVig5VaDGRmNHANfawcAdW2mfkd7y+uBHH6n9EHAxJmkO5Hw==", + "requires": { + "arg": "1.0.0", + "chalk": "2.3.0", + "clipboardy": "1.2.2", + "titleize": "1.0.0" + }, + "dependencies": { + "arg": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/arg/-/arg-1.0.0.tgz", + "integrity": "sha512-Wk7TEzl1KqvTGs/uyhmHO/3XLd3t1UeU4IstvPXVzGPM522cTjqjNZ99esCkcL52sjqjo8e8CTBcWhkxvGzoAw==" + } + } + }, + "titleize": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/titleize/-/titleize-1.0.0.tgz", + "integrity": "sha1-fTUHIgYYMLpmF2MeDP0+oIOY2Vo=" + }, + "tlds": { + "version": "1.210.0", + "resolved": "https://registry.npmjs.org/tlds/-/tlds-1.210.0.tgz", + "integrity": "sha512-5bzt4JE+NlnwiKpVW9yzWxuc44m+t2opmPG+eSKDp5V5qdyGvjMngKgBb5ZK8GiheQMbRTCKpRwFJeIEO6pV7Q==" + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" + }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -3319,17 +13911,20 @@ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" }, - "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==", - "dev": true + "token-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-1.0.0.tgz", + "integrity": "sha1-zCAOqyYT9BZtJ/+a/HylbUnfbrQ=" + }, + "truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/truncate/-/truncate-2.1.0.tgz", + "integrity": "sha512-em3E3SUDONOjTBcZ36DTm3RvDded3IRU9rX32oHwwXNt3rJD5MVaFlJTQvs8tJoHRoeYP36OuQ1eL/Q7bNEWIQ==" }, "ts-node": { "version": "9.1.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", - "dev": true, "requires": { "arg": "^4.1.0", "create-require": "^1.1.0", @@ -3339,35 +13934,16 @@ "yn": "3.1.1" } }, - "ts-node-dev": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-1.1.8.tgz", - "integrity": "sha512-Q/m3vEwzYwLZKmV6/0VlFxcZzVV/xcgOt+Tx/VjaaRHyiBcFlV0541yrT09QjzzCxlDZ34OzKjrFAynlmtflEg==", - "dev": true, - "requires": { - "chokidar": "^3.5.1", - "dynamic-dedupe": "^0.3.0", - "minimist": "^1.2.5", - "mkdirp": "^1.0.4", - "resolve": "^1.0.0", - "rimraf": "^2.6.1", - "source-map-support": "^0.5.12", - "tree-kill": "^1.2.2", - "ts-node": "^9.0.0", - "tsconfig": "^7.0.0" - } + "twostep": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/twostep/-/twostep-0.4.2.tgz", + "integrity": "sha1-hLxQh6hxV00ev3vjB54D4SLUFbY=" }, - "tsconfig": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz", - "integrity": "sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==", - "dev": true, - "requires": { - "@types/strip-bom": "^3.0.0", - "@types/strip-json-comments": "0.0.30", - "strip-bom": "^3.0.0", - "strip-json-comments": "^2.0.0" - } + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true }, "type-is": { "version": "1.6.18", @@ -3379,16 +13955,46 @@ } }, "typescript": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", - "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", - "dev": true + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.3.tgz", + "integrity": "sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==" + }, + "underscore": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz", + "integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==" + }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "requires": { + "imurmurhash": "^0.1.4" + } }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, + "url-regex-safe": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/url-regex-safe/-/url-regex-safe-1.0.2.tgz", + "integrity": "sha512-t1doIKbYDBRyqXZz7A98AXH/zimKmYapxFjBZwcz3BmqjB921rUPEUokAaShKyenaX3McOM8FbkWLvFvX3Jsyw==", + "requires": { + "ip-regex": "^4.1.0", + "re2": "^1.15.4", + "tlds": "^1.209.0" + } + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -3399,38 +14005,280 @@ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" - }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, + "video-extensions": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/video-extensions/-/video-extensions-1.1.0.tgz", + "integrity": "sha1-6qhrRfKahTwrhz6djiO1E3Epl9Y=" + }, + "void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha1-YU9/v42AHwu18GYfWy9XhXUOTwk=" + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "whoops": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/whoops/-/whoops-4.1.0.tgz", + "integrity": "sha512-42soctqvFs9FaU1r4ZadCy2F6A9dUc4SN3ud+tbDEdmyZDTeYBgKKqtIdo6NiQlnZnJegWRCyKLk2edYH9DsHA==", + "requires": { + "clean-stack": "~2.2.0", + "mimic-fn": "~3.0.0" + } + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "with": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/with/-/with-7.0.2.tgz", + "integrity": "sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==", + "requires": { + "@babel/parser": "^7.9.6", + "@babel/types": "^7.9.6", + "assert-never": "^1.2.1", + "babel-walk": "3.0.0-canary-5" + } + }, + "workerpool": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.2.tgz", + "integrity": "sha512-DSNyvOpFKrNusaaUwk+ej6cBj1bmhLcBfj80elGk+ZIo5JSkq+unB1dLKEOcNfJDZgjGICfhQ0Q5TbP0PvF4+Q==", + "dev": true + }, + "wrap-ansi": { + "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", + "strip-ansi": "^5.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" + } + } + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "ws": { - "version": "7.4.6", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", - "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", - "requires": {} + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.4.tgz", + "integrity": "sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw==" }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "y18n": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", + "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", "dev": true }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + }, + "yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + }, + "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 + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "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 + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "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-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "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" + } + } + } + }, + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "requires": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "dependencies": { + "camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "dev": true + }, + "decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true + } + } + }, + "yeast": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", + "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=" + }, "yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==" + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true } } diff --git a/backend/package.json b/backend/package.json index f63203f4..cf327f76 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,44 +1,78 @@ { "name": "api", "version": "1.0.0", - "description": "REST refers to the REST API (uses HTTP). WS refers to the WebSocket API (uses WS).", - "main": "index.js", + "description": "", + "main": "src/app.ts", "scripts": { "start": "ts-node src/app.ts", - "dev": "ts-node-dev --transpile-only src/app.ts" + "dev": "ts-node-dev src/app.ts", + "test": "ts-mocha test/test.ts" }, "keywords": [], - "author": "", + "author": "youtube.com/ADAMJR", "license": "ISC", "dependencies": { "body-parser": "^1.19.0", + "chai-things": "^0.2.0", + "colors": "^1.4.0", "cors": "^2.8.5", - "dotenv": "^10.0.0", + "dotenv": "^8.2.0", "express": "^4.17.1", - "faker": "^5.5.3", - "http-errors": "^1.7.2", + "express-async-errors": "^3.1.1", + "express-rate-limit": "^5.2.6", + "faker": "^5.4.0", + "got": "^11.7.0", + "helmet": "^4.4.1", "jsonwebtoken": "^8.5.1", - "mongoose": "^5.13.3", + "metascraper": "^5.14.14", + "metascraper-description": "^5.14.14", + "metascraper-image": "^5.14.14", + "metascraper-title": "^5.14.14", + "metascraper-url": "^5.14.14", + "mongoose": "^5.10.7", + "mongoose-unique-validator": "^2.0.3", + "node-fetch": "^2.6.1", + "nodemailer": "^6.5.0", + "nodemailer-pug-engine": "^2.0.0", "passport": "^0.4.1", "passport-local": "^1.0.0", - "passport-local-mongoose": "^6.1.0", - "socket.io": "^4.1.3", - "uuid": "^8.3.2" + "passport-local-mongoose": "^6.0.1", + "rate-limit-mongo": "^2.3.1", + "re2": "^1.16.0", + "socket.io": "^4.0.0", + "socket.io-client": "^4.0.0", + "ts-node": "^9.1.1", + "typescript": "^4.2.3" }, "devDependencies": { + "@types/chai": "^4.2.14", + "@types/chai-as-promised": "^7.1.3", + "@types/chai-spies": "^1.0.3", + "@types/chai-things": "^0.0.34", + "@types/colors": "^1.2.1", + "@types/cors": "^2.8.7", "@types/dotenv": "^8.2.0", - "@types/express": "^4.17.13", - "@types/faker": "^5.5.7", - "@types/http-errors": "^1.8.1", - "@types/jsonwebtoken": "^8.5.4", - "@types/mongoose": "^5.11.97", - "@types/passport": "^1.0.7", - "@types/passport-local": "^1.0.34", - "@types/passport-local-mongoose": "^4.0.15", - "@types/socket.io": "^3.0.2", - "@types/uuid": "^8.3.1", - "ts-node": "^9.1.1", - "ts-node-dev": "^1.1.8", - "typescript": "^4.3.5" + "@types/express": "^4.17.11", + "@types/express-rate-limit": "^5.1.1", + "@types/faker": "^5.1.6", + "@types/jsonwebtoken": "^8.5.0", + "@types/mocha": "^8.2.0", + "@types/mongoose": "^5.7.36", + "@types/node": "^14.11.2", + "@types/node-fetch": "^2.5.7", + "@types/nodemailer": "^6.4.1", + "@types/passport": "^1.0.4", + "@types/passport-local": "^1.0.33", + "@types/socket.io": "^2.1.13", + "@types/socket.io-client": "^1.4.36", + "@types/supertest": "^2.0.10", + "chai": "^4.2.0", + "chai-as-promised": "^7.1.1", + "chai-spies": "^1.0.0", + "i": "^0.3.6", + "mocha": "^8.2.1", + "npm": "^7.6.3", + "sazerac": "^2.0.0", + "supertest": "^6.1.3" } } diff --git a/backend/src/api/modules/api-error.ts b/backend/src/api/modules/api-error.ts new file mode 100644 index 00000000..83e5e0ca --- /dev/null +++ b/backend/src/api/modules/api-error.ts @@ -0,0 +1,16 @@ +const messages = { + 400: 'Bad Request', + 401: 'Unauthorized', + 402: 'Payment Required', + 403: 'Forbidden', + 404: 'Not Found', + 405: 'Method Not Allowed', + 429: 'You are being rate limited', + 500: 'We made an oopsie', +}; + +export class APIError extends TypeError { + constructor(public code: number, message = messages[code]) { + super(message); + } +} diff --git a/backend/src/api/modules/email/email-functions.ts b/backend/src/api/modules/email/email-functions.ts new file mode 100644 index 00000000..77e477d1 --- /dev/null +++ b/backend/src/api/modules/email/email-functions.ts @@ -0,0 +1,36 @@ +import { UserTypes } from '../../../data/types/entity-types'; +import Deps from '../../../utils/deps'; +import { Email } from './email'; +import { Verification } from './verification'; + +export class EmailFunctions { + constructor( + private email = Deps.get(Email), + private verification = Deps.get(Verification), + ) {} + + public async verifyCode(user: UserTypes.Self) { + const expiresIn = 5 * 60 * 1000; + await this.email.send('verify', { + expiresIn, + user, + code: this.verification.create(user.email, 'LOGIN', { expiresIn, codeLength: 6 }), + }, user.email as string); + } + public async verifyEmail(emailAddress: string, user: UserTypes.Self) { + const expiresIn = 24 * 60 * 60 * 1000; + await this.email.send('verify-email', { + expiresIn, + user, + code: this.verification.create(emailAddress, 'VERIFY_EMAIL', { expiresIn }), + }, emailAddress); + } + public async forgotPassword(emailAddress: string, user: UserTypes.Self) { + const expiresIn = 1 * 60 * 60 * 1000; + await this.email.send('forgot-password', { + expiresIn, + user, + code: this.verification.create(emailAddress, 'FORGOT_PASSWORD', { expiresIn }), + }, emailAddress); + } +} \ No newline at end of file diff --git a/backend/src/api/modules/email/email.ts b/backend/src/api/modules/email/email.ts new file mode 100644 index 00000000..588b5d9e --- /dev/null +++ b/backend/src/api/modules/email/email.ts @@ -0,0 +1,56 @@ +import { createTransport } from 'nodemailer'; +import Mail from 'nodemailer/lib/mailer'; +import { pugEngine } from 'nodemailer-pug-engine'; +import Log from '../../../utils/log'; +import { UserTypes } from '../../../data/types/entity-types'; + +export class Email { + private email: Mail; + + private readonly templateDir = __dirname + '/templates'; + + constructor() { + this.email = createTransport({ + service: 'gmail', + auth: { + user: process.env.EMAIL_ADDRESS, + pass: process.env.EMAIL_PASSWORD, + } + }); + + this.email.verify((error) => (error) + ? Log.error(error, 'email') + : Log.info('Logged in to email service', 'email')); + + this.email.use('compile', pugEngine({ + templateDir: this.templateDir, + pretty: true, + })); + } + + public async send(template: K, ctx: EmailTemplate[K], ...to: string[]) { + await this.email.sendMail({ + from: process.env.EMAIL_ADDRESS, + to: to.join(', '), + subject: subjects[template], + template, + ctx, + } as any); + } +} + +export interface EmailTemplate { + 'verify': { + expiresIn: number; + user: UserTypes.Self; + code: string; + }; + 'verify-email': this['verify']; + 'forgot-password': this['verify']; +} + +const subjects: { [k in keyof EmailTemplate]: string } = { + 'forgot-password': 'Accord - Forgot Password', + 'verify': 'Accord - Login Verification Code', + 'verify-email': 'Accord - Verify Email', +}; diff --git a/backend/src/api/modules/email/templates/email.css b/backend/src/api/modules/email/templates/email.css new file mode 100644 index 00000000..3f5f9ee3 --- /dev/null +++ b/backend/src/api/modules/email/templates/email.css @@ -0,0 +1,101 @@ +body { + color: #CACBCD; + font-family: 'Inter', 'Helvetica', sans-serif; + padding-top: 25px; + font-size: 16px; +} +header, section.body, footer { + margin: 25px; +} + +p, span { + color: #CACBCD; +} + +h1 { + padding-top: 20px; + font-size: 48px; +} +h1 + p.lead { + margin-top: 0; +} +h1, h2, h3, h4, h5 { + color: white; + letter-spacing: -.025em; + font-weight: 700; + margin-bottom: 0; +} + +h2.action { + font-size: 24px; +} + +.text-secondary { + color: #FAB795; +} +.text-tertiary { + color: #B877DB; +} + +strong.logo { + color: #B877DB +} + +p.lead { + color: #09F7A0; +} + +pre { + margin-top: 10px; + margin-bottom: 10px; +} +pre code { + font-size: 32px; + border-radius: 5px; + padding: 5px; + color: white; +} + +a { + text-decoration: none; + color: #B877DB; + cursor: pointer; +} +button { + font-size: 24px; + padding: 7.5px; + border-radius: 10px; + color: white !important; + border: none; +} + +.summary { + color: #25B2BC; + font-size: larger; + + padding: 15px; + border: 1px solid cyan; + border-radius: 5px; + background-color: #232530; + display: inline-block; + margin-bottom: 15px; +} +.cool { + color: #576067; + padding: 5px; + border-radius: 5px; +} + +hr { + border: 0; + height: 1px; + opacity: 0; +} + +.trivia { + margin-top: 25px; +} + +footer { + padding-bottom: 25px; +} \ No newline at end of file diff --git a/backend/src/api/modules/email/templates/forgot-password.pug b/backend/src/api/modules/email/templates/forgot-password.pug new file mode 100644 index 00000000..700cd897 --- /dev/null +++ b/backend/src/api/modules/email/templates/forgot-password.pug @@ -0,0 +1,32 @@ +head + include ./includes.pug + + style + include email.css +body(style='background-color: #2E303E') + - const url = `${process.env.WEBSITE_URL}/login?code=${code}&redirect=/channels/@me/settings/account`; + header + h1.logo + span accord + span.text-tertiary . + span.text-secondary app + h2 Hello #{user.username}! + p.lead Thank you for choosing us. + + section.body + div.summary A password reset was requested. + + h2.action Here is your new password... + p.lead You can change this at any time, in your user settings. + pre + code(style='background-color: #232530') #{code} + + a(href=url) + button(style='background-color: #E95678') Confirm Reset + + div.trivia + p This request expires in #{expiresIn / 60 / 1000} minutes. + + footer + hr + span accord.app \ No newline at end of file diff --git a/backend/src/api/modules/email/templates/includes.pug b/backend/src/api/modules/email/templates/includes.pug new file mode 100644 index 00000000..3f2acd56 --- /dev/null +++ b/backend/src/api/modules/email/templates/includes.pug @@ -0,0 +1,2 @@ +- var greetings = ['Hola', 'Hello', 'Hi', 'Hey', 'Hello there']; +- var i = Math.floor(Math.random() * greetings.length); \ No newline at end of file diff --git a/backend/src/api/modules/email/templates/verify-email.pug b/backend/src/api/modules/email/templates/verify-email.pug new file mode 100644 index 00000000..47775d03 --- /dev/null +++ b/backend/src/api/modules/email/templates/verify-email.pug @@ -0,0 +1,24 @@ +head + include ./includes.pug + + style + include email.css +body(style='background-color: #2E303E') + header + h1 #{greetings[i]}, #{user.username}! + p.lead Thank you for choosing accord.app. + + - var link = process.env.WEBSITE_URL + '/login?code=' + code; + section.body + div + span.summary A request was sent to verify this email. + + h2 #[a(href=link) Complete Verification] + + p This request expires in #{expiresIn / 60 / 60 / 1000} hours. + p Full Link - #[a(href=link) #{link}]. + p If this was not you, please contact support. + + footer + hr + span accord.app \ No newline at end of file diff --git a/backend/src/api/modules/email/templates/verify.pug b/backend/src/api/modules/email/templates/verify.pug new file mode 100644 index 00000000..b6a20377 --- /dev/null +++ b/backend/src/api/modules/email/templates/verify.pug @@ -0,0 +1,23 @@ +head + include ./includes.pug + + style + include email.css +body(style='background-color: #2E303E') + - const url = `${process.env.WEBSITE_URL}/login?code=${code}`; + header + h1 Hello #{user.username}! + p.lead Thank you for choosing accord.app. + + section.body + div + span.summary A verification code has been requested to login to your account. + + h2 Here it is... #[code.cool #{code}] + + p This code expires in #{expiresIn / 60 / 1000} minutes. + p You can use it to login with 2FA. #[br] Or #[a(href=url) click here] to login automatically. + + footer + hr + span accord.app \ No newline at end of file diff --git a/backend/src/api/modules/email/verification.ts b/backend/src/api/modules/email/verification.ts new file mode 100644 index 00000000..e5d0f30a --- /dev/null +++ b/backend/src/api/modules/email/verification.ts @@ -0,0 +1,45 @@ +import { generateInviteCode } from '../../../data/models/invite'; + +export class Verification { + private codes = new Map(); + + public create(email: string, type: VerifyCode['type'], options?: EmailOptions) { + options = { + ...options, + codeLength: 16, + expiresIn: 5 * 60 * 1000, + }; + + this.codes.delete(email); + + const value = generateInviteCode(options.codeLength); + this.codes.set(email, { type, value }); + + setTimeout(() => this.codes.delete(email), options.expiresIn); + + return value; + } + + public get(code: string) { + return this.codes.get(code); + } + public delete(email: string) { + this.codes.delete(email); + } + + public getEmailFromCode(code: string) { + return Array + .from(this.codes.entries()) + .find(([k,v]) => v.value === code)?.[0]; + } +} + +interface VerifyCode { + type: 'LOGIN' | 'VERIFY_EMAIL' | 'FORGOT_PASSWORD'; + value: string; +} + +export interface EmailOptions { + codeLength?: number; + expiresIn?: number; +} \ No newline at end of file diff --git a/backend/src/api/modules/middleware.ts b/backend/src/api/modules/middleware.ts new file mode 100644 index 00000000..27ec3cec --- /dev/null +++ b/backend/src/api/modules/middleware.ts @@ -0,0 +1,82 @@ +import { PermissionTypes } from '../../data/types/entity-types'; +import Guilds from '../../data/guilds'; +import { Guild, GuildDocument } from '../../data/models/guild'; +import Roles from '../../data/roles'; +import Users from '../../data/users'; +import Deps from '../../utils/deps'; +import { User } from '../../data/models/user'; +import { APIError } from './api-error'; +import { NextFunction, Request, Response } from 'express'; + +const guilds = Deps.get(Guilds); +const roles = Deps.get(Roles); +const users = Deps.get(Users); + +export async function fullyUpdateUser(req: Request, res: Response, next: NextFunction) { + try { + const key = req.get('Authorization') as string; + const id = users.idFromAuth(key); + + res.locals.user = await users.getSelf(id); + } finally { + return next(); + } +} +export async function updateUser(req: Request, res: Response, next: NextFunction) { + try { + const key = req.get('Authorization') as string; + const id = users.idFromAuth(key); + + res.locals.user = await users.getSelf(id, false); + } finally { + return next(); + } +} + +export async function updateUsername(req: Request, res: Response, next: NextFunction) { + if (!req.body.username) { + const user = await User.findOne({ email: req.body.email }); + req.body.username = user?.username; + } + return next(); +} + +export function validateUser(req: Request, res: Response, next: NextFunction) { + if (res.locals.user) + return next(); + throw new APIError(401, 'User not logged in'); +} + +export async function updateGuild(req: Request, res: Response, next: NextFunction) { + res.locals.guild = await guilds.get(req.params.id); + return next(); +} + +export async function validateGuildExists(req: Request, res: Response, next: NextFunction) { + const exists = await Guild.exists({ _id: req.params.id }); + return (exists) + ? next() + : res.status(404).json({ message: 'Guild does not exist' }); +} + +export async function validateGuildOwner(req: Request, res: Response, next: NextFunction) { + const userOwnsGuild = res.locals.guild.ownerId === res.locals.user.id; + if (userOwnsGuild) + return next(); + throw new APIError(401, 'You do not own this guild!'); +} + +export function validateHasPermission(permission: PermissionTypes.Permission) { + return async (req: Request, res: Response, next: NextFunction) => { + const guild: GuildDocument = res.locals.guild; + const member = guild.members.find(m => m.userId === res.locals.user.id); + if (!member) + throw new APIError(401, 'You are not a guild member'); + + const isOwner = guild.ownerId === res.locals.user.id; + const hasPerm = await roles.hasPermission(guild, member, permission); + if (hasPerm || isOwner) return next(); + + throw new APIError(401, 'Missing Permissions'); + }; +} diff --git a/backend/src/api/modules/rate-limiter.ts b/backend/src/api/modules/rate-limiter.ts new file mode 100644 index 00000000..6f48203c --- /dev/null +++ b/backend/src/api/modules/rate-limiter.ts @@ -0,0 +1,9 @@ +import rateLimit from 'express-rate-limit'; +import RateLimitStore from 'rate-limit-mongo'; + +export default rateLimit({ + max: 10 * 1000, + message: JSON.stringify({ message: 'You are being rate limited.' }), + store: new RateLimitStore({ uri: process.env.MONGO_URI }), + windowMs: 10 * 60 * 1000, +}); diff --git a/backend/src/api/modules/ws-guard.ts b/backend/src/api/modules/ws-guard.ts new file mode 100644 index 00000000..8e0b55c0 --- /dev/null +++ b/backend/src/api/modules/ws-guard.ts @@ -0,0 +1,97 @@ +import { Guild } from '../../data/models/guild'; +import { GuildMember } from '../../data/models/guild-member'; +import jwt from 'jsonwebtoken'; +import Deps from '../../utils/deps'; +import { WebSocket } from '../websocket/websocket'; +import { Socket } from 'socket.io'; +import Channels from '../../data/channels'; +import Roles from '../../data/roles'; +import { Lean, PermissionTypes } from '../../data/types/entity-types'; +import Users from '../../data/users'; +import Guilds from '../../data/guilds'; +import GuildMembers from '../../data/guild-members'; +import { Prohibited } from '../../data/types/ws-types'; + +export class WSGuard { + constructor( + private channels = Deps.get(Channels), + private guilds = Deps.get(Guilds), + private guildMembers = Deps.get(GuildMembers), + private roles = Deps.get(Roles), + private users = Deps.get(Users), + private ws = Deps.get(WebSocket), + ) {} + + public userId(client: Socket) { + return this.ws.sessions.get(client.id) ?? ''; + } + + public validateIsUser(client: Socket, userId: string) { + if (this.userId(client) !== userId) + throw new TypeError('Unauthorized'); + } + + public async validateIsOwner(client: Socket, guildId: string) { + const isOwner = await Guild.exists({ + _id: guildId, + ownerId: this.userId(client) + }); + if (!isOwner) + throw new TypeError('Only the guild owner can do this'); + } + + public async canAccessChannel(client: Socket, channelId?: string, withUse = false) { + const channel = await this.channels.get(channelId); + await this.canAccess(channel, client, withUse); + } + private async canAccess(channel: Lean.Channel, client: Socket, withUse = false) { + const userId = this.userId(client); + if (channel.type === 'TEXT') { + const perms = (!withUse) + ? PermissionTypes.Text.READ_MESSAGES + : PermissionTypes.Text.READ_MESSAGES | PermissionTypes.Text.SEND_MESSAGES; + await this.validateCan(client, channel.guildId, perms); + return; + } else if (channel.type === 'VOICE') { + const perms = (!withUse) + ? PermissionTypes.Voice.CONNECT + : PermissionTypes.Voice.CONNECT | PermissionTypes.Voice.SPEAK; + await this.validateCan(client, channel.guildId, perms); + return; + } + const inGroup = channel.memberIds?.includes(userId); + if (!inGroup) + throw new TypeError('Not DM Member'); + } + + public async validateCan(client: Socket, guildId: string | undefined, permission: PermissionTypes.Permission) { + const userId = this.userId(client); + + const member = await this.guildMembers.getInGuild(guildId, userId); + const guild = await this.guilds.get(guildId); + + const can = await this.roles.hasPermission(guild, member, permission) + || guild.ownerId === userId; + this.validate(can, permission); + } + private validate(can: boolean, permission: PermissionTypes.PermissionString) { + if (!can) + throw new TypeError(`Missing Permissions - ${PermissionTypes.All[permission]}`); + } + + public async decodeKey(key: string) { + const id = this.users.verifyToken(key); + return { id }; + } + + public validateKeys(type: K, partial: any) { + const contains = this.includesProhibited(partial, type); + if (contains) + throw new TypeError('Contains readonly values'); + } + + private includesProhibited(partial: any, type: K) { + const keys = Object.keys(partial); + return Prohibited[type].some(k => keys.includes(k)); + } +} diff --git a/backend/src/api/routes/api-routes.ts b/backend/src/api/routes/api-routes.ts new file mode 100644 index 00000000..64db1975 --- /dev/null +++ b/backend/src/api/routes/api-routes.ts @@ -0,0 +1,9 @@ +import { Router } from 'express'; + +export const router = Router(); + +router.get('/', (req, res) => res.json({ hello: 'earth' })); + +router.post('/error', (req, res) => { + res.json({ message: 'Received' }); +}); diff --git a/backend/src/api/routes/auth-routes.ts b/backend/src/api/routes/auth-routes.ts new file mode 100644 index 00000000..80536fde --- /dev/null +++ b/backend/src/api/routes/auth-routes.ts @@ -0,0 +1,107 @@ +import { Router } from 'express'; +import { SelfUserDocument, User } from '../../data/models/user'; +import passport from 'passport'; +import Deps from '../../utils/deps'; +import Users from '../../data/users'; +import { Verification } from '../modules/email/verification'; +import { fullyUpdateUser, updateUsername, validateUser } from '../modules/middleware'; +import { EmailFunctions } from '../modules/email/email-functions'; +import { APIError } from '../modules/api-error'; +import { generateInviteCode } from '../../data/models/invite'; +import { WebSocket } from '../websocket/websocket'; +import { Args } from '../websocket/ws-events/ws-event'; + +export const router = Router(); + +const sendEmail = Deps.get(EmailFunctions); +const users = Deps.get(Users); +const verification = Deps.get(Verification); +const ws = Deps.get(WebSocket); + +router.post('/login', + updateUsername, + passport.authenticate('local', { failWithError: true }), + async (req, res) => { + const user = await users.getByUsername(req.body.username); + if (!user) + throw new APIError(400, 'Invalid credentials'); + + if (user.verified) { + await sendEmail.verifyCode(user as any); + return res.status(200).json({ verify: true }); + } else if (req.body.email) + throw new APIError(400, 'Email is unverified'); + + return res.status(200).json(users.createToken(user.id)); +}); + +router.get('/verify-code', async (req, res) => { + const email = verification.getEmailFromCode(req.query.code as any); + const user = await User.findOne({ email }) as any; + if (!email || !user) + throw new APIError(400, 'Invalid code'); + + verification.delete(email); + + const code = verification.get(req.query.code as string); + if (code?.type === 'FORGOT_PASSWORD') { + await user.setPassword(req.body.newPassword); + await user.save(); + } + res.status(200).json(users.createToken(user.id)); +}); + +router.get('/send-verify-email', async (req, res) => { + const email = req.query.email?.toString(); + if (!email) + throw new APIError(400, 'Email not provided'); + + if (req.query.type === 'FORGOT_PASSWORD') { + const user = await users.getByEmail(email); + await sendEmail.forgotPassword(email, user); + + return res.status(200).json({ verify: true }); + } + const key = req.get('Authorization'); + const user = await users.getSelf(users.idFromAuth(key), false); + + await sendEmail.verifyEmail(email, user); + + user.email = email; + await user.save(); + + ws.to(user.id) + .emit('USER_UPDATE', { partialUser: { email: user.email } }); + + return res.status(200).json({ verify: true }); +}); + +router.get('/verify-email', async (req, res) => { + const email = verification.getEmailFromCode(req.query.code as string); + if (!email) + throw new APIError(400, 'Invalid code'); + + await User.updateOne( + { email }, + { verified: true }, + { runValidators: true, context: 'query' }, + ); + + res.redirect(`${process.env.WEBSITE_URL}/channels/@me?success=Successfully verified your email.`); +}); + +router.post('/change-password', async (req, res) => { + const user = await User.findOne({ + email: req.body.email, + verified: true, + }) as any; + if (!user) + throw new APIError(400, 'User Not Found'); + + await user.changePassword(req.body.oldPassword, req.body.newPassword); + await user.save(); + + return res.status(200).json( + users.createToken(user.id) + ); +}); diff --git a/backend/src/api/routes/channel-routes.ts b/backend/src/api/routes/channel-routes.ts new file mode 100644 index 00000000..904829fd --- /dev/null +++ b/backend/src/api/routes/channel-routes.ts @@ -0,0 +1,66 @@ +import { Router } from 'express'; +import Channels from '../../data/channels'; +import Messages from '../../data/messages'; +import { SelfUserDocument } from '../../data/models/user'; +import Pings from '../../data/pings'; +import { Lean } from '../../data/types/entity-types'; +import Deps from '../../utils/deps'; +import { updateUser, validateUser } from '../modules/middleware'; +import { WebSocket } from '../websocket/websocket'; +import { Args } from '../websocket/ws-events/ws-event'; + +export const router = Router(); + +const channels = Deps.get(Channels); +const messages = Deps.get(Messages); +const pings = Deps.get(Pings); +const ws = Deps.get(WebSocket); + +router.get('/', updateUser, validateUser, async (req, res) => { + const dms: Lean.Channel[] = await channels.getDMChannels(res.locals.user.id); + const guildsChannels = await channels.getGuildsChannels(res.locals.user); + const all = dms.concat(guildsChannels); + + res.json(all); +}); + +router.get('/:channelId/messages', updateUser, validateUser, async (req, res) => { + const channelId = req.params.channelId; + + const user: SelfUserDocument = res.locals.user; + const channelMsgs = (await messages + .getChannelMessages(channelId) ?? await messages + .getDMChannelMessages(channelId, res.locals.user.id)); + + const batchSize = 25; + const back = Math.max(channelMsgs.length - parseInt(req.query.back as string) + || batchSize, 0); + + const slicedMsgs = channelMsgs + .slice(back) + .map(m => { + const isIgnored = user.ignored.userIds.includes(m.authorId); + if (isIgnored) + m.content = 'This user is blocked, and this message content has been hidden.'; + return m; + }); + + const index = slicedMsgs.length - 1; + const lastMessage = slicedMsgs[index]; + if (lastMessage) { + await pings.markAsRead(user, lastMessage); + ws.io + .to(user.id) + .emit('USER_UPDATE', { + partialUser: { lastReadMessages: user.lastReadMessages }, + } as Args.UserUpdate); + } + + res.json(slicedMsgs); +}); + +router.get('/:id', updateUser, validateUser, async (req, res) => { + const channel = await channels.get(req.params.id); + res.json(channel); +}); + diff --git a/backend/src/api/routes/dev-routes.ts b/backend/src/api/routes/dev-routes.ts new file mode 100644 index 00000000..cecec5fe --- /dev/null +++ b/backend/src/api/routes/dev-routes.ts @@ -0,0 +1,81 @@ +import { Router } from 'express'; +import { Application } from '../../data/models/application'; +import { generateInviteCode } from '../../data/models/invite'; +import Users from '../../data/users'; +import Deps from '../../utils/deps'; +import { fullyUpdateUser, validateUser } from '../modules/middleware'; +import { WSGuard } from '../modules/ws-guard'; + +export const router = Router(); + +const users = Deps.get(Users); +const guard = Deps.get(WSGuard); + +router.get('/apps', async (req, res) => { + const start = parseInt(req.query.start as string); + const end = parseInt(req.query.end as string); + + const apps = (await Application + .find() + .select('-token') + .populate('user') + .populate('owner') + .exec()) + .slice(start || 0, end || 25); + + res.json(apps); +}); + +router.use(fullyUpdateUser, validateUser); + +router.get('/apps/user', async (req, res) => { + const apps = await Application.find({ owner: res.locals.user }); + res.json(apps); +}); + +router.get('/apps/new', async (req, res) => { + const user = res.locals.user; + const count = await Application.countDocuments({ owner: user }); + const maxApps = 16; + if (count >= maxApps) + return res.status(400).json({ message: 'Too many apps' }); + + const app = new Application({ owner: user.id as any }); + app.user = (await users.create(app.name, generateInviteCode(), true)).id as any; + app.token = users.createToken(user.id, false); + await app.save(); + + res.json(app); +}); + +router.get('/apps/:id', async (req, res) => { + const app = await Application + .findById(req.params.id) + .populate('user') + .exec(); + + return (app?.owner !== res.locals.user?.id) + ? res.status(403).json({ message: 'Forbidden' }) + : res.json(app); +}); + +router.patch('/apps/:id', async (req, res) => { + const app = await Application.findById(req.params.id); + if (!app || app.owner !== res.locals.user.id) + return res.status(403).json({ message: 'Forbidden' }); + + guard.validateKeys('app', req.body); + await app.update(req.body, { runValidators: true }); + res.json(app); +}); + +router.get('/apps/:id/regen-token', async (req, res) => { + const app = await Application.findById(req.params.id); + if (!app || app.owner !== res.locals.user.id) + return res.status(403).json({ message: 'Forbidden' }); + + app.token = users.createToken(app.user as any, false); + await app.save(); + + res.json(app.token); +}); \ No newline at end of file diff --git a/backend/src/api/routes/guilds-routes.ts b/backend/src/api/routes/guilds-routes.ts new file mode 100644 index 00000000..9d495331 --- /dev/null +++ b/backend/src/api/routes/guilds-routes.ts @@ -0,0 +1,47 @@ +import { Router } from 'express'; +import Deps from '../../utils/deps'; +import { updateGuild, fullyUpdateUser, validateHasPermission, validateUser } from '../modules/middleware'; +import Users from '../../data/users'; +import Guilds from '../../data/guilds'; +import { WebSocket } from '../websocket/websocket'; +import { Args } from '../websocket/ws-events/ws-event'; +import { PermissionTypes } from '../../data/types/entity-types'; +import GuildMembers from '../../data/guild-members'; + +export const router = Router(); + +const members = Deps.get(GuildMembers); +const guilds = Deps.get(Guilds); +const users = Deps.get(Users); +const ws = Deps.get(WebSocket); + +router.get('/', fullyUpdateUser, validateUser, async (req, res) => { + const user = await users.getSelf(res.locals.user.id, true); + res.json(user.guilds); +}); + +router.get('/:id/authorize/:botId', + fullyUpdateUser, validateUser, updateGuild, + validateHasPermission(PermissionTypes.General.MANAGE_GUILD), + async (req, res) => { + const guild = res.locals.guild; + const bot = await users.get(req.params.botId); + const member = await members.create(guild, bot); + + ws.io + .to(guild.id) + .emit('GUILD_MEMBER_ADD', { guildId: guild.id, member } as Args.GuildMemberAdd); + ws.io + .to(bot.id) + .emit('GUILD_JOIN', { guild } as Args.GuildJoin); + + res.json({ message: 'Success' }); +}); + +router.get('/:id/invites', + fullyUpdateUser, validateUser, updateGuild, + validateHasPermission(PermissionTypes.General.MANAGE_GUILD), + async (req, res) => { + const invites = await guilds.invites(req.params.id); + res.json(invites); +}); \ No newline at end of file diff --git a/backend/src/api/routes/invites-routes.ts b/backend/src/api/routes/invites-routes.ts new file mode 100644 index 00000000..9f3012e7 --- /dev/null +++ b/backend/src/api/routes/invites-routes.ts @@ -0,0 +1,11 @@ +import { Router } from 'express'; +import Invites from '../../data/invites'; +import Deps from '../../utils/deps'; + +export const router = Router(); +const invites = Deps.get(Invites); + +router.get('/:id', async (req, res) => { + const invite = await invites.get(req.params.id); + res.json(invite); +}); diff --git a/backend/src/api/routes/users-routes.ts b/backend/src/api/routes/users-routes.ts new file mode 100644 index 00000000..6c60639d --- /dev/null +++ b/backend/src/api/routes/users-routes.ts @@ -0,0 +1,82 @@ +import { Router } from 'express'; +import { User } from '../../data/models/user'; +import Users from '../../data/users'; +import Deps from '../../utils/deps'; +import { fullyUpdateUser, updateUser, validateUser } from '../modules/middleware'; +import Channels from '../../data/channels'; +import { SystemBot } from '../../system/bot'; +import { generateInviteCode } from '../../data/models/invite'; + +export const router = Router(); + +const bot = Deps.get(SystemBot); +const channels = Deps.get(Channels); +const users = Deps.get(Users); + +router.get('/', updateUser, validateUser, async (req, res) => { + const knownUsers = await users.getKnown(res.locals.user.id); + res.json(knownUsers); +}); + +router.delete('/:id', updateUser, validateUser, async (req, res) => { + const user = res.locals.user; + user.username = `deleted-user-${generateInviteCode(6)}`; + delete user.salt; + delete user.hash; + await user.save(); + + res.status(201).json({ message: 'Modified' }); +}); + +router.get('/check-username', async (req, res) => { + const username = req.query.value?.toString().toLowerCase(); + const exists = await User.exists({ + username: { + $regex: new RegExp(`^${username}$`, 'i') + }, + }); + res.json(exists); +}); + +router.get('/self', fullyUpdateUser, async (req, res) => res.json(res.locals.user)); + +router.get('/check-email', async (req, res) => { + const email = req.query.value?.toString().toLowerCase(); + const exists = await User.exists({ + email: { + $regex: new RegExp(`^${email}$`, 'i') + }, + verified: true, + }); + res.json(exists); +}); + +router.post('/', async (req, res) => { + const userCount = await User.countDocuments(); + if (userCount >= 75) + throw new TypeError('Max alpha tester limit reached'); + + const user = await users.create(req.body.username, req.body.password); + const dm = await channels.createDM(bot.self.id, user.id); + await bot.message(dm, + 'Hello there new user :smile:!\n' + + '**Alpha Testing Info** - https://docs.accord.app/legal/alpha' + ); + + res.status(201).json(users.createToken(user.id)); +}); + +router.get('/dm-channels', fullyUpdateUser, async (req, res) => { + const dmChannels = await channels.getDMChannels(res.locals.user.id); + res.json(dmChannels); +}); + +router.get('/bots', async (req, res) => { + const bots = await User.find({ bot: true }); + res.json(bots); +}); + +router.get('/:id', async (req, res) => { + const user = await users.get(req.params.id); + res.json(user); +}); diff --git a/backend/src/api/server.ts b/backend/src/api/server.ts new file mode 100644 index 00000000..358c142a --- /dev/null +++ b/backend/src/api/server.ts @@ -0,0 +1,89 @@ +import express, { NextFunction, Request, Response } from 'express'; +import 'express-async-errors'; + +import bodyParser from 'body-parser'; +import Log from '../utils/log'; +import LocalStrategy from 'passport-local'; +import passport from 'passport'; +import { router as apiRoutes } from './routes/api-routes'; +import { router as authRoutes } from './routes/auth-routes'; +import { router as channelsRoutes } from './routes/channel-routes'; +import { router as guildsRoutes } from './routes/guilds-routes'; +import { router as usersRoutes } from './routes/users-routes'; +import { router as invitesRoutes } from './routes/invites-routes'; +import { User } from '../data/models/user'; +import cors from 'cors'; +import { resolve } from 'path'; +import Deps from '../utils/deps'; +import { WebSocket } from './websocket/websocket'; +import { APIError } from './modules/api-error'; +import rateLimiter from './modules/rate-limiter'; + +export class API { + public app = express(); + private prefix = `/api/v1`; + + constructor(private ws = Deps.get(WebSocket)) { + this.setupMiddleware(); + this.setupRoutes(); + this.setupErrorHandling(); + this.serveWebsite(); + this.listen(); + } + + private setupMiddleware() { + passport.use(new LocalStrategy.Strategy((User as any).authenticate())); + passport.serializeUser((User as any).serializeUser()); + passport.deserializeUser((User as any).deserializeUser()); + + this.app.use(bodyParser.json()); + this.app.use(passport.initialize()); + this.app.use(cors()); + this.app.use(rateLimiter); + } + + private setupRoutes() { + this.app.use(`${this.prefix}`, express.static(resolve('./assets'))); + this.app.use(`${this.prefix}`, apiRoutes, authRoutes); + + this.app.use(`${this.prefix}/invites`, invitesRoutes); + // this.app.use(`${this.prefix}/devs`, devRoutes); + this.app.use(`${this.prefix}/channels`, channelsRoutes); + this.app.use(`${this.prefix}/guilds`, guildsRoutes); + this.app.use(`${this.prefix}/users`, usersRoutes); + } + + private setupErrorHandling() { + this.app.all(`${this.prefix}/*`, (req, res, next) => next(new APIError(404))); + + this.app.use(`/api`, () => { + throw new TypeError('Invalid API version number'); + }); + + this.app.use((error: APIError, req: Request, res: Response, next: NextFunction) => { + if (res.headersSent) + return next(error); + + const code = error.code || 400; + return res + .status(code) + .json({ message: error.message }); + }); + } + + private serveWebsite() { + const distPath = resolve('./dist/browser'); + this.app.use(express.static(distPath)); + this.app.all('*', (req, res) => res + .status(200) + .sendFile(`${distPath}/index.html`)); + } + + private listen() { + const port = process.env.PORT || 8080; + const server = this.app.listen(port, async () => { + Log.info(`API is running on port ${port}`); + await this.ws.init(server); + }); + } +} diff --git a/backend/src/api/websocket/modules/session-manager.ts b/backend/src/api/websocket/modules/session-manager.ts new file mode 100644 index 00000000..9ae7f76b --- /dev/null +++ b/backend/src/api/websocket/modules/session-manager.ts @@ -0,0 +1,18 @@ +import { Socket } from 'socket.io'; +import { patterns } from '../../../data/types/entity-types'; + +export class SessionManager extends Map { + public get(key: string): string { + const userId = super.get(key); + if (!userId) + throw new TypeError('User Not Logged In'); + + if (!patterns.snowflake.test(userId)) + throw new TypeError('Spoofed ID Not Allowed'); + return userId; + } + + public userId(client: Socket) { + return this.get(client.id); + } +} \ No newline at end of file diff --git a/backend/src/api/websocket/modules/ws-cooldowns.ts b/backend/src/api/websocket/modules/ws-cooldowns.ts new file mode 100644 index 00000000..ce7ec37d --- /dev/null +++ b/backend/src/api/websocket/modules/ws-cooldowns.ts @@ -0,0 +1,49 @@ +import { WSEventParams } from '../ws-events/ws-event'; + +// if bot user -> users should not be too fast +// for guild events: +// -> separate cooldowns for each guild / room ID + +export class WSCooldowns { + public readonly active = new Map(); + + // TODO: handle(userId, eventName, guildId) + public handle(userId: string, eventName: keyof WSEventParams) { + this.prune(userId); + this.add(userId, eventName); + + const clientEvents = this.get(userId).length; + const maxEvents = 60; + if (clientEvents > maxEvents) + throw new TypeError('You are doing too many things at once!'); + } + + private get(clientId: string) { + return this.active.get(clientId) + ?? this.active + .set(clientId, []) + .get(clientId) as EventLog[]; + } + + private add(clientId: string, eventName: keyof WSEventParams) { + this + .get(clientId) + .push({ eventName, timestamp: new Date().getTime() }); + } + + private prune(clientId: string) { + const logs = this.get(clientId); + const lastLog = logs[logs.length - 1]; + + const timeToDelete = 60 * 1000; + const expirationMs = lastLog?.timestamp + timeToDelete; + if (new Date().getTime() < expirationMs) return; + + this.active.delete(clientId); + } +} + +interface EventLog { + eventName: keyof WSEventParams; + timestamp: number; +} diff --git a/backend/src/api/websocket/modules/ws-rooms.ts b/backend/src/api/websocket/modules/ws-rooms.ts new file mode 100644 index 00000000..e0ba5ace --- /dev/null +++ b/backend/src/api/websocket/modules/ws-rooms.ts @@ -0,0 +1,54 @@ +import { Socket } from 'socket.io'; +import { SelfUserDocument } from '../../../data/models/user'; +import { Lean } from '../../../data/types/entity-types'; +import Users from '../../../data/users'; +import Deps from '../../../utils/deps'; +import { WSGuard } from '../../modules/ws-guard'; + +export class WSRooms { + constructor( + private users = Deps.get(Users), + private guard = Deps.get(WSGuard), + ) {} + + public async join(client: Socket, user: SelfUserDocument) { + const alreadyJoinedRooms = client.rooms.size > 1; + if (alreadyJoinedRooms) return; + + await client.join(await this.users.getRoomIds(user)); + + await this.joinGuildRooms(user, client); + await this.joinDMRooms(user, client); + } + + private async joinDMRooms(user: SelfUserDocument, client: Socket) { + const dms = await this.users.getDMChannels(user.id); + if (!dms) return; + + const ids = dms.map(c => c.id); + await client.join(ids); + } + + public async joinGuildRooms(user: SelfUserDocument, client: Socket) { + if (!user.guilds) return; + + const guildIds = user.guilds.map(g => g.id); + await client.join(guildIds); + + const channelIds = await this.getChannelIds(client, user.guilds as any); + await client.join(channelIds); + } + + private async getChannelIds(client: Socket, guilds: Lean.Guild[]) { + const ids: string[] = []; + const channelIds = guilds + .flatMap(g => g.channels.map(c => c.id)); + + for (const id of channelIds) + try { + await this.guard.canAccessChannel(client, id); + ids.push(id); + } catch {} + return ids; + } +} diff --git a/backend/src/api/websocket/websocket.ts b/backend/src/api/websocket/websocket.ts new file mode 100644 index 00000000..e624bcd2 --- /dev/null +++ b/backend/src/api/websocket/websocket.ts @@ -0,0 +1,72 @@ +import { Server } from 'http'; +import { Server as SocketServer } from 'socket.io'; +import Log from '../../utils/log'; +import { WSEvent, WSEventParams } from './ws-events/ws-event'; +import { resolve } from 'path'; +import { readdirSync } from 'fs'; +import { WSCooldowns } from './modules/ws-cooldowns'; +import Deps from '../../utils/deps'; +import { SessionManager } from './modules/session-manager'; +import { WSEventAsyncArgs } from '../../data/types/ws-types'; + +export class WebSocket { + public events = new Map>(); + public io: SocketServer; + public sessions = new SessionManager(); + + public get connectedUserIds() { + return Array.from(this.sessions.values()); + } + + constructor(private cooldowns = Deps.get(WSCooldowns)) {} + + public async init(server: Server) { + this.io = new SocketServer(server, { + cors: { + origin: process.env.WEBSITE_URL, + methods: ['GET', 'POST'], + allowedHeaders: ['Authorization'], + credentials: true, + }, + path: '/ws', + serveClient: false, + }); + + const dir = resolve(`${__dirname}/ws-events`); + const files = readdirSync(dir); + + for (const file of files) { + const Event = require(`./ws-events/${file}`).default; + try { + const event = new Event(); + this.events.set(event.on, event); + } catch {} + } + + Log.info(`Loaded ${this.events.size} handlers`, 'ws'); + + this.io.on('connection', (client) => { + for (const event of this.events.values()) + client.on(event.on, async (data: any) => { + try { + await event.invoke.bind(event)(this, client, data); + } catch (error) { + client.send(`Server error on executing: ${event.on}\n${error.message}`); + } finally { + try { + const userId = this.sessions.userId(client); + this.cooldowns.handle(userId, event.on); + } catch {} + } + }); + }); + + Log.info('Started WebSocket', 'ws'); + } + + public to(...rooms: string[]) { + return this.io.to(rooms) as { + emit: (name: K, args: WSEventAsyncArgs[K]) => any, + }; + } +} diff --git a/backend/src/api/websocket/ws-events/add-friend.ts b/backend/src/api/websocket/ws-events/add-friend.ts new file mode 100644 index 00000000..8f409c6f --- /dev/null +++ b/backend/src/api/websocket/ws-events/add-friend.ts @@ -0,0 +1,81 @@ +import { Socket } from 'socket.io'; +import Channels from '../../../data/channels'; +import { Channel, DMChannelDocument } from '../../../data/models/channel'; +import { SelfUserDocument, User, UserDocument } from '../../../data/models/user'; +import Users from '../../../data/users'; +import Deps from '../../../utils/deps'; +import { WebSocket } from '../websocket'; +import { WSEvent, Args, Params } from './ws-event'; + +export default class implements WSEvent<'ADD_FRIEND'> { + on = 'ADD_FRIEND' as const; + + constructor( + private channels = Deps.get(Channels), + private users = Deps.get(Users), + ) {} + + public async invoke(ws: WebSocket, client: Socket, { username }: Params.AddFriend) { + const senderId = ws.sessions.userId(client); + let sender = await this.users.getSelf(senderId); + let friend = await this.users.getByUsername(username); + + if (sender.friendRequestIds.includes(friend.id)) + throw new TypeError('Friend request already sent'); + else if (sender.friendIds.includes(friend.id)) + throw new TypeError('You are already friends'); + + const isBlocking = friend.ignored.userIds.includes(sender.id); + console.log(friend.ignored.userIds); + if (isBlocking) + throw new TypeError('This user is blocking you'); + + let dmChannel: DMChannelDocument; + ({ sender, friend, dmChannel } = await this.handle(sender, friend) as any); + + if (dmChannel) + await client.join(dmChannel.id); + + ws.io + .to(senderId) + .to(friend.id) + .emit('ADD_FRIEND', { + sender: this.users.secure(sender), + friend: this.users.secure(friend), + } as Args.AddFriend); + } + + private async handle(sender: SelfUserDocument, friend: SelfUserDocument): Promise { + if (sender.id === friend.id) + throw new TypeError('You cannot add yourself as a friend'); + + const hasReturnedRequest = friend.friendRequestIds.includes(sender.id); + if (hasReturnedRequest) return { + friend: await this.acceptRequest(friend, sender), + sender: await this.acceptRequest(sender, friend), + dmChannel: await this.channels.getDMByMembers(sender.id, friend.id) + ?? await this.channels.createDM(sender.id, friend.id), + } + + const hasSentRequest = sender.friendRequestIds.includes(friend.id); + if (!hasSentRequest) + await this.sendRequest(sender, friend); + + return { sender, friend }; + } + + private async sendRequest(sender: SelfUserDocument, friend: UserDocument) { + sender.friendRequestIds.push(friend.id); + return sender.save(); + } + + private async acceptRequest(sender: SelfUserDocument, friend: SelfUserDocument) { + const friendExists = sender.friendIds.includes(friend.id); + if (friendExists) return friend; + + const index = sender.friendRequestIds.indexOf(friend.id); + sender.friendRequestIds.splice(index, 1); + sender.friendIds.push(friend.id); + return sender.save(); + } +} diff --git a/backend/src/api/websocket/ws-events/channel-create.ts b/backend/src/api/websocket/ws-events/channel-create.ts new file mode 100644 index 00000000..bf99fd93 --- /dev/null +++ b/backend/src/api/websocket/ws-events/channel-create.ts @@ -0,0 +1,42 @@ +import { Socket } from 'socket.io'; +import { PermissionTypes } from '../../../data/types/entity-types'; +import { Channel } from '../../../data/models/channel'; +import { Guild } from '../../../data/models/guild'; +import { generateSnowflake } from '../../../data/snowflake-entity'; +import Deps from '../../../utils/deps'; +import { WSGuard } from '../../modules/ws-guard'; +import { WebSocket } from '../websocket'; +import { WSEvent, Args, Params, WSEventParams } from './ws-event'; + +export default class implements WSEvent<'CHANNEL_CREATE'> { + on = 'CHANNEL_CREATE' as const; + + constructor( + private guard = Deps.get(WSGuard) + ) {} + + public async invoke(ws: WebSocket, client: Socket, { partialChannel, guildId }: Params.ChannelCreate) { + await this.guard.validateCan(client, guildId, PermissionTypes.General.MANAGE_CHANNELS); + + const channel = await Channel.create({ + _id: generateSnowflake(), + name: partialChannel.name, + summary: partialChannel.summary, + guildId, + type: partialChannel.type as any, + memberIds: [] + }); + + await Guild.updateOne( + { _id: guildId }, + { $push: { channels: channel } }, + { runValidators: true }, + ); + + await client.join(channel.id); + + ws.io + .to(guildId) + .emit('CHANNEL_CREATE', { channel, guildId } as Args.ChannelCreate); + } +} diff --git a/backend/src/api/websocket/ws-events/channel-delete.ts b/backend/src/api/websocket/ws-events/channel-delete.ts new file mode 100644 index 00000000..9a5d13de --- /dev/null +++ b/backend/src/api/websocket/ws-events/channel-delete.ts @@ -0,0 +1,32 @@ +import { Socket } from 'socket.io'; +import { PermissionTypes } from '../../../data/types/entity-types'; +import { TextChannelDocument } from '../../../data/models/channel'; +import Deps from '../../../utils/deps'; +import { WSGuard } from '../../modules/ws-guard'; +import { WebSocket } from '../websocket'; +import { WSEvent, Args, Params } from './ws-event'; +import Channels from '../../../data/channels'; + +export default class implements WSEvent<'CHANNEL_DELETE'> { + on = 'CHANNEL_DELETE' as const; + + constructor( + private channels = Deps.get(Channels), + private guard = Deps.get(WSGuard), + ) {} + + public async invoke(ws: WebSocket, client: Socket, { channelId }: Params.ChannelDelete) { + const channel = await this.channels.get(channelId) as TextChannelDocument; + await this.guard.validateCan(client, channel.guildId, PermissionTypes.General.MANAGE_CHANNELS); + + await client.leave(channel.id); + await channel.deleteOne(); + + ws.io + .to(channel.guildId) + .emit('CHANNEL_DELETE', { + channelId: channel.id, + guildId: channel.guildId, + } as Args.ChannelDelete); + } +} diff --git a/backend/src/api/websocket/ws-events/disconnect.ts b/backend/src/api/websocket/ws-events/disconnect.ts new file mode 100644 index 00000000..186b72a6 --- /dev/null +++ b/backend/src/api/websocket/ws-events/disconnect.ts @@ -0,0 +1,44 @@ +import { Socket } from 'socket.io'; +import Channels from '../../../data/channels'; +import { Channel } from '../../../data/models/channel'; +import { UserDocument } from '../../../data/models/user'; +import { Lean } from '../../../data/types/entity-types'; +import Users from '../../../data/users'; +import Deps from '../../../utils/deps'; +import { WebSocket } from '../websocket'; +import { WSEvent, Args } from './ws-event'; + +export default class implements WSEvent<'disconnect'> { + on = 'disconnect' as const; + + constructor( + private users = Deps.get(Users), + ) {} + + public async invoke(ws: WebSocket, client: Socket) { + const userId = ws.sessions.get(client.id); + const user = await this.users.get(userId); + + ws.sessions.delete(client.id); + await this.setOfflineStatus(ws, client, user); + + client.rooms.clear(); + } + + public async setOfflineStatus(ws: WebSocket, client: Socket, user: UserDocument) { + const userConnected = ws.connectedUserIds.includes(user.id); + if (userConnected) return; + + user.status = 'OFFLINE'; + await user.save(); + + const guildIds = user.guilds.map(g => g.id); + + ws.io + .to(guildIds.concat(user.friendIds)) + .emit('PRESENCE_UPDATE', { + userId: user.id, + status: user.status + } as Args.PresenceUpdate); + } +} diff --git a/backend/src/api/websocket/ws-events/guild-create.ts b/backend/src/api/websocket/ws-events/guild-create.ts new file mode 100644 index 00000000..ed1da22a --- /dev/null +++ b/backend/src/api/websocket/ws-events/guild-create.ts @@ -0,0 +1,31 @@ +import { Socket } from 'socket.io'; +import Guilds from '../../../data/guilds'; +import { User } from '../../../data/models/user'; +import Users from '../../../data/users'; +import Deps from '../../../utils/deps'; +import { WSRooms } from '../modules/ws-rooms'; +import { WebSocket } from '../websocket'; +import { WSEvent, Params } from './ws-event'; + +export default class implements WSEvent<'GUILD_CREATE'> { + on = 'GUILD_CREATE' as const; + + constructor( + private guilds = Deps.get(Guilds), + private rooms = Deps.get(WSRooms), + private users = Deps.get(Users), + ) {} + + public async invoke(ws: WebSocket, client: Socket, { partialGuild }: Params.GuildCreate) { + const userId = ws.sessions.userId(client); + + const user = await this.users.getSelf(userId, true); + const guild = await this.guilds.create(partialGuild.name as any, user); + + await this.rooms.joinGuildRooms(user, client); + + ws.io + .to(userId) + .emit('GUILD_JOIN', { guild }); + } +} diff --git a/backend/src/api/websocket/ws-events/guild-delete.ts b/backend/src/api/websocket/ws-events/guild-delete.ts new file mode 100644 index 00000000..c3b2438b --- /dev/null +++ b/backend/src/api/websocket/ws-events/guild-delete.ts @@ -0,0 +1,42 @@ +import { Socket } from 'socket.io'; +import { Channel } from '../../../data/models/channel'; +import { Guild } from '../../../data/models/guild'; +import { GuildMember } from '../../../data/models/guild-member'; +import { Invite } from '../../../data/models/invite'; +import { Message } from '../../../data/models/message'; +import { Role } from '../../../data/models/role'; +import { User } from '../../../data/models/user'; +import Deps from '../../../utils/deps'; +import { WSGuard } from '../../modules/ws-guard'; +import { WebSocket } from '../websocket'; +import { WSEvent, Params, Args } from './ws-event'; + +export default class implements WSEvent<'GUILD_DELETE'> { + on = 'GUILD_DELETE' as const; + + constructor( + private guard = Deps.get(WSGuard), + ) {} + + public async invoke(ws: WebSocket, client: Socket, { guildId }: Params.GuildDelete) { + await this.guard.validateIsOwner(client, guildId); + + await User.updateMany( + { guilds: guildId }, + { $pull: { guilds: guildId } }, + ); + + const guildChannels = await Channel.find({ guildId }); + await Message.deleteMany({ channelId: guildChannels.map(c => c.id) as any }) + + await Guild.deleteOne({ _id: guildId }); + await GuildMember.deleteMany({ guildId }); + await Invite.deleteMany({ guildId }); + await Role.deleteMany({ guildId }); + await Channel.deleteMany({ guildId }); + + ws.io + .to(guildId) + .emit('GUILD_DELETE', { guildId } as Args.GuildDelete); + } +} diff --git a/backend/src/api/websocket/ws-events/guild-member-add.ts b/backend/src/api/websocket/ws-events/guild-member-add.ts new file mode 100644 index 00000000..2bc1918e --- /dev/null +++ b/backend/src/api/websocket/ws-events/guild-member-add.ts @@ -0,0 +1,60 @@ +import { Socket } from 'socket.io'; +import GuildMembers from '../../../data/guild-members'; +import Guilds from '../../../data/guilds'; +import Invites from '../../../data/invites'; +import { InviteDocument } from '../../../data/models/invite'; +import Users from '../../../data/users'; +import Deps from '../../../utils/deps'; +import { WSRooms } from '../modules/ws-rooms'; +import { WebSocket } from '../websocket'; +import { WSEvent, Args, Params } from './ws-event'; + +export default class implements WSEvent<'GUILD_MEMBER_ADD'> { + on = 'GUILD_MEMBER_ADD' as const; + + constructor( + private guilds = Deps.get(Guilds), + private members = Deps.get(GuildMembers), + private invites = Deps.get(Invites), + private rooms = Deps.get(WSRooms), + private users = Deps.get(Users), + ) {} + + public async invoke(ws: WebSocket, client: Socket, { inviteCode }: Params.GuildMemberAdd) { + const invite = await this.invites.get(inviteCode); + const guild = await this.guilds.get(invite.guildId); + const userId = ws.sessions.userId(client); + + const inGuild = guild.members.some(m => m.userId === userId); + if (inGuild) + throw new TypeError('User already in guild'); + + const user = await this.users.getSelf(userId); + if (inviteCode && user.bot) + throw new TypeError('Bot users cannot accept invites'); + + await this.handleInvite(invite); + const member = await this.members.create(guild, user); + await this.rooms.joinGuildRooms(user, client); + + ws.io + .to(guild.id) + .emit('GUILD_MEMBER_ADD', { guildId: guild.id, member } as Args.GuildMemberAdd); + + ws.io + .to(user.id) + .emit('GUILD_JOIN', { guild } as Args.GuildJoin); + } + + private async handleInvite(invite: InviteDocument) { + const inviteExpired = Number(invite.options?.expiresAt?.getTime()) < new Date().getTime(); + if (inviteExpired) + throw new TypeError('Invite expired'); + + invite.uses++; + + (invite.options?.maxUses && invite.uses >= invite.options.maxUses) + ? await invite.deleteOne() + : await invite.save(); + } +} diff --git a/backend/src/api/websocket/ws-events/guild-member-remove.ts b/backend/src/api/websocket/ws-events/guild-member-remove.ts new file mode 100644 index 00000000..1a972eb4 --- /dev/null +++ b/backend/src/api/websocket/ws-events/guild-member-remove.ts @@ -0,0 +1,56 @@ +import { Socket } from 'socket.io'; +import Guilds from '../../../data/guilds'; +import { GuildDocument } from '../../../data/models/guild'; +import { GuildMember } from '../../../data/models/guild-member'; +import { User } from '../../../data/models/user'; +import { PermissionTypes } from '../../../data/types/entity-types'; +import Deps from '../../../utils/deps'; +import { WSGuard } from '../../modules/ws-guard'; +import { WebSocket } from '../websocket'; +import { WSEvent, Args, Params } from './ws-event'; + +export default class implements WSEvent<'GUILD_MEMBER_REMOVE'> { + on = 'GUILD_MEMBER_REMOVE' as const; + + constructor( + private guilds = Deps.get(Guilds), + private guard = Deps.get(WSGuard), + ) {} + + public async invoke(ws: WebSocket, client: Socket, { guildId, memberId }: Params.GuildMemberRemove) { + const guild = await this.guilds.get(guildId, true); + const member = guild.members.find(m => m.id === memberId); + if (!member) + throw new TypeError('Member does not exist'); + + const selfUserId = ws.sessions.get(client.id); + if (guild.ownerId === member.userId) + throw new TypeError('You cannot leave a guild you own'); + + else if (selfUserId !== member.userId) + await this.guard.validateCan(client, guildId, PermissionTypes.General.KICK_MEMBERS); + + const user = await User.findById(member.userId) as any; + const index = user.guilds.indexOf(guildId); + user.guilds.splice(index, 1); + await user.save(); + + await GuildMember.deleteOne({ _id: memberId }); + + await this.leaveGuildRooms(client, guild); + + ws.io + .to(member.userId) + .emit('GUILD_LEAVE', { guildId } as Args.GuildLeave); + + ws.io + .to(guildId) + .emit('GUILD_MEMBER_REMOVE', { guildId, memberId: member.id } as Args.GuildMemberRemove); + } + + private async leaveGuildRooms(client: Socket, guild: GuildDocument) { + await client.leave(guild.id); + for (const channel of guild.channels) + await client.leave(channel.id); + } +} diff --git a/backend/src/api/websocket/ws-events/guild-member-update.ts b/backend/src/api/websocket/ws-events/guild-member-update.ts new file mode 100644 index 00000000..c7567598 --- /dev/null +++ b/backend/src/api/websocket/ws-events/guild-member-update.ts @@ -0,0 +1,57 @@ +import { Socket } from 'socket.io'; +import GuildMembers from '../../../data/guild-members'; +import Guilds from '../../../data/guilds'; +import { GuildMember } from '../../../data/models/guild-member'; +import { Role } from '../../../data/models/role'; +import Roles from '../../../data/roles'; +import { Lean, PermissionTypes } from '../../../data/types/entity-types'; +import Users from '../../../data/users'; +import Deps from '../../../utils/deps'; +import { array } from '../../../utils/utils'; +import { WSGuard } from '../../modules/ws-guard'; +import { WebSocket } from '../websocket'; +import { WSEvent, Args, Params } from './ws-event'; + +export default class implements WSEvent<'GUILD_MEMBER_UPDATE'> { + on = 'GUILD_MEMBER_UPDATE' as const; + + constructor( + private guard = Deps.get(WSGuard), + private guilds = Deps.get(Guilds), + private guildMembers = Deps.get(GuildMembers), + private roles = Deps.get(Roles), + ) {} + + public async invoke(ws: WebSocket, client: Socket, { memberId, partialMember }: Params.GuildMemberUpdate) { + const member = await this.guildMembers.get(memberId); + + const selfUserId = ws.sessions.userId(client); + const selfMember = await this.guildMembers.getInGuild(member.guildId, selfUserId); + + await this.guard.validateCan(client, selfMember.guildId, PermissionTypes.General.MANAGE_ROLES); + this.guard.validateKeys('guildMember', partialMember); + + const guild = await this.guilds.get(member.guildId); + const selfIsHigher = await this.roles.isHigher(guild, selfMember, member.roleIds); + + const isSelf = selfMember.id === memberId; + if (!isSelf && !selfIsHigher) + throw new TypeError('Member has higher roles'); + + const everyoneRole = guild.roles.find(r => r.name === '@everyone') as Lean.Role; + await member.updateOne({ + ...partialMember, + roleIds: [everyoneRole.id].concat(partialMember.roleIds ?? []), + }, + { runValidators: true }, + ); + + ws.io + .to(member.guildId) + .emit('GUILD_MEMBER_UPDATE', { + guildId: member.guildId, + memberId, + partialMember, + } as Args.GuildMemberUpdate); + } +} diff --git a/backend/src/api/websocket/ws-events/guild-role-create.ts b/backend/src/api/websocket/ws-events/guild-role-create.ts new file mode 100644 index 00000000..e43b8b55 --- /dev/null +++ b/backend/src/api/websocket/ws-events/guild-role-create.ts @@ -0,0 +1,34 @@ +import { Socket } from 'socket.io'; +import { PermissionTypes } from '../../../data/types/entity-types'; +import { Guild } from '../../../data/models/guild'; +import { Role } from '../../../data/models/role'; +import { generateSnowflake } from '../../../data/snowflake-entity'; +import Deps from '../../../utils/deps'; +import { WSGuard } from '../../modules/ws-guard'; +import { WebSocket } from '../websocket'; +import { WSEvent, Args, Params } from './ws-event'; +import Roles from '../../../data/roles'; + +export default class implements WSEvent<'GUILD_ROLE_CREATE'> { + on = 'GUILD_ROLE_CREATE' as const; + + constructor( + private roles = Deps.get(Roles), + private guard = Deps.get(WSGuard), + ) {} + + public async invoke(ws: WebSocket, client: Socket, { guildId, partialRole }: Params.GuildRoleCreate) { + await this.guard.validateCan(client, guildId, PermissionTypes.General.MANAGE_ROLES); + + const role = await this.roles.create(guildId, partialRole); + await Guild.updateOne( + { _id: guildId }, + { $push: { roles: role } }, + { runValidators: true }, + ); + + ws.io + .to(guildId) + .emit('GUILD_ROLE_CREATE', { guildId, role } as Args.GuildRoleCreate); + } +} diff --git a/backend/src/api/websocket/ws-events/guild-role-delete.ts b/backend/src/api/websocket/ws-events/guild-role-delete.ts new file mode 100644 index 00000000..b5e01cd6 --- /dev/null +++ b/backend/src/api/websocket/ws-events/guild-role-delete.ts @@ -0,0 +1,31 @@ +import { Socket } from 'socket.io'; +import { PermissionTypes } from '../../../data/types/entity-types'; +import { Role } from '../../../data/models/role'; +import Deps from '../../../utils/deps'; +import { WSGuard } from '../../modules/ws-guard'; +import { WebSocket } from '../websocket'; +import { WSEvent, Args, Params } from './ws-event'; +import { GuildMember } from '../../../data/models/guild-member'; + +export default class implements WSEvent<'GUILD_ROLE_DELETE'> { + on = 'GUILD_ROLE_DELETE' as const; + + constructor( + private guard = Deps.get(WSGuard) + ) {} + + public async invoke(ws: WebSocket, client: Socket, { roleId, guildId }: Params.GuildRoleDelete) { + await this.guard.validateCan(client, guildId, PermissionTypes.General.MANAGE_ROLES); + + await Role.deleteOne({ _id: roleId }); + await GuildMember.updateMany( + { guildId }, + { $pull: { roleIds: roleId } }, + { runValidators: true }, + ); + + ws.io + .to(guildId) + .emit('GUILD_ROLE_DELETE', { guildId, roleId } as Args.GuildRoleDelete); + } +} diff --git a/backend/src/api/websocket/ws-events/guild-role-update.ts b/backend/src/api/websocket/ws-events/guild-role-update.ts new file mode 100644 index 00000000..de33cdd8 --- /dev/null +++ b/backend/src/api/websocket/ws-events/guild-role-update.ts @@ -0,0 +1,30 @@ +import { Socket } from 'socket.io'; +import { PermissionTypes } from '../../../data/types/entity-types'; +import { Role } from '../../../data/models/role'; +import Deps from '../../../utils/deps'; +import { WSGuard } from '../../modules/ws-guard'; +import { WebSocket } from '../websocket'; +import { WSEvent, Args, Params, WSEventParams } from './ws-event'; + +export default class implements WSEvent<'GUILD_ROLE_UPDATE'> { + on = 'GUILD_ROLE_UPDATE' as const; + + constructor( + private guard = Deps.get(WSGuard), + ) {} + + public async invoke(ws: WebSocket, client: Socket, { roleId, partialRole, guildId }: Params.GuildRoleUpdate) { + await this.guard.validateCan(client, guildId, PermissionTypes.General.MANAGE_ROLES); + this.guard.validateKeys('role', partialRole); + + await Role.updateOne( + { _id: roleId }, + partialRole as any, + { runValidators: true }, + ); + + ws.io + .to(guildId) + .emit('GUILD_ROLE_UPDATE', { guildId, partialRole } as Args.GuildRoleUpdate); + } +} \ No newline at end of file diff --git a/backend/src/api/websocket/ws-events/guild-update.ts b/backend/src/api/websocket/ws-events/guild-update.ts new file mode 100644 index 00000000..0914a875 --- /dev/null +++ b/backend/src/api/websocket/ws-events/guild-update.ts @@ -0,0 +1,56 @@ +import { Socket } from 'socket.io'; +import { Lean, PermissionTypes } from '../../../data/types/entity-types'; +import { Partial } from '../../../data/types/ws-types'; +import { Guild } from '../../../data/models/guild'; +import Deps from '../../../utils/deps'; +import { WSGuard } from '../../modules/ws-guard'; +import { WebSocket } from '../websocket'; +import { WSEvent, Args, Params } from './ws-event'; +import Guilds from '../../../data/guilds'; + +export default class implements WSEvent<'GUILD_UPDATE'> { + on = 'GUILD_UPDATE' as const; + + constructor( + private guard = Deps.get(WSGuard), + private guilds = Deps.get(Guilds), + ) {} + + public async invoke(ws: WebSocket, client: Socket, { guildId, partialGuild }: Params.GuildUpdate) { + await this.guard.validateCan(client, guildId, PermissionTypes.General.MANAGE_GUILD); + + this.guard.validateKeys('guild', partialGuild); + + const guild = await this.guilds.get(guildId); + this.validateChannels(guild, partialGuild); + this.validateRoles(guild, partialGuild); + + await Guild.updateOne( + { _id: guildId }, + partialGuild as any, + { runValidators: true }, + ); + + ws.io + .to(guildId) + .emit('GUILD_UPDATE', { guildId, partialGuild } as Args.GuildUpdate); + } + + private validateChannels(guild: Lean.Guild, partialGuild: Partial.Guild) { + if (!partialGuild.channels) return; + if (guild.channels.length !== partialGuild.channels.length) + throw new TypeError('Cannot add or remove channels this way'); + } + + private validateRoles(guild: Lean.Guild, partialGuild: Partial.Guild) { + if (!partialGuild.roles) return; + if (guild.roles.length !== partialGuild.roles.length) + throw new TypeError('Cannot add or remove roles this way'); + + const oldEveryoneRoleId = guild.roles[0].id; + const newEveryoneRoleId: string = partialGuild?.roles?.[0] as any; + + if (oldEveryoneRoleId !== newEveryoneRoleId) + throw new TypeError('You cannot reorder the @everyone role'); + } +} diff --git a/backend/src/api/websocket/ws-events/invite-create.ts b/backend/src/api/websocket/ws-events/invite-create.ts new file mode 100644 index 00000000..1651091e --- /dev/null +++ b/backend/src/api/websocket/ws-events/invite-create.ts @@ -0,0 +1,29 @@ +import { Socket } from 'socket.io'; +import Invites from '../../../data/invites'; +import { PermissionTypes } from '../../../data/types/entity-types'; +import Deps from '../../../utils/deps'; +import { WSGuard } from '../../modules/ws-guard'; +import { WebSocket } from '../websocket'; +import { WSEvent, Args, Params, WSEventParams } from './ws-event'; + +export default class implements WSEvent<'INVITE_CREATE'> { + on = 'INVITE_CREATE' as const; + + constructor( + private guard = Deps.get(WSGuard), + private invites = Deps.get(Invites), + ) {} + + public async invoke(ws: WebSocket, client: Socket, params: Params.InviteCreate) { + await this.guard.validateCan(client, params.guildId, PermissionTypes.General.CREATE_INVITE); + + const invite = await this.invites.create(params, ws.sessions.userId(client)); + + ws.io + .to(params.guildId) + .emit('INVITE_CREATE', { + guildId: params.guildId, + invite, + } as Args.InviteCreate); + } +} diff --git a/backend/src/api/websocket/ws-events/invite-delete.ts b/backend/src/api/websocket/ws-events/invite-delete.ts new file mode 100644 index 00000000..80529780 --- /dev/null +++ b/backend/src/api/websocket/ws-events/invite-delete.ts @@ -0,0 +1,31 @@ +import { Socket } from 'socket.io'; +import Invites from '../../../data/invites'; +import { Guild } from '../../../data/models/guild'; +import { PermissionTypes } from '../../../data/types/entity-types'; +import Deps from '../../../utils/deps'; +import { WSGuard } from '../../modules/ws-guard'; +import { WebSocket } from '../websocket'; +import { WSEvent, Args, Params, WSEventParams } from './ws-event'; + +export default class implements WSEvent<'INVITE_DELETE'> { + on = 'INVITE_DELETE' as const; + + constructor( + private guard = Deps.get(WSGuard), + private invites = Deps.get(Invites), + ) {} + + public async invoke(ws: WebSocket, client: Socket, { inviteCode }: Params.InviteDelete) { + const invite = await this.invites.get(inviteCode); + await this.guard.validateCan(client, invite.guildId, PermissionTypes.General.MANAGE_GUILD); + + await invite.deleteOne(); + + ws.io + .to(invite.guildId) + .emit('INVITE_DELETE', { + guildId: invite.guildId, + inviteCode, + } as Args.InviteDelete); + } +} \ No newline at end of file diff --git a/backend/src/api/websocket/ws-events/message-create.ts b/backend/src/api/websocket/ws-events/message-create.ts new file mode 100644 index 00000000..0bf99971 --- /dev/null +++ b/backend/src/api/websocket/ws-events/message-create.ts @@ -0,0 +1,49 @@ +import { Socket } from 'socket.io'; +import { Message } from '../../../data/models/message'; +import { generateSnowflake } from '../../../data/snowflake-entity'; +import { WebSocket } from '../websocket'; +import { WSEvent, Args, Params } from './ws-event'; +import Deps from '../../../utils/deps'; +import { WSGuard } from '../../modules/ws-guard'; +import Messages from '../../../data/messages'; +import Pings from '../../../data/pings'; +import Channels from '../../../data/channels'; +import Users from '../../../data/users'; +import { Channel } from '../../../data/models/channel'; +import { User } from '../../../data/models/user'; + +export default class implements WSEvent<'MESSAGE_CREATE'> { + on = 'MESSAGE_CREATE' as const; + + constructor( + private messages = Deps.get(Messages), + private guard = Deps.get(WSGuard), + private users = Deps.get(Users), + ) {} + + public async invoke(ws: WebSocket, client: Socket, { channelId, partialMessage }: Params.MessageCreate) { + await this.guard.canAccessChannel(client, channelId, true); + + const authorId = ws.sessions.userId(client); + const message = await this.messages.create(authorId, channelId, partialMessage); + + if (!client.rooms.has(channelId)) + await client.join(channelId); + + await Channel.updateOne( + { _id: channelId }, + { lastMessageId: message.id } + ); + + const user = await this.users.getSelf(authorId, false); + user.lastReadMessages = { + ...user.lastReadMessages, + [channelId]: message.id, + }; + await user.save(); + + ws.io + .to(channelId) + .emit('MESSAGE_CREATE', { message } as Args.MessageCreate); + } +} diff --git a/backend/src/api/websocket/ws-events/message-delete.ts b/backend/src/api/websocket/ws-events/message-delete.ts new file mode 100644 index 00000000..39dce92c --- /dev/null +++ b/backend/src/api/websocket/ws-events/message-delete.ts @@ -0,0 +1,46 @@ +import { Socket } from 'socket.io'; +import Channels from '../../../data/channels'; +import Messages from '../../../data/messages'; +import { Message } from '../../../data/models/message'; +import { PermissionTypes } from '../../../data/types/entity-types'; +import Deps from '../../../utils/deps'; +import { WSGuard } from '../../modules/ws-guard'; +import { WebSocket } from '../websocket'; +import { WSEvent, Params, Args } from './ws-event'; + +export default class implements WSEvent<'MESSAGE_DELETE'> { + on = 'MESSAGE_DELETE' as const; + + constructor( + private channels = Deps.get(Channels), + private guard = Deps.get(WSGuard), + private messages = Deps.get(Messages), + ) {} + + public async invoke(ws: WebSocket, client: Socket, { messageId }: Params.MessageDelete) { + const message = await this.messages.get(messageId); + const channel = await this.channels.get(message.channelId); + + try { + this.guard.validateIsUser(client, message.authorId); + } catch { + if (channel.type === 'DM') + throw new TypeError('Only message author can do this'); + await this.guard.validateCan(client, channel.guildId, PermissionTypes.Text.MANAGE_MESSAGES); + } + await message.deleteOne(); + + if (message.id === channel.lastMessageId) { + const previousMessage = await Message.findOne({ channelId: channel.id }); + channel.lastMessageId = previousMessage?.id; + await (channel as any).save(); + } + + ws.io + .to(message.channelId) + .emit('MESSAGE_DELETE', { + channelId: message.channelId, + messageId: messageId, + } as Args.MessageDelete); + } +} diff --git a/backend/src/api/websocket/ws-events/message-update.ts b/backend/src/api/websocket/ws-events/message-update.ts new file mode 100644 index 00000000..a54edd2e --- /dev/null +++ b/backend/src/api/websocket/ws-events/message-update.ts @@ -0,0 +1,50 @@ +import { Socket } from 'socket.io'; +import { MessageDocument } from '../../../data/models/message'; +import { WebSocket } from '../websocket'; +import { WSEvent, Args, Params, WSEventParams } from './ws-event'; +import got from 'got'; +import { MessageTypes } from '../../../data/types/entity-types'; +import Deps from '../../../utils/deps'; +import { WSGuard } from '../../modules/ws-guard'; +import Messages from '../../../data/messages'; + +const metascraper = require('metascraper')([ + require('metascraper-description')(), + require('metascraper-image')(), + require('metascraper-title')(), + require('metascraper-url')() +]); + +export default class implements WSEvent<'MESSAGE_UPDATE'> { + on = 'MESSAGE_UPDATE' as const; + + constructor( + private guard = Deps.get(WSGuard), + private messages = Deps.get(Messages), + ) {} + + public async invoke(ws: WebSocket, client: Socket, { messageId, partialMessage, withEmbed }: Params.MessageUpdate) { + let message = await this.messages.get(messageId); + this.guard.validateIsUser(client, message.authorId); + this.guard.validateKeys('message', partialMessage); + + message = await message.update({ + content: partialMessage.content, + embed: (withEmbed) ? await this.getEmbed(message) : undefined, + updatedAt: new Date() + }, { runValidators: true }); + + ws.to(message.channelId) + .emit('MESSAGE_UPDATE', { message }); + } + + public async getEmbed(message: MessageDocument): Promise { + try { + const targetURL = /([https://].*)/.exec(message.content)?.[0]; + if (!targetURL) return; + + const { body: html, url } = await got(targetURL); + return await metascraper({ html, url }); + } catch {} + } +} diff --git a/backend/src/api/websocket/ws-events/ready.ts b/backend/src/api/websocket/ws-events/ready.ts new file mode 100644 index 00000000..f4ac63ef --- /dev/null +++ b/backend/src/api/websocket/ws-events/ready.ts @@ -0,0 +1,47 @@ +import { Socket } from 'socket.io'; +import { User } from '../../../data/models/user'; +import { Lean, UserTypes } from '../../../data/types/entity-types'; +import Users from '../../../data/users'; +import Deps from '../../../utils/deps'; +import { WSGuard } from '../../modules/ws-guard'; +import { WSRooms } from '../modules/ws-rooms'; +import { WebSocket } from '../websocket'; +import { WSEvent, Args, Params } from './ws-event'; + +export default class implements WSEvent<'READY'> { + public on = 'READY' as const; + public cooldown = 5; + + constructor( + private guard = Deps.get(WSGuard), + private rooms = Deps.get(WSRooms), + private users = Deps.get(Users), + ) {} + + public async invoke(ws: WebSocket, client: Socket, { key }: Params.Ready) { + const { id: userId } = await this.guard.decodeKey(key); + if (!userId) + throw new TypeError('Invalid User ID'); + + ws.sessions.set(client.id, userId); + + const user = await this.users.getSelf(userId); + await this.rooms.join(client, user); + + user.status = 'ONLINE'; + await user.save(); + + const guildIds = user.guilds.map(g => g.id); + + ws.io + .to(guildIds.concat(user.friendIds)) + .emit('PRESENCE_UPDATE', { + userId, + status: user.status + } as Args.PresenceUpdate); + + ws.io + .to(client.id) + .emit('READY', { user } as Args.Ready); + } +} diff --git a/backend/src/api/websocket/ws-events/remove-friend.ts b/backend/src/api/websocket/ws-events/remove-friend.ts new file mode 100644 index 00000000..776f8bf3 --- /dev/null +++ b/backend/src/api/websocket/ws-events/remove-friend.ts @@ -0,0 +1,50 @@ +import { Socket } from 'socket.io'; +import { SelfUserDocument, UserDocument } from '../../../data/models/user'; +import { Params } from '../../../data/types/ws-types'; +import Users from '../../../data/users'; +import Deps from '../../../utils/deps'; +import { WebSocket } from '../websocket'; +import { WSEvent, Args } from './ws-event'; + +export default class implements WSEvent<'REMOVE_FRIEND'> { + on = 'REMOVE_FRIEND' as const; + + constructor( + private users = Deps.get(Users), + ) {} + + public async invoke(ws: WebSocket, client: Socket, { friendId }: Params.RemoveFriend) { + const senderId = ws.sessions.userId(client); + let sender = await this.users.getSelf(senderId); + let friend = await this.users.getSelf(friendId); + + ({ sender, friend } = await this.handle(sender, friend) as any); + + ws.io + .to(senderId) + .to(friendId) + .emit('REMOVE_FRIEND', { + sender: this.users.secure(sender), + friend: this.users.secure(friend), + } as Args.RemoveFriend); + } + + private async handle(sender: SelfUserDocument, friend: SelfUserDocument): Promise { + if (sender.id === friend.id) + throw new TypeError('You cannot remove yourself as a friend'); + + await this.removeFriend(sender, friend); + await this.removeFriend(friend, sender); + + return { sender, friend }; + } + + private async removeFriend(sender: SelfUserDocument, friend: SelfUserDocument) { + const friendIndex = sender.friendIds.indexOf(friend.id); + sender.friendIds.splice(friendIndex, 1); + + const requestIndex = sender.friendRequestIds.indexOf(friend.id); + sender.friendRequestIds.splice(requestIndex, 1); + return sender.save(); + } +} diff --git a/backend/src/api/websocket/ws-events/typing-start.ts b/backend/src/api/websocket/ws-events/typing-start.ts new file mode 100644 index 00000000..04f116be --- /dev/null +++ b/backend/src/api/websocket/ws-events/typing-start.ts @@ -0,0 +1,19 @@ +import { Socket } from 'socket.io'; +import { WebSocket } from '../websocket'; +import { WSEvent, Args, Params, WSEventParams } from './ws-event'; + +export default class implements WSEvent<'TYPING_START'> { + on = 'TYPING_START' as const; + + public async invoke(ws: WebSocket, client: Socket, { channelId }: Params.TypingStart) { + if (!client.rooms.has(channelId)) + await client.join(channelId); + + client.broadcast + .to(channelId) + .emit('TYPING_START', { + userId: ws.sessions.userId(client), + channelId, + } as Args.TypingStart); + } +} diff --git a/backend/src/api/websocket/ws-events/user-update.ts b/backend/src/api/websocket/ws-events/user-update.ts new file mode 100644 index 00000000..9aaf83d0 --- /dev/null +++ b/backend/src/api/websocket/ws-events/user-update.ts @@ -0,0 +1,32 @@ +import { Socket } from 'socket.io'; +import Users from '../../../data/users'; +import Deps from '../../../utils/deps'; +import { WSGuard } from '../../modules/ws-guard'; +import { WebSocket } from '../websocket'; +import { WSEvent, Args, Params } from './ws-event'; + +export default class implements WSEvent<'USER_UPDATE'> { + on = 'USER_UPDATE' as const; + + constructor( + private users = Deps.get(Users), + private guard = Deps.get(WSGuard), + ) {} + + public async invoke(ws: WebSocket, client: Socket, { key, partialUser }: Params.UserUpdate) { + const { id: userId } = await this.guard.decodeKey(key); + + const user = await this.users.get(userId); + if (partialUser.guilds?.length !== user.guilds.length) + throw new TypeError('You add or remove user guilds this way'); + + this.guard.validateKeys('user', partialUser); + + await user.updateOne( + partialUser, + { runValidators: true, context: 'query' }, + ); + + client.emit('USER_UPDATE', { partialUser } as Args.UserUpdate); + } +} diff --git a/backend/src/api/websocket/ws-events/ws-event.ts b/backend/src/api/websocket/ws-events/ws-event.ts new file mode 100644 index 00000000..6d9d733c --- /dev/null +++ b/backend/src/api/websocket/ws-events/ws-event.ts @@ -0,0 +1,12 @@ +import { Socket } from 'socket.io'; +import { WSEventParams } from '../../../data/types/ws-types'; +import { WebSocket } from '../websocket'; + +export interface WSEvent { + on: K; + cooldown?: number; + + invoke: (ws: WebSocket, client: Socket, params: WSEventParams[K]) => Promise; +} + +export { Args, Params, WSEventParams } from '../../../data/types/ws-types'; diff --git a/backend/src/app.ts b/backend/src/app.ts index 29382a28..90f9cac2 100644 --- a/backend/src/app.ts +++ b/backend/src/app.ts @@ -1,12 +1,23 @@ -import { config } from 'dotenv'; -config({ path: '.env' }); - -import { connect } from 'mongoose'; -import { Deps } from './utils/deps'; -import { WS } from './ws/websocket'; - -connect(process.env.MONGO_URI, - { useNewUrlParser: true, useUnifiedTopology: true }, - () => console.log(`Connected to MongoDB`)); - -Deps.add(WS, new WS()); \ No newline at end of file +import './data/types/env'; +import { config } from 'dotenv'; +config(); + +import { connect } from 'mongoose'; +import { API } from './api/server'; +import { SystemBot } from './system/bot'; +import Deps from './utils/deps'; +import Log from './utils/log'; + +connect(process.env.MONGO_URI, { + useUnifiedTopology: true, + useNewUrlParser: true, + useFindAndModify: false, + useCreateIndex: true, + serverSelectionTimeoutMS: 0, +}, (error) => (error) + ? Log.error(error.message, 'db') + : Log.info('Connected to database.') +); + +Deps.get(SystemBot).init(); +Deps.get(API); diff --git a/backend/src/data/channels.ts b/backend/src/data/channels.ts new file mode 100644 index 00000000..a9ec7b04 --- /dev/null +++ b/backend/src/data/channels.ts @@ -0,0 +1,70 @@ +import DBWrapper from './db-wrapper'; +import { Channel, ChannelDocument, DMChannelDocument, TextChannelDocument, VoiceChannelDocument } from './models/channel'; +import { SelfUserDocument } from './models/user'; +import { generateSnowflake } from './snowflake-entity'; +import { Lean } from './types/entity-types'; + +export default class Channels extends DBWrapper { + public async get(id: string | undefined) { + const channel = await Channel.findById(id); + if (!channel) + throw new TypeError('Channel Not Found'); + return channel; + } + + public async getDMByMembers(...memberIds: string[]) { + return await Channel.findOne({ memberIds }) as DMChannelDocument; + } + + public async getDM(id: string) { + return await Channel.findById(id) as DMChannelDocument; + } + public async getText(id: string) { + return await Channel.findById(id) as TextChannelDocument; + } + public async getVoice(id: string) { + return await Channel.findById(id) as VoiceChannelDocument; + } + + public async getDMChannels(userId: string): Promise { + return await Channel.find({ memberIds: userId }) as DMChannelDocument[]; + } + public async getGuildsChannels(user: SelfUserDocument): Promise { + const guildIds = user.guilds.map(c => c.id); + return await Channel.find({ + guildId: { $in: guildIds }, + }) as ChannelDocument[]; + } + + public create(options?: Partial): Promise { + return Channel.create({ + _id: generateSnowflake(), + name: 'chat', + memberIds: [], + type: 'TEXT', + ...options as any, + }); + } + + public createDM(senderId: string, friendId: string) { + return this.create({ + memberIds: [senderId, friendId], + name: 'DM Channel', + type: 'DM', + }) as Promise; + } + public async createText(guildId: string) { + return this.create({ guildId }) as Promise; + } + public createVoice(guildId: string) { + return this.create({ + name: 'Talk', + guildId, + type: 'VOICE', + }) as Promise; + } + + public async getSystem(guildId: string) { + return await Channel.findOne({ guildId, type: 'TEXT' }); + } +} diff --git a/backend/src/data/data-utils.ts b/backend/src/data/data-utils.ts deleted file mode 100644 index 59008fb0..00000000 --- a/backend/src/data/data-utils.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Document } from 'mongoose'; - -export function useId(this: Document) { - const obj = this.toObject(); - - this.id = this._id; - delete this._id; - - return obj; -} \ No newline at end of file diff --git a/backend/src/data/db-wrapper.ts b/backend/src/data/db-wrapper.ts new file mode 100644 index 00000000..2021b687 --- /dev/null +++ b/backend/src/data/db-wrapper.ts @@ -0,0 +1,9 @@ +import { Document } from 'mongoose'; + +export default abstract class DBWrapper { + public abstract get(identifier: K | undefined): Promise; + + save(savedType: T) { + return savedType?.save(); + } +} \ No newline at end of file diff --git a/backend/src/data/guild-members.ts b/backend/src/data/guild-members.ts new file mode 100644 index 00000000..38f5f435 --- /dev/null +++ b/backend/src/data/guild-members.ts @@ -0,0 +1,50 @@ +import DBWrapper from './db-wrapper'; +import { GuildDocument } from './models/guild'; +import { GuildMember, GuildMemberDocument } from './models/guild-member'; +import { Role } from './models/role'; +import { UserDocument } from './models/user'; +import { generateSnowflake } from './snowflake-entity'; +import { Lean } from './types/entity-types'; + +export default class GuildMembers extends DBWrapper { + public async get(id: string | undefined) { + const member = await GuildMember.findById(id); + if (!member) + throw new TypeError('Guild Member Not Found'); + return member; + } + + public async getInGuild(guildId: string | undefined, userId: string | undefined) { + const member = await GuildMember.findOne({ guildId, userId }); + if (!member) + throw new TypeError('Guild Member Not Found'); + return member; + } + + public async create(guild: GuildDocument, user: UserDocument, ...roles: Lean.Role[]) { + const member = await GuildMember.create({ + _id: generateSnowflake(), + guildId: guild.id, + userId: user.id, + roleIds: (roles.length > 0) + ? roles.map(r => r.id) + : [await this.getEveryoneRoleId(guild.id) as string], + }); + await this.joinGuild(user, guild, member); + + return member; + } + + private async joinGuild(user: UserDocument, guild: GuildDocument, member: GuildMemberDocument) { + user.guilds.push(guild as any); + await user.save(); + + guild.members.push(member as any); + await guild.save(); + } + + private async getEveryoneRoleId(guildId: string) { + const role = await Role.findOne({ guildId, name: '@everyone' }); + return role?.id; + } +} \ No newline at end of file diff --git a/backend/src/data/guilds.ts b/backend/src/data/guilds.ts new file mode 100644 index 00000000..6f08bffd --- /dev/null +++ b/backend/src/data/guilds.ts @@ -0,0 +1,65 @@ +import { Guild, GuildDocument } from './models/guild'; +import DBWrapper from './db-wrapper'; +import { generateSnowflake } from './snowflake-entity'; +import Deps from '../utils/deps'; +import Channels from './channels'; +import GuildMembers from './guild-members'; +import Roles from './roles'; +import { UserDocument } from './models/user'; +import { Invite } from './models/invite'; +import { APIError } from '../api/modules/api-error'; +import { getNameAcronym } from '../utils/utils'; + +export default class Guilds extends DBWrapper { + constructor( + private channels = Deps.get(Channels), + private members = Deps.get(GuildMembers), + private roles = Deps.get(Roles), + ) { super(); } + + public async get(id: string | undefined, populate = true) { + const guild = (populate) + ? await Guild + .findById(id) + ?.populate('members') + .populate('roles') + .populate('channels') + .exec() + : await Guild.findById(id); + if (!guild) + throw new APIError(404, 'Guild Not Found'); + + return guild; + } + + public async getFromChannel(id: string) { + return await Guild.findOne({ channels: { $in: id } as any }); + } + + public async create(name: string, owner: UserDocument): Promise { + const guildId = generateSnowflake(); + const everyoneRole = await this.roles.create(guildId, { + name: '@everyone', + }); + + const guild = await Guild.create({ + _id: guildId, + name, + ownerId: owner.id, + roles: [ everyoneRole ], + nameAcronym: getNameAcronym(name), + members: [], + channels: [ + await this.channels.createText(guildId), + await this.channels.createVoice(guildId), + ], + }); + await this.members.create(guild, owner, everyoneRole); + + return guild; + } + + public async invites(guildId: string) { + return await Invite.find({ guildId }); + } +} diff --git a/backend/src/data/invites.ts b/backend/src/data/invites.ts new file mode 100644 index 00000000..66eac17f --- /dev/null +++ b/backend/src/data/invites.ts @@ -0,0 +1,23 @@ +import { APIError } from '../api/modules/api-error'; +import DBWrapper from './db-wrapper'; +import { generateInviteCode, Invite, InviteDocument } from './models/invite'; +import { Params } from './types/ws-types'; + +export default class Invites extends DBWrapper { + public async get(code: string | undefined): Promise { + const invite = await Invite.findById(code); + if (!invite) + throw new APIError(404, 'Invite Not Found'); + return invite; + } + + public async create({ guildId, options }: Params.InviteCreate, userId: string) { + return Invite.create({ + _id: generateInviteCode(), + guildId, + inviterId: userId, + options, + uses: 0, + }); + } +} \ No newline at end of file diff --git a/backend/src/data/messages.ts b/backend/src/data/messages.ts new file mode 100644 index 00000000..0b7f23da --- /dev/null +++ b/backend/src/data/messages.ts @@ -0,0 +1,54 @@ +import got from 'got/dist/source'; +import DBWrapper from './db-wrapper'; +import { Channel } from './models/channel'; +import { Message, MessageDocument } from './models/message'; +import { generateSnowflake } from './snowflake-entity'; +import { MessageTypes } from './types/entity-types'; +import { Partial } from './types/ws-types'; + +const metascraper = require('metascraper')([ + require('metascraper-description')(), + require('metascraper-image')(), + require('metascraper-title')(), + require('metascraper-url')() +]); + +export default class Messages extends DBWrapper { + public async get(id: string | undefined) { + const message = await Message.findById(id); + if (!message) + throw new TypeError('Message Not Found'); + return message; + } + + public async create(authorId: string, channelId: string, partialMessage: Partial.Message) { + return await Message.create({ + _id: generateSnowflake(), + authorId, + channelId, + content: partialMessage.content as string, + embed: await this.getEmbed(partialMessage), + }); + } + + public async getEmbed(message: Partial.Message): Promise { + try { + const targetURL = /([https://].*)/.exec(message.content as string)?.[0]; + if (!targetURL) return; + + const { body: html, url } = await got(targetURL); + return await metascraper({ html, url }); + } catch {} + } + + public async getChannelMessages(channelId: string) { + return await Message.find({ channelId }); + } + + public async getDMChannelMessages(channelId: string, memberId: string) { + const isMember = await Channel.exists({ _id: channelId, memberIds: memberId }); + if (isMember) + throw new TypeError('You cannot access this channel'); + return await Message.find({ channelId }); + } +} \ No newline at end of file diff --git a/backend/src/data/models/application.ts b/backend/src/data/models/application.ts new file mode 100644 index 00000000..d6b0889b --- /dev/null +++ b/backend/src/data/models/application.ts @@ -0,0 +1,47 @@ +import { Document, model, Schema } from 'mongoose'; +import { generateSnowflake } from '../snowflake-entity'; +import { Lean, patterns } from '../types/entity-types'; +import { createdAtToDate, generateUsername, useId } from '../../utils/utils'; + +export interface ApplicationDocument extends Document, Lean.App { + _id: string | never; + id: string; + token: string; +} + +export const Application = model('application', new Schema({ + _id: { + type: String, + default: generateSnowflake, + }, + user: { + type: String, + ref: 'user', + required: [true, 'User is required'], + validate: [patterns.snowflake, 'Invalid Snowflake ID'], + }, + createdAt: { + type: Date, + get: createdAtToDate, + }, + description: { + default: 'A new bot, that can do cool things.', + type: String, + required: [true, 'Description is required'], + maxlength: [1000, 'Description too long'], + }, + name: { + type: String, + default: generateUsername, + required: [true, 'Name is required'], + maxlength: [32, 'Name is too long'], + validate: [patterns.username, 'Name contains invalid characters'], + }, + token: String, + owner: { + type: String, + ref: 'user', + required: [true, 'Owner is required'], + validate: [patterns.snowflake, 'Invalid Snowflake ID'], + }, +}, { toJSON: { getters: true } }).method('toClient', useId)); \ No newline at end of file diff --git a/backend/src/data/models/channel.ts b/backend/src/data/models/channel.ts index 6f8d910a..2683a477 100644 --- a/backend/src/data/models/channel.ts +++ b/backend/src/data/models/channel.ts @@ -1,13 +1,81 @@ -import { model, Schema } from 'mongoose'; -import { generateSnowflake } from '../../utils/snowflake'; -import { useId } from '../data-utils'; - -export interface ChannelDocument extends Entity.Channel, Document {} - -export const Channel = model('channel', new Schema({ - _id: { type: String, default: generateSnowflake }, - createdAt: { type: Date, default: () => new Date() }, - channelId: String, - guildId: String, - name: String, -}, { toJSON: { getters: true } }).method('toClient', useId)); +import { Document, model, Schema } from 'mongoose'; +import { createdAtToDate, useId, validators } from '../../utils/utils'; +import { generateSnowflake } from '../snowflake-entity'; +import { ChannelTypes } from '../types/entity-types'; + +export interface DMChannelDocument extends Document, ChannelTypes.DM { + _id: string | never; + id: string; + createdAt: never; +} +export interface TextChannelDocument extends Document, ChannelTypes.Text { + _id: string | never; + id: string; + createdAt: never; + guildId: string; +} +export interface VoiceChannelDocument extends Document, ChannelTypes.Voice { + _id: string | never; + id: string; + createdAt: never; + guildId: string; +} +export type ChannelDocument = DMChannelDocument | TextChannelDocument | VoiceChannelDocument; + +export const Channel = model('channel', new Schema({ + _id: { + type: String, + default: generateSnowflake, + }, + createdAt: { + type: Date, + get: createdAtToDate, + }, + guildId: { + type: String, + validate: { + validator: validators.optionalSnowflake, + message: 'Invalid Snowflake ID', + }, + }, + memberIds: { + type: [String], + default: [], + validate: { + validator: validators.maxLength(50), + message: 'Channel member limit reached', + } + }, + name: { + type: String, + required: [true, 'Name is required'], + maxlength: [32, 'Name too long'], + validate: { + validator: function(val: string) { + const type = (this as any).type; + const pattern = /^[A-Za-z\-\d]+$/; + return type === 'TEXT' + && pattern.test(val) + || type !== 'TEXT'; + }, + message: 'Invalid name' + } + }, + lastMessageId: { + type: String, + validate: { + validator: validators.optionalSnowflake, + message: 'Invalid Snowflake ID' + }, + }, + summary: { + type: String, + maxlength: [128, 'Summary too long'], + }, + type: { + type: String, + required: [true, 'Type is required'], + validate: [/^TEXT$|^VOICE$|^DM$/, 'Invalid type'], + }, +}, { toJSON: { getters: true } }) +.method('toClient', useId)); diff --git a/backend/src/data/models/guild-member.ts b/backend/src/data/models/guild-member.ts new file mode 100644 index 00000000..8c8bf21c --- /dev/null +++ b/backend/src/data/models/guild-member.ts @@ -0,0 +1,37 @@ +import { Document, model, Schema } from 'mongoose'; +import { useId, validators } from '../../utils/utils'; +import { generateSnowflake } from '../snowflake-entity'; +import { Lean, patterns } from '../types/entity-types'; + +export interface GuildMemberDocument extends Document, Lean.GuildMember { + _id: string | never; + id: string; + createdAt: never; +} + +export const GuildMember = model('guildMember', new Schema({ + _id: { + type: String, + default: generateSnowflake, + }, + guildId: { + type: String, + required: [true, 'Guild ID is required'], + validate: [patterns.snowflake, 'Invalid Snowflake ID'], + }, + userId: { + type: String, + required: [true, 'User ID is required'], + validate: [patterns.snowflake, 'Invalid Snowflake ID'], + }, + roleIds: { + type: [String], + default: [], + required: [true, 'Role IDs is required'], + validate: { + validator: validators.minLength(1), + message: 'At least 1 role is required', + } + }, +}, { toJSON: { getters: true } }) +.method('toClient', useId)); diff --git a/backend/src/data/models/guild.ts b/backend/src/data/models/guild.ts index b5597682..f9d94280 100644 --- a/backend/src/data/models/guild.ts +++ b/backend/src/data/models/guild.ts @@ -1,17 +1,63 @@ -import { model, Schema } from 'mongoose'; -import { generateSnowflake } from '../../utils/snowflake'; -import { useId } from '../data-utils'; - -export interface GuildDocument extends Entity.Guild, Document {} - -export const Guild = model('guild', new Schema({ - _id: { type: String, default: generateSnowflake }, - channels: { type: [String], ref: 'channel' }, - createdAt: { type: Date, default: () => new Date() }, - iconURL: String, - members: { type: [String], ref: 'user' }, - invites: { type: [String], ref: 'invite' }, - // roles: { type: [String], ref: 'role' }, - name: String, - ownerId: String, -}, { toJSON: { getters: true } }).method('toClient', useId)); +import { Document, model, Schema } from 'mongoose'; +import { createdAtToDate, getNameAcronym, useId, validators } from '../../utils/utils'; +import { generateSnowflake } from '../snowflake-entity'; +import { Lean, patterns } from '../types/entity-types'; + +export interface GuildDocument extends Document, Lean.Guild { + id: string; + createdAt: never; +} + +export const Guild = model('guild', new Schema({ + _id: { + type: String, + default: generateSnowflake, + }, + name: { + type: String, + required: [true, 'Name is required'], + maxlength: [32, 'Name is too long'], + }, + createdAt: { + type: Date, + get: createdAtToDate, + }, + nameAcronym: { + type: String, + get: function(this: GuildDocument) { + return getNameAcronym(this.name); + } + }, + iconURL: String, + ownerId: { + type: String, + required: true, + validate: [patterns.snowflake, 'Invalid Snowflake ID'], + }, + channels: { + type: [{ + type: String, + ref: 'channel', + }], + validate: { + validator: validators.maxLength(250), + message: 'Channel limit reached', + }, + }, + members: [{ + type: String, + ref: 'guildMember', + }], + roles: { + type: [{ + type: String, + ref: 'role', + }], + validate: { + validator: validators.minLength(1), + message: 'Guild must have at least one role', + }, + }, +}, { + toJSON: { getters: true } +}).method('toClient', useId)); \ No newline at end of file diff --git a/backend/src/data/models/invite.ts b/backend/src/data/models/invite.ts index 224ba919..e13f912b 100644 --- a/backend/src/data/models/invite.ts +++ b/backend/src/data/models/invite.ts @@ -1,14 +1,49 @@ -import { model, Schema } from 'mongoose'; -import generateInvite from '../../utils/generate-invite'; -import { useId } from '../data-utils'; - -export interface InviteDocument extends Entity.Invite, Document {} - -export const Invite = model('invite', new Schema({ - _id: { type: String, default: generateInvite }, - creatorId: String, - createdAt: { type: Date, default: () => new Date() }, - guildId: String, - options: Object, - uses: Number, -}, { toJSON: { getters: true } }).method('toClient', useId)); +import { Document, model, Schema } from 'mongoose'; +import { useId } from '../../utils/utils'; +import { Lean, InviteTypes, patterns } from '../types/entity-types'; + +export interface InviteDocument extends Document, Lean.Invite { + _id: string | never; + id: string; + createdAt: never; +} + +export function generateInviteCode(codeLength = 7) { + const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + const charactersLength = characters.length; + + let result = ''; + for (let i = 0; i < codeLength; i++) + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + return result; +} + +export const Invite = model('invite', new Schema({ + _id: { + type: String, + default: generateInviteCode, + }, + createdAt: { + type: Date, + default: new Date(), + }, + options: new Schema({ + expiresAt: Date, + maxUses: { + type: Number, + min: [1, 'Max uses too low'], + max: [1000, 'Max uses too high'], + }, + }), + inviterId: { + type: String, + required: [true, 'Inviter ID is required'], + validate: [patterns.snowflake, 'Invalid Snowflake ID'], + }, + guildId: { + type: String, + required: [true, 'Guild ID is required'], + validate: [patterns.snowflake, 'Invalid Snowflake ID'], + }, + uses: Number, +}, { toJSON: { getters: true } }).method('toClient', useId)); diff --git a/backend/src/data/models/message.ts b/backend/src/data/models/message.ts index dae4dcfe..5912ba01 100644 --- a/backend/src/data/models/message.ts +++ b/backend/src/data/models/message.ts @@ -1,14 +1,38 @@ -import { model, Schema } from 'mongoose'; -import { useId } from '../data-utils'; -import { generateSnowflake } from '../../utils/snowflake'; - -export interface MessageDocument extends Entity.Message, Document {} - -export const Message = model('message', new Schema({ - _id: { type: String, default: generateSnowflake }, - authorId: String, - content: String, - createdAt: { type: Date, default: () => new Date() }, - channelId: String, - updatedAt: Date, -}, { toJSON: { getters: true } }).method('toClient', useId)); +import { Document, model, Schema } from 'mongoose'; +import { createdAtToDate, useId } from '../../utils/utils'; +import { generateSnowflake } from '../snowflake-entity'; +import { Lean, patterns } from '../types/entity-types'; + +export interface MessageDocument extends Document, Lean.Message { + _id: string | never; + id: string; + createdAt: never; +} + +export const Message = model('message', new Schema({ + _id: { + type: String, + default: generateSnowflake, + }, + authorId: { + type: String, + required: [true, 'Author ID is required'], + validate: [patterns.snowflake, 'Invalid Snowflake ID'], + }, + channelId: { + type: String, + required: [true, 'Channel ID is required'], + validate: [patterns.snowflake, 'Invalid Snowflake ID'], + }, + content: { + type: String, + minlength: [1, 'Content too short'], + maxlength: [3000, 'Content too long'], + }, + createdAt: { + type: Date, + get: createdAtToDate, + }, + embed: Object, // TODO: make, and unit test embed schema + updatedAt: Date, +}, { toJSON: { getters: true } }).method('toClient', useId)); diff --git a/backend/src/data/models/role.ts b/backend/src/data/models/role.ts new file mode 100644 index 00000000..99614244 --- /dev/null +++ b/backend/src/data/models/role.ts @@ -0,0 +1,62 @@ +import { Document, model, Schema } from 'mongoose'; +import { createdAtToDate, useId } from '../../utils/utils'; +import { generateSnowflake } from '../snowflake-entity'; +import { Lean, patterns, PermissionTypes } from '../types/entity-types'; + +export function hasPermission(current: number, required: number) { + return Boolean(current & required) + || Boolean(current & PermissionTypes.General.ADMINISTRATOR); +} + +const everyoneColor = '#ffffff'; + +export interface RoleDocument extends Document, Lean.Role { + _id: string | never; + id: string; + createdAt: never; +} + +export const Role = model('role', new Schema({ + _id: { + type: String, + default: generateSnowflake, + }, + color: { + type: String, + default: everyoneColor, + validate: { + validator: function(this: RoleDocument, val: string) { + return this?.name !== '@everyone' + || val === everyoneColor + || !val; + }, + message: 'Cannot change @everyone role color', + } + }, + createdAt: { + type: Date, + get: createdAtToDate, + }, + guildId: { + type: String, + required: [true, 'Owner ID is required'], + validate: [patterns.snowflake, 'Invalid Snowflake ID'], + }, + hoisted: Boolean, + mentionable: Boolean, + name: { + type: String, + required: [true, 'Name is required'], + maxlength: [32, 'Name too long'], + }, + permissions: { + type: Number, + default: PermissionTypes.defaultPermissions, + required: [true, 'Permissions is required'], + validate: { + validator: (val: number) => Number.isInteger(val) && val >= 0, + message: 'Invalid permissions integer', + }, + } +}, { toJSON: { getters: true } }) +.method('toClient', useId)); diff --git a/backend/src/data/models/user.ts b/backend/src/data/models/user.ts index a18c7735..0ed0e351 100644 --- a/backend/src/data/models/user.ts +++ b/backend/src/data/models/user.ts @@ -1,24 +1,120 @@ -import { model, Schema } from 'mongoose'; -import { useId } from '../data-utils'; -import { generateSnowflake } from '../../utils/snowflake'; -import passportLocalMongoose from 'passport-local-mongoose'; - -export interface UserDocument extends Entity.User, Document { - locked: boolean; -} - -const UserSchema = new Schema({ - _id: { type: String, default: generateSnowflake }, - avatarURL: { type: String, default: `/assets/avatars/avatar_grey.png` }, - createdAt: { type: Date, default: () => new Date() }, - discriminator: Number, - email: { type: String, required: true }, - locked: Boolean, - username: { type: String, required: true }, - updatedAt: Date, - guildIds: [String], -}, { toJSON: { getters: true } }) -.method('toClient', useId) -.plugin(passportLocalMongoose, { usernameField: 'email' }); - -export const User = model('user', UserSchema); +import { Document, model, Schema } from 'mongoose'; +import passportLocalMongoose from 'passport-local-mongoose'; +import { createdAtToDate, useId, validators } from '../../utils/utils'; +import { Lean, patterns, UserTypes } from '../types/entity-types'; +import uniqueValidator from 'mongoose-unique-validator'; +import { generateSnowflake } from '../snowflake-entity'; + +export interface UserDocument extends Document, Lean.User { + _id: string | never; + id: string; + createdAt: never; +} +export interface SelfUserDocument extends Document, UserTypes.Self { + _id: string | never; + id: string; + createdAt: never; + + changePassword: (...args) => Promise; + register: (...args) => Promise; +} + +export const User = model('user', new Schema({ + _id: { + type: String, + default: generateSnowflake, + }, + avatarURL: { + type: String, + required: [true, 'Avatar URL is required'], + }, + badges: { + type: [String], + default: [], + }, + bot: Boolean, + createdAt: { + type: Date, + get: createdAtToDate, + }, + email: { + type: String, + unique: [true, 'Email is already in use'], + uniqueCaseInsensitive: true, + validate: { + validator: (val: string) => !val || patterns.email.test(val), + message: 'Invalid email address' + }, + }, + friendIds: { + type: Array, + ref: 'user', + default: [], + validate: { + validator: validators.maxLength(100), + message: 'Clout limit reached', + }, + }, + friendRequestIds: { + type: Array, + ref: 'user', + default: [], + validate: { + validator: validators.maxLength(100), + message: 'Max friend requests reached', + }, + }, + guilds: { + type: Array, + ref: 'guild', + validate: { + validator: validators.maxLength(100), + message: 'Guild limit reached', + }, + }, + ignored: { + type: Object, + default: new UserTypes.Ignored(), + validate: { + validator: function (this: UserDocument, val) { + return !val || !val.userIds?.includes(this.id); + }, + message: 'Cannot block self', + }, + channelIds: { + type: [String], + default: [] + }, + guildIds: { + type: [String], + default: [] + }, + userIds: { + type: [String], + default: [] + }, + }, + lastReadMessages: { + type: Object, + default: {} + }, + status: { + type: String, + required: [true, 'Status is required'], + validate: [patterns.status, 'Invalid status'], + }, + username: { + type: String, + required: [true, 'Username is required'], + unique: [true, 'Username is taken'], + uniqueCaseInsensitive: true, + validate: { + validator: patterns.username, + message: `Invalid username`, + }, + }, + verified: Boolean, +}, { toJSON: { getters: true } }) +.plugin(passportLocalMongoose) +.plugin(uniqueValidator) +.method('toClient', useId)); diff --git a/backend/src/data/pings.ts b/backend/src/data/pings.ts new file mode 100644 index 00000000..2b838af8 --- /dev/null +++ b/backend/src/data/pings.ts @@ -0,0 +1,16 @@ +import { SelfUserDocument } from './models/user'; +import { Lean, UserTypes } from './types/entity-types'; + +export default class Pings { + public markAsRead(user: SelfUserDocument, message: Lean.Message) { + user.lastReadMessages[message.channelId] = message.id; + return user.updateOne(user); + } + + public isIgnored(self: UserTypes.Self, channel: Lean.Channel, message: Lean.Message) { + return self.id === message.authorId + || self.ignored.channelIds.includes(channel.id) + || self.ignored.guildIds.includes(channel.guildId as string) + || self.ignored.userIds.includes(message.authorId); + } +} diff --git a/backend/src/data/roles.ts b/backend/src/data/roles.ts new file mode 100644 index 00000000..8c5c92ad --- /dev/null +++ b/backend/src/data/roles.ts @@ -0,0 +1,45 @@ +import DBWrapper from './db-wrapper'; +import { Lean, PermissionTypes } from './types/entity-types'; +import { Partial } from './types/ws-types'; +import { hasPermission, Role, RoleDocument } from './models/role'; +import { generateSnowflake } from './snowflake-entity'; + +export default class Roles extends DBWrapper { + public async get(id: string | undefined) { + const role = await Role.findById(id); + if (!role) + throw new TypeError('Role Not Found'); + return role; + } + + public async isHigher(guild: Lean.Guild, selfMember: Lean.GuildMember, roleIds: string[]) { + const highestRole: Lean.Role = guild.roles[guild.roles.length - 1]; + + return selfMember.userId === guild?.ownerId + || (selfMember.roleIds.includes(highestRole?.id) + && !roleIds.includes(highestRole.id)); + } + + public async hasPermission(guild: Lean.Guild, member: Lean.GuildMember, permission: PermissionTypes.PermissionString) { + const totalPerms = guild.roles + .filter(r => member.roleIds.includes(r.id)) + .reduce((acc, value) => value.permissions | acc, 0); + + const permNumber = (typeof permission === 'string') + ? PermissionTypes.All[PermissionTypes.All[permission as string]] + : permission; + return hasPermission(totalPerms, permNumber as any); + } + + public create(guildId: string, options?: Partial.Role) { + return Role.create({ + _id: generateSnowflake(), + guildId, + mentionable: false, + hoisted: false, + name: 'New Role', + permissions: PermissionTypes.defaultPermissions, + ...options, + }); + } +} diff --git a/backend/src/data/snowflake-entity.ts b/backend/src/data/snowflake-entity.ts new file mode 100644 index 00000000..fb90e7b9 --- /dev/null +++ b/backend/src/data/snowflake-entity.ts @@ -0,0 +1,49 @@ +import cluster from 'cluster'; + +let inc = 0; +let lastSnowflake: string; +const accordEpoch = 1577836800000; + +export function generateSnowflake() { + const msSince = (new Date().getTime() - accordEpoch) + .toString(2) + .padStart(42, '0'); + const pid = process.pid + .toString(2) + .slice(0, 5) + .padStart(5, '0'); + const wid = (cluster.worker?.id ?? 0) + .toString(2) + .slice(0, 5) + .padStart(5, '0'); + const getInc = (add: number) => (inc + add) + .toString(2) + .padStart(12, '0'); + + let snowflake = `0b${msSince}${wid}${pid}${getInc(0)}`; + (snowflake === lastSnowflake) + ? snowflake = `0b${msSince}${wid}${pid}${getInc(1)}` + : inc = 0; + + lastSnowflake = snowflake; + return BigInt(snowflake).toString(); +} + +function binary64(val: string) { + try { + return `0b${BigInt(val) + .toString(2) + .padStart(64, '0')}`; + } catch (e) { + return ''; + } +} + +// what this method does +// -> https://discord.com/developers/docs/reference#convert-snowflake-to-datetime +export function snowflakeToDate(snowflake: string) { + const sinceEpochMs = Number( + binary64(snowflake).slice(0, 42 + 2) + ); + return new Date(sinceEpochMs + accordEpoch); +} diff --git a/backend/src/data/types/entity-types.ts b/backend/src/data/types/entity-types.ts new file mode 100644 index 00000000..85122bdf --- /dev/null +++ b/backend/src/data/types/entity-types.ts @@ -0,0 +1,206 @@ +// REMEMBER: Sync types below with Website project. +// -> in entity-types.ts +export namespace Lean { + export interface App { + id: string; + createdAt: Date; + description: string; + name: string; + owner: User; + user: User; + token: string | never; + } + export interface Channel { + id: string; + createdAt: Date; + guildId?: string; + memberIds?: string[]; + name?: string; + summary?: string; + lastMessageId?: null | string; + type: ChannelTypes.Type; + } + export interface Guild { + id: string; + name: string; + createdAt: Date; + nameAcronym: string; + iconURL?: string; + ownerId: string; + channels: Channel[]; + members: GuildMember[]; + roles: Role[]; + } + export interface GuildMember { + id: string; + createdAt: Date; + guildId: string; + roleIds: string[]; + userId: string; + } + export interface Invite { + id: string; + createdAt: Date; + options?: InviteTypes.Options; + inviterId: string; + guildId: string; + uses: number; + } + export interface Message { + id: string; + authorId: string; + channelId: string; + content: string; + createdAt: Date; + embed?: MessageTypes.Embed; + updatedAt?: Date; + } + export interface Role { + id: string; + color?: string; + createdAt: Date; + guildId: string; + hoisted: boolean; + mentionable: boolean; + name: string; + permissions: number; + } + export interface User { + id: string; + avatarURL: string; + badges: UserTypes.Badge[]; + bot: boolean; + createdAt: Date; + friendIds: string[]; + friendRequestIds: string[]; + guilds: string[] | Lean.Guild[]; + status: UserTypes.StatusType; + username: string; + } +} + +export namespace ChannelTypes { + export type Type = 'DM' | 'TEXT' | 'VOICE'; + + export interface DM extends Lean.Channel { + memberIds: string[]; + guildId: never; + summary: never; + type: 'DM'; + } + export interface Text extends Lean.Channel { + memberIds: never; + type: 'TEXT'; + } + export interface Voice extends Lean.Channel { + memberIds: string[]; + summary: never; + type: 'VOICE'; + } +} + +export namespace GeneralTypes { + export interface SnowflakeEntity { + id: string; + } +} + +export namespace InviteTypes { + export interface Options { + expiresAt?: Date; + maxUses?: number; + } +} + +export namespace MessageTypes { + export interface Embed { + description: string; + image: string; + title: string; + url: string; + } +} + +export namespace PermissionTypes { + export enum General { + VIEW_CHANNELS = 1024, + MANAGE_NICKNAMES = 512, + CHANGE_NICKNAME = 256, + CREATE_INVITE = 128, + KICK_MEMBERS = 64, + BAN_MEMBERS = 32, + MANAGE_CHANNELS = 16, + MANAGE_ROLES = 8, + MANAGE_GUILD = 4, + VIEW_AUDIT_LOG = 2, + ADMINISTRATOR = 1 + } + export enum Text { + ADD_REACTIONS = 2048 * 16, + MENTION_EVERYONE = 2048 * 8, + READ_MESSAGES = 2048 * 4, + MANAGE_MESSAGES = 2048 * 2, + SEND_MESSAGES = 2048 + } + export enum Voice { + MOVE_MEMBERS = 32768 * 8, + MUTE_MEMBERS = 32768 * 4, + SPEAK = 32768 * 2, + CONNECT = 32768 + } + export const All = { + ...General, + ...Text, + ...Voice, + } + export type Permission = General | Text | Voice; + + export type PermissionString = keyof typeof All; + + export const defaultPermissions = + PermissionTypes.General.VIEW_CHANNELS + | PermissionTypes.General.CREATE_INVITE + | PermissionTypes.Text.SEND_MESSAGES + | PermissionTypes.Text.READ_MESSAGES + | PermissionTypes.Text.ADD_REACTIONS + | PermissionTypes.Voice.CONNECT + | PermissionTypes.Voice.SPEAK; +} + +export namespace UserTypes { + export type Badge = + | 'BUG_1' + | 'BUG_2' + | 'BUG_3' + | 'OG' + | 'STAFF'; + export class Ignored { + channelIds: string[] = []; + guildIds: string[] = []; + userIds: string[] = []; + } + export type StatusType = 'ONLINE' | 'OFFLINE'; + export interface Self extends Lean.User { + guilds: Lean.Guild[]; + email: string; + verified: true; + lastReadMessages: { + [k: string]: string + }; + ignored: { + channelIds: string[]; + guildIds: string[]; + userIds: string[]; + }; + } +} + +export const patterns = { + email: /(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/, + hexColor: /^#(?:[0-9a-fA-F]{3}){1,2}$/, + password: /(?=.*[a-zA-Z0-9!@#$%^&*])/, + snowflake: /^\d{18}$/, + status: /^ONLINE|^BUSY$|^AFK$|^OFFLINE$/, + textChannelName: /^[A-Za-z\-\d]{2,32}$/, + username: /(^(?! |^everyone$|^here$|^me$|^someone$|^discordtag$)[A-Za-z\d\-\_]{2,32}(? in ws.service.ts +import { ChannelTypes, Lean, UserTypes, InviteTypes } from './entity-types'; + +/** WS Params are what is sent to the websocket. */ +export interface WSEventParams { + /** Add a friend, by username, by sending an outgoing request or accepting an incoming request. */ + 'ADD_FRIEND': Params.AddFriend; + /** Create a channel in a guild. */ + 'CHANNEL_CREATE': Params.ChannelCreate; + /** Delete a channel in a guild. */ + 'CHANNEL_DELETE': Params.ChannelDelete; + /** Create a guild. */ + 'GUILD_CREATE': Params.GuildCreate; + /** Delete a guild. */ + 'GUILD_DELETE': Params.GuildDelete; + /** Accept a guild invite. */ + 'GUILD_MEMBER_ADD': Params.GuildMemberAdd; + /** Remove a member from a guild. */ + 'GUILD_MEMBER_REMOVE': Params.GuildMemberRemove; + /** Update a members roles or other properties on a member. */ + 'GUILD_MEMBER_UPDATE': Params.GuildMemberUpdate; + /** Create a role in a guild. */ + 'GUILD_ROLE_CREATE': Params.GuildRoleCreate; + /** Delete a role in a guild. */ + 'GUILD_ROLE_DELETE': Params.GuildRoleDelete; + /** Update a guild role permissions or other properties. */ + 'GUILD_ROLE_UPDATE': Params.GuildRoleUpdate; + /** Update the settings of a guild. */ + 'GUILD_UPDATE': Params.GuildUpdate; + /** Create an invite in a guild */ + 'INVITE_CREATE': Params.InviteCreate; + /** Delete an existing invite in a guild. */ + 'INVITE_DELETE': Params.InviteDelete; + /** Create a message in a text-based channel. */ + 'MESSAGE_CREATE': Params.MessageCreate; + /** Delete an existing message in a text-based channel. */ + 'MESSAGE_DELETE': Params.MessageDelete; + /** Update an existing message in a text-based channel. */ + 'MESSAGE_UPDATE': Params.MessageUpdate; + /** Bootstrap your websocket client to be able to use other websocket events. + * - Associate ws client ID with user ID. + * - Join user rooms. + * - Set online status. */ + 'READY': Params.Ready; + /** Cancel a friend request, or remove an existing friend. */ + 'REMOVE_FRIEND': Params.RemoveFriend; + /** Indicate that you are typing in a text-based channel. */ + 'TYPING_START': Params.TypingStart; + /** Update user settings. */ + 'USER_UPDATE': Params.UserUpdate; + /** Manually disconnect from the websocket; logout. */ + 'disconnect': any; +} + +export interface WSEventAsyncArgs { + /** Called after you sent an outgoing friend request, or of an incoming friend request. */ + 'ADD_FRIEND': Args.AddFriend; + /** Called when a guild channel is created. */ + 'CHANNEL_CREATE': Args.ChannelCreate; + /** Callled when a guild channel is deleted. */ + 'CHANNEL_DELETE': Args.ChannelDelete; + /** Called when a guild is deleted. */ + 'GUILD_DELETE': Args.GuildDelete; + /** Called when the client joins a guild. */ + 'GUILD_JOIN': Args.GuildJoin; + /** Called when the client leaves a guild. */ + 'GUILD_LEAVE': Args.GuildLeave; + /** Called when someone joins a guild by an invite, or a bot is added. */ + 'GUILD_MEMBER_ADD': Args.GuildMemberAdd; + /** Called when member roles are updated, or other properties. */ + 'GUILD_MEMBER_UPDATE': Args.GuildMemberUpdate; + /** Called when a guild member is removed, or leaves the guild. */ + 'GUILD_MEMBER_REMOVE': Args.GuildMemberRemove; + /** Called when a guild role is created. */ + 'GUILD_ROLE_CREATE': Args.GuildRoleCreate; + /** Called when a guild role is deleted. */ + 'GUILD_ROLE_DELETE': Args.GuildRoleDelete; + /** Called when properties on a guild role are updated. */ + 'GUILD_ROLE_UPDATE': Args.GuildRoleUpdate; + /** Called when guild settings are updated. */ + 'GUILD_UPDATE': Args.GuildUpdate; + /** Called when a guild invite is created. */ + 'INVITE_CREATE': Args.InviteCreate; + /** Called when an existing guild invite is deleted. */ + 'INVITE_DELETE': Args.InviteDelete; + /** Called when a message is created in a text-based channel. */ + 'MESSAGE_CREATE': Args.MessageCreate; + /** Called when a message is deleted in a text-based channel. */ + 'MESSAGE_DELETE': Args.MessageDelete; + /** Called when an existing message is updated in a text-based channel. */ + 'MESSAGE_UPDATE': Args.MessageUpdate; + /** Called when a message is sent in a channel you are not ignoring. */ + 'PING': Args.Ping; + /** Called when a user goes online or offline. */ + 'PRESENCE_UPDATE': Args.PresenceUpdate; + /** Called when the websocket accepts that you are ready. */ + 'READY': Args.Ready; + /** Called when you are removed as a friend, or you remove a friend request, or an existing friend. */ + 'REMOVE_FRIEND': Args.RemoveFriend; + /** Called when someone is typing in a text-based channel. */ + 'TYPING_START': Args.TypingStart; + /** Called the client user settings are updated. */ + 'USER_UPDATE': Args.UserUpdate; +} + +/** WS Args are what is received from the websocket. */ +export interface WSEventArgs { + /** Called after you sent an outgoing friend request, or of an incoming friend request. */ + 'ADD_FRIEND': (args: Args.AddFriend) => any; + /** Called when a guild channel is created. */ + 'CHANNEL_CREATE': (args: Args.ChannelCreate) => any; + /** Callled when a guild channel is deleted. */ + 'CHANNEL_DELETE': (args: Args.ChannelDelete) => any; + /** Called when a guild is deleted. */ + 'GUILD_DELETE': (args: Args.GuildDelete) => any; + /** Called when the client joins a guild. */ + 'GUILD_JOIN': (args: Args.GuildJoin) => any; + /** Called when the client leaves a guild. */ + 'GUILD_LEAVE': (args: Args.GuildLeave) => any; + /** Called when someone joins a guild by an invite, or a bot is added. */ + 'GUILD_MEMBER_ADD': (args: Args.GuildMemberAdd) => any; + /** Called when a guild member is removed, or leaves the guild. */ + 'GUILD_MEMBER_REMOVE': (args: Args.GuildMemberRemove) => any; + /** Called when member roles are updated, or other properties. */ + 'GUILD_MEMBER_UPDATE': (args: Args.GuildMemberUpdate) => any; + /** Called when a guild role is created. */ + 'GUILD_ROLE_CREATE': (args: Args.GuildRoleCreate) => any; + /** Called when a guild role is deleted. */ + 'GUILD_ROLE_DELETE': (args: Args.GuildRoleDelete) => any; + /** Called when properties on a guild role are updated. */ + 'GUILD_ROLE_UPDATE': (args: Args.GuildRoleUpdate) => any; + /** Called when guild settings are updated. */ + 'GUILD_UPDATE': (args: Args.GuildUpdate) => any; + /** Called when a guild invite is created. */ + 'INVITE_CREATE': (args: Args.InviteCreate) => any; + /** Called when an existing guild invite is deleted. */ + 'INVITE_DELETE': (args: Args.InviteDelete) => any; + /** Called when a message is created in a text-based channel. */ + 'MESSAGE_CREATE': (args: Args.MessageCreate) => any; + /** Called when a message is deleted in a text-based channel. */ + 'MESSAGE_DELETE': (args: Args.MessageDelete) => any; + /** Called when an existing message is updated in a text-based channel. */ + 'MESSAGE_UPDATE': (args: Args.MessageUpdate) => any; + /** Called when a message is sent in a channel you are not ignoring. */ + 'PING': (args: Args.Ping) => any; + /** Called when a user goes online or offline. */ + 'PRESENCE_UPDATE': (args: Args.PresenceUpdate) => any; + /** Called when the websocket accepts that you are ready. */ + 'READY': (args: Args.Ready) => any; + /** Called when you are removed as a friend, or you remove a friend request, or an existing friend. */ + 'REMOVE_FRIEND': (args: Args.RemoveFriend) => any; + /** Called when someone is typing in a text-based channel. */ + 'TYPING_START': (args: Args.TypingStart) => any; + /** Called the client user settings are updated. */ + 'USER_UPDATE': (args: Args.UserUpdate) => any; + /** Called when a websocket message is sent. */ + 'message': (message: string) => any; +} + +export namespace Params { + export interface AddFriend { + /** Username of user (case insensitive). */ + username: string; + } + export interface ChannelCreate { + guildId: string; + partialChannel: Partial.Channel; + } + export interface ChannelDelete { + /** ID of the channel to delete. */ + channelId: string; + } + export interface GuildCreate { + /** Properties with the guild. */ + partialGuild: Partial.Guild; + } + export interface GuildDelete { + guildId: string; + } + export interface GuildMemberAdd { + inviteCode: string; + } + export interface GuildMemberRemove { + /** ID of the guild. */ + guildId: string; + /** ID of the member, not the same as a user ID. */ + memberId: string; + } + export interface GuildMemberUpdate { + /** ID of the member, not the same as a user ID. */ + memberId: string; + partialMember: Partial.GuildMember; + } + export interface GuildRoleCreate { + guildId: string; + partialRole: Partial.Role; + } + export interface GuildRoleDelete { + roleId: string; + guildId: string; + } + export interface GuildRoleUpdate { + roleId: string; + guildId: string; + partialRole: Partial.Role; + } + export interface GuildUpdate { + guildId: string; + partialGuild: Partial.Guild; + } + export interface InviteCreate { + guildId: string; + options: InviteTypes.Options; + } + export interface InviteDelete { + inviteCode: string; + } + export interface MessageCreate { + channelId: string; + partialMessage: Partial.Message; + } + export interface MessageDelete { + messageId: string; + } + export interface MessageUpdate { + messageId: string; + partialMessage: Partial.Message; + withEmbed: boolean; + } + export interface MessageCreate { + partialMessage: Partial.Message; + } + export interface Ready { + key: string; + } + export interface RemoveFriend { + friendId: string; + } + export interface TypingStart { + channelId: string; + } + export interface UserUpdate { + partialUser: Partial.User; + key: string; + } +} + +export namespace Args { + export interface AddFriend { + /** The recipient who received the friend request. */ + friend: Lean.User; + /** User who sent or accepted the friend request. */ + sender: Lean.User; + /** Only available if both users add each other as a friend. */ + dmChannel?: ChannelTypes.DM; + } + /** */ + export interface ChannelCreate { + /** ID of guild that channel is in. */ + guildId: string; + /** The full object fo the channel that was created. */ + channel: Lean.Channel; + } + export interface ChannelDelete { + /** ID of guild that channel is in. */ + guildId: string; + /** The ID of the channel that is deleted. */ + channelId: string; + } + export interface GuildJoin { + /** The full object of the guild that was joined. */ + guild: Lean.Guild; + } + export interface GuildLeave { + /** ID of the guild that was left. */ + guildId: string; + } + export interface GuildDelete { + /** ID of the guild. */ + guildId: string; + } + /** Called when a member accepts an invite, or a bot was added to a guild. */ + export interface GuildMemberAdd { + /** ID of the guild. */ + guildId: string; + /** Full object of the member that was added to the guild. */ + member: Lean.GuildMember; + } + export interface GuildMemberRemove { + /** ID of the guild. */ + guildId: string; + /** ID of member that was removed. */ + memberId: string; + } + export interface GuildMemberUpdate { + /** ID of the guild. */ + guildId: string; + /** Properties of updated guild member. */ + partialMember: Partial.GuildMember; + /** ID of the guild member. Not the same as a user ID. */ + memberId: string; + } + export interface GuildRoleCreate { + /** ID of the guild. */ + guildId: string; + /** Full object of the role that was created. */ + role: Lean.Role; + } + export interface GuildRoleDelete { + /** ID of the guild. */ + guildId: string; + /** The ID of the role that was deleted. */ + roleId: string; + } + export interface GuildRoleUpdate { + /** Guild ID associated with role. */ + guildId: string; + /** Properties to update the role. */ + partialRole: Partial.Role; + /** The ID of the role that was updated. */ + roleId: string; + } + export interface GuildUpdate { + /** ID of the guild. */ + guildId: string; + /** Properties to update a guild. */ + partialGuild: Partial.Guild; + } + export interface InviteCreate { + /** ID of the guild. */ + guildId: string; + /** Full object of the invite. */ + invite: Lean.Invite; + } + /** Called when a guild invite is delted. */ + export interface InviteDelete { + /** ID of the guild. */ + guildId: string; + /** The ID or the code of the invite. */ + inviteCode: string; + } + export interface MessageCreate { + /** Full object of the message that was created. */ + message: Lean.Message; + } + export interface MessageDelete { + /** ID of the channel with the message. */ + channelId: string; + /** The ID of the message that was deleted. */ + messageId: string; + } + export interface MessageUpdate { + /** Full object of the message that was updated. */ + message: Lean.Message; + } + export interface Ping { + channelId: string; + guildId?: string; + } + export interface PresenceUpdate { + userId: string; + status: UserTypes.StatusType; + } + export interface Ready { + user: UserTypes.Self; + } + export interface RemoveFriend { + friend: Lean.User; + sender: Lean.User; + } + export interface TypingStart { + channelId: string; + userId: string; + } + /** PRIVATE - contains private data */ + export interface UserUpdate { + partialUser: Partial.User; + } +} + +/** Partial classes involved in updating things. + * Some properties (e.g. id) cannot be updated. + * + * **Tip**: Only provide what properties are being updated. */ +export namespace Partial { + export type Application = Partial; + export type Channel = Partial; + export type Guild = Partial; + export type GuildMember = Partial; + export type Message = Partial; + export type Role = Partial; + export type User = Partial; +} + +/** Keys of objects that cannot be updated. */ +export namespace Prohibited { + export const general: any = ['id', 'createdAt']; + + export const app: (keyof Lean.App)[] = [ + ...general, + 'owner', + 'user', + ]; + export const channel: (keyof Lean.Channel)[] = [ + ...general, + 'guildId', + 'lastMessageId', + 'memberIds', + 'type', + ]; + export const guild: (keyof Lean.Guild)[] = [ + ...general, + 'members', + 'nameAcronym', + ]; + export const guildMember: (keyof Lean.GuildMember)[] = [ + ...general, + 'guildId', + 'userId', + ]; + export const message: (keyof Lean.Message)[] = [ + ...general, + 'authorId', + 'channelId', + 'updatedAt', + ]; + export const role: (keyof Lean.Role)[] = [ + ...general, + 'guildId', + ]; + export const user: (keyof UserTypes.Self)[] = [ + ...general, + 'badges', + 'bot', + 'email', + 'friendIds', + 'friendRequestIds', + 'verified', + ]; +} diff --git a/backend/src/data/users.ts b/backend/src/data/users.ts new file mode 100644 index 00000000..6874522c --- /dev/null +++ b/backend/src/data/users.ts @@ -0,0 +1,180 @@ +import DBWrapper from './db-wrapper'; +import jwt from 'jsonwebtoken'; +import { SelfUserDocument, User, UserDocument } from './models/user'; +import { generateSnowflake } from './snowflake-entity'; +import { readdirSync } from 'fs'; +import { resolve } from 'path'; +import { Lean, UserTypes } from './types/entity-types'; +import { Guild } from './models/guild'; +import { APIError } from '../api/modules/api-error'; +import Deps from '../utils/deps'; +import Guilds from './guilds'; +import { Channel } from './models/channel'; + +export default class Users extends DBWrapper { + private avatarNames: string[] = []; + private systemUser: UserDocument; + + constructor(private guilds = Deps.get(Guilds)) { + super(); + + this.avatarNames = readdirSync(resolve('assets/avatars')) + .filter(n => n.startsWith('avatar')); + } + + public async get(id: string | undefined): Promise { + const user = await User.findById(id); + if (!user) + throw new APIError(404, 'User Not Found'); + + return this.secure(user); + } + + // TODO: test that this is fully secure + public secure(user: UserDocument): UserDocument { + delete user['email']; + delete user['verified']; + delete user['ignored']; + delete user['lastReadMessages']; + return user; + } + + public async getSelf(id: string | undefined, populateGuilds = true): Promise { + const user = await this.get(id) as SelfUserDocument; + if (populateGuilds) + user.guilds = (await this.populateGuilds(user)).guilds as Lean.Guild[]; + + return user; + } + + private async populateGuilds(user: UserDocument) { + const guilds: Lean.Guild[] = []; + for (const id of user.guilds) { + const isDuplicate = guilds.some(g => g.id === id); + if (isDuplicate) continue; + + try { + const guild = await this.guilds.get(id as string, true); + guilds.push(new Guild(guild).toJSON()); + } catch {} + } + user.guilds = guilds as any; + return user; + } + + public async getByUsername(username: string): Promise { + const user = await User.findOne({ username }) as SelfUserDocument; + if (!user) + throw new APIError(404, 'User Not Found'); + return user; + } + public async getByEmail(email: string): Promise { + const user = await User.findOne({ email }) as SelfUserDocument; + if (!user) + throw new APIError(404, 'User Not Found'); + return user; + } + + public async getKnown(userId: string) { + const user = await this.getSelf(userId); + + return await User.find({ + _id: await this.getKnownIds(user) as any, + }) as UserDocument[]; + } + + public async getRoomIds(user: UserTypes.Self) { + const dmUsers = await Channel.find({ memberIds: user.id }); + const dmUserIds = dmUsers.flatMap(u => u.memberIds); + + return Array.from(new Set([ + user.id, + this.systemUser?.id, + ...dmUserIds, + ...user.friendRequestIds, + ...user.friendIds, + ])); + } + + public async getKnownIds(user: UserTypes.Self) { + const incomingUsers = await User.find({ + friendIds: user.id, + friendRequestIds: user.id, + }); + const incomingUserIds = incomingUsers.map(u => u.id); + + const guildUserIds = user.guilds + .flatMap(g => g.members.map(g => g.userId)); + + const dmUsers = await Channel.find({ memberIds: user.id }); + const dmUserIds = dmUsers.flatMap(u => u.memberIds); + + return Array.from(new Set([ + user.id, + this.systemUser?.id, + ...dmUserIds, + ...guildUserIds, + ...incomingUserIds, + ...user.friendRequestIds, + ...user.friendIds, + ])); + } + + public async getDMChannels(userId: string) { + return await Channel.find({ memberIds: userId }); + } + + public async updateSystemUser() { + const username = '2PG'; + return this.systemUser = await User.findOne({ username }) + ?? await User.create({ + _id: generateSnowflake(), + avatarURL: `${process.env.API_URL ?? 'http://localhost:3000'}/avatars/bot.png`, + friendRequestIds: [], + badges: [], + bot: true, + status: 'ONLINE', + username, + friendIds: [], + guilds: [], + }); + } + + public createToken(userId: string, expire = true) { + return jwt.sign( + { _id: userId }, + 'secret', + (expire) ? { expiresIn: '7d' } : {} + ); + } + + public idFromAuth(auth: string | undefined): string { + const token = auth?.slice('Bearer '.length); + return this.verifyToken(token); + } + public verifyToken(token: string | undefined): string { + const key = jwt.verify(token as string, 'secret') as UserToken; + return key?._id; + } + + public create(username: string, password: string, bot = false): Promise { + const randomAvatar = this.getRandomAvatar(); + return (User as any).register({ + _id: generateSnowflake(), + username, + avatarURL: `${process.env.API_URL ?? 'http://localhost:3000'}/avatars/${randomAvatar}`, + badges: [], + bot, + email: `${generateSnowflake()}@avoid-mongodb-error.com`, // FIXME + friends: [], + status: 'ONLINE', + }, password); + } + + private getRandomAvatar() { + const randomIndex = Math.floor(Math.random() * this.avatarNames.length); + return this.avatarNames[randomIndex]; + } +} + +interface UserToken { _id: string }; diff --git a/backend/src/rest/apply-middleware.ts b/backend/src/rest/apply-middleware.ts deleted file mode 100644 index debebdec..00000000 --- a/backend/src/rest/apply-middleware.ts +++ /dev/null @@ -1,19 +0,0 @@ -import cors from 'cors'; -import passport from 'passport'; -import { User } from '../data/models/user'; -import { Strategy as LocalStrategy } from 'passport-local'; -import { Express } from 'express-serve-static-core'; -import bodyParser from 'body-parser'; - -export default (app: Express) => { - app.use(cors()); - app.use(bodyParser.json()); - app.use(passport.initialize(), passport.session()); - - passport.use(new LocalStrategy( - { usernameField: 'email' }, - (User as any).authenticate(), - )); - passport.serializeUser((User as any).serializeUser()); - passport.deserializeUser((User as any).deserializeUser()); -}; \ No newline at end of file diff --git a/backend/src/rest/apply-routes.ts b/backend/src/rest/apply-routes.ts deleted file mode 100644 index fdc4ef5c..00000000 --- a/backend/src/rest/apply-routes.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { Express } from 'express-serve-static-core'; -import express from 'express'; -import { Guild } from '../data/models/guild'; -import { Message } from '../data/models/message'; -import { router as authRoutes } from './routes/auth-routes'; -import path from 'path'; -import { loggedIn, updateUser } from './middleware'; -import createError from 'http-errors'; - -export default (app: Express) => { - const prefix = process.env.API_PREFIX; - - app.get(`${prefix}/channels/:channelId/messages`, async (req, res, next) => { - // validate has access to the channel - const userInGuild = await Guild.findOne({ channels: req.params.channelId as any }); - if (!userInGuild) - return next(createError(401, 'Insufficient access')); - - const messages = await Message.find({ channelId: req.params.channelId }); - res.json(messages); - }); - - /* user.guilds: - + can be populated easily to get user guilds - - extra baggage - - confusing to store guilds on user - - GET .../guilds - + guilds are separate to user - + http is faster than ws for larger objects - - an extra http call needed to fetch items - - = guild reordering can still be done either way - */ - app.get(`${prefix}/guilds`, loggedIn, updateUser, async (req, res) => { - const user: Entity.User = res.locals.user; - const guilds = await Guild - .find({ _id: user.guildIds }) - .populate({ path: 'channels' }) - .populate({ path: 'invites' }) - .populate({ path: 'members' }) - // .populate({ path: 'roles' }) - .exec(); - - res.json(guilds); - }); - - // v7: guild members - app.get(`${prefix}/users`, loggedIn, updateUser, async (req, res) => { - const user: Entity.User = res.locals.user; - const guilds = await Guild - .find({ _id: user.guildIds }) - .populate({ path: 'members' }); - - const members = guilds - .flatMap(g => g.members) - .map((u: any) => { - u.email = undefined; - u.locked = undefined; - u.guildIds = undefined; - return u; - }); - - res.json([ - ...new Set(members.filter(u => u.id)) - ]); - }); - - app.use(`${prefix}/auth`, authRoutes); - app.all(`${prefix}/*`, (req, res) => res - .status(404) - .json({ message: 'Not Found' })); - - app.use((err, req, res, next) => res.json(err)); - - // no prefix -> does not change with api versions - // not part of api, but cdn - const assetPath = path.resolve(`${__dirname}/../../assets`); - app.use(`/assets`, express.static(assetPath)); - app.use(`/assets/*`, (req, res) => res.sendFile(`${assetPath}/avatars/unknown.png`)); - - const buildPath = path.resolve(`${__dirname}/../../../frontend/build`); - app.use(express.static(buildPath)); - app.all('*', (req, res) => res.sendFile(`${buildPath}/index.html`)); -} \ No newline at end of file diff --git a/backend/src/rest/middleware.ts b/backend/src/rest/middleware.ts deleted file mode 100644 index aecfc2c5..00000000 --- a/backend/src/rest/middleware.ts +++ /dev/null @@ -1,25 +0,0 @@ -import jwt from 'jsonwebtoken'; -import createError from 'http-errors'; -import { User } from '../data/models/user'; - -export const loggedIn = (req, res, next) => { - const payload = jwt.verify( - req.headers.authorization, - process.env.JWT_SECRET_KEY, - ) as Auth.Payload; - - if (!payload.userId) - throw next(createError(401, 'Unauthorized')); - - res.locals.userId = payload.userId; - - return next(); -}; - -export const updateUser = async (req, res, next) => { - const user = res.locals.user = await User.findById(res.locals.userId); - if (!user) - throw next(createError(404, 'Unauthorized')); - - return next(); -}; \ No newline at end of file diff --git a/backend/src/rest/routes/auth-routes.ts b/backend/src/rest/routes/auth-routes.ts deleted file mode 100644 index 3196965d..00000000 --- a/backend/src/rest/routes/auth-routes.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Router } from 'express'; -import { authenticate } from 'passport'; -import { User, UserDocument } from '../../data/models/user'; -import createError from 'http-errors'; -import jwt from 'jsonwebtoken'; - -export const router = Router(); - -router.post('/login', authenticate('local'), (req, res, next) => { - const user = req.user as UserDocument; - const userId = user.id; - const token = jwt.sign({ userId }, process.env.JWT_SECRET_KEY); - - if (user.locked) - next(createError(401, 'This account is locked')); - - res.json(token); -}); - -router.post('/register', async (req, res, next) => { - const username = req.body.username; - const usernameCount = await User.countDocuments({ username }); - - const maxDiscriminator = 9999; - if (usernameCount >= maxDiscriminator) - next(createError('Username is unavailable')); - - try { - const user = await (User as any).register({ - username, - email: req.body.email, - discriminator: usernameCount + 1, - }, req.body.password); - - const token = jwt.sign( - { userId: user.id }, - process.env.JWT_SECRET_KEY, - ); - res.json(token); - } catch (error) { - res.status(400).json({ message: error.message }); - } -}); diff --git a/backend/src/rest/server.ts b/backend/src/rest/server.ts deleted file mode 100644 index 408131ed..00000000 --- a/backend/src/rest/server.ts +++ /dev/null @@ -1,15 +0,0 @@ -import express from 'express'; -import applyMiddleware from './apply-middleware'; -import applyRoutes from './apply-routes'; - -export class REST { - public readonly app = express(); - - public listen() { - applyMiddleware(this.app); - applyRoutes(this.app); - - const port = process.env.API_PORT; - return this.app.listen(port, () => console.log(`Connected to server on port ${port}`)); - } -} diff --git a/backend/src/system/bot.ts b/backend/src/system/bot.ts new file mode 100644 index 00000000..585d4335 --- /dev/null +++ b/backend/src/system/bot.ts @@ -0,0 +1,46 @@ +import { Channel } from '../data/models/channel'; +import { UserDocument } from '../data/models/user'; +import { generateSnowflake } from '../data/snowflake-entity'; +import Log from '../utils/log'; +import Deps from '../utils/deps'; +import Users from '../data/users'; +import { WSService } from './ws-service'; +import { Lean } from '../data/types/entity-types'; +import Channels from '../data/channels'; + +export class SystemBot { + private _self: UserDocument; + get self() { return this._self; } + + constructor( + private channels = Deps.get(Channels), + private users = Deps.get(Users), + private ws = Deps.get(WSService), + ) {} + + public async init() { + if (this.self) return; + + this._self = await this.users.updateSystemUser(); + await this.readyUp(); + } + + private async readyUp() { + const key = this.users.createToken(this.self?.id); + const { user } = await this.ws.emitAsync('READY', { key }); + this._self = user as any; + + Log.info('Initialized bot', 'bot'); + } + + public message(channel: Lean.Channel, content: string) { + return this.ws.emitAsync('MESSAGE_CREATE', { + channelId: channel.id, + partialMessage: { content }, + }); + } + + public async getDMChannel(user: UserDocument) { + return this.channels.getDMByMembers(this.self.id, user.id); + } +} diff --git a/backend/src/system/ws-service.ts b/backend/src/system/ws-service.ts new file mode 100644 index 00000000..73debcc8 --- /dev/null +++ b/backend/src/system/ws-service.ts @@ -0,0 +1,29 @@ +import { WSEventArgs, WSEventAsyncArgs, WSEventParams } from '../data/types/ws-types'; +import io from 'socket.io-client'; + +export class WSService { + public readonly socket = io.connect(`${process.env.ROOT_ENDPOINT}`); + + public on(name: K, callback: WSEventArgs[K]): this { + this.socket.on(name, callback); + + return this; + } + + public emit(name: K, params: WSEventParams[K]) { + this.socket.emit(name, params); + } + + public emitAsync

(name: P, params: WSEventParams[P]): Promise { + return new Promise((resolve, reject) => { + this.on('message', (message: string) => { + if (!message.includes('Server error')) return; + + return reject(message); + }); + + this.on(name as keyof WSEventArgs, (args) => resolve(args)); + this.emit(name, params); + }); + } +} diff --git a/backend/src/types b/backend/src/types deleted file mode 120000 index 8788aa28..00000000 --- a/backend/src/types +++ /dev/null @@ -1 +0,0 @@ -../../types \ No newline at end of file diff --git a/backend/src/utils/deps.ts b/backend/src/utils/deps.ts index 27b19374..4fcc69b9 100644 --- a/backend/src/utils/deps.ts +++ b/backend/src/utils/deps.ts @@ -1,14 +1,14 @@ -export class Deps { - private static deps = new Map(); - - public static get(type: any): T { - return this.deps.get(type) - ?? this.add(type, new type()); - } - - public static add(type: any, instance: T): T { - return this.deps - .set(type, instance) - .get(type); - } +export default class Deps { + private static deps = new Map(); + + public static get(type: any): T { + return this.deps.get(type) + ?? this.add(type, new type()); + } + + public static add(type: any, instance: T): T { + return this.deps + .set(type, instance) + .get(type); + } } \ No newline at end of file diff --git a/backend/src/utils/generate-invite.ts b/backend/src/utils/generate-invite.ts deleted file mode 100644 index 028175b6..00000000 --- a/backend/src/utils/generate-invite.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { v4 } from 'uuid'; - -export default function (length = 6) { - return v4() - .replace(/-/g, '') - .slice(0, length); -} \ No newline at end of file diff --git a/backend/src/utils/log.ts b/backend/src/utils/log.ts new file mode 100644 index 00000000..782d2046 --- /dev/null +++ b/backend/src/utils/log.ts @@ -0,0 +1,25 @@ +import 'colors'; + +export default class Log { + static getSource(src?: string) { + return src?.toUpperCase() || 'OTHER'; + } + static info(message?: any, src?: string) { + console.log(`[${ + this.toHHMMSS(new Date()).cyan + }] INFO [${this.getSource(src).blue}] ${message?.toString().blue}`) + } + static error(err?: any, src?: string) { + const message: string = err?.message || err || 'Unknown error'; + console.error(`[${ + this.toHHMMSS(new Date()).cyan + }] ERROR [${this.getSource(src).blue}] ${message.red}`) + } + + private static toHHMMSS(time: Date) { + let hours = time.getHours().toString().padStart(2, '0'); + let minutes = time.getMinutes().toString().padStart(2, '0'); + let seconds = time.getSeconds().toString().padStart(2, '0'); + return `${hours}:${minutes}:${seconds}`; + } +} diff --git a/backend/src/utils/snowflake.ts b/backend/src/utils/snowflake.ts deleted file mode 100644 index 581f27fc..00000000 --- a/backend/src/utils/snowflake.ts +++ /dev/null @@ -1,23 +0,0 @@ -import cluster from 'cluster'; - -let inc = 0; -let lastSnowflake: string; -const dcloneEpoch = 1609459200000; - -export function generateSnowflake() { - const pad = (num: number, by: number) => - num.toString(2).padStart(by, '0'); - - const msSince = pad(new Date().getTime() - dcloneEpoch, 42); - const pid = pad(process.pid, 5).slice(0, 5); - const wid = pad(cluster.worker?.id ?? 0, 5); - const getInc = (add: number) => pad(inc + add, 12); - - let snowflake = `0b${msSince}${wid}${pid}${getInc(0)}`; - (snowflake === lastSnowflake) - ? snowflake = `0b${msSince}${wid}${pid}${getInc(1)}` - : inc = 0; - - lastSnowflake = snowflake; - return BigInt(snowflake).toString(); -} \ No newline at end of file diff --git a/backend/src/utils/utils.ts b/backend/src/utils/utils.ts new file mode 100644 index 00000000..68c948d9 --- /dev/null +++ b/backend/src/utils/utils.ts @@ -0,0 +1,52 @@ +import { hacker } from 'faker'; +import { Document } from 'mongoose'; +import { snowflakeToDate } from '../data/snowflake-entity'; +import { patterns } from '../data/types/entity-types'; + +export function getNameAcronym(name: string) { + return name + .split(' ') + .slice(0, 3) + .map(str => str[0]) + .join(''); +} + +export const validators = { + minLength: (min: number) => (val: string | any[]) => val.length >= min, + maxLength: (max: number) => (val: string | any[]) => val.length <= max, + optionalSnowflake: (val: string) => !val || patterns.snowflake.test(val), +}; + +export function createdAtToDate(this: Document) { + return snowflakeToDate(this.id); +} + +export function generateUsername() { + return `${hacker + .adjective() + .replace(/ /, '')}-${hacker + .noun() + .replace(/ /, '')}` +} + +export function useId(this: Document) { + const obj = this.toObject(); + + this.id = this._id; + delete this._id; + + return obj; +} + +export function checkForDuplicates(array: any[]) { + return new Set(array).size !== array.length +} + +export const array = { + ascending: (a, b) => (a > b) ? 1 : -1, +}; + +export function randomFrom(...arr: any[]) { + const index = Math.floor(Math.random() * arr.length); + return arr[index]; +} diff --git a/backend/src/ws/events/channel-create.ts b/backend/src/ws/events/channel-create.ts deleted file mode 100644 index 35f87810..00000000 --- a/backend/src/ws/events/channel-create.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { WSEvent } from './ws-event'; -import { Socket } from 'socket.io'; -import { WS } from '../websocket'; -import { Channel } from '../../data/models/channel'; -import { Guild } from '../../data/models/guild'; - -export default class implements WSEvent<'CHANNEL_CREATE'> { - public on = 'CHANNEL_CREATE' as const; - - public async invoke({ io, sessions }: WS, client: Socket, params: API.WSPayload.ChannelCreate) { - const userId = sessions.get(client.id); - const guild = await Guild.findById(params.guildId); - if (!guild) - throw new TypeError('Guild not found'); - - if (guild.ownerId !== userId) - throw new TypeError('Only the guild owner can do this'); - - // actually create channel - const channel = await Channel.create({ - guildId: params.guildId, - name: params.name, - }); - - // add channel to guild - guild.channels.push(channel); - await guild.save(); - - // join all sockets to channel, when created - const connectedClients = await io.in(guild.id).fetchSockets(); - for (const client of connectedClients) - client.join(channel.id); - - io.to(guild.id) - .emit('CHANNEL_CREATE', { channel, creatorId: userId } as API.WSResponse.ChannelCreate); - } -} \ No newline at end of file diff --git a/backend/src/ws/events/channel-delete.ts b/backend/src/ws/events/channel-delete.ts deleted file mode 100644 index 9f8979c1..00000000 --- a/backend/src/ws/events/channel-delete.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { WSEvent } from './ws-event'; -import { Socket } from 'socket.io'; -import { WS } from '../websocket'; -import { Channel } from '../../data/models/channel'; -import { Guild } from '../../data/models/guild'; - -export default class implements WSEvent<'CHANNEL_DELETE'> { - public on = 'CHANNEL_DELETE' as const; - - public async invoke({ io, sessions }: WS, client: Socket, { channelId, guildId }: API.WSPayload.ChannelDelete) { - const userId = sessions.get(client.id); - const guild = await Guild.findById(guildId); - if (!guild) - throw new TypeError('Guild not found'); - - if (guild.ownerId !== userId) - throw new TypeError('Only the guild owner can do this'); - - await Channel.deleteOne({ _id: channelId }); - - io.to(guild.id) - .emit('CHANNEL_DELETE', { channelId, guildId } as API.WSResponse.ChannelDelete); - } -} \ No newline at end of file diff --git a/backend/src/ws/events/disconnect.ts b/backend/src/ws/events/disconnect.ts deleted file mode 100644 index c7b69068..00000000 --- a/backend/src/ws/events/disconnect.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Socket } from 'socket.io'; -import { WS } from '../websocket'; -import { WSEvent } from './ws-event'; - -export default class Disconnect implements WSEvent<'disconnect'> { - public on = 'disconnect' as const; - - public async invoke(ws: WS, client: Socket) { - ws.sessions.delete(client.id); - } -} \ No newline at end of file diff --git a/backend/src/ws/events/guild-create.ts b/backend/src/ws/events/guild-create.ts deleted file mode 100644 index 7106f880..00000000 --- a/backend/src/ws/events/guild-create.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { WSEvent } from './ws-event'; -import { Socket } from 'socket.io'; -import { WS } from '../websocket'; -import { Guild } from '../../data/models/guild'; -import { Channel } from '../../data/models/channel'; -import { User } from '../../data/models/user'; - -export default class implements WSEvent<'GUILD_CREATE'> { - public on = 'GUILD_CREATE' as const; - - public async invoke({ sessions }: WS, client: Socket, params: API.WSPayload.GuildCreate) { - const ownerId = sessions.get(client.id); - const guild = new Guild({ name: params.name, ownerId }); - - // create guild - const systemChannel = await Channel.create({ - name: 'general', - guildId: guild.id, - }); - const selfMember = (await User.findById(ownerId))!; - selfMember.guildIds.push(guild.id); - await selfMember.save(); - - // add default channels and members - guild.channels.push(systemChannel); - guild.members.push(selfMember); - // guild.roles.push(everyoneRole); - await guild.save(); - - // join guild rooms - await client.join(guild.id); - for (const channel of guild.channels) - await client.join(channel.id); - - client.emit('GUILD_CREATE', { guild } as API.WSResponse.GuildCreate); - } -} \ No newline at end of file diff --git a/backend/src/ws/events/guild-delete.ts b/backend/src/ws/events/guild-delete.ts deleted file mode 100644 index 2e48c2eb..00000000 --- a/backend/src/ws/events/guild-delete.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { WSEvent } from './ws-event'; -import { Socket } from 'socket.io'; -import { WS } from '../websocket'; -import { Guild } from '../../data/models/guild'; -import { User } from '../../data/models/user'; - -export default class implements WSEvent<'GUILD_DELETE'> { - public on = 'GUILD_DELETE' as const; - - public async invoke(ws: WS, client: Socket, { guildId }: API.WSPayload.GuildDelete) { - const userId = ws.sessions.get(client.id); - const guild = await Guild.findById(guildId); - if (!guild) - throw new TypeError('Guild not found'); - - if (guild.ownerId !== userId) - throw new TypeError('Only the guild owner can do this'); - - await guild.deleteOne(); - await this.updateMembers(ws, guild); - - ws.io - .to(guild.id) - .emit('GUILD_DELETE', { guildId } as API.WSResponse.GuildDelete); - } - - private async updateMembers({ sessions, io }: WS, guild: Entity.Guild) { - const clientIds = io.sockets.adapter.rooms.get(guild.id)!; - - // tell connected guild members that they're no longer in that guild - for (const clientId of clientIds) { - const client = io.sockets.sockets.get(clientId)!; - const userId = sessions.get(clientId); - const { guildIds } = (await User.findById(userId))!; - - const index = guildIds.indexOf(guild.id); - guildIds.splice(index, 1); - await User.updateOne({ _id: userId }, { guildIds }); - - client.emit('USER_UPDATE', { payload: { guildIds }, userId } as API.WSResponse.UserUpdate); - } - } -} \ No newline at end of file diff --git a/backend/src/ws/events/guild-member-add.ts b/backend/src/ws/events/guild-member-add.ts deleted file mode 100644 index 7ba1e18f..00000000 --- a/backend/src/ws/events/guild-member-add.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { Socket } from 'socket.io'; -import { Guild } from '../../data/models/guild'; -import { Invite } from '../../data/models/invite'; -import { User } from '../../data/models/user'; -import { WS } from '../websocket'; -import { WSEvent } from './ws-event'; - -export default class implements WSEvent<'GUILD_MEMBER_ADD'> { - public on = 'GUILD_MEMBER_ADD' as const; - - // >v6: make sure user not banned - // >v6: validate invite options - public async invoke({ io, sessions }: WS, client: Socket, { inviteCode }: API.WSPayload.GuildMemberAdd) { - const invite = await Invite.findById(inviteCode); - if (!invite) - throw new TypeError('Invite not found'); - - const userId = sessions.get(client.id); - const guildId = invite.guildId; - - // user cannot be undefined if authenticated, and validated to exist in ready - const user = (await User.findById(userId))!; - // prevent user from joining own guild - if (user.guildIds.includes(guildId)) - throw new TypeError('You are already in this guild'); - - user.guildIds.push(guildId); - await user.save(); - - // add use to invites - invite.uses = invite.uses++ || 0; - await invite.save(); - - // actually add guild member - const guild = await Guild.findById(guildId); - if (!guild) - throw new TypeError('Guild not found'); - - guild.members.push(user); - await guild.save(); - - io.to(invite.guildId) - .emit('GUILD_MEMBER_ADD', { guildId, member: user } as API.WSResponse.GuildMemberAdd); - - await guild - .populate({ path: 'channels' }) - .populate({ path: 'invites' }) - .populate({ path: 'members' }) - // .populate({ path: 'roles' }) - .execPopulate(); - - // join guild rooms - await client.join(guild.id); - for (const channel of guild.channels) - await client.join(channel.id); - - client.emit('GUILD_CREATE', { guild } as API.WSResponse.GuildCreate); - } -} \ No newline at end of file diff --git a/backend/src/ws/events/guild-member-remove.ts b/backend/src/ws/events/guild-member-remove.ts deleted file mode 100644 index a6d0adc1..00000000 --- a/backend/src/ws/events/guild-member-remove.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Socket } from 'socket.io'; -import { Guild } from '../../data/models/guild'; -import { Invite } from '../../data/models/invite'; -import { User } from '../../data/models/user'; -import { WS } from '../websocket'; -import { WSEvent } from './ws-event'; - -export default class implements WSEvent<'GUILD_MEMBER_REMOVE'> { - public on = 'GUILD_MEMBER_REMOVE' as const; - - public async invoke({ io, sessions }: WS, client: Socket, { guildId, userId }: API.WSPayload.GuildMemberRemove) { - // validate user is not owner - const guild = await Guild.findById(guildId); - if (!guild) - throw new TypeError('Guild not found'); - - const selfUserId = sessions.get(client.id); - const isOwner = guild.ownerId === selfUserId; - if (isOwner && userId === selfUserId) - throw new TypeError('You cannot leave a server you own'); - else if (!isOwner) - throw new TypeError('You cannot manage other members'); - - // remove guildId from user.guilds - // FIXME: - const user = await User.findById(userId); - user!.guildIds = user!.guildIds.filter(id => id !== guildId); - await user!.save(); - - // actually remove member from guild - guild.members = guild.members.filter(id => (id as any) !== userId); - await guild.save(); - - // if self user left, emit GUILD_DELETE - if (selfUserId === userId) - io.to(guildId) - .emit('GUILD_DELETE', { guildId } as API.WSResponse.GuildDelete); - - io.to(guildId) - .emit('GUILD_MEMBER_REMOVE', { guildId, userId } as API.WSResponse.GuildMemberRemove); - } -} \ No newline at end of file diff --git a/backend/src/ws/events/guild-update.ts b/backend/src/ws/events/guild-update.ts deleted file mode 100644 index 529250ac..00000000 --- a/backend/src/ws/events/guild-update.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { WSEvent } from './ws-event'; -import { Socket } from 'socket.io'; -import { WS } from '../websocket'; -import { Guild } from '../../data/models/guild'; - -export default class implements WSEvent<'GUILD_UPDATE'> { - public on = 'GUILD_UPDATE' as const; - - public async invoke({ io, sessions }: WS, client: Socket, { guildId, payload }: API.WSPayload.GuildUpdate) { - const userId = sessions.get(client.id); - const guild = await Guild.findById(guildId); - if (!guild) - throw new TypeError('Guild not found'); - - if (guild.ownerId !== userId) - throw new TypeError('Only the guild owner can do this'); - - const partialGuild = { - name: payload.name, - iconURL: payload.iconURL, - }; - await guild.updateOne(partialGuild); - - io.to(guild.id) - .emit('GUILD_UPDATE', { guildId, payload } as API.WSResponse.GuildUpdate); - } -} \ No newline at end of file diff --git a/backend/src/ws/events/invite-create.ts b/backend/src/ws/events/invite-create.ts deleted file mode 100644 index f8af0e7c..00000000 --- a/backend/src/ws/events/invite-create.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Socket } from 'socket.io'; -import { Guild } from '../../data/models/guild'; -import { Invite } from '../../data/models/invite'; -import { WS } from '../websocket'; -import { WSEvent } from './ws-event'; - -export default class implements WSEvent<'INVITE_CREATE'> { - public on = 'INVITE_CREATE' as const; - - public async invoke({ io, sessions }: WS, client: Socket, { guildId }: API.WSPayload.InviteCreate) { - const userId = sessions.get(client.id); - - const guild = await Guild.findById(guildId); - if (!guild) - throw new TypeError('Guild not found'); - - const memberIds = guild?.members as any as string[] | undefined; - const inGuild = memberIds?.some(id => id === userId); - if (!inGuild) - throw new TypeError('Member not in guild'); - - const invite = await Invite.create({ - creatorId: userId, - guildId, - }); - - io.to(guildId) - .emit('INVITE_CREATE', { invite } as API.WSResponse.InviteCreate); - } -} \ No newline at end of file diff --git a/backend/src/ws/events/message-create.ts b/backend/src/ws/events/message-create.ts deleted file mode 100644 index 9d37acba..00000000 --- a/backend/src/ws/events/message-create.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Socket } from 'socket.io'; -import { Message } from '../../data/models/message'; -import { WS } from '../websocket'; -import { WSEvent } from './ws-event'; - -export default class implements WSEvent<'MESSAGE_CREATE'> { - public on = 'MESSAGE_CREATE' as const; - - public async invoke({ io, sessions }: WS, client: Socket, params: API.WSPayload.MessageCreate) { - // >v6 -> ensure author has access to channel - const message = await Message.create({ - content: params.content, - channelId: params.channelId, - authorId: sessions.get(client.id) as string, - }); - - io.to(params.channelId) - .emit('MESSAGE_CREATE', { message } as API.WSResponse.MessageCreate); - } -} \ No newline at end of file diff --git a/backend/src/ws/events/message-delete.ts b/backend/src/ws/events/message-delete.ts deleted file mode 100644 index a2e71966..00000000 --- a/backend/src/ws/events/message-delete.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Socket } from 'socket.io'; -import { WS } from '../websocket'; -import { Message } from '../../data/models/message'; -import { WSEvent } from './ws-event'; -import { Guild } from '../../data/models/guild'; - -export default class MessageDelete implements WSEvent<'MESSAGE_DELETE'> { - public on = 'MESSAGE_DELETE' as const; - - public async invoke({ io, sessions }: WS, client: Socket, params: API.WSPayload.MessageDelete) { - const message = await Message.findById(params.messageId); - if (!message) - throw new TypeError('Message not found'); - - // validate guild owner, or message author - const userId = sessions.get(client.id); - const guild = await Guild.findOne({ channels: message.channelId as any }); - if (!guild) - throw new TypeError('Guild not found'); - - const canManage = guild.ownerId === userId - || message.authorId === userId; - if (!canManage) - throw new TypeError('You cannot manage this message'); - - await message.deleteOne(); - - io.to(message.channelId) - .emit('MESSAGE_DELETE', params as API.WSResponse.MessageDelete); - } -} \ No newline at end of file diff --git a/backend/src/ws/events/message-update.ts b/backend/src/ws/events/message-update.ts deleted file mode 100644 index df843237..00000000 --- a/backend/src/ws/events/message-update.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Socket } from 'socket.io'; -import { WS } from '../websocket'; -import { Message } from '../../data/models/message'; -import { WSEvent } from './ws-event'; -import { Guild } from '../../data/models/guild'; - -export default class MessageDelete implements WSEvent<'MESSAGE_UPDATE'> { - public on = 'MESSAGE_UPDATE' as const; - - public async invoke({ io, sessions }: WS, client: Socket, { messageId, payload }: API.WSPayload.MessageUpdate) { - const userId = sessions.get(client.id); - const message = await Message.findById(messageId); - if (!message) - throw new TypeError('Message not found'); - - // validate is message author, or guild owner - const guild = await Guild.findOne({ channels: message.channelId as any }); - if (!guild) - throw new TypeError('Guild not found'); - - const canManage = guild.ownerId === userId - || message.authorId === userId; - if (!canManage) - throw new TypeError('You cannot manage this message'); - - payload = { - content: payload.content, - updatedAt: new Date(), - }; - await message.updateOne(payload); - - io.to(message.channelId) - .emit('MESSAGE_UPDATE', { payload, messageId } as API.WSResponse.MessageUpdate); - } -} \ No newline at end of file diff --git a/backend/src/ws/events/ready.ts b/backend/src/ws/events/ready.ts deleted file mode 100644 index 3a2e777c..00000000 --- a/backend/src/ws/events/ready.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Socket } from 'socket.io'; -import { User } from '../../data/models/user'; -import { WS } from '../websocket'; -import { WSEvent } from './ws-event'; -import jwt from 'jsonwebtoken'; -import { Guild } from '../../data/models/guild'; - -export default class Ready implements WSEvent<'READY'> { - public on = 'READY' as const; - - public async invoke(ws: WS, client: Socket, { token }) { - const payload = jwt.verify(token, process.env.JWT_SECRET_KEY) as Auth.Payload; - if (!payload.userId) - throw new TypeError('Invalid token'); - - const user = await User.findById(payload.userId); - if (!user) - throw new TypeError('User not found'); - - ws.sessions.set(client.id, user.id); - - const guilds = await Guild.find({ _id: user.guildIds }); - const channelIds = guilds.flatMap(g => g.channels) as any; - - await client.join(user.guildIds); - await client.join(channelIds); - - client.emit('READY', { user } as API.WSResponse.Ready); - } -} \ No newline at end of file diff --git a/backend/src/ws/events/typing-start.ts b/backend/src/ws/events/typing-start.ts deleted file mode 100644 index c7eedad9..00000000 --- a/backend/src/ws/events/typing-start.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Socket } from 'socket.io'; -import { WS } from '../websocket'; -import { WSEvent } from './ws-event'; - -export default class implements WSEvent<'TYPING_START'> { - public on = 'TYPING_START' as const; - - public async invoke({ io, sessions }: WS, client: Socket, { channelId }: API.WSPayload.TypingStart) { - io.to(channelId) - .except(client.id) - .emit('TYPING_START', { - channelId, - userId: sessions.get(client.id), - } as API.WSResponse.TypingStart); - } -} \ No newline at end of file diff --git a/backend/src/ws/events/user-delete.ts b/backend/src/ws/events/user-delete.ts deleted file mode 100644 index 3c716fc3..00000000 --- a/backend/src/ws/events/user-delete.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { WSEvent } from './ws-event'; -import { Socket } from 'socket.io'; -import { WS } from '../websocket'; -import { User } from '../../data/models/user'; -import generateInvite from '../../utils/generate-invite'; - -export default class implements WSEvent<'USER_DELETE'> { - public on = 'USER_DELETE' as const; - - public async invoke({ io, sessions }: WS, client: Socket) { - const userId = sessions.get(client.id); - - const user = (await User.findById({ _id: userId }))!; - const payload = { - discriminator: 0, - username: `Deleted User ${generateInvite(6)}`, - }; - - user.locked = true; - await user.updateOne(payload); - - client.emit('USER_DELETE'); - - io.to(user.guildIds) - .emit('USER_UPDATE', { userId, payload } as API.WSResponse.UserUpdate); - - client.disconnect(); - } -} \ No newline at end of file diff --git a/backend/src/ws/events/user-update.ts b/backend/src/ws/events/user-update.ts deleted file mode 100644 index 28d390e9..00000000 --- a/backend/src/ws/events/user-update.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { WSEvent } from './ws-event'; -import { Socket } from 'socket.io'; -import { WS } from '../websocket'; -import { User } from '../../data/models/user'; - -export default class implements WSEvent<'USER_UPDATE'> { - public on = 'USER_UPDATE' as const; - - public async invoke({ io, sessions }: WS, client: Socket, { payload }: API.WSPayload.UserUpdate) { - const userId = sessions.get(client.id); - const user = (await User.findById(userId))!; - - // validate username is available - const discriminator = await User.countDocuments({ username: payload.username }); - if (discriminator >= 9999) - throw new TypeError('Username is not available'); - - const updated = { - avatarURL: payload.avatarURL, - discriminator: discriminator + 1, - email: payload.email, - username: payload.username, - }; - await user.updateOne(updated, { runValidators: true }); - - // discrim is also updated so we want to return it to client - client.emit('USER_UPDATE', { userId, payload: updated } as API.WSResponse.UserUpdate); - } -} \ No newline at end of file diff --git a/backend/src/ws/events/ws-event.ts b/backend/src/ws/events/ws-event.ts deleted file mode 100644 index 9c935216..00000000 --- a/backend/src/ws/events/ws-event.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Socket } from 'socket.io'; -import { WS } from '../websocket'; - -export interface WSEvent { - on: K; - - invoke: (ws: WS, client: Socket, params: WSEventParams[K]) => Promise; -} \ No newline at end of file diff --git a/backend/src/ws/session-manager.ts b/backend/src/ws/session-manager.ts deleted file mode 100644 index ad858884..00000000 --- a/backend/src/ws/session-manager.ts +++ /dev/null @@ -1,9 +0,0 @@ -export default class SessionManager extends Map { - get(clientId: string) { - const userId = super.get(clientId); - if (!userId) - throw new TypeError('User Not Authenticated'); - - return userId; - } -} \ No newline at end of file diff --git a/backend/src/ws/websocket.ts b/backend/src/ws/websocket.ts deleted file mode 100644 index 8ad28b21..00000000 --- a/backend/src/ws/websocket.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { Server } from 'socket.io'; -import { REST } from '../rest/server'; -import { Deps } from '../utils/deps'; -import { WSEvent } from './events/ws-event'; -import path from 'path'; -import fs from 'fs'; -import SessionManager from './session-manager'; - -export class WS { - public events = new Map>(); - public readonly io = new Server(); - public readonly sessions = new SessionManager(); - - constructor(rest = Deps.get(REST)) { - const app = rest.listen(); - this.io.listen(app, { - cors: { - origin: process.env.WEBSITE_URL, - methods: ['GET', 'POST'], - allowedHeaders: ['Authorization'], - credentials: true, - }, - path: '/ws', - serveClient: false, - }); - - this.loadEvents(); - this.hook(); - } - - private async loadEvents() { - const dir = path.resolve(`${__dirname}/events`); - const files = fs.readdirSync(dir); - - for (const file of files) { - const { default: Event } = await import(`./events/${file}`); - try { - const event = new Event(); - this.events.set(event.on, event); - } catch {} - } - - console.log(`Loaded ${this.events.size} handlers`, 'ws'); - } - - private hook() { - this.io.on('connection', (client) => { - for (const event of Array.from(this.events.values())) - client.on(event.on, async (data: any) => { - try { - await event.invoke.bind(event)(this, client, data); - } catch (error) { - client.emit('error', { - on: event.on, - message: error.message, - }); - } - }); - }); - } -} diff --git a/backend/test/integration/_test-template.ts b/backend/test/integration/_test-template.ts new file mode 100644 index 00000000..97aa73fb --- /dev/null +++ b/backend/test/integration/_test-template.ts @@ -0,0 +1,44 @@ +import SendFriendRequest from '../../src/api/websocket/ws-events/send-friend-request'; +import { WebSocket } from '../../src/api/websocket/websocket'; +import io from 'socket.io-client'; +import { Mock } from '../mock'; +import { UserDocument } from '../../src/data/models/user'; +import { GuildDocument } from '../../src/data/models/guild'; +import { expect } from 'chai'; + +describe(addeventnamehere, () => { + const client = io(`http://localhost:${process.env.PORT}`) as any; + let event: SendFriendRequest; + let ws: WebSocket; + + let user: UserDocument; + let guild: GuildDocument; + + beforeEach(async () => { + ({ event, ws, guild, user } = await Mock.defaultSetup(client, SendFriendRequest)); + }); + + afterEach(async () => await Mock.afterEach(ws)); + after(async () => await Mock.after(client)); + + it('fulfilled', async () => { + const result = () => event.invoke(ws, client, { + + }); + + await expect(result()).to.be.fulfilled; + }); + + it('rejected', async () => { + const result = () => event.invoke(ws, client, { + + }); + + await expect(result()).to.be.rejectedWith(); + }); + + async function makeGuildOwner() { + ws.sessions.set(client.id, guild.ownerId); + await Mock.clearRolePerms(guild); + } +}); diff --git a/backend/test/integration/routes/auth-routes.tests.ts b/backend/test/integration/routes/auth-routes.tests.ts new file mode 100644 index 00000000..15d8184d --- /dev/null +++ b/backend/test/integration/routes/auth-routes.tests.ts @@ -0,0 +1,210 @@ +import { Mock } from '../../mock/mock'; +import { EmailMock } from '../../mock/email-mock'; +import { assert, expect, spy } from 'chai'; +import Deps from '../../../src/utils/deps'; +import { API } from '../../../src/api/server'; +import request from 'supertest'; +import Users from '../../../src/data/users'; +import { UserDocument, User, SelfUserDocument } from '../../../src/data/models/user'; +import { generateUsername } from '../../../src/utils/utils'; +import { generateInviteCode } from '../../../src/data/models/invite'; +import { Email } from '../../../src/api/modules/email/email'; + +describe.skip('auth-routes', () => { + const endpoint = `/api/v1`; + + let app: Express.Application; + let users: Users; + let credentials: { username?, password?, email? }; + let user: SelfUserDocument; + let email: EmailMock; + let authorization: string; + + beforeEach(async () => { + email = Deps.add(Email, new EmailMock()); + + app = Deps.get(API).app; + users = Deps.get(Users); + + credentials = { + username: generateUsername(), + password: 'Testing@123', + }; + user = await users.create(credentials.username, credentials.password) as any; + credentials.email = user.email; + + authorization = `Bearer ${users.createToken(user.id)}`; + }); + + afterEach(async () => await Mock.cleanDB()); + + it('POST /login, invalid username and password, invalid credentials', async () => { + await request(app) + .post(`${endpoint}/login`) + .expect(400); + }); + + it('POST /login, email used is unverified', async () => { + delete credentials.username; + + await request(app) + .post(`${endpoint}/login`) + .send(credentials) + .expect(400) + .expect({ message: 'Email is unverified' }); + }); + + it('POST /login, valid username and password, unverified user, sends jwt', async () => { + delete credentials.email; + + await request(app) + .post(`${endpoint}/login`) + .send(credentials) + .expect(200) + .expect(res => assert( + typeof res.body === 'string', + 'User token should be returned.', + )); + }); + + it('POST /login, verified email, sends email', async () => { + user.verified = true; + await user.save(); + + const send = spy.on(email, 'send'); + + await request(app) + .post(`${endpoint}/login`) + .send(credentials); + + expect(send).to.have.been.called.with('verify'); + }); + + it('POST /change-password, no email provided, user not found', async () => { + await request(app) + .post(`${endpoint}/change-password`) + .send({ + newPassword: 'a', + oldPassword: 'b' + }) + .expect(400) + .expect({ + message: 'User not found' + }); + }); + + it('POST /change-password, old password incorrect, 400', async () => { + await request(app) + .post(`${endpoint}/change-password`) + .send({ + email: user.email, + newPassword: 'a', + oldPassword: 'b' + }) + .expect(400); + }); + + it('POST /change-password, old password correct, sends jwt', async () => { + await request(app) + .post(`${endpoint}/change-password`) + .send({ + email: user.email, + newPassword: credentials.password, + oldPassword: generateInviteCode(), + }) + .expect(200) + .expect(res => assert( + typeof res.body === 'string', + 'User token should be returned.', + )); + }); + + it('POST /change-password, new password equals old, 400', async () => { + await request(app) + .post(`${endpoint}/change-password`) + .send({ + email: user.email, + newPassword: credentials.password, + oldPassword: credentials.password, + }) + .expect(400) + .expect({ message: 'New password must be different' }); + }); + + it('GET /verify-code, invalid code', async () => { + await request(app) + .get(`${endpoint}/verify-code`) + .query({ code: '1234' }) + .expect(400) + .expect({ message: 'Invalid code' }); + }); + + it('GET /verify-code, valid code, returns key', async () => { + await request(app) + .post(`${endpoint}/login`) + .send(credentials); + + const code = email.emails.get([user.email])[1].code; + + await request(app) + .get(`${endpoint}/verify-code`) + .query({ code }) + .expect(200) + .expect(res => assert( + typeof res.body === 'string', + 'User token should be returned.', + )); + }); + + it('GET /send-verify-email, email not provided', async () => { + await request(app) + .get(`${endpoint}/send-verify-email`) + .set('Authorization', authorization) + .expect(400) + .expect({ message: 'Email not provided' }); + }); + + it('GET /send-verify-email, email is sent', async () => { + const send = spy.on(email, 'send'); + + await request(app) + .get(`${endpoint}/send-verify-email`) + .set('Authorization', authorization) + .query({ email: user.email }); + + expect(send).to.have.been.called.with('verify-email'); + }); + + it('GET /send-verify-email, sets user email', async () => { + const newEmail = 'adam@d-cl.one'; + + await request(app) + .get(`${endpoint}/send-verify-email`) + .set('Authorization', authorization) + .query({ email: newEmail }); + + user = await User.findById(user.id) as SelfUserDocument; + expect(user.email).to.equal(newEmail); + }); + + it('GET /verify-email, invalid code', async () => { + await request(app) + .get(`${endpoint}/verify-email`) + .expect(400) + .expect({ message: 'Invalid code' }); + }); + + it('GET /verify-email, valid code, email updated', async () => { + await request(app) + .get(`${endpoint}/send-verify-email`) + .set('Authorization', authorization) + .query({ email: user.email }); + + const code = email.emails.get([user.email])[1]; + + await request(app) + .get(`${endpoint}/verify-email`) + .query({ code }) + .expect(302); + }); +}); diff --git a/backend/test/integration/routes/channel-routes.tests.ts b/backend/test/integration/routes/channel-routes.tests.ts new file mode 100644 index 00000000..c0360354 --- /dev/null +++ b/backend/test/integration/routes/channel-routes.tests.ts @@ -0,0 +1,82 @@ +import { Mock } from '../../mock/mock'; +import Deps from '../../../src/utils/deps'; +import { API } from '../../../src/api/server'; +import request from 'supertest'; +import Users from '../../../src/data/users'; +import { UserDocument } from '../../../src/data/models/user'; +import { expect } from 'chai'; +import { InviteDocument } from '../../../src/data/models/invite'; +import { GuildDocument } from '../../../src/data/models/guild'; +import { assert } from 'console'; +import { Channel } from '../../../src/data/models/channel'; +import { generateSnowflake } from '../../../src/data/snowflake-entity'; +import { Lean } from '../../../src/data/types/entity-types'; + +describe('channel-routes', () => { + const endpoint = `/api/v1/channels`; + + let app: Express.Application; + let authorization: string; + let users: Users; + let user: UserDocument; + let channel: Lean.Channel; + let guild: GuildDocument; + + beforeEach(async () => { + app = Deps.get(API).app; + users = Deps.get(Users); + + guild = await Mock.guild(); + channel = guild.channels[0]; + user = await users.get(guild.ownerId); + + authorization = `Bearer ${users.createToken(user.id)}`; + }); + + afterEach(async () => await Mock.cleanDB()); + + it('GET /, returns text and dm channels', async () => { + await Mock.channel({ + memberIds: [user.id, guild.ownerId], + type: 'DM', + }); + + await request(app) + .get(endpoint) + .set('Authorization', authorization) + .expect(200) + .expect(res => expect(res.body.length).to.equal(2)); + }); + + it('GET /:channelId/messages, returns messages', async () => { + const channel = guild.channels[0]; + + await request(app) + .get(`${endpoint}/${channel.id}/messages`) + .set('Authorization', authorization) + .expect(200) + .expect(res => expect(res.body.length).to.equal(2)); + }); + + it('GET /:channelId/messages, returns batch size of most recent messages', async () => { + const messages = []; + for (let i = 0; i < 50; i++) + messages.push(await Mock.message(user, channel.id)); + + await request(app) + .get(`${endpoint}/${channel.id}/messages`) + .set('Authorization', authorization) + .expect(200) + .expect(res => expect(res.body[0].id).to.equal(messages[25].id)); + }); + + it('GET /:channelId/messages, batch size 1, returns last message', async () => { + const message = await Mock.message(user, channel.id) + + await request(app) + .get(`${endpoint}/${channel.id}/messages?back=1`) + .set('Authorization', authorization) + .expect(200) + .expect(res => expect(res.body[0].id).to.equal(message.id)); + }); +}); diff --git a/backend/test/integration/routes/guilds-routes.tests.ts b/backend/test/integration/routes/guilds-routes.tests.ts new file mode 100644 index 00000000..a743c432 --- /dev/null +++ b/backend/test/integration/routes/guilds-routes.tests.ts @@ -0,0 +1,99 @@ +import { Mock } from '../../mock/mock'; +import Deps from '../../../src/utils/deps'; +import { API } from '../../../src/api/server'; +import request from 'supertest'; +import Users from '../../../src/data/users'; +import { UserDocument } from '../../../src/data/models/user'; +import { expect } from 'chai'; +import { InviteDocument } from '../../../src/data/models/invite'; +import { GuildDocument } from '../../../src/data/models/guild'; +import { Role } from '../../../src/data/models/role'; +import { Lean, PermissionTypes } from '../../../src/data/types/entity-types'; + +describe('guilds-routes', () => { + const endpoint = `/api/v1/guilds`; + + let app: Express.Application; + let users: Users; + let user: UserDocument; + let authorization: string; + let invite: InviteDocument; + let guild: GuildDocument; + + beforeEach(async () => { + app = Deps.get(API).app; + users = Deps.get(Users); + + guild = await Mock.guild(); + user = await users.get(guild.ownerId); + invite = await Mock.invite(guild.id); + + authorization = `Bearer ${users.createToken(user.id)}`; + }); + + afterEach(async () => await Mock.cleanDB()); + + it('GET /, not logged in, 401', async () => { + await request(app) + .get(endpoint) + .expect(401) + .expect({ message: 'Unauthorized' }); + }); + + it('GET /, is logged in, returns populated guilds', async () => { + await request(app) + .get(endpoint) + .set('Authorization', authorization) + .expect(200) + .expect(res => { + const firstGuild: Lean.Guild = res.body[0]; + expect(typeof firstGuild.roles[0]).to.equal('object'); + expect(typeof firstGuild.members[0]).to.equal('object'); + expect(typeof firstGuild.channels[0]).to.equal('object'); + }); + }); + + it('GET /:id/authorize/:botId, bot exists, success', async () => { + const botUser = await Mock.bot(); + + await request(app) + .get(`${endpoint}/${guild.id}/authorize/${botUser.id}`) + .set('Authorization', authorization) + .expect(200) + .expect({ message: 'Success' }); + }); + + it('GET /:id/invites, cannot manage guild, missing permissions', async () => { + const botUser = await Mock.bot(); + + await request(app) + .get(`${endpoint}/${guild.id}/authorize/${botUser.id}`) + .set('Authorization', authorization) + .expect(200) + .expect({ message: 'Success' }); + }); + + it('GET /:id/invites, is guild owner, returns all invites', async () => { + await request(app) + .get(`${endpoint}/${guild.id}/invites`) + .set('Authorization', authorization) + .expect(200) + .expect(res => + expect(res.body).to.deep.equal([invite.toJSON()]) + ); + }); + it('GET /:id/invites, is guild manager, returns all invites', async () => { + authorization = `Bearer ${users.createToken(guild.members[1].userId)}`; + + const role = await Role.findById(guild.roles[0].id); + await Mock.giveRolePerms(role, PermissionTypes.General.MANAGE_GUILD); + + await request(app) + .get(`${endpoint}/${guild.id}/invites`) + .set('Authorization', authorization) + .expect(200) + .expect(res => + expect(JSON.stringify(res.body)).to.deep.equal([invite.toJSON()]) + ); + }); +}); diff --git a/backend/test/integration/routes/invites-routes.tests.ts b/backend/test/integration/routes/invites-routes.tests.ts new file mode 100644 index 00000000..684d0fbd --- /dev/null +++ b/backend/test/integration/routes/invites-routes.tests.ts @@ -0,0 +1,49 @@ +import { Mock } from '../../mock/mock'; +import Deps from '../../../src/utils/deps'; +import { API } from '../../../src/api/server'; +import request from 'supertest'; +import Users from '../../../src/data/users'; +import { UserDocument } from '../../../src/data/models/user'; +import { expect } from 'chai'; +import { InviteDocument } from '../../../src/data/models/invite'; +import { GuildDocument } from '../../../src/data/models/guild'; + +describe('invite-routes', () => { + const endpoint = `/api/v1/invites`; + + let app: Express.Application; + let users: Users; + let user: UserDocument; + let authorization: string; + let invite: InviteDocument; + let guild: GuildDocument; + + beforeEach(async () => { + app = Deps.get(API).app; + users = Deps.get(Users); + + guild = await Mock.guild(); + user = await users.get(guild.ownerId); + invite = await Mock.invite(guild.id); + + authorization = `Bearer ${users.createToken(user.id)}`; + }); + + afterEach(async () => await Mock.cleanDB()); + + it('GET /:id, invite not found, 404', async () => { + await invite.deleteOne(); + + await request(app) + .get(`${endpoint}/${invite.id}`) + .expect(404) + .expect({ message: 'Invite Not Found' }); + }); + + it('GET /:id, invite found, returns invite', async () => { + await request(app) + .get(`${endpoint}/${invite.id}`) + .expect(200) + .expect(invite.toJSON()); + }); +}); diff --git a/backend/test/integration/ws/add-friend.tests.ts b/backend/test/integration/ws/add-friend.tests.ts new file mode 100644 index 00000000..9f52aa23 --- /dev/null +++ b/backend/test/integration/ws/add-friend.tests.ts @@ -0,0 +1,119 @@ +import AddFriend from '../../../src/api/websocket/ws-events/add-friend'; +import RemoveFriend from '../../../src/api/websocket/ws-events/remove-friend'; +import { WebSocket } from '../../../src/api/websocket/websocket'; +import io from 'socket.io-client'; +import { Mock } from '../../mock/mock'; +import { expect } from 'chai'; +import { SelfUserDocument, User } from '../../../src/data/models/user'; +import { Channel } from '../../../src/data/models/channel'; + +describe('add-friend', () => { + const client = io(`http://localhost:${process.env.PORT}`) as any; + let event: AddFriend; + let ws: WebSocket; + + let sender: SelfUserDocument; + let friend: SelfUserDocument; + + beforeEach(async () => { + ({ event, ws, user: sender as any } = await Mock.defaultSetup(client, AddFriend)); + + friend = await Mock.self(); + }); + + afterEach(async () => await Mock.afterEach(ws)); + after(async () => await Mock.after(client)); + + it('user sends request, fulfilled', async () => { + await expect(addFriend()).to.be.fulfilled; + }); + + it('user adds self as friend, rejected', async () => { + friend.username = sender.username; + + await expect(addFriend()).to.be.rejectedWith('You cannot add yourself as a friend'); + }); + + it('friend blocked sender, rejected', async () => { + friend.ignored.userIds.push(sender.id); + await friend.updateOne(friend); + + await expect(addFriend()).to.be.rejectedWith('This user is blocking you'); + }); + + it('user adds non existing user, rejected', async () => { + await friend.deleteOne(); + + await expect(addFriend()).to.be.rejectedWith('User Not Found'); + }); + + it('both users add each other, creates one dm channel', async () => { + await addFriend(); + await returnFriend(); + + const exists = await Channel.countDocuments({ memberIds: sender.id }); + expect(exists).to.equal(1); + }); + + it('user add, remove, then re-add each other, reuses old dm channel', async () => { + await addFriend(); + await returnFriend(); + + await removeFriend(); + + await addFriend(); + await returnFriend(); + + const docs = await Channel.countDocuments({ type: 'DM' }); + expect(docs).to.equal(1); + }); + + it('both users add each other, friend request removed', async () => { + await addFriend(); + await returnFriend(); + + sender = await User.findById(sender.id) as any; + friend = await User.findById(friend.id) as any; + + expect(sender.friendRequestIds).to.be.empty; + expect(friend.friendRequestIds).to.be.empty; + }); + + it('both users add each other, friend added', async () => { + await addFriend(); + await returnFriend(); + + friend = await User.findById(friend.id) as any; + sender = await User.findById(sender.id) as any; + + expect(friend.friendIds.length).to.equal(1); + expect(sender.friendIds.length).to.equal(1); + }); + + it('user adds friends twice, rejected', async () => { + await addFriend(); + await expect(addFriend()).to.be.rejectedWith('Friend request already sent'); + }); + + it('user already friends, rejected', async () => { + await addFriend(); + await returnFriend(); + + await expect(addFriend()).to.be.rejectedWith('You are already friends'); + }); + + async function addFriend() { + return event.invoke(ws, client, { username: friend.username }); + } + + async function returnFriend() { + ws.sessions.set(client.id, friend.id); + await event.invoke(ws, client, { username: sender.username }); + + ws.sessions.set(client.id, sender.id); + } + + async function removeFriend() { + await new RemoveFriend().invoke(ws, client, { friendId: friend.id }); + } +}); diff --git a/backend/test/integration/ws/channel-create.tests.ts b/backend/test/integration/ws/channel-create.tests.ts new file mode 100644 index 00000000..073f7dd2 --- /dev/null +++ b/backend/test/integration/ws/channel-create.tests.ts @@ -0,0 +1,68 @@ +import { WebSocket } from '../../../src/api/websocket/websocket'; +import io from 'socket.io-client'; +import ChannelCreate from '../../../src/api/websocket/ws-events/channel-create'; +import { Mock } from '../../mock/mock'; +import { expect, spy } from 'chai'; +import { Guild, GuildDocument } from '../../../src/data/models/guild'; +import { Lean, PermissionTypes } from '../../../src/data/types/entity-types'; +import { RoleDocument } from '../../../src/data/models/role'; + +describe('channel-create', () => { + const client = io(`http://localhost:${process.env.PORT}`) as any; + let ws: WebSocket; + let event: ChannelCreate; + + let guild: GuildDocument; + let role: RoleDocument; + + beforeEach(async () => { + ({ ws, event, guild, role } = await Mock.defaultSetup(client, ChannelCreate)) + }); + + afterEach(async () => await Mock.afterEach(ws)); + after(async () => await Mock.after(client)); + + it('member creates channel, manage channels perms, fulfilled', async () => { + role.permissions |= PermissionTypes.General.MANAGE_CHANNELS; + await role.save(); + + await expect(createChannel()).to.be.fulfilled; + }); + + it('member successfully creates channel, new channel added', async () => { + const oldLength = guild.channels.length; + + await makeGuildOwner(); + await createChannel(); + + guild = await Guild.findById(guild.id); + expect(guild.channels.length).to.be.greaterThan(oldLength); + }); + + it('member successfully creates channel, client joins channel room', async () => { + const join = spy.on(client, 'join'); + + await makeGuildOwner(); + await createChannel(); + + guild = await Guild.findById(guild.id); + + expect(join).to.be.called(); + }); + + async function createChannel(partialChannel?: Partial) { + return event.invoke(ws, client, { + guildId: guild.id, + partialChannel: partialChannel ?? { + name: 'chat', + type: 'TEXT', + summary: '', + } + }); + } + + async function makeGuildOwner() { + ws.sessions.set(client.id, guild.ownerId); + await Mock.clearRolePerms(guild); + } +}); diff --git a/backend/test/integration/ws/guild-create.tests.ts b/backend/test/integration/ws/guild-create.tests.ts new file mode 100644 index 00000000..75243963 --- /dev/null +++ b/backend/test/integration/ws/guild-create.tests.ts @@ -0,0 +1,45 @@ +import { WebSocket } from '../../../src/api/websocket/websocket'; +import io from 'socket.io-client'; +import GuildCreate from '../../../src/api/websocket/ws-events/guild-create'; +import { User, UserDocument } from '../../../src/data/models/user'; +import { Mock } from '../../mock/mock'; +import { expect } from 'chai'; +import { GuildMemberDocument } from '../../../src/data/models/guild-member'; +import { Lean } from '../../../src/data/types/entity-types'; + +describe('guild-create', () => { + const client = io(`http://localhost:${process.env.PORT}`) as any; + let ws: WebSocket; + let event: GuildCreate; + + let user: UserDocument; + let member: GuildMemberDocument; + + beforeEach(async () => { + ({ ws, event, member, user } = await Mock.defaultSetup(client, GuildCreate)); + }); + + afterEach(async () => Mock.afterEach(ws)); + after(async () => await Mock.after(client)); + + it('user creates guild, fulfilled', async () => { + await expect(guildCreate()).to.be.fulfilled; + }); + + it('user creates guild, added to user.guilds', async () => { + const oldCount = user.guilds.length; + await guildCreate(); + + user = await User.findById(user.id); + expect(user.guilds.length).to.be.greaterThan(oldCount); + }); + + function guildCreate(partialGuild?: Partial) { + return event.invoke(ws, client, { + partialGuild: { + name: 'Mock Guild', + ...partialGuild, + }, + }); + } +}); \ No newline at end of file diff --git a/backend/test/integration/ws/guild-delete.tests.ts b/backend/test/integration/ws/guild-delete.tests.ts new file mode 100644 index 00000000..cd63973e --- /dev/null +++ b/backend/test/integration/ws/guild-delete.tests.ts @@ -0,0 +1,96 @@ +import { WebSocket } from '../../../src/api/websocket/websocket'; +import io from 'socket.io-client'; +import GuildDelete from '../../../src/api/websocket/ws-events/guild-delete'; +import { User, UserDocument } from '../../../src/data/models/user'; +import { Mock } from '../../mock/mock'; +import { expect } from 'chai'; +import { GuildMember, GuildMemberDocument } from '../../../src/data/models/guild-member'; +import { Guild, GuildDocument } from '../../../src/data/models/guild'; +import { Channel } from '../../../src/data/models/channel'; +import { Invite } from '../../../src/data/models/invite'; +import { Role } from '../../../src/data/models/role'; + +describe('guild-delete', () => { + const client = io(`http://localhost:${process.env.PORT}`) as any; + let ws: WebSocket; + let event: GuildDelete; + + let user: UserDocument; + let member: GuildMemberDocument; + let guild: GuildDocument; + + beforeEach(async () => { + ({ ws, event, member, user, guild } = await Mock.defaultSetup(client, GuildDelete)); + + await makeOwner(); + }); + + afterEach(async () => Mock.afterEach(ws)); + after(async () => await Mock.after(client)); + + it('noob member deletes guild, rejected', async () => { + await makeNoob(); + + await expect(guildDelete()).to.be.rejectedWith('Only the guild owner can do this'); + }); + + it('owner deletes guild, fulfilled', async () => { + await expect(guildDelete()).to.be.fulfilled; + }); + + it('owner deletes guild, channels are removed from db', async () => { + await guildDelete(); + + const channels = await Channel.find({ guildId: guild.id }); + expect(channels).to.be.empty; + }); + + it('owner deletes guild, members are removed from db', async () => { + await guildDelete(); + + const members = await GuildMember.find({ guildId: guild.id }); + expect(members).to.be.empty; + }); + + it('owner deletes guild, invites are removed from db', async () => { + await guildDelete(); + + const invites = await Invite.find({ guildId: guild.id }); + expect(invites).to.be.empty; + }); + + it('owner deletes guild, roles are removed from db', async () => { + await guildDelete(); + + const roles = await Role.find({ guildId: guild.id }); + expect(roles).to.be.empty; + }); + + it('owner deletes guild, guild is removed from db', async () => { + await guildDelete(); + + guild = await Guild.findById(guild.id); + expect(guild).to.be.null; + }); + + it('owner deletes guild, removed from all user.guilds', async () => { + const oldCount = user.guilds.length; + await guildDelete(); + + user = await User.findById(user.id); + expect(user.guilds.length).to.be.lessThan(oldCount); + }); + + function guildDelete() { + return event.invoke(ws, client, { guildId: guild.id }); + } + + async function makeOwner() { + ws.sessions.set(client.id, guild.ownerId); + user = await User.findById(guild.ownerId); + } + async function makeNoob() { + user = await User.findById(guild.members[1].userId); + ws.sessions.set(client.id, user.id); + } +}); diff --git a/backend/test/integration/ws/guild-member-add.tests.ts b/backend/test/integration/ws/guild-member-add.tests.ts new file mode 100644 index 00000000..70c85e63 --- /dev/null +++ b/backend/test/integration/ws/guild-member-add.tests.ts @@ -0,0 +1,105 @@ +import GuildMemberAdd from '../../../src/api/websocket/ws-events/guild-member-add'; +import { WebSocket } from '../../../src/api/websocket/websocket'; +import Deps from '../../../src/utils/deps'; +import io from 'socket.io-client'; +import { Mock } from '../../mock/mock'; +import { UserDocument } from '../../../src/data/models/user'; +import { GuildDocument } from '../../../src/data/models/guild'; +import { Invite, InviteDocument } from '../../../src/data/models/invite'; +import { expect, spy } from 'chai'; +import Guilds from '../../../src/data/guilds'; +import Users from '../../../src/data/users'; + +describe('guild-member-add', () => { + const client = io(`http://localhost:${process.env.PORT}`) as any; + let event: GuildMemberAdd; + let guilds: Guilds; + let users: Users; + let ws: WebSocket; + + let user: UserDocument; + let guild: GuildDocument; + let invite: InviteDocument; + + beforeEach(async() => { + ({ event, ws, user, guild } = await Mock.defaultSetup(client, GuildMemberAdd)); + + guilds = Deps.get(Guilds); + users = Deps.get(Users); + + user = await Mock.user([]); + ws.sessions.set(client.id, user.id); + + invite = await Mock.invite(guild.id); + }); + + afterEach(async () => await Mock.afterEach(ws)); + after(async () => await Mock.after(client)); + + it('valid invite and code, fulfilled', async () => { + await expect(guildMemberAdd()).to.be.fulfilled; + }); + + it('user already joined, rejected', async () => { + await guildMemberAdd(); + + await expect(guildMemberAdd()).to.be.rejectedWith('User already in guild'); + }); + + it('valid invite and code, is bot user, rejected', async () => { + const bot = await Mock.bot(); + ws.sessions.set(client.id, bot.id); + + await expect(guildMemberAdd()).to.be.rejectedWith('Bot users cannot accept invites'); + }); + + it('valid invite and code, member added to guild', async () => { + const oldMemberCount = guild.members.length; + await guildMemberAdd(); + + guild = await guilds.get(guild.id); + expect(guild.members.length).to.be.greaterThan(oldMemberCount); + }); + + it('valid invite and code, guild added to user guilds', async () => { + await guildMemberAdd(); + + user = await users.get(user.id); + expect(user.guilds.length).to.equal(1); + }); + + it('invite has reached max uses, is deleted', async () => { + invite.options = { maxUses: 1 }; + await invite.save(); + + await guildMemberAdd(); + invite = await Invite.findById(invite.id); + expect(invite).to.be.null; + }); + + it('invite expired, rejected', async () => { + invite.options = { expiresAt: new Date(0) }; + await invite.save(); + + await expect(guildMemberAdd()).to.be.rejectedWith('Invite expired'); + }); + + it('invalid invite code, rejected', async () => { + invite.id = ''; + + await expect(guildMemberAdd()).to.be.rejectedWith('Invite Not Found'); + }); + + it('valid invite and code, emits to guild room', async () => { + const to = spy.on(ws.io, 'to'); + + await guildMemberAdd(); + + guild = await guilds.get(guild.id); + expect(to).to.have.been.called.with(guild.id); + }); + + function guildMemberAdd() { + return event.invoke(ws, client, { inviteCode: invite.id }); + } +}); diff --git a/backend/test/integration/ws/guild-member-remove.tests.ts b/backend/test/integration/ws/guild-member-remove.tests.ts new file mode 100644 index 00000000..115c8798 --- /dev/null +++ b/backend/test/integration/ws/guild-member-remove.tests.ts @@ -0,0 +1,79 @@ +import GuildMemberRemove from '../../../src/api/websocket/ws-events/guild-member-remove'; +import { WebSocket } from '../../../src/api/websocket/websocket'; +import io from 'socket.io-client'; +import { Mock } from '../../mock/mock'; +import { Guild, GuildDocument } from '../../../src/data/models/guild'; +import { expect } from 'chai'; +import { GuildMemberDocument } from '../../../src/data/models/guild-member'; +import { User, UserDocument } from '../../../src/data/models/user'; + +describe('guild-member-remove', () => { + const client = io(`http://localhost:${process.env.PORT}`) as any; + + let event: GuildMemberRemove; + let ws: WebSocket; + let user: UserDocument; + let member: GuildMemberDocument; + let guild: GuildDocument; + + beforeEach(async() => { + ({ event, ws, guild, member, user } = await Mock.defaultSetup(client, GuildMemberRemove)); + }); + + afterEach(async () => await Mock.afterEach(ws)); + after(async () => await Mock.after(client)); + + it('leaves guild that is member of, fulfilled', async () => { + await expect(guildMemberRemove()).to.be.fulfilled; + }); + + it('leaves guild that is member of, fulfilled', async () => { + await expect(guildMemberRemove()).to.be.fulfilled; + }); + + it('cannot leave owned guild, rejected', async () => { + makeGuildOwner(); + + await expect(guildMemberRemove()).to.be.rejectedWith('You cannot leave a guild you own'); + }); + + it('leaves guild, removed from user guilds', async () => { + await guildMemberRemove(); + + user = await User.findById(user.id); + expect(user.guilds).to.not.include(guild.id); + }); + + it('kick noob member, as noob member, missing permissions', async () => { + await makeAnotherNoob(); + + await expect(guildMemberRemove()).to.be.rejectedWith('Missing Permissions'); + }); + + it('kick noob member, as guild owner, removed from user guilds', async () => { + makeGuildOwner(); + + member = guild.members[1] as GuildMemberDocument; + await guildMemberRemove(); + + user = await User.findById(member.userId); + expect(user.guilds).to.not.include(guild.id); + }); + + function guildMemberRemove() { + return event.invoke(ws, client, { + guildId: guild.id, + memberId: member.id, + }); + } + + function makeGuildOwner() { + member = guild.members[0] as GuildMemberDocument; + ws.sessions.set(client.id, guild.ownerId); + } + + async function makeAnotherNoob() { + const user = await Mock.user(); + member = await Mock.guildMember(user, guild); + } +}); diff --git a/backend/test/integration/ws/guild-member-update.tests.ts b/backend/test/integration/ws/guild-member-update.tests.ts new file mode 100644 index 00000000..7cb8cd9f --- /dev/null +++ b/backend/test/integration/ws/guild-member-update.tests.ts @@ -0,0 +1,107 @@ +import GuildMemberUpdate from '../../../src/api/websocket/ws-events/guild-member-update'; +import { WebSocket } from '../../../src/api/websocket/websocket'; +import io from 'socket.io-client'; +import { Mock } from '../../mock/mock'; +import { GuildDocument } from '../../../src/data/models/guild'; +import { expect } from 'chai'; +import { GuildMember, GuildMemberDocument } from '../../../src/data/models/guild-member'; +import { Role, RoleDocument } from '../../../src/data/models/role'; +import { PermissionTypes } from '../../../src/data/types/entity-types'; +import { Partial } from '../../../src/data/types/ws-types'; + +describe('guild-member-update', () => { + const client = io(`http://localhost:${process.env.PORT}`) as any; + + let event: GuildMemberUpdate; + let ws: WebSocket; + let member: GuildMemberDocument; + let guild: GuildDocument; + let role: RoleDocument; + + beforeEach(async() => { + ({ event, ws, member, guild, role } = await Mock.defaultSetup(client, GuildMemberUpdate)); + }); + + afterEach(async () => await Mock.afterEach(ws)); + after(async () => await Mock.after(client)); + + it('is noob member, missing permissions', async () => { + await expect(guildMemberUpdate()).to.be.rejectedWith('Missing Permissions'); + }); + + it('is role manager, fulfilled', async () => { + await makeAllManager(); + await expect(guildMemberUpdate()).to.be.fulfilled; + }); + + it('is role manager, fulfilled', async () => { + await makeAllManager(); + await expect(guildMemberUpdate()).to.be.fulfilled; + }); + + it('managed has same roles, rejected', async () => { + member = await Mock.guildMember(await Mock.user(), guild); + await makeAllManager(); + + await expect(guildMemberUpdate()).to.be.rejectedWith('Member has higher roles'); + }); + + it('managed is owner, rejected', async () => { + member = new GuildMember(guild.members[0]); + await makeAllManager(); + + await expect(guildMemberUpdate()).to.be.rejectedWith('Member has higher roles'); + }); + + it('roles updated, still has @everyone role', async () => { + await makeAllManager(); + await guildMemberUpdate(); + + member = await GuildMember.findById(member.id); + role = await Role.findById(member.roleIds[0]); + + expect(role.name).to.equal('@everyone'); + }); + + it('is guild owner, updates guild manager', async () => { + makeGuildOwner(); + await makeAllManager(); + + await expect(guildMemberUpdate()).to.be.fulfilled; + }); + + it('is role manager, updates noob member, fulfilled', async () => { + await makeRoleManager(); + await expect(guildMemberUpdate()).to.be.fulfilled; + }); + + it('includes banned keys, rejected', async () => { + await makeRoleManager(); + await expect(guildMemberUpdate({ id: '123' })).to.be.rejectedWith('Contains readonly values'); + }); + + async function makeRoleManager() { + const manager = await Mock.guildMember(await Mock.user(), guild); + await Mock.givePerm(guild, manager, PermissionTypes.General.MANAGE_ROLES); + + ws.sessions.set(client.id, manager.userId); + } + + function makeGuildOwner() { + ws.sessions.set(client.id, guild.ownerId); + } + + function makeAllManager() { + return Mock.giveRolePerms(role, PermissionTypes.General.MANAGE_ROLES); + } + + function guildMemberUpdate(partial?: Partial.GuildMember) { + return event.invoke(ws, client, { + memberId: member.id, + partialMember: { + ...partial, + roleIds: [], + }, + }); + } +}); diff --git a/backend/test/integration/ws/guild-update.tests.ts b/backend/test/integration/ws/guild-update.tests.ts new file mode 100644 index 00000000..46d06061 --- /dev/null +++ b/backend/test/integration/ws/guild-update.tests.ts @@ -0,0 +1,125 @@ +import { WebSocket } from '../../../src/api/websocket/websocket'; +import io from 'socket.io-client'; +import GuildUpdate from '../../../src/api/websocket/ws-events/guild-update'; +import { Guild, GuildDocument } from '../../../src/data/models/guild'; +import { UserDocument } from '../../../src/data/models/user'; +import { Mock } from '../../mock/mock'; +import { expect } from 'chai'; +import { PermissionTypes } from '../../../src/data/types/entity-types'; +import { GuildMemberDocument } from '../../../src/data/models/guild-member'; +import { generateSnowflake } from '../../../src/data/snowflake-entity'; +import { Partial } from '../../../src/data/types/ws-types'; + +describe('guild-update', () => { + const client = io(`http://localhost:${process.env.PORT}`) as any; + let ws: WebSocket; + let event: GuildUpdate; + + let guild: GuildDocument; + let user: UserDocument; + let member: GuildMemberDocument; + + beforeEach(async () => { + ({ ws, event, guild, member, user } = await Mock.defaultSetup(client, GuildUpdate)); + }); + + afterEach(async () => Mock.afterEach(ws)); + after(async () => await Mock.after(client)); + + it('spoofed user, rejected', async () => { + ws.sessions.set(client.id, generateSnowflake()); + await expect(guildUpdate(guild)).to.be.rejectedWith('Member Not Found'); + }); + + it('member has insufficient perms, rejected', async () => { + await Mock.clearRolePerms(guild); + await expect(guildUpdate({})).to.be.rejectedWith('Missing Permissions'); + }); + + it('user has MANAGE_GUILD perms, fulfilled', async () => { + await Mock.givePerm(guild, member, PermissionTypes.All.MANAGE_GUILD); + await expect(guildUpdate({})).to.be.fulfilled; + }); + + it('user is owner, fulfilled', async () => { + makeOwner(); + await expect(guildUpdate({})).to.be.fulfilled; + }); + + it('name acronym updated', async () => { + makeOwner(); + await guildUpdate({ name: 'K E K K' }); + + guild = await Guild.findById(guild.id); + expect(guild.nameAcronym).to.equal('KEK'); + }); + + it('contains banned keys, rejected', async () => { + makeOwner(); + await expect(guildUpdate({ id: '123' })).to.be.rejectedWith('Contains readonly values'); + }); + + it('reordered roles correctly, fulfilled', async () => { + makeOwner(); + + const roleIds = guild.roles.map(r => r.id); + await expect(guildUpdate({ roles: roleIds as any })).to.be.fulfilled; + }); + + it('reordered roles, added a role, rejected', async () => { + makeOwner(); + const newRole = await Mock.role(guild); + const roleIds = guild.roles.concat(newRole).map(r => r.id); + + await expect(guildUpdate({ roles: roleIds as any })).to.be.rejectedWith('Cannot add or remove roles this way'); + }); + + it('reordered roles, removed a role, rejected', async () => { + makeOwner(); + await Mock.role(guild); + const roleIds = guild.roles.slice(0, 1); + + await expect(guildUpdate({ roles: roleIds as any })).to.be.rejectedWith('Cannot add or remove roles this way'); + }); + + it('reordered roles, moved everyone role, rejected', async () => { + makeOwner(); + const roleIds = [generateSnowflake(), ...guild.roles]; + + await expect(guildUpdate({ roles: roleIds as any })).to.be.rejectedWith('Cannot reorder the @everyone role'); + }); + + it('sorted channels, did not add channels, fulfilled', async () => { + makeOwner(); + + const roleIds = guild.channels.map(r => r.id); + await expect(guildUpdate({ channels: roleIds as any })).to.be.fulfilled; + }); + + it('sorted channels, added a channel, rejected', async () => { + makeOwner(); + const channel = await Mock.channel(guild); + const channelIds = guild.channels.concat(channel).map(r => r.id); + + await expect(guildUpdate({ channels: channelIds as any })).to.be.rejectedWith('Cannot add or remove channels this way'); + }); + + it('sorted channels, removed a channel, rejected', async () => { + makeOwner(); + await Mock.channel(guild); + const channelIds = guild.channels.slice(0, 1); + + await expect(guildUpdate({ channels: channelIds as any })).to.be.rejectedWith('Cannot add or remove channels this way'); + }); + + function guildUpdate(partialGuild: Partial.Guild) { + return event.invoke(ws, client, { + guildId: guild.id, + partialGuild, + }); + } + + function makeOwner() { + ws.sessions.set(client.id, guild.ownerId); + } +}); \ No newline at end of file diff --git a/backend/test/integration/ws/invite-create.tests.ts b/backend/test/integration/ws/invite-create.tests.ts new file mode 100644 index 00000000..95605e34 --- /dev/null +++ b/backend/test/integration/ws/invite-create.tests.ts @@ -0,0 +1,41 @@ +import InviteCreate from '../../../src/api/websocket/ws-events/invite-create'; +import { WebSocket } from '../../../src/api/websocket/websocket'; +import io from 'socket.io-client'; +import { Mock } from '../../mock/mock'; +import { GuildDocument } from '../../../src/data/models/guild'; +import { expect } from 'chai'; +import { Role, RoleDocument } from '../../../src/data/models/role'; +import { PermissionTypes } from '../../../src/data/types/entity-types'; + +describe('invite-create', () => { + const client = io(`http://localhost:${process.env.PORT}`) as any; + let event: InviteCreate; + let ws: WebSocket; + + let guild: GuildDocument; + let role: RoleDocument; + + beforeEach(async () => { + ({ event, ws, guild, role } = await Mock.defaultSetup(client, InviteCreate)); + }); + + afterEach(async () => await Mock.afterEach(ws)); + after(async () => await Mock.after(client)); + + it('member with create invite perms, fulfilled', async () => { + await Mock.giveRolePerms(role, PermissionTypes.General.CREATE_INVITE); + await expect(inviteCreate()).to.be.fulfilled; + }); + + it('member with no perms, rejected', async () => { + await Mock.clearRolePerms(guild); + await expect(inviteCreate()).to.be.rejectedWith('Missing Permissions'); + }); + + function inviteCreate() { + return event.invoke(ws, client, { + guildId: guild.id, + options: {}, + }); + } +}); diff --git a/backend/test/integration/ws/invite-delete.tests.ts b/backend/test/integration/ws/invite-delete.tests.ts new file mode 100644 index 00000000..26e2d984 --- /dev/null +++ b/backend/test/integration/ws/invite-delete.tests.ts @@ -0,0 +1,65 @@ +import InviteDelete from '../../../src/api/websocket/ws-events/invite-delete'; +import { WebSocket } from '../../../src/api/websocket/websocket'; +import io from 'socket.io-client'; +import { Mock } from '../../mock/mock'; +import { GuildDocument } from '../../../src/data/models/guild'; +import { Invite, InviteDocument } from '../../../src/data/models/invite'; +import { expect } from 'chai'; +import { PermissionTypes } from '../../../src/data/types/entity-types'; +import { RoleDocument } from '../../../src/data/models/role'; + +describe('invite-delete', () => { + const client = io(`http://localhost:${process.env.PORT}`) as any; + let event: InviteDelete; + let ws: WebSocket; + + let guild: GuildDocument; + let invite: InviteDocument; + let role: RoleDocument; + + beforeEach(async () => { + ({ event, ws, guild, role } = await Mock.defaultSetup(client, InviteDelete)); + + invite = await Mock.invite(guild.id); + }); + + afterEach(async () => await Mock.afterEach(ws)); + after(async () => await Mock.after(client)); + + it('guild member deletes invite, default perms, rejected', async () => { + await expect(inviteDelete()).to.be.rejectedWith('Missing Permissions'); + }); + + it('guild owner deletes invite, fulfilled', async () => { + await Mock.clearRolePerms(guild); + ws.sessions.set(client.id, guild.ownerId); + + await expect(inviteDelete()).to.be.fulfilled; + }); + + it('guild member, has manage guild perms, fulfilled', async () => { + await Mock.giveRolePerms(role, PermissionTypes.General.MANAGE_GUILD); + + await expect(inviteDelete()).to.be.fulfilled; + }); + + it('invite deleted from db', async () => { + await Mock.giveRolePerms(role, PermissionTypes.General.MANAGE_GUILD); + await inviteDelete(); + + invite = await Invite.findById(guild.id); + expect(invite).to.be.null; + }); + + it('invite does not exist, rejected', async () => { + await invite.deleteOne(); + + await expect(inviteDelete()).to.be.rejectedWith('Invite Not Found'); + }); + + function inviteDelete() { + return event.invoke(ws, client, { + inviteCode: invite.id, + }); + } +}); diff --git a/backend/test/integration/ws/message-create.tests.ts b/backend/test/integration/ws/message-create.tests.ts new file mode 100644 index 00000000..d8586d8a --- /dev/null +++ b/backend/test/integration/ws/message-create.tests.ts @@ -0,0 +1,72 @@ +import MessageCreate from '../../../src/api/websocket/ws-events/message-create'; +import { WebSocket } from '../../../src/api/websocket/websocket'; +import { expect } from 'chai'; +import io from 'socket.io-client'; +import { Mock } from '../../mock/mock'; +import { GuildDocument } from '../../../src/data/models/guild'; +import { Channel, ChannelDocument } from '../../../src/data/models/channel'; +import { SelfUserDocument, User, UserDocument } from '../../../src/data/models/user'; + +describe('message-create', () => { + const client = io(`http://localhost:${process.env.PORT}`) as any; + let event: MessageCreate; + let ws: WebSocket; + + let user: SelfUserDocument; + let channel: ChannelDocument; + let guild: GuildDocument; + + beforeEach(async () => { + ({ ws, event, guild, user: user as any, channel } = await Mock.defaultSetup(client, MessageCreate)); + }); + + afterEach(async () => await Mock.afterEach(ws)); + after(async () => await Mock.after(client)); + + it('not a guild member, rejected', async () => { + const user = await Mock.user(); + ws.sessions.set(client.id, user.id); + + await expect(messageCreate()).to.be.rejectedWith('Member Not Found'); + }); + + it('user is guild member with send message perms, fulfilled', async () => { + await expect(messageCreate()).to.be.fulfilled; + }); + + it('user is guild member without chat perms, rejected', async () => { + await Mock.clearRolePerms(guild); + + await expect(messageCreate()).to.be.rejectedWith('Missing Permissions'); + }); + + it('user is guild owner, fulfilled', async () => { + ws.sessions.set(client.id, guild.ownerId); + + await expect(messageCreate()).to.be.fulfilled; + }); + + it('lastMessageId in channel is updated', async () => { + await messageCreate(); + + channel = await Channel.findById(channel.id); + expect(channel.lastMessageId).to.be.a('string'); + }); + + it('lastReadMessages updated in author', async () => { + await messageCreate(); + + channel = await Channel.findById(channel.id) as any; + user = await User.findById(user.id) as any; + expect(user.lastReadMessages[channel.id]).to.equal(channel.lastMessageId); + }); + + function messageCreate() { + return event.invoke(ws, client, { + channelId: channel.id, + partialMessage: { + content: 'hi' + }, + }); + } +}); diff --git a/backend/test/integration/ws/message-delete.tests.ts b/backend/test/integration/ws/message-delete.tests.ts new file mode 100644 index 00000000..32746110 --- /dev/null +++ b/backend/test/integration/ws/message-delete.tests.ts @@ -0,0 +1,93 @@ +import MessageDelete from '../../../src/api/websocket/ws-events/message-delete'; +import { WebSocket } from '../../../src/api/websocket/websocket'; +import { expect } from 'chai'; +import io from 'socket.io-client'; +import { Mock } from '../../mock/mock'; +import { GuildDocument } from '../../../src/data/models/guild'; +import { UserDocument } from '../../../src/data/models/user'; +import { Message, MessageDocument } from '../../../src/data/models/message'; +import { generateSnowflake } from '../../../src/data/snowflake-entity'; +import { Channel } from '../../../src/data/models/channel'; + +describe('message-delete', () => { + const client = io(`http://localhost:${process.env.PORT}`) as any; + + let event: MessageDelete; + let ws: WebSocket; + let user: UserDocument; + let guild: GuildDocument; + let message: MessageDocument; + let channelId: string; + + beforeEach(async () => { + ({ ws, event, user, guild } = await Mock.defaultSetup(client, MessageDelete)); + + channelId = guild.channels[0].id; + message = await Mock.message(user, channelId); + }); + + afterEach(async () => await Mock.afterEach(ws)); + after(async () => await Mock.after(client)); + + it('user is random member, rejected', async () => { + await message.update({ authorId: generateSnowflake() }); + + await expect(deleteMessage()).to.be.rejectedWith('Missing Permissions'); + }); + + it('not in guild channel but message author, fulfilled', async () => { + const channel = await Mock.channel({ type: 'DM' }); + message = await Mock.message(user, channel.id); + + await expect(deleteMessage()).to.be.fulfilled; + }); + + it('not in guild channel and not message author, fulfilled', async () => { + const channel = await Mock.channel({ type: 'DM' }); + message = await Mock.message(await Mock.user(), channel.id); + + await expect(deleteMessage()).to.be.rejectedWith('Only message author can do this'); + }); + + it('user is author, fulfilled', async () => { + await expect(deleteMessage()).to.be.fulfilled; + }); + + it('user is author, message deleted', async () => { + await deleteMessage(); + message = await Message.findById(message.id); + + expect(message).to.be.null; + }); + + it('message does not exist, rejected', async () => { + await message.deleteOne(); + + await expect(deleteMessage()).to.be.rejectedWith('Message Not Found'); + }); + + it('message is last channel message, channel last message updated to previous', async () => { + const previousMessage = message; + message = await Mock.message(user, channelId); + + await deleteMessage(); + + const channel = await Channel.findById(channelId); + expect(channel.lastMessageId).to.equal(previousMessage.id); + }); + + it('message is only channel message, channel last message updated to null', async () => { + await deleteMessage(); + + const channel = await Channel.findById(channelId); + expect(channel.lastMessageId).to.be.null; + }); + + async function deleteMessage() { + return event.invoke(ws, client, { messageId: message.id }); + } + async function makeGuildOwner() { + ws.sessions.set(client.id, guild.ownerId); + await Mock.clearRolePerms(guild); + } +}); diff --git a/backend/test/integration/ws/message-update.tests.ts b/backend/test/integration/ws/message-update.tests.ts new file mode 100644 index 00000000..9b243877 --- /dev/null +++ b/backend/test/integration/ws/message-update.tests.ts @@ -0,0 +1,64 @@ +import MessageUpdate from '../../../src/api/websocket/ws-events/message-update'; +import { WebSocket } from '../../../src/api/websocket/websocket'; +import Deps from '../../../src/utils/deps'; +import { expect } from 'chai'; +import io from 'socket.io-client'; +import { Mock } from '../../mock/mock'; +import { GuildDocument } from '../../../src/data/models/guild'; +import { User, UserDocument } from '../../../src/data/models/user'; +import { ChannelDocument } from '../../../src/data/models/channel'; +import { MessageDocument } from '../../../src/data/models/message'; +import { generateSnowflake } from '../../../src/data/snowflake-entity'; +import { Partial } from '../../../src/data/types/ws-types'; + +describe('message-update', () => { + const client = io(`http://localhost:${process.env.PORT}`) as any; + + let channel: ChannelDocument; + let event: MessageUpdate; + let ws: WebSocket; + let user: UserDocument; + let guild: GuildDocument; + let message: MessageDocument; + + beforeEach(async () => { + ({ event, ws, guild, user, channel } = await Mock.defaultSetup(client, MessageUpdate)); + + message = await Mock.message(user, channel.id); + }); + + afterEach(async () => await Mock.afterEach(ws)); + after(async () => await Mock.after(client)); + + it('user not author, rejected', async () => { + await makeGuildOwner(); + + await expect(messageUpdate()).to.be.rejectedWith('Unauthorized'); + }); + + it('message does not exist, rejected', async () => { + message.id = generateSnowflake(); + + await expect(messageUpdate()).to.be.rejectedWith('Message Not Found'); + }); + + it('update includes banned keys, rejected', async () => { + await expect(messageUpdate({ id: '123' })).to.be.rejectedWith('Contains readonly values'); + }); + + function messageUpdate(options?: Partial.Message) { + return event.invoke(ws, client, { + messageId: message.id, + partialMessage: { + ...options, + content: 'test', + }, + withEmbed: true + }); + } + + async function makeGuildOwner() { + ws.sessions.set(client.id, guild.ownerId); + await Mock.clearRolePerms(guild); + } +}); diff --git a/backend/test/integration/ws/ready.tests.ts b/backend/test/integration/ws/ready.tests.ts new file mode 100644 index 00000000..41c19aa2 --- /dev/null +++ b/backend/test/integration/ws/ready.tests.ts @@ -0,0 +1,124 @@ +import Deps from '../../../src/utils/deps'; +import { API } from '../../../src/api/server'; +import Ready from '../../../src/api/websocket/ws-events/ready'; +import Disconnect from '../../../src/api/websocket/ws-events/disconnect'; +import { User, UserDocument } from '../../../src/data/models/user'; +import { expect } from 'chai'; +import Users from '../../../src/data/users'; +import { Mock } from '../../mock/mock'; +import { WebSocket } from '../../../src/api/websocket/websocket'; +import io from 'socket.io-client'; +import { SystemBot } from '../../../src/system/bot'; +import { GuildDocument } from '../../../src/data/models/guild'; + +describe('ready', () => { + const client = io(`http://localhost:${process.env.PORT}`) as any; + + let event: Ready; + let users: Users; + let key: string; + let user: UserDocument; + let guild: GuildDocument; + let ws: WebSocket; + + beforeEach(async () => { + ({ event, user, ws, guild } = await Mock.defaultSetup(client, Ready)); + + users = new Users(); + key = users.createToken(user.id); + }); + + afterEach(async () => await Mock.afterEach(ws)); + after(async () => await Mock.after(client)); + + it('user already logged in, fulfilled', async () => { + await ready(); + await expect(ready()).to.be.fulfilled; + }); + + it('user already logged in, fulfilled', async () => { + await ready(); + await expect(ready()).to.be.fulfilled; + }); + + it('joins self user room', async () => { + await ready(); + expect(rooms()).to.include(user.id); + }); + + it('joins system bot room', async () => { + const systemBot = Deps.get(SystemBot); + await systemBot.init(); + + await ready(); + + expect(rooms()).to.include(systemBot.self.id); + }); + + it('joins dm channel room', async () => { + const dm = await Mock.channel({ type: 'DM' }); + dm.memberIds.push(user.id); + await dm.update(dm); + + await ready(); + + expect(rooms()).to.include(dm.id); + }); + + it('joins self room', async () => { + await ready(); + expect(rooms()).to.include(user.id); + }); + + it('joins guild room', async () => { + await makeOwner(); + await ready(); + + expect(rooms()).to.include(guild.id); + }); + + it('joins guild channel rooms', async () => { + const newChannel = await Mock.channel({ type: 'TEXT', guild.id }); + + await makeOwner(); + await ready(); + + expect(rooms()).to.contain(guild.channels[0].id); + expect(rooms()).to.contain(guild.channels[1].id); + expect(rooms()).to.contain(newChannel.id); + }); + + it('ready, user is online', async () => { + await ready(); + + user = await User.findById(user.id); + expect(user.status).to.equal('ONLINE'); + }); + + it('ready, user is online', async () => { + await ready(); + + await disconnect(); + await ready(); + + user = await User.findById(user.id); + expect(user.status).to.equal('ONLINE'); + }); + + function ready() { + return event.invoke(ws, client, { key }); + } + function disconnect() { + return new Disconnect().invoke(ws, client); + } + + function rooms() { + return (Array + .from(client.rooms.values()) as any) + .flat(); + } + async function makeOwner() { + ws.sessions.set(client.id, guild.ownerId); + key = users.createToken(user.id); + } +}); diff --git a/backend/test/integration/ws/remove-friend.tests.ts b/backend/test/integration/ws/remove-friend.tests.ts new file mode 100644 index 00000000..0063dd4d --- /dev/null +++ b/backend/test/integration/ws/remove-friend.tests.ts @@ -0,0 +1,62 @@ +import RemoveFriend from '../../../src/api/websocket/ws-events/remove-friend'; +import { WebSocket } from '../../../src/api/websocket/websocket'; +import io from 'socket.io-client'; +import { Mock } from '../../mock/mock'; +import { expect } from 'chai'; +import { generateSnowflake } from '../../../src/data/snowflake-entity'; +import { SelfUserDocument, User, UserDocument } from '../../../src/data/models/user'; + +describe('remove-friend', () => { + const client = io(`http://localhost:${process.env.PORT}`) as any; + let event: RemoveFriend; + let ws: WebSocket; + + let sender: SelfUserDocument; + let friend: SelfUserDocument; + + beforeEach(async () => { + ({ event, ws, user: sender as any } = await Mock.defaultSetup(client, RemoveFriend)); + friend = await Mock.self(); + }); + + afterEach(async () => await Mock.afterEach(ws)); + after(async () => await Mock.after(client)); + + it('user sends request, fulfilled', async () => { + await expect(removeFriend()).to.be.fulfilled; + }); + + it('user removes self as friend, rejected', async () => { + friend.id = sender.id; + + await expect(removeFriend()).to.be.rejectedWith('You cannot remove yourself as a friend'); + }); + + it('removes non existing friend, rejected', async () => { + friend.id = generateSnowflake(); + + await expect(removeFriend()).to.be.rejectedWith('User Not Found'); + }); + + it('sender cancels request, friend request removed', async () => { + await removeFriend(); + + sender = await User.findById(sender.id) as any; + + expect(sender.friendRequestIds).to.be.empty; + }); + + it('sender removes friend, friend removed on both users', async () => { + await removeFriend(); + + friend = await User.findById(friend.id) as any; + sender = await User.findById(sender.id) as any; + + expect(friend.friendIds.length).to.equal(0); + expect(sender.friendIds.length).to.equal(0); + }); + + async function removeFriend() { + return event.invoke(ws, client, { friendId: friend.id }); + } +}); diff --git a/backend/test/integration/ws/user-update.tests.ts b/backend/test/integration/ws/user-update.tests.ts new file mode 100644 index 00000000..2a616392 --- /dev/null +++ b/backend/test/integration/ws/user-update.tests.ts @@ -0,0 +1,84 @@ +import UserUpdate from '../../../src/api/websocket/ws-events/user-update'; +import { WebSocket } from '../../../src/api/websocket/websocket'; +import io from 'socket.io-client'; +import { Mock } from '../../mock/mock'; +import { User, UserDocument } from '../../../src/data/models/user'; +import { expect } from 'chai'; +import Deps from '../../../src/utils/deps'; +import Users from '../../../src/data/users'; +import { Lean, UserTypes } from '../../../src/data/types/entity-types'; + +describe('user-update', () => { + const client = io(`http://localhost:${process.env.PORT}`) as any; + let event: UserUpdate; + let ws: WebSocket; + + let user: UserDocument; + let key: string; + + beforeEach(async () => { + ({ event, ws, user } = await Mock.defaultSetup(client, UserUpdate)); + + regenToken(); + }); + + afterEach(async () => { + await user.deleteOne(); + await Mock.afterEach(ws); + }); + after(async () => await Mock.after(client)); + + it('client updates user, fulfilled', async () => { + await expect(updateUser()).to.be.fulfilled; + }); + + it('client updates user, user is updated', async () => { + await updateUser(); + + const newUser = await User.findById(user.id); + expect(user.username).to.not.equal(newUser.username); + }); + + it('client is impostor, rejected', async () => { + await regenToken('23u8123u12hg31873g183y21ufg321yt3'); + + await expect(updateUser()).to.be.rejectedWith('User Not Found'); + }); + + it('contains banned keys, rejected', async () => { + await expect(updateUser({ id: '123' })).to.be.rejectedWith('Contains readonly values'); + }); + + it('reorders guilds correctly, fulfilled', async () => { + const guildIds = user.guilds.map(g => g.id); + await expect(updateUser({ guilds: guildIds as any })).to.be.fulfilled; + }); + + it('reorders guilds but adds, rejected', async () => { + const newGuild = await Mock.guild(); + const guildIds = (user.guilds as any).concat(newGuild).map(g => g.id); + + await expect(updateUser({ guilds: guildIds as any })).to.be('Cannot add or remove guilds this way'); + }); + + it('reorders guilds but removes, rejected', async () => { + await expect(updateUser({ guilds: [] })).to.be('Cannot add or remove guilds this way'); + }); + + async function updateUser(options?: Partial) { + return event.invoke(ws, client, { + key, + partialUser: { + avatarURL: 'https://example.com', + username: 'mock-user', + ...options, + }, + }); + } + + async function regenToken(id = user.id) { + key = Deps + .get(Users) + .createToken(id, false); + } +}); diff --git a/backend/test/integration/ws/ws-guard.tests.ts b/backend/test/integration/ws/ws-guard.tests.ts new file mode 100644 index 00000000..2e407ce1 --- /dev/null +++ b/backend/test/integration/ws/ws-guard.tests.ts @@ -0,0 +1,231 @@ +import Deps from '../../../src/utils/deps'; +import io from 'socket.io-client'; +import { Mock } from '../../mock/mock'; +import { GuildDocument } from '../../../src/data/models/guild'; +import { UserDocument } from '../../../src/data/models/user'; +import { WSGuard } from '../../../src/api/modules/ws-guard'; +import { expect } from 'chai'; +import { WebSocket } from '../../../src/api/websocket/websocket'; +import { PermissionTypes } from '../../../src/data/types/entity-types'; +import { GuildMember } from '../../../src/data/models/guild-member'; +import { Role } from '../../../src/data/models/role'; +import { TextChannelDocument, VoiceChannelDocument } from '../../../src/data/models/channel'; + +describe('ws-guard', () => { + const client = io(`http://localhost:${process.env.PORT}`) as any; + + let guard: WSGuard; + let guild: GuildDocument; + let user: UserDocument; + let ws: WebSocket; + let textChannel: TextChannelDocument; + let voiceChannel: VoiceChannelDocument; + + beforeEach(async () => { + ({ ws, guild, user } = await Mock.defaultSetup(client)); + + textChannel = guild.channels[0] as any; + voiceChannel = guild.channels[1] as any; + + guard = Deps.get(WSGuard); + }); + + afterEach(async () => await Mock.afterEach(ws)); + after(async () => await Mock.after(client)); + + it('validateIsOwner, is not owner, throws error', async () => { + await expect( + guard.validateIsOwner(client, guild.id) + ).to.be.rejectedWith('Only the guild owner can do this'); + }); + + it('validateIsOwner, is owner, fulfilled', async () => { + ws.sessions.set(client.id, guild.ownerId); + + await expect( + guard.validateIsOwner(client, guild.id) + ).to.be.fulfilled; + }); + + it('validateIsUser, is impostor, throws error', async () => { + const result = () => guard.validateIsUser(client, guild.ownerId); + expect(result).to.throw('Unauthorized'); + }); + + it('validateIsUser, is user, fulfilled', async () => { + const result = () => guard.validateIsUser(client, user.id); + expect(result).to.not.throw; + }); + + it('can access text channel, default perms, fulfilled', async () => { + await expect( + guard.canAccessChannel(client, textChannel.id) + ).to.be.fulfilled; + }); + + it('can access text channel, no perms, rejected', async () => { + await Mock.clearRolePerms(guild); + + await expect( + guard.canAccessChannel(client, textChannel.id) + ).to.be.rejectedWith('Missing Permissions'); + }); + + it('can access channel without use, can only read messages, fulfilled', async () => { + await Mock.clearRolePerms(guild); + + await updateEveryoneRole(PermissionTypes.Text.READ_MESSAGES); + + await expect( + guard.canAccessChannel(client, textChannel.id) + ).to.be.fulfilled; + }); + + it('can access channel with use, can only read messages, rejected', async () => { + await Mock.clearRolePerms(guild); + + await updateEveryoneRole(PermissionTypes.Text.READ_MESSAGES); + + await expect( + guard.canAccessChannel(client, textChannel.id) + ).to.be.fulfilled; + }); + + it('can access channel with use, can send and read messages, rejected', async () => { + await Mock.clearRolePerms(guild); + + await updateEveryoneRole(PermissionTypes.Text.READ_MESSAGES + | PermissionTypes.Text.SEND_MESSAGES); + + await expect( + guard.canAccessChannel(client, textChannel.id) + ).to.be.fulfilled; + }); + + it('can access text channel, guild admin, fulfilled', async () => { + await Mock.giveEveryoneAdmin(guild); + + await expect( + guard.canAccessChannel(client, textChannel.id) + ).to.be.fulfilled; + }); + + it('can access text channel, guild owner, fulfilled', async () => { + await makeGuildOwner(); + + await expect( + guard.canAccessChannel(client, textChannel.id) + ).to.be.fulfilled; + }); + + it('can access voice channel, no perms, rejected', async () => { + await Mock.clearRolePerms(guild); + + await expect( + guard.canAccessChannel(client, voiceChannel.id) + ).to.be.rejectedWith('Missing Permissions'); + }); + + it('can access voice channel with use, can only connect, rejected', async () => { + await Mock.clearRolePerms(guild); + await updateEveryoneRole(PermissionTypes.Voice.CONNECT); + + await expect( + guard.canAccessChannel(client, voiceChannel.id) + ).to.be.fulfilled; + }); + + it('can access voice channel with use, can connect and speak, fulfilled', async () => { + await Mock.clearRolePerms(guild); + + await updateEveryoneRole(PermissionTypes.Voice.CONNECT + | PermissionTypes.Voice.SPEAK); + + await expect( + guard.canAccessChannel(client, voiceChannel.id) + ).to.be.fulfilled; + }); + + it('can access voice channel, default perms, fulfilled', async () => { + await expect( + guard.canAccessChannel(client, voiceChannel.id) + ).to.be.fulfilled; + }); + + it('can access voice channel, guild owner, fulfilled', async () => { + await makeGuildOwner(); + + await expect( + guard.canAccessChannel(client, voiceChannel.id) + ).to.be.fulfilled; + }); + + it('can access channel, dm, not a member, rejected', async () => { + const dm = await Mock.channel({ type: 'DM' }); + + await expect( + guard.canAccessChannel(client, dm.id) + ).to.be.rejectedWith('Not DM Member'); + }); + + it('can access channel, dm, is member, fulfilled', async () => { + const dm = await Mock.channel({ type: 'DM' }); + dm.memberIds.push(user.id); + await dm.updateOne(dm); + + await expect( + guard.canAccessChannel(client, dm.id) + ).to.be.fulfilled; + }); + + it('can, not a guild member, rejected', async () => { + await GuildMember.deleteOne({ userId: user.id }); + + await expect( + guard.validateCan(client, guild.id, PermissionTypes.Text.SEND_MESSAGES) + ).to.be.rejectedWith('Member Not Found'); + }); + + it('can, guild does not exist, rejected', async () => { + await guild.deleteOne(); + + await expect( + guard.validateCan(client, guild.id, PermissionTypes.Text.SEND_MESSAGES) + ).to.be.rejectedWith('Guild Not Found'); + }); + + it('can, missing perms, rejected', async () => { + await Mock.clearRolePerms(guild); + + await expect( + guard.validateCan(client, guild.id, PermissionTypes.Text.SEND_MESSAGES) + ).to.be.rejectedWith('Missing Permissions'); + }); + + it('can, send messages, default perms, fulfilled', async () => { + await expect( + guard.validateCan(client, guild.id, PermissionTypes.Text.SEND_MESSAGES) + ).to.be.fulfilled; + }); + + it('can, is owner, fulfilled', async () => { + await makeGuildOwner(); + + await expect( + guard.validateCan(client, guild.id, PermissionTypes.Text.SEND_MESSAGES) + ).to.be.fulfilled; + }); + + async function updateEveryoneRole(permissions: PermissionTypes.Permission) { + const role = guild.roles[0]; + await Role.updateOne( + { _id: role.id }, + { permissions }, + ); + } + + async function makeGuildOwner() { + ws.sessions.set(client.id, guild.ownerId); + await Mock.clearRolePerms(guild); + } +}); diff --git a/backend/test/mock/email-mock.ts b/backend/test/mock/email-mock.ts new file mode 100644 index 00000000..5b4b9b38 --- /dev/null +++ b/backend/test/mock/email-mock.ts @@ -0,0 +1,9 @@ +import { EmailTemplate } from '../../src/api/modules/email/email'; + +export class EmailMock { + public emails = new Map(); + + public async send(template: K, ctx: EmailTemplate[K], ...to: string[]) { + this.emails.set(to, [template, ctx]); + } +} \ No newline at end of file diff --git a/backend/test/mock/mock.ts b/backend/test/mock/mock.ts new file mode 100644 index 00000000..58bfd804 --- /dev/null +++ b/backend/test/mock/mock.ts @@ -0,0 +1,207 @@ +import { Channel, ChannelDocument } from '../../src/data/models/channel'; +import { Guild, GuildDocument } from '../../src/data/models/guild'; +import { GuildMember, GuildMemberDocument } from '../../src/data/models/guild-member'; +import { User, SelfUserDocument, UserDocument } from '../../src/data/models/user'; +import { generateSnowflake } from '../../src/data/snowflake-entity'; +import { Role, RoleDocument } from '../../src/data/models/role'; +import { ChannelTypes, InviteTypes, Lean, PermissionTypes, UserTypes } from '../../src/data/types/entity-types'; +import { Message } from '../../src/data/models/message'; +import { Invite } from '../../src/data/models/invite'; +import { Application } from '../../src/data/models/application'; +import { API } from '../../src/api/server'; +import { WebSocket } from '../../src/api/websocket/websocket'; +import Deps from '../../src/utils/deps'; +import Guilds from '../../src/data/guilds'; +import GuildMembers from '../../src/data/guild-members'; +import Channels from '../../src/data/channels'; + +// mostly replace will data wrappers +export class Mock { + private static channels = Deps.get(Channels); + private static guilds = Deps.get(Guilds); + private static guildMembers = Deps.get(GuildMembers); + + public static async defaultSetup(client: any, eventType: any = function() {}) { + Deps.get(API); + + const event = new (eventType as any)(); + const ws = Deps.get(WebSocket); + + const guild = await Mock.guild(); + const member = new GuildMember(guild.members[1]); + const role = new Role(guild.roles[0]); + const user = await User.findById(member.userId); + const channel = new Channel(guild.channels[0]); + + Mock.ioClient(client); + + ws.sessions.set(client.id, user.id); + + return { event, guild, user, member, ws, role, channel }; + } + public static async afterEach(ws) { + ws.sessions.clear(); + await Mock.cleanDB(); + } + public static async after(client) { + client.disconnect(); + } + + public static ioClient(client: any) { + client.rooms = new Map(); + client.join = async (...args) => { + for (const arg of args) + client.rooms.set(arg, arg); + }; + client.leave = async (...args) => { + for (const arg of args) + client.rooms.delete(arg); + }; + } + + public static async message(author: Lean.User, channelId: string) { + return await Message.create({ + _id: generateSnowflake(), + authorId: author.id, + channelId, + content: 'hi', + }); + } + + public static async guild(): Promise { + const owner = await Mock.user(); + const memberUser = await Mock.user(); + + const guild = await this.guilds.create('Mock Guild', owner); + + owner.guilds.push(guild.id as any); + await owner.save(); + + await this.guildMembers.create(guild, memberUser, guild.roles[0] as any); + return guild; + } + + public static async user(guildIds: string[] = []): Promise { + return await User.create({ + _id: generateSnowflake(), + avatarURL: 'a', + bot: false, + badges: [], + friendIds: [], + friendRequestIds: [], + email: `${generateSnowflake()}@gmail.com`, + verified: true, + guilds: guildIds, + status: 'OFFLINE', + username: `mock-user-${generateSnowflake()}`, + } as any); + } + + public static async self(guildIds: string[] = []) { + return await this.user(guildIds) as SelfUserDocument; + } + + public static async bot(guildIds: string[] = []): Promise { + return await User.create({ + _id: generateSnowflake(), + avatarURL: 'a', + bot: true, + badges: [], + friendIds: [], + friendRequestIds: [], + email: `${generateSnowflake()}@gmail.com`, // FIXME + guilds: guildIds, + status: 'ONLINE', + username: `mock-bot-${generateSnowflake()}`, + } as any) as SelfUserDocument; + } + + public static async guildMember(user: UserDocument, guild: GuildDocument): Promise { + return await this.guildMembers.create(guild, user); + } + + public static async channel(options?: Partial): Promise { + const channel = await this.channels.create(options); + + if (options?.guildId) { + const guild = await Guild.findById(options.guildId); + guild.channels.push(channel); + await guild.save(); + } + return channel; + } + + public static async role(guild: GuildDocument, permissions?: number): Promise { + const role = await Role.create({ + _id: generateSnowflake(), + guildId: guild.id, + hoisted: false, + mentionable: true, + name: 'Mock Role', + permissions: permissions ?? PermissionTypes.defaultPermissions, + }); + guild.roles.push(role.id as any); + await guild.save(); + + return role; + } + + public static async everyoneRole(guildId: string, permissions = PermissionTypes.defaultPermissions) { + return await Role.create({ + _id: generateSnowflake(), + guildId, + hoisted: false, + mentionable: true, + name: '@everyone', + permissions, + }); + } + + public static async invite(guildId: string, options?: InviteTypes.Options) { + return await Invite.create({ + _id: generateSnowflake(), + inviterId: generateSnowflake(), + options, + guildId, + uses: 0, + }); + } + + public static async clearRolePerms(guild: Lean.Guild) { + await Role.updateOne( + { _id: guild.roles?.[0].id }, + { permissions: 0 }, + ); + } + + public static async giveRolePerms(role: RoleDocument, permissions: PermissionTypes.Permission) { + role.permissions |= permissions; + await role.save(); + } + + public static async giveEveryoneAdmin(guild: Lean.Guild) { + await Role.updateOne( + { _id: guild.roles[0].id }, + { permissions: PermissionTypes.General.ADMINISTRATOR }, + ); + } + + public static async givePerm(guild: GuildDocument, member: GuildMemberDocument, permissions: PermissionTypes.Permission) { + const role = await this.role(guild, permissions); + + + member.roleIds.push(role.id); + await member.save(); + } + + public static async cleanDB() { + await Application.deleteMany({}); + await Channel.deleteMany({}); + await Guild.deleteMany({}); + await GuildMember.deleteMany({}); + await Invite.deleteMany({}); + await Message.deleteMany({}); + await Role.deleteMany({}); + await User.deleteMany({}); + } +} diff --git a/backend/test/test-utils.ts b/backend/test/test-utils.ts new file mode 100644 index 00000000..a8eadf55 --- /dev/null +++ b/backend/test/test-utils.ts @@ -0,0 +1,15 @@ +import { generateSnowflake } from '../src/data/snowflake-entity'; + +export function longString(length: number) { + return new Array(length).fill('a').join(); +} +export function longArray(length: number) { + return new Array(length).fill(generateSnowflake()); +} +export function mongooseError(error: any): string | boolean { + if (error) { + const key = Object.keys(error.errors)[0]; + return error?.errors[key].message; + } + return true; +} diff --git a/backend/test/test.ts b/backend/test/test.ts new file mode 100644 index 00000000..194e0d98 --- /dev/null +++ b/backend/test/test.ts @@ -0,0 +1,63 @@ +import '../src/data/types/env'; +import { config } from 'dotenv'; +import { execSync } from 'child_process'; +config({ path: 'test/.env' }); + +import { should, use } from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import chaiSpies from 'chai-spies'; +import chaiThings from 'chai-things'; +import { connect } from 'mongoose'; + +use(chaiAsPromised); +use(chaiSpies); +use(chaiThings); +use(should); + +(async() => { + await connect(process.env.MONGO_URI, { + useUnifiedTopology: true, + useNewUrlParser: true, + useFindAndModify: false, + useCreateIndex: true, + }, (error) => error && console.log(error)); + + try { + // remove glitched test processes + execSync(`kill -9 $(lsof -i :${process.env.PORT} | tail -n 1 | cut -d ' ' -f5)`); + } catch {} + + // await import('./integration/routes/auth-routes.tests'); + await import('./integration/routes/invites-routes.tests'); + await import('./integration/routes/guilds-routes.tests'); + await import('./integration/routes/channel-routes.tests'); + + await import('./integration/ws/add-friend.tests'); + await import('./integration/ws/channel-create.tests'); + await import('./integration/ws/guild-member-add.tests'); + await import('./integration/ws/guild-member-remove.tests'); + await import('./integration/ws/guild-member-update.tests'); + await import('./integration/ws/guild-create.tests'); + await import('./integration/ws/guild-delete.tests'); + await import('./integration/ws/guild-update.tests'); + await import('./integration/ws/invite-create.tests'); + await import('./integration/ws/invite-delete.tests'); + await import('./integration/ws/message-create.tests'); + await import('./integration/ws/message-update.tests'); + await import('./integration/ws/message-delete.tests'); + await import('./integration/ws/ready.tests'); + await import('./integration/ws/remove-friend.tests'); + await import('./integration/ws/user-update.tests'); + await import('./integration/ws/ws-guard.tests'); + + await import('./unit/models/application.tests'); + await import('./unit/models/channel.tests'); + await import('./unit/models/guild.tests'); + await import('./unit/models/guild-member.tests'); + await import('./unit/models/invite.tests'); + await import('./unit/models/message.tests'); + await import('./unit/models/role.tests'); + await import('./unit/models/user.tests'); + await import('./unit/snowflake-entity.tests'); + await import('./unit/ws/ws-cooldowns.tests'); +})(); diff --git a/backend/test/unit/models/application.tests.ts b/backend/test/unit/models/application.tests.ts new file mode 100644 index 00000000..3abd589a --- /dev/null +++ b/backend/test/unit/models/application.tests.ts @@ -0,0 +1,35 @@ +import { generateSnowflake } from '../../../src/data/snowflake-entity'; +import { test, given } from 'sazerac'; +import { longString, mongooseError } from '../../test-utils'; +import { Application } from '../../../src/data/models/application'; + +test(createApplication, () => { + given().expect(true); + given({ _id: null }).expect(true); + given({ description: '' }).expect('Description is required'); + given({ description: longString(1001) }).expect('Description too long'); + given({ description: 'Very epic' }).expect(true); + given({ name: '' }).expect('Name is required'); + given({ name: longString(33) }).expect('Name is too long'); + given({ name: 'Epic Bot' }).expect('Name contains invalid characters'); + given({ name: 'Epic-Bot' }).expect(true); + given({ owner: '' }).expect('Owner is required'); + given({ owner: '123' }).expect('Invalid Snowflake ID'); + given({ owner: generateSnowflake() }).expect(true); + given({ user: '' }).expect('User is required'); + given({ user: '123' }).expect('Invalid Snowflake ID'); + given({ user: generateSnowflake() }).expect(true); +}); + +function createApplication(message: any) { + const error = new Application({ + _id: generateSnowflake(), + user: generateSnowflake(), + owner: generateSnowflake(), + name: 'Epic-Bot', + description: 'Very epic', + ...message, + }).validateSync(); + + return mongooseError(error); +} diff --git a/backend/test/unit/models/channel.tests.ts b/backend/test/unit/models/channel.tests.ts new file mode 100644 index 00000000..f2a4de80 --- /dev/null +++ b/backend/test/unit/models/channel.tests.ts @@ -0,0 +1,43 @@ +import { Channel } from '../../../src/data/models/channel'; +import { generateSnowflake } from '../../../src/data/snowflake-entity'; +import { test, given } from 'sazerac'; +import { longArray, longString, mongooseError } from '../../test-utils'; +import { Lean } from '../../../src/data/types/entity-types'; + +test(createChannel, () => { + given().expect(true); + given({ guildId: '123' }).expect('Invalid Snowflake ID'); + given({ guildId: generateSnowflake() }).expect(true); + given({ memberIds: longArray(51) }).expect('Channel member limit reached'); + given({ memberIds: longArray(49) }).expect(true); + given({ guildId: null }).expect(true); + given({ guildId: '123' }).expect('Invalid Snowflake ID'); + given({ guildId: generateSnowflake() }).expect(true); + given({ name: '' }).expect('Name is required'); + given({ name: longString(33) }).expect('Name too long'); + given({ name: 'channel-name' }).expect(true); + given({ name: 'channel name', type: 'TEXT' }).expect('Invalid name'); + given({ name: 'channel name', type: 'VOICE' }).expect(true); + given({ name: 'channel name', type: 'DM' }).expect(true); + given({ summary: longString(129) }).expect('Summary too long'); + given({ summary: 'cool channel' }).expect(true); + given({ type: 'A' }).expect('Invalid type'); + given({ type: 'TEXT' }).expect(true); + given({ type: 'VOICE' }).expect(true); + given({ type: 'DM' }).expect(true); + given({ lastMessageId: generateSnowflake() }).expect(true); + given({ lastMessageId: '' }).expect(true); + given({ lastMessageId: '123' }).expect('Invalid Snowflake ID'); +}); + +function createChannel(channel: Partial) { + const error = new Channel({ + _id: generateSnowflake(), + name: `mock-channel`, + summary: 'Cool channel, I guess...', + type: 'TEXT', + ...channel, + }).validateSync(); + + return mongooseError(error); +} diff --git a/backend/test/unit/models/guild-member.tests.ts b/backend/test/unit/models/guild-member.tests.ts new file mode 100644 index 00000000..85d2da96 --- /dev/null +++ b/backend/test/unit/models/guild-member.tests.ts @@ -0,0 +1,29 @@ +import { generateSnowflake } from '../../../src/data/snowflake-entity'; +import { test, given } from 'sazerac'; +import { mongooseError } from '../../test-utils'; +import { GuildMember } from '../../../src/data/models/guild-member'; + +test(createGuildMember, () => { + given().expect(true); + given({ guildId: '' }).expect('Guild ID is required'); + given({ guildId: '123' }).expect('Invalid Snowflake ID'); + given({ guildId: generateSnowflake() }).expect(true); + given({ userId: '' }).expect('User ID is required'); + given({ userId: '123' }).expect('Invalid Snowflake ID'); + given({ userId: generateSnowflake() }).expect(true); + given({ roleIds: null }).expect('Role IDs is required'); + given({ roleIds: [] }).expect('At least 1 role is required'); + given({ roleIds: [generateSnowflake()] }).expect(true); +}); + +function createGuildMember(member: any) { + const error = new GuildMember({ + _id: generateSnowflake(), + guildId: generateSnowflake(), + userId: generateSnowflake(), + roleIds: [generateSnowflake()], + ...member, + }).validateSync(); + + return mongooseError(error); +} diff --git a/backend/test/unit/models/guild.tests.ts b/backend/test/unit/models/guild.tests.ts new file mode 100644 index 00000000..be5c4a0c --- /dev/null +++ b/backend/test/unit/models/guild.tests.ts @@ -0,0 +1,34 @@ +import { generateSnowflake } from '../../../src/data/snowflake-entity'; +import { test, given } from 'sazerac'; +import { longArray, longString, mongooseError } from '../../test-utils'; +import { Guild } from '../../../src/data/models/guild'; + +test(createGuild, () => { + given().expect(true); + given({ channels: longArray(251) }).expect('Channel limit reached'); + given({ channels: longArray(250) }).expect(true); + given({ name: '' }).expect('Name is required'); + given({ name: 'Mock Guild' }).expect(true); + given({ name: longString(33) }).expect('Name is too long'); + given({ ownerId: '123' }).expect('Invalid Snowflake ID'); + given({ ownerId: generateSnowflake() }).expect(true); + given({ roles: [] }).expect('Guild must have at least one role'); + given({ roles: [generateSnowflake()] }).expect(true); +}); + +function createGuild(guild: any) { + const error = new Guild({ + _id: generateSnowflake(), + name: 'Mock Guild', + ownerId: generateSnowflake(), + roles: [generateSnowflake()], + channels: [ + generateSnowflake(), + generateSnowflake(), + ], + members: [generateSnowflake()], + ...guild + }).validateSync(); + + return mongooseError(error); +} diff --git a/backend/test/unit/models/invite.tests.ts b/backend/test/unit/models/invite.tests.ts new file mode 100644 index 00000000..4d406a73 --- /dev/null +++ b/backend/test/unit/models/invite.tests.ts @@ -0,0 +1,37 @@ +import { generateSnowflake } from '../../../src/data/snowflake-entity'; +import { test, given } from 'sazerac'; +import { longArray, longString, mongooseError } from '../../test-utils'; +import { Invite } from '../../../src/data/models/invite'; + +test(createInvite, () => { + given().expect(true); + given({ guildId: '' }).expect('Guild ID is required'); + given({ guildId: '123' }).expect('Invalid Snowflake ID'); + given({ guildId: generateSnowflake() }).expect(true); + given({ inviterId: '' }).expect('Inviter ID is required'); + given({ inviterId: '123' }).expect('Invalid Snowflake ID'); + given({ inviterId: generateSnowflake() }).expect(true); + given({ options: inviteOptions() }).expect(true); + given({ options: inviteOptions({ maxUses: 0 }) }).expect('Max uses too low'); + given({ options: inviteOptions({ maxUses: 100 }) }).expect(true); + given({ options: inviteOptions({ maxUses: 1001 }) }).expect('Max uses too high'); +}); + +function createInvite(invite: any) { + const error = new Invite({ + _id: generateSnowflake(), + inviterId: generateSnowflake(), + guildId: generateSnowflake(), + content: 'hi', + ...invite, + }).validateSync(); + + return mongooseError(error); +} +function inviteOptions(options?: any) { + return { + expiresAt: new Date(), + maxUses: 100, + ...options, + } +} diff --git a/backend/test/unit/models/message.tests.ts b/backend/test/unit/models/message.tests.ts new file mode 100644 index 00000000..a89c43d4 --- /dev/null +++ b/backend/test/unit/models/message.tests.ts @@ -0,0 +1,41 @@ +import { generateSnowflake } from '../../../src/data/snowflake-entity'; +import { test, given } from 'sazerac'; +import { longArray, longString, mongooseError } from '../../test-utils'; +import { Message } from '../../../src/data/models/message'; + +test(createMessage, () => { + given().expect(true); + given({ authorId: '' }).expect('Author ID is required'); + given({ authorId: '123' }).expect('Invalid Snowflake ID'); + given({ authorId: generateSnowflake() }).expect(true); + given({ channelId: '' }).expect('Channel ID is required'); + given({ channelId: '123' }).expect('Invalid Snowflake ID'); + given({ channelId: generateSnowflake() }).expect(true); + given({ content: '' }).expect('Content too short'); + given({ content: longString(3001) }).expect('Content too long'); + given({ content: 'hi' }).expect(true); + given({ embed: null }).expect(true); + given({ embed: createEmbed() }).expect(true); +}); + +function createMessage(message: any) { + const error = new Message({ + _id: generateSnowflake(), + authorId: generateSnowflake(), + channelId: generateSnowflake(), + content: 'hi', + ...message, + }).validateSync(); + + return mongooseError(error); +} + +function createEmbed(embed?: any) { + return { + description: 'Cool embed', + image: 'Cool image', + title: 'Cool title', + url: 'https://example.com', + ...embed, + }; +} diff --git a/backend/test/unit/models/role.tests.ts b/backend/test/unit/models/role.tests.ts new file mode 100644 index 00000000..adbfba27 --- /dev/null +++ b/backend/test/unit/models/role.tests.ts @@ -0,0 +1,37 @@ +import { generateSnowflake } from '../../../src/data/snowflake-entity'; +import { test, given } from 'sazerac'; +import { longString, mongooseError } from '../../test-utils'; +import { Role } from '../../../src/data/models/role'; +import { PermissionTypes } from '../../../src/data/types/entity-types'; + +test(createRole, () => { + given().expect(true); + given({ color: '#FFFFFF', name: '@everyone' }).expect('Cannot change @everyone role color'); + given({ color: '#FFFFFF' }).expect(true); + given({ color: '' }).expect(true); + given({ name: '' }).expect('Name is required'); + given({ name: longString(33) }).expect('Name too long'); + given({ name: 'Mock Role' }).expect(true); + given({ permissions: -1 }).expect('Invalid permissions integer'); + given({ permissions: 255 }).expect(true); + given({ permissions: 4 }).expect(true); + given({ permissions: 1 }).expect(true); + given({ permissions: null }).expect('Permissions is required'); + given({ permissions: PermissionTypes.defaultPermissions }).expect(true); + given({ permissions: 0 }).expect(true); +}); + +function createRole(guild: any) { + const error = new Role({ + _id: generateSnowflake(), + color: '#FFFFFF', + guildId: generateSnowflake(), + hoisted: false, + mentionable: true, + name: 'Mock Role', + permissions: PermissionTypes.defaultPermissions, + ...guild, + }).validateSync(); + + return mongooseError(error); +} diff --git a/backend/test/unit/models/user.tests.ts b/backend/test/unit/models/user.tests.ts new file mode 100644 index 00000000..6b0db7a1 --- /dev/null +++ b/backend/test/unit/models/user.tests.ts @@ -0,0 +1,78 @@ +import { generateSnowflake } from '../../../src/data/snowflake-entity'; +import { test, given } from 'sazerac'; +import { longArray, mongooseError } from '../../test-utils'; +import { User } from '../../../src/data/models/user'; +import { UserTypes } from '../../../src/data/types/entity-types'; +import { Mock } from '../../mock/mock'; +import { expect } from 'chai'; + +test(createUser, () => { + given().expect(true); + given({ avatarURL: '' }).expect('Avatar URL is required'); + given({ avatarURL: 'a' }).expect(true); + given({ friendIds: longArray(101) }).expect('Clout limit reached'); + given({ friendIds: [] }).expect(true); + given({ friendRequestIds: longArray(101) }).expect('Max friend requests reached'); + given({ friendRequestIds: [] }).expect(true); + given({ status: '' }).expect('Status is required'); + given({ status: 'A' }).expect('Invalid status'); + given({ status: 'ONLINE' }).expect(true); + given({ status: 'BUSY' }).expect(true); + given({ status: 'AFK' }).expect(true); + given({ status: 'OFFLINE' }).expect(true); + given({ email: '' }).expect(true); + given({ email: 'a' }).expect('Invalid email address'); + given({ email: 'a@a' }).expect('Invalid email address'); + given({ email: 'adam@d-cl.one' }).expect(true); + given({ ignored: null }).expect(true); + given({ ignored: { channelIds: [] } }).expect(true); + given({ ignored: { guildIds: [] } }).expect(true); + given({ ignored: { userIds: [] } }).expect(true); + given({ _id: '123', ignored: { userIds: ['123'] } }).expect('Cannot block self'); + given({ username: '' }).expect('Username is required'); + given({ username: 'ADAMJR' }).expect(true); + given({ username: 'ADAM JR' }).expect('Invalid username'); + given({ username: 'a' }).expect('Invalid username'); + given({ username: 'ADAM-JR' }).expect(true); + + it('email is taken, rejected', async () => { + const user = await Mock.self(); + user.email = 'adam@d-cl.one'; + await user.save(); + + const user2 = await Mock.self(); + user2.email = 'adam@d-cl.one'; + + await expect(user2.validate()).to.be.rejectedWith('expected `email` to be unique'); + }); + + it('username is taken, rejected', async () => { + const user = await Mock.self(); + user.username = 'Adam'; + await user.save(); + + const user2 = await Mock.user(); + user2.username = 'adam'; + + await expect(user2.validate()).to.be.rejectedWith('expected `username` to be unique'); + }); + + after(() => Mock.cleanDB()); +}); + +function createUser(user: any) { + const error = new User({ + _id: generateSnowflake(), + avatarURL: 'a', + bot: false, + badges: [], + friendIds: [], + friendRequestIds: [], + guilds: [generateSnowflake()], + status: 'ONLINE', + username: `mock-user-${generateSnowflake()}`, + ...user, + }).validateSync(); + + return mongooseError(error); +} diff --git a/backend/test/unit/snowflake-entity.tests.ts b/backend/test/unit/snowflake-entity.tests.ts new file mode 100644 index 00000000..93442080 --- /dev/null +++ b/backend/test/unit/snowflake-entity.tests.ts @@ -0,0 +1,31 @@ +import { expect } from 'chai'; +import { generateSnowflake, snowflakeToDate } from '../../src/data/snowflake-entity'; +import { patterns } from '../../src/data/types/entity-types'; + +describe('data/snowflake-entity', () => { + it('get snowflake, 2 ids in same ms, snowflake is different', () => { + expect(generateSnowflake()).to.not.equal(generateSnowflake()); + }); + + it('snowflake to date, get date from snowflake, very similar time', () => { + const date = new Date(); + const snowflake = generateSnowflake(); + const createdAt = snowflakeToDate(snowflake); + + expect(date.toString()).to.equal(createdAt.toString()); + }); + + it('lots of snowflakes generated, all are unique', () => { + const oneIsUnique = (val, i, arr) => val !== arr[Math.min(0, i - 1)]; + + const equal = new Array(100) + .map(generateSnowflake) + .every(oneIsUnique); + + expect(equal).to.be.true; + }); + + it('snowflake id matches regex pattern', () => { + expect(generateSnowflake()).to.match(patterns.snowflake); + }); +}); diff --git a/backend/test/unit/ws/ws-cooldowns.tests.ts b/backend/test/unit/ws/ws-cooldowns.tests.ts new file mode 100644 index 00000000..5ea66878 --- /dev/null +++ b/backend/test/unit/ws/ws-cooldowns.tests.ts @@ -0,0 +1,44 @@ +import { expect } from 'chai'; +import { WSCooldowns } from '../../../src/api/websocket/modules/ws-cooldowns'; +import { generateInviteCode } from '../../../src/data/models/invite'; + +describe('ws-cooldowns', () => { + let clientId: string; + let cooldowns: WSCooldowns; + + beforeEach(() => { + cooldowns = new WSCooldowns(); + clientId = generateInviteCode(); + }); + + it('handle, adds cooldown', () => { + handle(); + expect(cooldowns.active.size).to.equal(1); + }); + + it('handle, no cooldowns, no error', () => { + expect(handle).to.not.throw(); + }); + + it('handle, exceeds max cooldowns, throws error', () => { + for (let i = 0; i < 60; i++) + handle(); + + expect(handle).to.throw('You are doing too many things at once!'); + }); + + it('handle, prunes old cooldowns', () => { + cooldowns.active.set(clientId, [ + { eventName: 'ADD_FRIEND', timestamp: new Date(0).getTime() }, + { eventName: 'ADD_FRIEND', timestamp: new Date(0).getTime() }, + ]); + + handle(); + + expect(cooldowns.active.size).to.equal(1); + }); + + function handle() { + return cooldowns.handle(clientId, 'ADD_FRIEND'); + } +}); diff --git a/backend/tsconfig.json b/backend/tsconfig.json index 7d813bc6..840743f1 100644 --- a/backend/tsconfig.json +++ b/backend/tsconfig.json @@ -1,72 +1,21 @@ -{ - "compilerOptions": { - /* Visit https://aka.ms/tsconfig.json to read more about this file */ - - /* Basic Options */ - // "incremental": true, /* Enable incremental compilation */ - "target": "es2021", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */ - "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ - // "lib": [], /* Specify library files to be included in the compilation. */ - // "allowJs": true, /* Allow javascript files to be compiled. */ - // "checkJs": true, /* Report errors in .js files. */ - // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */ - // "declaration": true, /* Generates corresponding '.d.ts' file. */ - // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - // "sourceMap": true, /* Generates corresponding '.map' file. */ - // "outFile": "./", /* Concatenate and emit output to single file. */ - // "outDir": "./", /* Redirect output structure to the directory. */ - // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ - // "composite": true, /* Enable project compilation */ - // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ - // "removeComments": true, /* Do not emit comments to output. */ - // "noEmit": true, /* Do not emit outputs. */ - // "importHelpers": true, /* Import emit helpers from 'tslib'. */ - // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ - // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ - - /* Strict Type-Checking Options */ - "strict": true, /* Enable all strict type-checking options. */ - // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* Enable strict null checks. */ - // "strictFunctionTypes": true, /* Enable strict checking of function types. */ - // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ - // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ - // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ - // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ - - /* Additional Checks */ - // "noUnusedLocals": true, /* Report errors on unused locals. */ - // "noUnusedParameters": true, /* Report errors on unused parameters. */ - // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ - // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ - // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ - // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */ - // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ - - /* Module Resolution Options */ - // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ - // "baseUrl": "../types", /* Base directory to resolve non-absolute module names. */ /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ - // "rootDirs": ["../"], /* List of root folders whose combined content represents the structure of the project at runtime. */ - // "typeRoots": ["node_modules/@types", "./"], /* List of folders to include type definitions from. */ - // "types": ["*"], /* Type declaration files to be included in compilation. */ - // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ - "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ - // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - - /* Source Map Options */ - // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ - // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ - - /* Experimental Options */ - // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ - // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ - - /* Advanced Options */ - "skipLibCheck": true, /* Skip type checking of declaration files. */ - "forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */ - "noImplicitAny": false - } -} +{ + "compileOnSave": true, + "compilerOptions": { + "downlevelIteration": true, + "esModuleInterop": true, + "inlineSourceMap": true, + "lib": ["ES2020", "ES2019"], + "module": "CommonJS", + "noImplicitAny": false, + "outDir": "lib", + "removeComments": true, + "resolveJsonModule": true, + "strict": true, + "strictNullChecks": true, + "strictPropertyInitialization": false, + "target": "ES2020", + "skipLibCheck": true, + "watch": true + }, + "include": ["src", "env"] +} \ No newline at end of file diff --git a/frontend/src/components/pages/page-wrapper.tsx b/frontend/src/components/pages/page-wrapper.tsx index 9586d6d7..26c7c8b0 100644 --- a/frontend/src/components/pages/page-wrapper.tsx +++ b/frontend/src/components/pages/page-wrapper.tsx @@ -11,8 +11,7 @@ import WSListener from '../ws-listener'; export type PageWrapperProps = React.DetailedHTMLProps< React.HTMLAttributes, - HTMLDivElement ->; + HTMLDivElement> & { pageTitle?: string; }; // all separate pages should be wrapped by this const PageWrapper: React.FunctionComponent = (props) => { diff --git a/frontend/src/store/services/ws.ts b/frontend/src/store/services/ws.ts index 8e813631..4b2af840 100644 --- a/frontend/src/store/services/ws.ts +++ b/frontend/src/store/services/ws.ts @@ -12,8 +12,8 @@ ws.io.on('open', () => console.log('Connected to WS Server')); export default ws as WSClient; interface WSClient { - emit: (event: K, callback: (args: API.ToWSAPI[K]) => any) => any, - on: (event: K | 'error' | 'disconnect', callback: (args: API.FromWSAPI[K]) => any) => any, - off: (event: string, callback?: any) => any, - disconnect: () => any, + emit: (event: K, callback: (args: API.ToWSAPI[K]) => any) => any; + on: (event: K | 'error' | 'disconnect', callback: (args: API.FromWSAPI[K]) => any) => any; + off: (event: string, callback?: any) => any; + disconnect: () => any; } \ No newline at end of file diff --git a/types/entity.d.ts b/types/entity.d.ts index 1fbf6523..2ba8b7ad 100644 --- a/types/entity.d.ts +++ b/types/entity.d.ts @@ -49,5 +49,6 @@ declare namespace Entity { id: string; username: string; guildIds: string[]; + status: string; } } \ No newline at end of file diff --git a/types/patterns.ts b/types/patterns.ts new file mode 100644 index 00000000..41cc2dec --- /dev/null +++ b/types/patterns.ts @@ -0,0 +1,9 @@ +export default { + email: /(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/, + hexColor: /^#(?:[0-9a-fA-F]{3}){1,2}$/, + password: /(?=.*[a-zA-Z0-9!@#$%^&*])/, + snowflake: /^\d{18}$/, + status: /^ONLINE|^BUSY$|^AFK$|^OFFLINE$/, + textChannelName: /^[A-Za-z\-\d]{2,32}$/, + username: /(^(?! |^everyone$|^here$|^me$|^someone$|^discordtag$)[A-Za-z\d\-\_]{2,32}(?