From e574b6e7786b0219fcc8c4e97e36c7bd5604181e Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Sat, 9 May 2020 23:19:47 +0200 Subject: [PATCH] Add magic links --- package.json | 3 +- src/auth/magic_link/MagicLinkController.ts | 110 +++++++ .../magic_link/MagicLinkWebSocketListener.ts | 56 ++++ src/auth/migrations/CreateMagicLinksTable.ts | 23 ++ src/auth/models/MagicLink.ts | 125 ++++++++ yarn.lock | 299 +++++++++++++++++- 6 files changed, 609 insertions(+), 7 deletions(-) create mode 100644 src/auth/magic_link/MagicLinkController.ts create mode 100644 src/auth/magic_link/MagicLinkWebSocketListener.ts create mode 100644 src/auth/migrations/CreateMagicLinksTable.ts create mode 100644 src/auth/models/MagicLink.ts diff --git a/package.json b/package.json index 4cb6749..62ec5a7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "wms-core", - "version": "0.4.26", + "version": "0.4.28", "description": "Node web framework", "repository": "git@gitlab.com:ArisuOngaku/wms-core.git", "author": "Alice Gaudon ", @@ -38,6 +38,7 @@ "@types/nunjucks": "^3.1.3", "@types/redis": "^2.8.18", "@types/ws": "^7.2.4", + "argon2": "^0.26.2", "config": "^3.3.1", "connect-flash": "^0.1.1", "connect-redis": "^4.0.4", diff --git a/src/auth/magic_link/MagicLinkController.ts b/src/auth/magic_link/MagicLinkController.ts new file mode 100644 index 0000000..21e988a --- /dev/null +++ b/src/auth/magic_link/MagicLinkController.ts @@ -0,0 +1,110 @@ +import Controller from "../../Controller"; +import {Request, Response} from "express"; +import MagicLinkWebSocketListener from "./MagicLinkWebSocketListener"; +import {BadRequestError, NotFoundHttpError} from "../../HttpError"; +import querystring from "querystring"; +import Throttler from "../../Throttler"; +import Mail, {MailTemplate} from "../../Mail"; +import MagicLink from "../models/MagicLink"; +import config from "config"; + +export default abstract class MagicLinkController extends Controller { + public static async sendMagicLink(sessionID: string, actionType: string, email: string, mailTemplate: MailTemplate, data: object, req: Request, res: Response): Promise { + Throttler.throttle('magic_link', 2, MagicLink.validityPeriod(), sessionID, 0, 0); + Throttler.throttle('magic_link', 1, MagicLink.validityPeriod(), email, 0, 0); + + let link = await MagicLink.bySessionID(sessionID, actionType); + if (!link) { + link = new MagicLink({ + session_id: sessionID, + }); + } + + const token = await link.generateToken(email); + await link.save(); + + // Send email + await new Mail(mailTemplate, Object.assign(data, { + link: `${req.protocol}://${req.get('host') + Controller.route('magic_link')}?${querystring.stringify({ + id: link.id, + token: token, + })}`, + })).send(email); + + res.redirect(Controller.route('magic_link_lobby')); + } + + + protected readonly magicLinkWebsocketPath: string; + + protected constructor(magicLinkWebsocketListener: MagicLinkWebSocketListener) { + super(); + this.magicLinkWebsocketPath = magicLinkWebsocketListener.path(); + } + + getRoutesPrefix(): string { + return '/magic'; + } + + routes(): void { + this.get('/lobby', this.getLobby, 'magic_link_lobby'); + this.get('/link', this.getMagicLink, 'magic_link'); + } + + private async getLobby(req: Request, res: Response): Promise { + const link = await MagicLink.bySessionID(req.sessionID!); + if (!link) { + throw new NotFoundHttpError('magic link', req.url); + } + + if (!await link.isValid()) { + req.flash('error', 'This magic link has expired. Please try again.'); + res.redirect(link.getOriginalURL()); + return; + } + + if (await link.isAuthorized()) { + await this.performAction(link, req, res); + return; + } + + res.render('magic_link_lobby', { + email: link.getEmail(), + type: await link.getActionType(), + validUntil: link.getExpirationDate().getTime(), + websocketUrl: config.get('public_websocket_url') + this.magicLinkWebsocketPath, + }); + } + + private async getMagicLink(req: Request, res: Response): Promise { + const id = parseInt(req.query.id); + const token = req.query.token; + if (!id || !token) throw new BadRequestError('Need parameters id, token.', 'Please try again.', req.originalUrl); + + let success = true; + let err; + const magicLink = await MagicLink.getById(id); + if (!magicLink) { + res.status(404); + err = `Couldn't find this magic link. Perhaps it has already expired.`; + success = false; + } else if (!await magicLink.isAuthorized()) { + err = await magicLink.verifyToken(token); + if (err === null) { + // Validation success, authenticate the user + magicLink.authorize(); + await magicLink.save(); + MagicLinkWebSocketListener.refreshMagicLink(magicLink.getSessionID()); + } + } + + res.render('magic_link', { + actionType: await magicLink?.getActionType(), + actionMessage: await magicLink?.getActionMessage(), + err: err, + success: success && err === null, + }); + } + + protected abstract async performAction(magicLink: MagicLink, req: Request, res: Response): Promise; +} \ No newline at end of file diff --git a/src/auth/magic_link/MagicLinkWebSocketListener.ts b/src/auth/magic_link/MagicLinkWebSocketListener.ts new file mode 100644 index 0000000..beb7fa9 --- /dev/null +++ b/src/auth/magic_link/MagicLinkWebSocketListener.ts @@ -0,0 +1,56 @@ +import WebSocket from "ws"; +import {IncomingMessage} from "http"; +import WebSocketListener from "../../WebSocketListener"; +import MagicLink from "../models/MagicLink"; + +export default class MagicLinkWebSocketListener extends WebSocketListener { + private static readonly connections: { [p: string]: (() => void)[] } = {}; + + public static refreshMagicLink(sessionID: string) { + const fs = MagicLinkWebSocketListener.connections[sessionID]; + if (fs) { + fs.forEach(f => f()); + } + } + + async handle(socket: WebSocket, request: IncomingMessage, session: Express.SessionData): Promise { + // Refuse any incoming data + socket.on('message', () => { + socket.close(1003); + }); + + // Get magic link + const magicLink = await MagicLink.bySessionID(session.id); + + // Refresh if immediately applicable + if (!magicLink || !await magicLink.isValid() || await magicLink.isAuthorized()) { + socket.send('refresh'); + socket.close(1000); + return; + } + + const validityTimeout = setTimeout(() => { + socket.send('refresh'); + socket.close(1000); + }, magicLink.getExpirationDate().getTime() - new Date().getTime()); + + const f = () => { + clearTimeout(validityTimeout); + socket.send('refresh'); + socket.close(1000); + }; + + socket.on('close', () => { + MagicLinkWebSocketListener.connections[session.id] = MagicLinkWebSocketListener.connections[session.id].filter(f => f !== f); + if (MagicLinkWebSocketListener.connections[session.id].length === 0) delete MagicLinkWebSocketListener.connections[session.id]; + }); + + if (!MagicLinkWebSocketListener.connections[session.id]) MagicLinkWebSocketListener.connections[session.id] = []; + + MagicLinkWebSocketListener.connections[session.id].push(f); + } + + path(): string { + return '/magic-link'; + } +} \ No newline at end of file diff --git a/src/auth/migrations/CreateMagicLinksTable.ts b/src/auth/migrations/CreateMagicLinksTable.ts new file mode 100644 index 0000000..059fe0d --- /dev/null +++ b/src/auth/migrations/CreateMagicLinksTable.ts @@ -0,0 +1,23 @@ +import Migration from "../../db/Migration"; +import {query} from "../../db/MysqlConnectionManager"; + +export default class CreateMagicLinksTable extends Migration { + async install(): Promise { + await query('CREATE TABLE magic_links(' + + 'id INT NOT NULL AUTO_INCREMENT,' + + 'session_id CHAR(32) UNIQUE NOT NULL,' + + 'email VARCHAR(254) NOT NULL,' + + 'token CHAR(96) NOT NULL,' + + 'action_type VARCHAR(64) NOT NULL,' + + 'original_url VARCHAR(1745) NOT NULL,' + + 'generated_at DATETIME NOT NULL,' + + 'authorized BOOLEAN NOT NULL,' + + 'PRIMARY KEY(id)' + + ')'); + } + + async rollback(): Promise { + await query('DROP TABLE magic_links'); + } + +} \ No newline at end of file diff --git a/src/auth/models/MagicLink.ts b/src/auth/models/MagicLink.ts new file mode 100644 index 0000000..c531af1 --- /dev/null +++ b/src/auth/models/MagicLink.ts @@ -0,0 +1,125 @@ +import crypto from "crypto"; +import config from "config"; +import Model, {EMAIL_REGEX} from "../../db/Model"; +import AuthProof from "../AuthProof"; +import Validator from "../../db/Validator"; +import User from "./User"; +import argon2 from "argon2"; + +export default class MagicLink extends Model implements AuthProof { + public static async bySessionID(sessionID: string, actionType?: string | string[]): Promise { + let query = this.select().where('session_id', sessionID); + if (actionType !== undefined) { + if (typeof actionType === 'string') { + query = query.where('action_type', actionType); + } else { + query = query.whereIn('action_type', actionType); + } + } + const links = await this.models(query.first()); + return links.length > 0 ? links[0] : null; + } + + public static validityPeriod(): number { + return config.get('magic_link.validity_period') * 1000; + } + + private session_id?: string; + private email?: string; + private token?: string; + private action_type?: string; + private original_url?: string; + private generated_at?: Date; + private authorized?: boolean; + + constructor(data: any) { + super(data); + if (this.action_type === undefined) throw new Error('Action type must always be defined.'); + if (this.original_url === undefined) throw new Error('Origin url must always be defined.'); + if (this.authorized === undefined) { + this.authorized = false; + } + } + + protected defineProperties(): void { + this.defineProperty('session_id', new Validator().defined().length(32).unique(this)); + this.defineProperty('email', new Validator().defined().regexp(EMAIL_REGEX)); + this.defineProperty('token', new Validator().defined().length(96)); + this.defineProperty('action_type', new Validator().defined().maxLength(64)); + this.defineProperty('original_url', new Validator().defined().maxLength(1745)); + this.defineProperty('generated_at', new Validator()); + this.defineProperty('authorized', new Validator().defined()); + } + + public async isOwnedBy(userId: number): Promise { + const user = await this.getUser(); + return user !== null && user.id === userId; + } + + public async getUser(): Promise { + return await User.fromEmail(await this.getEmail()); + } + + public async revoke(): Promise { + await this.delete(); + } + + public async isValid(): Promise { + if (await this.isAuthorized()) return true; + + return new Date().getTime() < this.getExpirationDate().getTime(); + } + + public async isAuthorized(): Promise { + return this.authorized!; + } + + public async generateToken(email: string): Promise { + const rawToken = crypto.randomBytes(48).toString('base64'); // Raw token length = 64 + this.email = email; + this.generated_at = new Date(); + this.token = await argon2.hash(rawToken, { + timeCost: 10, + memoryCost: 4096, + parallelism: 4 + }); + return rawToken; + } + + /** + * @returns {@code null} if the token is valid, an error {@code string} otherwise. + */ + public async verifyToken(tokenGuess: string): Promise { + if (this.token === undefined || this.generated_at === undefined) return 'This token was not generated.'; // There is no token + if (new Date().getTime() - this.generated_at.getTime() > MagicLink.validityPeriod()) return 'This token has expired.'; // Token has expired + if (!await argon2.verify(this.token, tokenGuess)) return 'This token is invalid.'; + + return null; + } + + public getSessionID(): string { + return this.session_id!; + } + + public async getEmail(): Promise { + return this.email!; + } + + public getActionType(): string { + return this.action_type!; + } + + public getOriginalURL(): string { + return this.original_url!; + } + + public getExpirationDate(): Date { + if (!this.generated_at) return new Date(); + + return new Date(this.generated_at?.getTime() + MagicLink.validityPeriod()); + } + + public authorize() { + this.authorized = true; + } +} diff --git a/yarn.lock b/yarn.lock index c0f6501..78f1c65 100644 --- a/yarn.lock +++ b/yarn.lock @@ -448,6 +448,13 @@ "@types/yargs" "^15.0.0" chalk "^3.0.0" +"@phc/format@^0.5.0": + version "0.5.0" + resolved "https://registry.toot.party/@phc%2fformat/-/format-0.5.0.tgz#a99d27a83d78b3100a191412adda04315e2e3aba" + integrity sha512-JWtZ5P1bfXU0bAtTzCpOLYHDXuxSVdtL/oqz4+xa97h8w9E5IlVN333wugXVFv8vZ1hbXObKQf1ptXmFFcMByg== + dependencies: + safe-buffer "^5.1.2" + "@sinonjs/commons@^1.7.0": version "1.7.2" resolved "https://registry.toot.party/@sinonjs%2fcommons/-/commons-1.7.2.tgz#505f55c74e0272b43f6c52d81946bed7058fc0e2" @@ -778,6 +785,16 @@ ansi-escapes@^4.2.1: dependencies: type-fest "^0.11.0" +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.toot.party/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.toot.party/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + ansi-regex@^4.1.0: version "4.1.0" resolved "https://registry.toot.party/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" @@ -819,11 +836,33 @@ anymatch@^3.0.3, anymatch@~3.1.1: normalize-path "^3.0.0" picomatch "^2.0.4" +aproba@^1.0.3: + version "1.2.0" + resolved "https://registry.toot.party/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== + +are-we-there-yet@~1.1.2: + version "1.1.5" + resolved "https://registry.toot.party/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" + integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + arg@^4.1.0: version "4.1.3" resolved "https://registry.toot.party/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== +argon2@^0.26.2: + version "0.26.2" + resolved "https://registry.toot.party/argon2/-/argon2-0.26.2.tgz#16c62637f79f8511d666c9a824dd991aa3a7731c" + integrity sha512-Tk9I/r3KIHCIHU5x2UawKsPi+g7MByAYnUZghXztQDXRp/997P31wa4qvdvokTaFBpsu6jOZACd+2qkBGGssRA== + dependencies: + "@phc/format" "^0.5.0" + node-addon-api "^2.0.0" + node-pre-gyp "^0.14.0" + argparse@^1.0.7: version "1.0.10" resolved "https://registry.toot.party/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -1201,6 +1240,11 @@ chokidar@^3.0.0, chokidar@^3.3.0: optionalDependencies: fsevents "~2.1.2" +chownr@^1.1.1: + version "1.1.4" + resolved "https://registry.toot.party/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + ci-info@^2.0.0: version "2.0.0" resolved "https://registry.toot.party/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" @@ -1246,6 +1290,11 @@ co@^4.6.0: resolved "https://registry.toot.party/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.toot.party/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= + collect-v8-coverage@^1.0.0: version "1.0.1" resolved "https://registry.toot.party/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" @@ -1345,6 +1394,11 @@ connect-redis@^4.0.4: resolved "https://registry.toot.party/connect-redis/-/connect-redis-4.0.4.tgz#b194abe2f3754551f38086e1a28cb9e68d6c3b28" integrity sha512-aXk7btMlG0J5LqtPNRpFKa5fglzlTzukYNx+Fq8cghbUIQHN/gyK9c3+b0XEROMwiSxMoZDADqjp9tdpUoZLAg== +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.toot.party/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= + content-disposition@0.5.3: version "0.5.3" resolved "https://registry.toot.party/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" @@ -1485,6 +1539,13 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3: dependencies: ms "2.0.0" +debug@^3.2.6: + version "3.2.6" + resolved "https://registry.toot.party/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== + dependencies: + ms "^2.1.1" + debug@^4.1.0, debug@^4.1.1: version "4.1.1" resolved "https://registry.toot.party/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" @@ -1544,6 +1605,11 @@ delayed-stream@~1.0.0: resolved "https://registry.toot.party/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.toot.party/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= + denque@^1.4.1: version "1.4.1" resolved "https://registry.toot.party/denque/-/denque-1.4.1.tgz#6744ff7641c148c3f8a69c307e51235c1f4a37cf" @@ -1564,6 +1630,11 @@ destroy@~1.0.4: resolved "https://registry.toot.party/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= +detect-libc@^1.0.2: + version "1.0.3" + resolved "https://registry.toot.party/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= + detect-newline@^3.0.0: version "3.1.0" resolved "https://registry.toot.party/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" @@ -2016,6 +2087,13 @@ fresh@0.5.2: resolved "https://registry.toot.party/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= +fs-minipass@^1.2.5: + version "1.2.7" + resolved "https://registry.toot.party/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" + integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== + dependencies: + minipass "^2.6.0" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.toot.party/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -2026,6 +2104,20 @@ fsevents@^2.1.2, fsevents@~2.1.2: resolved "https://registry.toot.party/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== +gauge@~2.7.3: + version "2.7.4" + resolved "https://registry.toot.party/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + 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" + gensync@^1.0.0-beta.1: version "1.0.0-beta.1" resolved "https://registry.toot.party/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269" @@ -2119,6 +2211,11 @@ has-flag@^4.0.0: resolved "https://registry.toot.party/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.toot.party/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= + has-value@^0.3.1: version "0.3.1" resolved "https://registry.toot.party/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" @@ -2243,13 +2340,20 @@ human-signals@^1.1.1: resolved "https://registry.toot.party/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== -iconv-lite@0.4.24: +iconv-lite@0.4.24, iconv-lite@^0.4.4: version "0.4.24" resolved "https://registry.toot.party/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== dependencies: safer-buffer ">= 2.1.2 < 3" +ignore-walk@^3.0.1: + version "3.0.3" + resolved "https://registry.toot.party/ignore-walk/-/ignore-walk-3.0.3.tgz#017e2447184bfeade7c238e4aefdd1e8f95b1e37" + integrity sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw== + dependencies: + minimatch "^3.0.4" + image-size@^0.7.3: version "0.7.5" resolved "https://registry.toot.party/image-size/-/image-size-0.7.5.tgz#269f357cf5797cb44683dfa99790e54c705ead04" @@ -2286,7 +2390,7 @@ inherits@2.0.3: resolved "https://registry.toot.party/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= -ini@^1.3.4: +ini@^1.3.4, ini@~1.3.0: version "1.3.5" resolved "https://registry.toot.party/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== @@ -2393,6 +2497,13 @@ is-extglob@^2.1.1: resolved "https://registry.toot.party/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.toot.party/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= + dependencies: + number-is-nan "^1.0.0" + is-fullwidth-code-point@^2.0.0: version "2.0.0" resolved "https://registry.toot.party/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" @@ -3303,6 +3414,21 @@ minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5: resolved "https://registry.toot.party/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== +minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: + version "2.9.0" + resolved "https://registry.toot.party/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" + integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + +minizlib@^1.2.1: + version "1.3.3" + resolved "https://registry.toot.party/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" + integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== + dependencies: + minipass "^2.9.0" + mixin-deep@^1.2.0: version "1.3.2" resolved "https://registry.toot.party/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" @@ -3661,6 +3787,13 @@ mkdirp@1.x, mkdirp@~1.0.3: resolved "https://registry.toot.party/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== +mkdirp@^0.5.0, mkdirp@^0.5.1: + version "0.5.5" + resolved "https://registry.toot.party/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + dependencies: + minimist "^1.2.5" + ms@2.0.0: version "2.0.0" resolved "https://registry.toot.party/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -3708,6 +3841,15 @@ natural-compare@^1.4.0: resolved "https://registry.toot.party/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= +needle@^2.2.1: + version "2.4.1" + resolved "https://registry.toot.party/needle/-/needle-2.4.1.tgz#14af48732463d7475696f937626b1b993247a56a" + integrity sha512-x/gi6ijr4B7fwl6WYL9FwlCvRQKGlUNvnceho8wxkwXqN8jvVmmmATTmZPRRG7b/yC1eode26C2HO9jl78Du9g== + dependencies: + debug "^3.2.6" + iconv-lite "^0.4.4" + sax "^1.2.4" + negotiator@0.6.2: version "0.6.2" resolved "https://registry.toot.party/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" @@ -3725,6 +3867,11 @@ no-case@^2.2.0: dependencies: lower-case "^1.1.1" +node-addon-api@^2.0.0: + version "2.0.0" + resolved "https://registry.toot.party/node-addon-api/-/node-addon-api-2.0.0.tgz#f9afb8d777a91525244b01775ea0ddbe1125483b" + integrity sha512-ASCL5U13as7HhOExbT6OlWJJUV/lLzL2voOSP1UVehpRD8FbSrSDjfScK/KwAvVTI5AS6r4VwbOMlIqtvRidnA== + node-int64@^0.4.0: version "0.4.0" resolved "https://registry.toot.party/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" @@ -3746,12 +3893,28 @@ node-notifier@^6.0.0: shellwords "^0.1.1" which "^1.3.1" +node-pre-gyp@^0.14.0: + version "0.14.0" + resolved "https://registry.toot.party/node-pre-gyp/-/node-pre-gyp-0.14.0.tgz#9a0596533b877289bcad4e143982ca3d904ddc83" + integrity sha512-+CvDC7ZttU/sSt9rFjix/P05iS43qHCOOGzcr3Ry99bXG7VX953+vFyEuph/tfqoYu8dttBkE86JSKBO2OzcxA== + dependencies: + detect-libc "^1.0.2" + mkdirp "^0.5.1" + needle "^2.2.1" + nopt "^4.0.1" + npm-packlist "^1.1.6" + npmlog "^4.0.2" + rc "^1.2.7" + rimraf "^2.6.1" + semver "^5.3.0" + tar "^4.4.2" + nodemailer@^6.4.6: version "6.4.6" resolved "https://registry.toot.party/nodemailer/-/nodemailer-6.4.6.tgz#d37f504f6560b36616f646a606894fe18819107f" integrity sha512-/kJ+FYVEm2HuUlw87hjSqTss+GU35D4giOpdSfGp7DO+5h6RlJj7R94YaYHOkoxu1CSaM0d3WRBtCzwXrY6MKA== -nopt@^4.0.3: +nopt@^4.0.1, nopt@^4.0.3: version "4.0.3" resolved "https://registry.toot.party/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48" integrity sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg== @@ -3781,6 +3944,27 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: resolved "https://registry.toot.party/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== +npm-bundled@^1.0.1: + version "1.1.1" + resolved "https://registry.toot.party/npm-bundled/-/npm-bundled-1.1.1.tgz#1edd570865a94cdb1bc8220775e29466c9fb234b" + integrity sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA== + dependencies: + npm-normalize-package-bin "^1.0.1" + +npm-normalize-package-bin@^1.0.1: + version "1.0.1" + resolved "https://registry.toot.party/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" + integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== + +npm-packlist@^1.1.6: + version "1.4.8" + resolved "https://registry.toot.party/npm-packlist/-/npm-packlist-1.4.8.tgz#56ee6cc135b9f98ad3d51c1c95da22bbb9b2ef3e" + integrity sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A== + dependencies: + ignore-walk "^3.0.1" + npm-bundled "^1.0.1" + npm-normalize-package-bin "^1.0.1" + npm-run-path@^2.0.0: version "2.0.2" resolved "https://registry.toot.party/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" @@ -3795,6 +3979,16 @@ npm-run-path@^4.0.0: dependencies: path-key "^3.0.0" +npmlog@^4.0.2: + version "4.1.2" + resolved "https://registry.toot.party/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + 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" + nth-check@~1.0.1: version "1.0.2" resolved "https://registry.toot.party/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" @@ -3802,6 +3996,11 @@ nth-check@~1.0.1: dependencies: boolbase "~1.0.0" +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.toot.party/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= + nunjucks@^3.2.1: version "3.2.1" resolved "https://registry.toot.party/nunjucks/-/nunjucks-3.2.1.tgz#f229539281e92c6ad25d8c578c9bdb41655caf83" @@ -3823,6 +4022,11 @@ oauth-sign@~0.9.0: resolved "https://registry.toot.party/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== +object-assign@^4.1.0: + version "4.1.1" + resolved "https://registry.toot.party/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + object-copy@^0.1.0: version "0.1.0" resolved "https://registry.toot.party/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" @@ -4138,6 +4342,16 @@ raw-body@2.4.0: iconv-lite "0.4.24" unpipe "1.0.0" +rc@^1.2.7: + version "1.2.8" + resolved "https://registry.toot.party/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + react-is@^16.12.0: version "16.13.1" resolved "https://registry.toot.party/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" @@ -4162,7 +4376,7 @@ read-pkg@^5.2.0: parse-json "^5.0.0" type-fest "^0.6.0" -readable-stream@2.3.7: +readable-stream@2.3.7, readable-stream@^2.0.6: version "2.3.7" resolved "https://registry.toot.party/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -4342,6 +4556,13 @@ ret@~0.1.10: resolved "https://registry.toot.party/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== +rimraf@^2.6.1: + version "2.7.1" + resolved "https://registry.toot.party/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + rimraf@^3.0.0: version "3.0.2" resolved "https://registry.toot.party/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" @@ -4391,6 +4612,11 @@ sane@^4.0.3: minimist "^1.1.1" walker "~1.0.5" +sax@^1.2.4: + version "1.2.4" + resolved "https://registry.toot.party/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + saxes@^3.1.9: version "3.1.11" resolved "https://registry.toot.party/saxes/-/saxes-3.1.11.tgz#d59d1fd332ec92ad98a2e0b2ee644702384b1c5b" @@ -4398,7 +4624,7 @@ saxes@^3.1.9: dependencies: xmlchars "^2.1.1" -"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.6.0: +"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.6.0: version "5.7.1" resolved "https://registry.toot.party/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -4437,7 +4663,7 @@ serve-static@1.14.1: parseurl "~1.3.3" send "0.17.1" -set-blocking@^2.0.0: +set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.toot.party/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= @@ -4669,6 +4895,23 @@ string-length@^3.1.0: astral-regex "^1.0.0" strip-ansi "^5.2.0" +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.toot.party/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +"string-width@^1.0.2 || 2": + version "2.1.1" + resolved "https://registry.toot.party/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + string-width@^3.0.0, string-width@^3.1.0: version "3.1.0" resolved "https://registry.toot.party/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" @@ -4701,6 +4944,20 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.toot.party/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.toot.party/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: version "5.2.0" resolved "https://registry.toot.party/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" @@ -4730,6 +4987,11 @@ strip-final-newline@^2.0.0: resolved "https://registry.toot.party/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.toot.party/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + supports-color@^5.3.0: version "5.5.0" resolved "https://registry.toot.party/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -4757,6 +5019,19 @@ symbol-tree@^3.2.2: resolved "https://registry.toot.party/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== +tar@^4.4.2: + version "4.4.13" + resolved "https://registry.toot.party/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" + integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== + dependencies: + chownr "^1.1.1" + fs-minipass "^1.2.5" + minipass "^2.8.6" + minizlib "^1.2.1" + mkdirp "^0.5.0" + safe-buffer "^5.1.2" + yallist "^3.0.3" + terminal-link@^2.0.0: version "2.1.1" resolved "https://registry.toot.party/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" @@ -5143,6 +5418,13 @@ which@^2.0.1, which@^2.0.2: dependencies: isexe "^2.0.0" +wide-align@^1.1.0: + version "1.1.3" + resolved "https://registry.toot.party/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== + dependencies: + string-width "^1.0.2 || 2" + word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.toot.party/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" @@ -5211,6 +5493,11 @@ yallist@^2.1.2: resolved "https://registry.toot.party/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= +yallist@^3.0.0, yallist@^3.0.3: + version "3.1.1" + resolved "https://registry.toot.party/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + yargs-parser@18.x, yargs-parser@^18.1.1: version "18.1.3" resolved "https://registry.toot.party/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0"