├── .env.example ├── .gitignore ├── LICENSE ├── Procfile ├── README.md ├── package.json ├── prisma ├── migrations │ ├── 20210601052456_init │ │ └── migration.sql │ └── migration_lock.toml └── schema.prisma ├── src ├── bot.ts ├── game.ts ├── hn.ts ├── index.ts ├── render.ts └── util.ts ├── tsconfig.json └── yarn.lock /.env.example: -------------------------------------------------------------------------------- 1 | SLACK_BOT_TOKEN= 2 | SLACK_APP_TOKEN= 3 | SLACK_SIGNING_SECRET= 4 | HN_API_KEY= 5 | BOT_SLACK_ID= 6 | DATABASE_URL= 7 | SHADOW_DATABASE_URL= 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .env 4 | yarn-error.log 5 | prisma/*.db 6 | prisma/*.db-journal 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Benjamin Ashbaugh 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: yarn start -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Slack Tetris 2 | 3 | A simple Tetris game for Slack. 4 | 5 | Instructions coming soon. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "slack-tetris", 3 | "version": "0.1.0", 4 | "description": "Tetris game for Slack", 5 | "main": "dist/index.js", 6 | "repository": "https://github.com/scitronboy/slack-tetris.git", 7 | "author": "Benjamin Ashbaugh ", 8 | "license": "MIT", 9 | "scripts": { 10 | "dev": "tsnd src/index.ts", 11 | "build": "tsc --project tsconfig.json", 12 | "start": "node dist/index.js", 13 | "postinstall": "yarn build", 14 | "prisma-gen": "yarn prisma generate", 15 | "db-push": "yarn prisma db push" 16 | }, 17 | "devDependencies": { 18 | "@types/clone-deep": "^4.0.1", 19 | "dotenv": "^10.0.0", 20 | "prisma": "2.23.0", 21 | "ts-node-dev": "^1.1.6", 22 | "typescript": "^4.2.4" 23 | }, 24 | "dependencies": { 25 | "@prisma/client": "2.23.0", 26 | "@slack/bolt": "^3.3.0", 27 | "@slack/web-api": "^6.2.3", 28 | "body-parser": "^1.19.0", 29 | "clone-deep": "^4.0.1", 30 | "express": "^4.17.1", 31 | "graphql": "^15.5.0", 32 | "graphql-request": "^3.4.0" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /prisma/migrations/20210601052456_init/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "TwoPlayerGame" ( 3 | "id" SERIAL NOT NULL, 4 | "offerTs" TEXT NOT NULL, 5 | "channel" TEXT NOT NULL, 6 | "user" TEXT NOT NULL, 7 | "opponent" TEXT NOT NULL, 8 | "started" BOOLEAN, 9 | "winner" TEXT, 10 | 11 | PRIMARY KEY ("id") 12 | ); 13 | 14 | -- CreateTable 15 | CREATE TABLE "Bet" ( 16 | "id" SERIAL NOT NULL, 17 | "gameId" INTEGER NOT NULL, 18 | "user" TEXT NOT NULL, 19 | "betOn" TEXT NOT NULL, 20 | "amount" INTEGER NOT NULL, 21 | 22 | PRIMARY KEY ("id") 23 | ); 24 | 25 | -- CreateTable 26 | CREATE TABLE "Score" ( 27 | "id" SERIAL NOT NULL, 28 | "datetime" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 29 | "user" TEXT NOT NULL, 30 | "score" INTEGER NOT NULL DEFAULT 0, 31 | 32 | PRIMARY KEY ("id") 33 | ); 34 | 35 | -- CreateIndex 36 | CREATE INDEX "Score.score_index" ON "Score"("score"); 37 | 38 | -- AddForeignKey 39 | ALTER TABLE "Bet" ADD FOREIGN KEY ("gameId") REFERENCES "TwoPlayerGame"("id") ON DELETE CASCADE ON UPDATE CASCADE; 40 | -------------------------------------------------------------------------------- /prisma/migrations/migration_lock.toml: -------------------------------------------------------------------------------- 1 | # Please do not edit this file manually 2 | # It should be added in your version-control system (i.e. Git) 3 | provider = "postgresql" -------------------------------------------------------------------------------- /prisma/schema.prisma: -------------------------------------------------------------------------------- 1 | // This is your Prisma schema file, 2 | // learn more about it in the docs: https://pris.ly/d/prisma-schema 3 | 4 | // datasource db { 5 | // provider = "sqlite" 6 | // url = "file:./dev.db" 7 | // } 8 | 9 | datasource db { 10 | provider = "postgresql" 11 | url = env("DATABASE_URL") 12 | shadowDatabaseUrl = env("SHADOW_DATABASE_URL") 13 | } 14 | 15 | generator client { 16 | provider = "prisma-client-js" 17 | } 18 | 19 | model TwoPlayerGame { 20 | id Int @id @default(autoincrement()) 21 | offerTs String 22 | channel String 23 | user String 24 | opponent String 25 | started Boolean? 26 | winner String? 27 | bets Bet[] 28 | } 29 | 30 | model Bet { 31 | id Int @id @default(autoincrement()) 32 | game TwoPlayerGame @relation(fields: [gameId], references: [id]) 33 | gameId Int 34 | user String // Which user placed the bet? 35 | betOn String // Which user was this bet placed on? 36 | amount Int 37 | } 38 | 39 | model Score { 40 | id Int @id @default(autoincrement()) 41 | datetime DateTime @default(now()) 42 | user String 43 | score Int @default(0) 44 | 45 | @@index([score]) 46 | } 47 | -------------------------------------------------------------------------------- /src/bot.ts: -------------------------------------------------------------------------------- 1 | import { Game, NewGameConfig } from './game' 2 | import { 3 | renderLeaderboardBlocks, 4 | complete2pGameOffer, 5 | create2pGameOffer, 6 | GameButtonAction, 7 | send2pGameEndingAnnouncement, 8 | update2pGameOffer, 9 | sendEphemeral, 10 | sendMessage, 11 | GAME_BUTTONS 12 | } from './render' 13 | import { Prisma, PrismaClient } from '@prisma/client' 14 | import { App } from '@slack/bolt' 15 | import { sendPayment } from './hn' 16 | 17 | const prisma = new PrismaClient() 18 | 19 | const HELP_TEXT = `Hello! Do you want to play Tetris? To start a game, type \`/tetris\`. You'll probably want to be in #tetris. 20 | 21 | By default, it will be in open mode, meaning anyone can press the controls and move the pieces, so you'll have to work together. Or, you can type \`/tetris 1p\` to restrict control over the game to just yourself. 22 | 23 | You can also offer to start a two-player game by typing \`/tetris 2p\`. Once someone accepts the offer, players and others in the channel will be given the opportunity to place bets on the outcome of the match. After the game starts, you will play normally, except every line you clear (row filled) will be ADDED to the bottom of the other opponent's grid as a line of gray - so whoever can clear lines fastest will win. 24 | 25 | *Controls:* 26 | ${GAME_BUTTONS.btn_rotate} ${GAME_BUTTONS.btn_left} ${GAME_BUTTONS.btn_right} - Rotate or move the active piece left/right 27 | ${GAME_BUTTONS.btn_down} - Drop the active piece down into place 28 | ${GAME_BUTTONS.btn_hold} - Place the active piece in the hold and replaces it with currently held piece 29 | ${GAME_BUTTONS.btn_stop} - Forfeit game 30 | 31 | *Source*: https://github.com/scitronboy/slack-tetris` 32 | 33 | const games: Record = {} 34 | 35 | /** Timestamp, offerer ID */ 36 | const twoPlayerOffers: Record = {} 37 | 38 | const startGame = (cfg: NewGameConfig) => { 39 | const game = new Game(cfg) 40 | game.startGame() 41 | .then(ts => { games[ts] = game }) 42 | return game 43 | } 44 | 45 | export function registerBotListeners(bot: App) { 46 | bot.command('/tetris', async ({ command, ack, say, client }) => { 47 | let mode = command.text 48 | 49 | if (!(mode === '1p' || mode === '2p')) mode = null 50 | 51 | if (mode === '2p') { 52 | const offerTs = await create2pGameOffer(command.channel_id, command.user_id) 53 | twoPlayerOffers[offerTs] = command.user_id 54 | } else startGame({ 55 | channel: command.channel_id, 56 | user: command.user_id, 57 | mode: mode as '1p' | '2p' 58 | }) 59 | 60 | ack() 61 | }) 62 | 63 | bot.action(/btn_.+/, async ({ ack, body, client }) => { 64 | ack() 65 | 66 | const actionId: GameButtonAction = (body as any).actions[0].action_id 67 | const gameTs: string = (body as any).message.ts 68 | 69 | const game = games[gameTs] 70 | if (!game) { 71 | client.chat.update({ 72 | channel: body.channel.id, 73 | ts: gameTs, 74 | text: `Oh no. My server may have restarted because I don't remeber this game. Start another?` 75 | }) 76 | return 77 | } 78 | 79 | // If this isn't an open game and the user is not a player, ignore 80 | if (game.cfg.mode !== 'open' && game.cfg.user !== body.user.id) return 81 | 82 | switch (actionId) { 83 | case 'btn_left': 84 | case 'btn_right': 85 | game.movePiece(actionId.slice(4) as any) // Slice of `btn_` part 86 | break 87 | case 'btn_down': 88 | game.dropPiece() 89 | break 90 | case 'btn_rotate': 91 | game.rotatePiece() 92 | break 93 | case 'btn_hold': 94 | game.holdPiece() 95 | break 96 | case 'btn_stop': 97 | game.endGame() 98 | break 99 | } 100 | }) 101 | 102 | bot.action('join-2p-game', async ({ ack, body, client }) => { 103 | ack() 104 | 105 | const offerTs: string = (body as any).message.ts 106 | const offer_user = twoPlayerOffers[offerTs] 107 | if (!offer_user) { 108 | client.chat.update({ 109 | channel: body.channel.id, 110 | ts: offerTs, 111 | text: `Something went wrong, please create a new offer with \`/tetris 2p\`` 112 | }) 113 | return 114 | } 115 | 116 | const game = await prisma.twoPlayerGame.create({ 117 | data: { 118 | offerTs, 119 | channel: body.channel.id, 120 | user: offer_user, 121 | opponent: body.user.id, 122 | } 123 | }) 124 | 125 | update2pGameOffer(body.channel.id, offerTs, offer_user, body.user.id, game.id.toString()) 126 | }) 127 | 128 | bot.action('start-2p-game', async ({ ack, body, client }) => { 129 | ack() 130 | 131 | const offerTs: string = (body as any).message.ts 132 | 133 | const game = await prisma.twoPlayerGame.findFirst({ 134 | where: { 135 | offerTs 136 | } 137 | }) 138 | 139 | if (!game) { 140 | client.chat.update({ 141 | channel: body.channel.id, 142 | ts: offerTs, 143 | text: `Something went wrong, please create a new offer with \`/tetris 2p\`` 144 | }) 145 | return 146 | } 147 | 148 | // Only a player can start the game 149 | if (game.user !== body.user.id && game.opponent !== body.user.id) return 150 | 151 | const gameCfg: Omit = { 152 | channel: body.channel.id, 153 | mode: '2p', 154 | startDelay: 5000, 155 | matchId: game.id.toString() 156 | } 157 | 158 | await prisma.twoPlayerGame.update({ 159 | where: { id: game.id }, 160 | data: { started: true } 161 | }) 162 | 163 | // Start a game for each player 164 | const g1 = startGame({ 165 | ...gameCfg, 166 | user: game.user, 167 | }) 168 | const g2 = startGame({ 169 | ...gameCfg, 170 | user: game.opponent, 171 | }) 172 | g1.opponent = g2 173 | g2.opponent = g1 174 | 175 | complete2pGameOffer(body.channel.id, offerTs, game.user, game.opponent) 176 | }) 177 | 178 | bot.event('app_mention', async ({ event, client }) => { 179 | // say() sends a message to the channel where the event was triggered 180 | if (event.thread_ts) return 181 | sendEphemeral(event.channel, event.user, HELP_TEXT) 182 | }) 183 | 184 | bot.command('/tetris-leaderboard', async ({ command, ack, say, client }) => { 185 | const allScores = await prisma.score.findMany({ 186 | select: { 187 | user: true, 188 | score: true 189 | }, 190 | orderBy: { 191 | score: 'desc' 192 | } 193 | }) 194 | 195 | const highScores = allScores.reduce((scores: typeof allScores, score) => { 196 | if (scores.length === 10) return scores 197 | if (scores.find(s => s.user === score.user)) return scores // This user is already in high scores 198 | 199 | return scores.concat([score]) 200 | }, []) 201 | 202 | ack({ 203 | response_type: 'ephemeral', 204 | ...renderLeaderboardBlocks(highScores), 205 | }) 206 | }) 207 | } 208 | 209 | // Hooked by game class on game end 210 | export async function onGameEnd(gameInst: Game) { 211 | await prisma.score.create({ 212 | data: { 213 | score: gameInst.score, 214 | user: gameInst.cfg.user 215 | } 216 | }) 217 | 218 | if (gameInst.cfg.mode === '2p') { 219 | const player = gameInst.cfg.user 220 | 221 | const id = parseInt(gameInst.cfg.matchId) 222 | const game = await prisma.twoPlayerGame.findFirst({ 223 | where: { id }, 224 | include: { 225 | bets: true 226 | } 227 | }) 228 | if (!game || game.winner) return 229 | 230 | // First player to finish loses 231 | const winner = game.user === player ? game.opponent : game.user 232 | const loser = game.user === player ? game.user : game.opponent 233 | 234 | await prisma.twoPlayerGame.update({ 235 | where: { id }, 236 | data: { 237 | winner 238 | } 239 | }) 240 | 241 | complete2pGameOffer(game.channel, game.offerTs, game.user, game.opponent, winner) 242 | send2pGameEndingAnnouncement(game.channel, game.offerTs, winner, player) 243 | 244 | // TODO write this entire betting logic better: 245 | 246 | const totalBetAmount = game.bets.reduce((total, bet) => total + bet.amount, 0) 247 | const totalWinningBetsAmount = game.bets.reduce((total, bet) => { 248 | return bet.betOn === winner ? total + bet.amount : 0 249 | }, 0) 250 | 251 | const viewerBets = game.bets.filter(b => !(b.user === game.user || b.user === game.opponent)) 252 | const playerBets = game.bets.filter(b => (b.user === game.user || b.user === game.opponent)) 253 | 254 | for (const bet of viewerBets) { 255 | if (bet.betOn === winner) { 256 | const proportion = bet.amount / totalWinningBetsAmount 257 | const payout = Math.floor(proportion * totalBetAmount) 258 | sendPayment(bet.user, payout, `Bet won on Tetris game ${game.id}`) 259 | sendEphemeral(game.channel, bet.user, `:fastparrot: You won ${payout}‡ back from your ${bet.amount}‡ bet!!!`) 260 | } else { 261 | sendEphemeral(game.channel, bet.user, `:sadparrot: You lost your bet.`) 262 | } 263 | } 264 | 265 | // Players have a seperate betting pool and can only bet equal amounts 266 | const totalWinnerBet = playerBets.reduce((total, bet) => total + (bet.user === winner && bet.amount), 0) 267 | const totalLoserBet = playerBets.reduce((total, bet) => total + (bet.user === loser && bet.amount), 0) 268 | const minPlayerBet = Math.min(totalWinnerBet, totalLoserBet) 269 | 270 | const winnerPayout = minPlayerBet * 2 + (totalWinnerBet - minPlayerBet) 271 | if (winnerPayout) { 272 | sendPayment(winner, winnerPayout, `Winnings from Tetris game ${game.id}`) 273 | sendEphemeral(game.channel, winner, `:ultrafastparrot: Congrats on your win. I'm sending you ${minPlayerBet*2}‡`) 274 | } 275 | 276 | // Loser gets some of their bet refunded if winner did not risk as much as loser. 277 | const loserBetRefund = totalLoserBet - minPlayerBet 278 | if (loserBetRefund) { 279 | sendPayment(player, loserBetRefund, `Refund from Tetris bet on game ${game.id}`) 280 | sendEphemeral(game.channel, player, `:coin-mario: I've refunded ${loserBetRefund}‡ of your bet.`) 281 | } 282 | } 283 | } 284 | 285 | export async function onPayment (fromId: string, amount: number, reason: string) { 286 | const refund = (text: string, channel?: string) => { 287 | if (channel) sendEphemeral(channel, fromId, text) 288 | else sendMessage(fromId, text) 289 | sendPayment(fromId, amount, 'Refund payment') 290 | } 291 | 292 | let id: number, betTargetPlayer: number 293 | try { 294 | [id, betTargetPlayer] = reason.split('-').map(Number) 295 | if (id < 1 || !(betTargetPlayer === 1 || betTargetPlayer === 2)) throw Error() 296 | } catch { 297 | refund(`I received ${amount}‡ from you, but don't understand why, so I'm sending it back.`) 298 | return 299 | } 300 | 301 | const game = await prisma.twoPlayerGame.findFirst({ 302 | where: { id } 303 | }) 304 | 305 | if (!game || game.started) { 306 | refund(`Game ${id} either doesn't exist or has already started. Refunding your payment.`) 307 | return 308 | } 309 | 310 | if ((fromId === game.user && betTargetPlayer !== 1) || (fromId === game.opponent && betTargetPlayer !== 2)) { 311 | refund(`You can only bet on yourself. Refunding your payment.`, game.channel) 312 | return 313 | } 314 | 315 | const betOn = betTargetPlayer === 1 ? game.user : game.opponent 316 | 317 | try { 318 | await prisma.bet.create({ 319 | data: { 320 | user: fromId, 321 | betOn, 322 | amount, 323 | gameId: game.id, 324 | } 325 | }) 326 | sendEphemeral(game.channel, fromId, `Your bet of ${amount}‡ on <@${betOn}> was received.`) 327 | } catch { 328 | refund(`Something went wrong and your bet couldn't be replaced. Refunding you ${amount}‡`, game.channel) 329 | return 330 | } 331 | 332 | const viewerBets = await prisma.bet.findMany({ 333 | where: { 334 | gameId: game.id, 335 | NOT: { 336 | OR: [ 337 | { user: game.user }, 338 | { user: game.opponent } 339 | ] 340 | } 341 | } 342 | }) 343 | 344 | const total = viewerBets.reduce((total, bet) => total + bet.amount, 0) 345 | 346 | update2pGameOffer( 347 | game.channel, 348 | game.offerTs, 349 | game.user, 350 | game.opponent, 351 | game.id.toString(), 352 | total 353 | ) 354 | } 355 | -------------------------------------------------------------------------------- /src/game.ts: -------------------------------------------------------------------------------- 1 | import { iterateMatrix, shuffleArray } from './util' 2 | import { TetrisBlocksGrid, createGame, updateGame } from './render' 3 | import cloneDeep from 'clone-deep' 4 | import { onGameEnd } from './bot' 5 | 6 | const GRID_WIDTH = 10 7 | const GRID_HEIGHT = 16 8 | 9 | const SCORE_TABLE = { 10 | lineClears: { 11 | 1: 40, 12 | 2: 100, 13 | 3: 300, 14 | 4: 1200 15 | }, 16 | pointsPerRowSkipped: 1 17 | } 18 | 19 | // Ex: after a score of 40, there will be a 1200ms delay between loops 20 | const LEVELS: [scoreThreshold: number, intervalDuration: number][] = [ 21 | [0, 1400], 22 | [40, 1000], 23 | [200, 900], 24 | [800, 800], 25 | [2000, 600], 26 | [20000, 400], 27 | [50000, 200], 28 | [500000, 50], 29 | ] 30 | 31 | export type TetrominoName = 'I' | 'J' | 'L' | 'S' | 'Z' | 'T' | 'O' 32 | 33 | // 0 is standard position as seen here 34 | // 1 is rotated 90 degrees clockwise, 2 is 180, etc. 35 | type Rotation = 0 | 1 | 2 | 3 36 | 37 | interface Tetromino { 38 | type: 'tetromino' 39 | name: TetrominoName 40 | position: [y: number, x: number] // dist from bottom, left of grid to bottom, left of tetrimino shape 41 | rotation: Rotation 42 | shape: boolean[][] // 2d MxM matrix 43 | } 44 | 45 | interface LineClear { 46 | type: 'clear' 47 | row: number 48 | } 49 | 50 | interface LineFill { 51 | type: 'fill' 52 | } 53 | 54 | type Piece = Tetromino | LineClear | LineFill 55 | 56 | // - = blank space, # = fill, , = new row 57 | // All shapes are represented by a 3x3 matrix, except I and O 58 | const TETROMINO_SHAPES: Record, string> = { 59 | I: '--#-,--#-,--#-,--#-', 60 | O: '##,##', 61 | T: '---,###,-#-', 62 | S: '---,-##,##-', 63 | L: '#--,#--,##-', 64 | J: '--#,--#,-##', 65 | Z: '---,##-,-##', 66 | } 67 | 68 | const getTetromino = (type: TetrominoName, rotation: Rotation): Omit => { 69 | const shapeRows = TETROMINO_SHAPES[type].split(',') 70 | // Take a row of - and # and convert it to an array of false and true: 71 | let shape: boolean[][] = shapeRows.map(row => Array.from(row).map(char => char === '#')) 72 | 73 | // Rotate the shape array clockwise `rotation` times by transposing and reversing the 2d matrix: 74 | for (let r=0; r shape.map(row => row[colInd])) 77 | const newShape = transposedShape.map(a => a.reverse()) 78 | shape = newShape 79 | } 80 | 81 | // Reverse the shape so that row 0 is the bottom of the shape 82 | shape = shape.reverse() 83 | 84 | return { 85 | type: 'tetromino', 86 | name: type, 87 | rotation, 88 | shape 89 | } 90 | } 91 | 92 | export type GameMode = 'open' | '1p' | '2p' 93 | 94 | export interface NewGameConfig { 95 | channel: string, 96 | user: string, 97 | mode?: GameMode 98 | matchId?: string 99 | thread_ts?: string 100 | startDelay?: number 101 | } 102 | 103 | export class Game { 104 | cfg: NewGameConfig 105 | ts: string 106 | opponent: Game 107 | 108 | private pieces: Piece[] // Array of pieces and other events such as line clears. Latest piece is last. 109 | private activePiece: Tetromino 110 | private nextTetrominoes: TetrominoName[] // New tetrominos to place. Next is first. 111 | private hold?: TetrominoName 112 | private hasAlreadyHeld: boolean 113 | private fillSlot: number // Where is the slot placed in filler rows? 114 | private lastLevel: number 115 | private loopInterval: NodeJS.Timeout 116 | 117 | startedAt: number 118 | endedAt: number 119 | score: number 120 | gameOver: boolean 121 | 122 | constructor (cfg: NewGameConfig) { 123 | this.pieces = [] 124 | this.nextTetrominoes = [] 125 | this.cfg = cfg 126 | if (!this.cfg.mode) this.cfg.mode = 'open' 127 | this.score = 0 128 | 129 | // Between the first and last rows (exclusive) 130 | this.fillSlot = Math.floor(Math.random() * (GRID_WIDTH - 2) + 1) 131 | } 132 | 133 | /** Creates the game message and starts the loop */ 134 | public async startGame () { 135 | this.ts = await createGame(this.cfg.channel, this.cfg.thread_ts) 136 | 137 | setTimeout(() => { 138 | this.loopInterval = setInterval(() => this.loop(), this.levelDelay) 139 | }, this.cfg.startDelay || 0) 140 | 141 | // Start game after 1 second 142 | setTimeout(() => this.update(), 1000) 143 | 144 | console.log(`Game starting in ${this.cfg.channel}`) 145 | 146 | return this.ts 147 | } 148 | 149 | /** Moves active tetromino down one square, adds new pieces, etc. */ 150 | private loop () { 151 | if (!this.activePiece) { // game not started 152 | this.startedAt = new Date().getTime() 153 | this.addPiece() 154 | } else { 155 | const didMoveDown = this.updateActivePiece(piece => ({ 156 | ...piece, 157 | position: [piece.position[0] - 1, piece.position[1]] 158 | }), true) 159 | 160 | if (!didMoveDown) { // Can't move down any further: finalize move, update score, and add a new piece 161 | this.addPiece() // Must finalize move first so that it can be properly cleared 162 | this.clearLinesAndUpdateScore() 163 | this.hasAlreadyHeld = false 164 | } 165 | } 166 | 167 | // If we are on a new level, cancel the loop interval and set a new one with a shorter duration 168 | if (this.level > this.lastLevel) { 169 | clearInterval(this.loopInterval) 170 | this.loopInterval = setInterval(() => this.loop(), this.levelDelay) 171 | } 172 | 173 | this.lastLevel = this.level 174 | 175 | this.update() 176 | } 177 | 178 | /** Renders the grid and updates the game message */ 179 | private update() { 180 | updateGame(this.cfg.channel, this.ts, { 181 | startedBy: this.cfg.user, 182 | mode: this.cfg.mode, 183 | blocks: this.renderBlockGrid(true).reverse(), // Render top-side up!, 184 | score: this.score, 185 | level: this.level, 186 | gameOver: this.gameOver, 187 | duration: (this.endedAt || new Date().getTime()) - this.startedAt || 0, 188 | nextPiece: this.nextTetrominoes[0], 189 | heldPiece: this.hold, 190 | startingIn: !this.startedAt && this.cfg.startDelay 191 | }) 192 | } 193 | 194 | /** Converts the piece array into a 2d matrix of blocks. */ 195 | private renderBlockGrid (includeActive = true): TetrisBlocksGrid { 196 | const grid: TetrisBlocksGrid = new Array(GRID_HEIGHT).fill(null).map(_ => new Array(GRID_WIDTH).fill(null)) 197 | 198 | const pieces = this.pieces.concat(includeActive && this.activePiece ? [this.activePiece] : []) 199 | 200 | for (const [pieceIndex, piece] of pieces.entries()) { 201 | // Check which cells this shape fills and fill the corresponding cells on the grid: 202 | if (piece.type === 'tetromino') { 203 | iterateMatrix(piece.shape, (block, i, j) => { 204 | if (block) { 205 | grid[piece.position[0] + i][piece.position[1] + j] = piece.name 206 | } 207 | }) 208 | } 209 | 210 | if (piece.type === 'clear') { 211 | grid.splice(piece.row, 1) // Remove cleared row 212 | grid.push(new Array(GRID_WIDTH).fill(null)) // Add new empty row at top 213 | } 214 | 215 | if (piece.type === 'fill') { 216 | grid.pop() // Remove row at top 217 | grid.unshift(new Array(GRID_WIDTH).fill('FILL')) // Add new filled row at bottom 218 | grid[0][this.fillSlot] = null // Leave a slot in the fill so that it can be cleared 219 | } 220 | } 221 | 222 | return grid 223 | } 224 | 225 | /** Get level, computed from current score */ 226 | public get level (): number { 227 | return LEVELS.reduce((lvl, [threshold]) => lvl += this.score >= Number(threshold) ? 1 : 0, 0) 228 | } 229 | 230 | /** Get current delay between loops */ 231 | private get levelDelay (): number { 232 | return LEVELS[this.level - 1][1] 233 | } 234 | 235 | /** Creates a new active piece and spawns it at the top */ 236 | private addPiece (name?: TetrominoName) { 237 | if (this.nextTetrominoes.length < 3) { // Running out of new pieces; add 7 more 238 | const newSet = shuffleArray(Object.keys(TETROMINO_SHAPES) as TetrominoName[]) 239 | this.nextTetrominoes = this.nextTetrominoes.concat(newSet) 240 | } 241 | 242 | const nextPieceType = name || this.nextTetrominoes.shift() 243 | const nextPiece = getTetromino(nextPieceType, 0) as Tetromino 244 | nextPiece.position = [GRID_HEIGHT - nextPiece.shape.length, Math.ceil(GRID_WIDTH / 2) - 2] 245 | 246 | if (!this.isValidPosition(nextPiece)) { // Can't place any more pieces; game over! 247 | this.endGame() 248 | return 249 | } 250 | 251 | if (this.activePiece) this.pieces.push(this.activePiece) 252 | this.activePiece = nextPiece 253 | } 254 | 255 | /** Checks for line clears and increases the score if needed */ 256 | private clearLinesAndUpdateScore () { 257 | const grid = this.renderBlockGrid(false) 258 | 259 | const lineClears = grid.reduce((fullRows, row, rowIndex) => { 260 | const cleared = row.find(b => !b) === undefined // No empty places found 261 | const isFillClear = row[0] === 'FILL' 262 | 263 | // The user has cleared this row!!! 264 | if (cleared) this.pieces.push({ 265 | type: 'clear', 266 | // Subtract number of rows that are already cleared, because they (should) no longer exist when this clear happens: 267 | row: rowIndex - fullRows 268 | }) 269 | 270 | // Fill clears don't count :( 271 | return fullRows + ((cleared && !isFillClear) ? 1 : 0) 272 | }, 0) 273 | 274 | this.score += (SCORE_TABLE.lineClears[lineClears] || 0) * this.lastLevel 275 | 276 | if (this.opponent) this.opponent.addFillLines(lineClears) 277 | } 278 | 279 | /** Add fill lines to the bottom of this game's grid. Used by opponent game when they clear a line. */ 280 | public addFillLines (num = 1) { 281 | for (let i = 0; i < num; i++) { 282 | this.pieces.push({ 283 | type: 'fill', 284 | }) 285 | } 286 | // Also need to move active piece up to make sure it doesn't overlap 287 | this.activePiece.position[0] = this.activePiece.position[0] + num 288 | } 289 | 290 | /** Checks the position of a piece to make sure it doens't overlap with another piece, or the walls */ 291 | private isValidPosition (piece: Tetromino): boolean { 292 | const grid = this.renderBlockGrid(false) // Grid of pieces, excluding active piece 293 | 294 | const foundConflict = iterateMatrix(piece.shape, (block, i, j) => { 295 | // A filled spot is going below the grid; this is invalid: 296 | if (block && piece.position[0] + i < 0) return true 297 | // A filled spot is passing the walls: 298 | if (block && (piece.position[1] + j < 0 || piece.position[1] + j > GRID_WIDTH - 1)) return true 299 | // If the shape wants to fill a cell that's already filled on the grid, there's a conflict: 300 | if (block && grid[piece.position[0] + i][piece.position[1] + j]) return true 301 | }) 302 | 303 | return !foundConflict 304 | } 305 | 306 | /** Accepts a function argument which is called to edit the piece; then checks validity of the new position and rejects it if it's invalid */ 307 | private updateActivePiece (getNewPiece: (piece: Tetromino) => Tetromino, skipUpdate?: boolean) { 308 | const newPiece = getNewPiece(cloneDeep(this.activePiece)) 309 | 310 | if (this.isValidPosition(newPiece)) { 311 | this.activePiece = newPiece 312 | if (!skipUpdate) this.update() 313 | return true 314 | } 315 | return false // Ignore the move if it's not valid 316 | } 317 | 318 | /** Rotates active piece clockwise */ 319 | public rotatePiece () { 320 | this.updateActivePiece(piece => ({ 321 | ...piece, 322 | ...getTetromino(piece.name, piece.rotation === 3 ? 0 : piece.rotation + 1 as Rotation) 323 | })) 324 | } 325 | 326 | /** Moves the active piece left/right */ 327 | public movePiece(direction: 'left' | 'right') { 328 | this.updateActivePiece(piece => { 329 | const newX = direction === 'left' ? piece.position[1] - 1 : piece.position[1] + 1 330 | return { 331 | ...piece, 332 | position: [piece.position[0], newX] as [number, number] 333 | } 334 | }) 335 | } 336 | 337 | /** Drops the active piece into place */ 338 | public dropPiece() { 339 | let continueMovingDown = true 340 | while (continueMovingDown) { 341 | continueMovingDown = this.updateActivePiece(piece => ({ 342 | ...piece, 343 | position: [piece.position[0] - 1, piece.position[1]] 344 | }), true) 345 | 346 | if (continueMovingDown) this.score += SCORE_TABLE.pointsPerRowSkipped * this.level 347 | } 348 | this.update() 349 | } 350 | 351 | /** Hold the current active piece */ 352 | public holdPiece() { 353 | if (this.hasAlreadyHeld) return // Can only hold once per piece 354 | 355 | // Take new tetromino type from hold or next stack, and add current type to hold. 356 | const newType = this.hold || this.nextTetrominoes.shift() 357 | this.hold = this.activePiece.name 358 | 359 | // Remove the current aative piece and add the held one. 360 | this.activePiece = null 361 | this.addPiece(newType) 362 | 363 | this.hasAlreadyHeld = true 364 | this.update() 365 | } 366 | 367 | /** Stops the game */ 368 | public endGame() { 369 | onGameEnd(this) 370 | clearInterval(this.loopInterval) 371 | this.gameOver = true 372 | this.endedAt = new Date().getTime() 373 | this.update() 374 | } 375 | } 376 | -------------------------------------------------------------------------------- /src/hn.ts: -------------------------------------------------------------------------------- 1 | import * as bodyParser from 'body-parser' 2 | import { GraphQLClient, gql } from 'graphql-request' 3 | import { onPayment } from './bot' 4 | import { Express } from 'express' 5 | 6 | const jsonParser = bodyParser.json() 7 | 8 | const gqlClient = new GraphQLClient('https://hn.rishi.cx/') 9 | gqlClient.setHeader('secret', process.env.HN_API_KEY) 10 | 11 | export function registerHNWebhookListeners(router: Express) { 12 | router.get('/', (req, res) => { 13 | res.send('Hi there. https://github.com/scitronboy/slack-tetris') 14 | }) 15 | 16 | router.post('/hn/payment', jsonParser, async (req) => { 17 | const paymentId = req.body?.body?.id 18 | 19 | if (!paymentId) return 20 | 21 | const { transaction } = await gqlClient.request(gql` 22 | query p($id: String!) { 23 | transaction(id: $id) { 24 | validated 25 | from { 26 | id 27 | } 28 | balance 29 | for 30 | } 31 | } 32 | `, { 33 | id: paymentId 34 | }).catch(console.error) 35 | 36 | if (transaction.validated) onPayment(transaction.from.id, transaction.balance, transaction.for) 37 | }) 38 | } 39 | 40 | /** Sends an HN payment and returns the transaction ID */ 41 | export function sendPayment(to: string, amount: number, reason: string): Promise { 42 | return gqlClient.request(gql` 43 | mutation payment($from: String!, $to: String!, $amount: Float!, $reason: String!) { 44 | send ( 45 | data: { 46 | from: $from, 47 | to: $to, 48 | balance: $amount, 49 | for: $reason 50 | } 51 | ) { 52 | id 53 | } 54 | } 55 | `, { to, amount, reason, from: process.env.BOT_SLACK_ID }) 56 | .then(t => t.send.id) 57 | .catch(console.error) 58 | } 59 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | if (process.env.NODE_ENV !== 'production') require('dotenv').config() 2 | import { App, ExpressReceiver } from '@slack/bolt' 3 | import { registerHNWebhookListeners } from './hn' 4 | import { registerBotListeners } from './bot' 5 | import express from 'express' 6 | import https from 'https' 7 | 8 | const router = express() 9 | 10 | export const bot = new App({ 11 | token: process.env.SLACK_BOT_TOKEN, 12 | appToken: process.env.SLACK_APP_TOKEN, 13 | socketMode: true, 14 | }) 15 | 16 | registerBotListeners(bot) 17 | registerHNWebhookListeners(router) 18 | 19 | async function start() { 20 | const port = parseInt(process.env.PORT) || 5000 21 | router.listen(port) 22 | await bot.start(port) 23 | console.log('⚡️ Bot started') 24 | 25 | // To keep Heroku awake. 26 | // TODO don't hardcode 27 | setInterval(() => { 28 | https.get('https://slack-tetris.herokuapp.com') 29 | }, 300000) 30 | } 31 | 32 | start() 33 | -------------------------------------------------------------------------------- /src/render.ts: -------------------------------------------------------------------------------- 1 | import { GameMode, TetrominoName } from './game' 2 | import { bot } from '.' 3 | import { formatMilliseconds, pickRandom } from './util' 4 | import { WebClient, retryPolicies } from '@slack/web-api' 5 | 6 | const client = new WebClient(process.env.SLACK_BOT_TOKEN, { 7 | retryConfig: { 8 | ...retryPolicies.fiveRetriesInFiveMinutes 9 | }, 10 | }) 11 | 12 | const noRetryClient = new WebClient(process.env.SLACK_BOT_TOKEN, { 13 | retryConfig: { 14 | retries: 0 15 | }, 16 | rejectRateLimitedCalls: true 17 | }) 18 | 19 | export const sendEphemeral = (channel: string, user: string, text: string) => client.chat.postEphemeral({ 20 | channel, 21 | user, 22 | text 23 | }) 24 | 25 | export const sendMessage = (channel: string, text: string, thread_ts?: string) => client.chat.postMessage({ 26 | channel, 27 | thread_ts, 28 | text 29 | }) 30 | 31 | const BLOCK_EMOJI: Record = { 32 | Z: ':tetris-block-z:', // red 33 | S: ':tetris-block-s:', // green 34 | J: ':tetris-block-j:', // blue 35 | I: ':tetris-block-i:', // cyan 36 | L: ':tetris-block-l:', // orange 37 | O: ':tetris-block-o:', // yellow 38 | T: ':tetris-block-t:', // purple 39 | FILL: ':tetris-block-gray:', 40 | } 41 | 42 | const TETROMINO_EMOJI: Record = { 43 | Z: ':tetromino-z:', // red 44 | S: ':tetromino-s:', // green 45 | J: ':tetromino-j:', // blue 46 | I: ':tetromino-i:', // cyan 47 | L: ':tetromino-l:', // orange 48 | O: ':tetromino-o:', // yellow 49 | T: ':tetromino-t:', // purple 50 | } 51 | 52 | const BLANK_EMOJI = ':blank:' 53 | const INVISIBLE_CHARACTER = '⁣' // We can use this to force emojis down to their smaller size if needed. 54 | 55 | const WALL_LEFT = ':tetris-wall-left:' 56 | const WALL_RIGHT = ':tetris-wall-right:' 57 | 58 | export const GAME_BUTTONS = { 59 | 'btn_rotate': ':tetris-control-rotate:', 60 | 'btn_left': ':tetris-control-left:', 61 | 'btn_right': ':tetris-control-right:', 62 | 'btn_down': ':tetris-control-down:', 63 | 'btn_hold': ':tetris-control-switch:', 64 | 'btn_stop': ':tetris-control-stop:' 65 | } 66 | 67 | const SPLASH_TEXT = ` 68 | \`\`\` 69 | _____ _____ _____ _____ _ _____ 70 | |_ _| | ____| |_ _| | _ \\ | | / ___/ 71 | | | | |__ | | | |_| | | | | |___ 72 | | | | __| | | | _ / | | \\___ \\ 73 | | | | |___ | | | | \\ \\ | | ___| | 74 | |_| |_____| |_| |_| \\_\\ |_| /_____/ 75 | \`\`\` 76 | 77 | :parrotwave1: :parrotwave2: :parrotwave3: :parrotwave4: :parrotwave5: :parrotwave6: :parrotwave7:` 78 | 79 | export type GameButtonAction = keyof typeof GAME_BUTTONS 80 | 81 | export type TetrisBlocksGrid = (TetrominoName | 'FILL' | null)[][] 82 | 83 | const renderBlockGrid = (blocks: TetrisBlocksGrid) => 84 | blocks.reduce((str, line) => str + '\n' + WALL_LEFT + line.map(b => b ? BLOCK_EMOJI[b] : BLANK_EMOJI).join('') + WALL_RIGHT, '') + INVISIBLE_CHARACTER 85 | 86 | // TODO render level too 87 | export interface GameMessageData { 88 | startedBy: string // user ID 89 | mode: GameMode 90 | blocks?: TetrisBlocksGrid 91 | nextPiece?: TetrominoName 92 | heldPiece?: TetrominoName 93 | score: number 94 | level: number 95 | gameOver: boolean 96 | duration: number 97 | startingIn?: number 98 | } 99 | 100 | function renderGameBlocks(game: GameMessageData): { blocks: any, text: string } { 101 | const nextPieceText = `\n> *Next*\n> ${TETROMINO_EMOJI[game.nextPiece] || INVISIBLE_CHARACTER}` 102 | const heldPieceText = `\n> *Hold*\n> ${TETROMINO_EMOJI[game.heldPiece] || INVISIBLE_CHARACTER}` 103 | 104 | const blocks: any = [ 105 | { 106 | "type": "section", 107 | "text": { 108 | "type": "mrkdwn", 109 | "text": game.gameOver 110 | ? `<@${game.startedBy}> played Tetris for ${formatMilliseconds(game.duration, true)}. Final score: *${game.score}*` 111 | : `<@${game.startedBy}> is playing in ${game.mode} mode. Score: *${game.score}* | ${formatMilliseconds(game.duration)} | Lvl ${game.level}` 112 | } 113 | }, 114 | { 115 | "type": "divider" 116 | }, 117 | { 118 | "type": "section", 119 | "text": { 120 | "type": "mrkdwn", 121 | "text": `${renderBlockGrid(game.blocks || [])}` 122 | } 123 | }, 124 | ] 125 | 126 | if (game.startingIn) { 127 | blocks.push( 128 | { 129 | "type": "divider" 130 | }, { 131 | "type": "section", 132 | "text": { 133 | "type": "mrkdwn", 134 | "text": `Starting in about ${game.startingIn / 1000} seconds.` 135 | } 136 | } 137 | ) 138 | } 139 | else if (!game.gameOver) { 140 | blocks.push({ 141 | "type": "actions", 142 | "elements": Object.entries(GAME_BUTTONS).map(([action_id, text]) => ({ 143 | "type": "button", 144 | "text": { 145 | "type": "plain_text", 146 | "emoji": true, 147 | text, 148 | }, 149 | action_id 150 | })) 151 | }) 152 | 153 | blocks.splice(1, 0, { 154 | "type": "section", 155 | "fields": [ 156 | { 157 | "type": "mrkdwn", 158 | "text": nextPieceText 159 | }, 160 | { 161 | "type": "mrkdwn", 162 | "text": heldPieceText 163 | } 164 | ] 165 | }) 166 | } else { 167 | blocks.push( 168 | { 169 | "type": "divider" 170 | }, { 171 | "type": "section", 172 | "text": { 173 | "type": "mrkdwn", 174 | "text": `~ ~ ~ ~ ~ ~ ~ *GAME OVER* ~ ~ ~ ~ ~ ~ ~` 175 | } 176 | } 177 | ) 178 | } 179 | 180 | return { 181 | text: 'Tetris game', 182 | blocks 183 | } 184 | } 185 | 186 | export async function createGame (channel: string, thread_ts?: string): Promise { 187 | const msg = await sendMessage(channel, SPLASH_TEXT, thread_ts) 188 | 189 | return msg.ts 190 | } 191 | 192 | export async function updateGame (channel: string, ts: string, game: GameMessageData) { 193 | // Retries completely break the game when out-of-date state arrives many seconds later. 194 | const clientForUpdate = game.gameOver ? client : noRetryClient 195 | await clientForUpdate.chat.update({ 196 | channel, 197 | ts, 198 | ...renderGameBlocks(game) 199 | }) 200 | } 201 | 202 | export async function create2pGameOffer (channel: string, user: string): Promise { 203 | const msg = await client.chat.postMessage({ 204 | channel, 205 | text: 'Want to play 2-player Tetris?', 206 | blocks: [ 207 | { 208 | "type": "section", 209 | "text": { 210 | "type": "mrkdwn", 211 | "text": `<@${user}> wants to play two-player Tetris.` 212 | }, 213 | "accessory": { 214 | "type": "button", 215 | "text": { 216 | "type": "plain_text", 217 | "text": "Accept Challenge", 218 | "emoji": true 219 | }, 220 | "action_id": "join-2p-game" 221 | } 222 | }, 223 | ] 224 | }) 225 | 226 | return msg.ts 227 | } 228 | 229 | export async function update2pGameOffer ( 230 | channel: string, 231 | ts: string, 232 | user: string, 233 | opponent: string, 234 | bettingId: string, 235 | betsTotal: number = 0 236 | ) { 237 | const msg = await client.chat.update({ 238 | channel, 239 | ts, 240 | text: 'Two-player Tetris offer accepted', 241 | blocks: [ 242 | { 243 | "type": "section", 244 | "text": { 245 | "type": "mrkdwn", 246 | "text": `<@${user}> (player 1) is playing against <@${opponent}> (player 2).` 247 | } 248 | }, 249 | { 250 | "type": "section", 251 | "text": { 252 | "type": "mrkdwn", 253 | "text": `To place a bet on this match, send HN to me with reason \`${bettingId}-PLAYER\` (replace PLAYER with \`1\` or \`2\`). Ex: \`/send-hn 4 to @tetris for ${bettingId}-1\`. :money_with_wings: \n\nPlayers: You can only win a maximum of 2x your own bet. You will be refunded any amount bet in excess of your opponent's bet, so agree before starting on the amount to bet. \n\n*Current pool*: ${betsTotal}‡` 254 | } 255 | }, 256 | { 257 | "type": "actions", 258 | "elements": [ 259 | { 260 | "type": "button", 261 | "text": { 262 | "type": "plain_text", 263 | "text": "Start Game", 264 | "emoji": true 265 | }, 266 | "action_id": "start-2p-game" 267 | } 268 | ] 269 | } 270 | ] 271 | }) 272 | } 273 | 274 | const GAME_DESCRIPTION_WORDS = [ 275 | 'EPIC', 276 | 'UNBELIEVABLE', 277 | 'MINDBLOWING', 278 | 'cowful', 279 | 'cool', 280 | 'SHOCKING', 281 | 'UNMISSABLE' 282 | ] 283 | 284 | const GAME_WINNER_VERB = [ 285 | 'beat', 286 | 'destroyed', 287 | 'absolutely shattered', 288 | 'WHACCCKKKEED' 289 | ] 290 | 291 | export async function complete2pGameOffer (channel: string, ts: string, user: string, opponent: string, winner?: string) { 292 | const winnerOpponent = winner && (user === winner ? opponent : user) 293 | const text = winner 294 | ? `<@${winner}> ${pickRandom(GAME_WINNER_VERB)} <@${winnerOpponent}> in a Tetris game!` 295 | : `<@${user}> is playing against <@${opponent}> in a totally ${pickRandom(GAME_DESCRIPTION_WORDS)} game!\n↓ ↓ ↓ ↓` 296 | 297 | await client.chat.update({ 298 | token: process.env.SLACK_BOT_TOKEN, 299 | channel, 300 | ts, 301 | text, 302 | blocks: [] 303 | }) 304 | } 305 | 306 | export async function send2pGameEndingAnnouncement (channel: string, thread_ts: string, winner: string, loser: string) { 307 | const text = `<@${winner}> won!!! Better luck next time, <@${loser}>` 308 | 309 | const msg = await client.chat.postMessage({ 310 | token: process.env.SLACK_BOT_TOKEN, 311 | channel, 312 | thread_ts, 313 | reply_broadcast: true, 314 | text 315 | }) 316 | } 317 | 318 | export function renderLeaderboardBlocks(scores: {user: string, score: number}[]) { 319 | return { 320 | text: 'Tetris leaderboard', 321 | blocks: [ 322 | { 323 | "type": "header", 324 | "text": { 325 | "type": "plain_text", 326 | "text": ":medal: :tetromino-t: Top Players", 327 | "emoji": true 328 | } 329 | }, 330 | { 331 | "type": "section", 332 | "fields": [ 333 | { 334 | "type": "mrkdwn", 335 | "text": `Player` 336 | }, 337 | { 338 | "type": "mrkdwn", 339 | "text": `High score` 340 | } 341 | ] 342 | }, 343 | { 344 | "type": "divider" 345 | }, 346 | ...[].concat(...scores.map((s, i) => ([ 347 | { 348 | "type": "section", 349 | "fields": [ 350 | { 351 | "type": "mrkdwn", 352 | "text": `*${i}.*\t<@${s.user}>` 353 | }, 354 | { 355 | "type": "mrkdwn", 356 | "text": `${s.score}` 357 | } 358 | ] 359 | }, 360 | { 361 | "type": "divider" 362 | } 363 | ]))), 364 | ] 365 | } 366 | } 367 | -------------------------------------------------------------------------------- /src/util.ts: -------------------------------------------------------------------------------- 1 | 2 | // https://stackoverflow.com/a/12646864 3 | export function shuffleArray(array: T[]): T[] { 4 | const shuffled = array.slice() 5 | 6 | for (let i = shuffled.length - 1; i > 0; i--) { 7 | const j = Math.floor(Math.random() * (i + 1)); 8 | [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]; 9 | } 10 | 11 | return shuffled 12 | } 13 | 14 | /** Like forEach but for a 2d array (used for tetromino shapes). If the function returns a non-falsy value the loop will break and return the result */ 15 | export function iterateMatrix(array: T[][], forEach: (el: T, i: number, j: number) => I): I | false { 16 | for (let i = 0; i < array.length; i++) { 17 | for (let j = 0; j < array[i].length; j++) { 18 | const funcResult = forEach(array[i][j], i, j) 19 | if (funcResult) return funcResult 20 | } 21 | } 22 | 23 | return false 24 | } 25 | 26 | /** Return a `minutes:seconds` or` __ minutes and __ seconds` string from milliseconds */ 27 | export function formatMilliseconds(ms: number, long?: boolean) { 28 | const minutes = Math.floor(ms / 1000 / 60) 29 | const seconds = ms / 1000 % 60 30 | const secondsRounded = Math.floor(seconds) 31 | return long 32 | ? `${minutes} minutes and ${secondsRounded} seconds` 33 | : `${minutes < 10 ? '0' + minutes : minutes}:${seconds < 10 ? '0' + secondsRounded : secondsRounded}` 34 | } 35 | 36 | export const pickRandom = (a: T[]): T => a[Math.floor(Math.random() * a.length)] 37 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["esnext"], 4 | "experimentalDecorators": true, 5 | "esModuleInterop": true, 6 | "outDir": "./dist", 7 | "baseUrl": "src", 8 | "target": "ES5", 9 | "downlevelIteration": true, 10 | "moduleResolution": "node", 11 | "types": [] 12 | }, 13 | "include": [ 14 | "./src/**/*.ts" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@prisma/client@2.23.0": 6 | version "2.23.0" 7 | resolved "https://registry.yarnpkg.com/@prisma/client/-/client-2.23.0.tgz#4bf16ab19b140873ba79bd159da86842b1746e0a" 8 | integrity sha512-xsHdo3+wIH0hJVGfKHYTEKtifStjKH0b5t8t7hV32Fypq6+3uxhAi3F25yxuI4XSHXg21nb7Ha82lNwU/0TERA== 9 | dependencies: 10 | "@prisma/engines-version" "2.23.0-36.adf5e8cba3daf12d456d911d72b6e9418681b28b" 11 | 12 | "@prisma/engines-version@2.23.0-36.adf5e8cba3daf12d456d911d72b6e9418681b28b": 13 | version "2.23.0-36.adf5e8cba3daf12d456d911d72b6e9418681b28b" 14 | resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-2.23.0-36.adf5e8cba3daf12d456d911d72b6e9418681b28b.tgz#c813279bbea48dedad039b0bc3b044117d2dbaa1" 15 | integrity sha512-VNgnOe+oPQKmy3HOtWi/Q1fvcKZUQkf1OfTD1pzrLBx9tJPejyxt1Mq54L+OOAuYvfrua6bmfojFVLh7uXuWVw== 16 | 17 | "@prisma/engines@2.23.0-36.adf5e8cba3daf12d456d911d72b6e9418681b28b": 18 | version "2.23.0-36.adf5e8cba3daf12d456d911d72b6e9418681b28b" 19 | resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-2.23.0-36.adf5e8cba3daf12d456d911d72b6e9418681b28b.tgz#440abe0ebef44b6e1bdaf2b4d14fcde9fe74f18c" 20 | integrity sha512-Tgk3kggO5B9IT6mimJAw6HSxbFoDAuDKL3sHHSS41EnQm76j/nf4uhGZFPzOQwZWOLeT5ZLO2khr4/FCA9Nkhw== 21 | 22 | "@slack/bolt@^3.3.0": 23 | version "3.3.0" 24 | resolved "https://registry.yarnpkg.com/@slack/bolt/-/bolt-3.3.0.tgz#9bfb9252091f845ab20cac20d6801edae794a169" 25 | integrity sha512-rG9OHszzbJB8ZQHUl8v/IR22OodOhx+jGUGm0ZEb0kKmISUNuOGY7FLbPav4soafQg+fKfj4sWtDNfXesZrDag== 26 | dependencies: 27 | "@slack/logger" "^3.0.0" 28 | "@slack/oauth" "^2.0.0" 29 | "@slack/socket-mode" "^1.0.0" 30 | "@slack/types" "^2.0.0" 31 | "@slack/web-api" "^6.0.0" 32 | "@types/express" "^4.16.1" 33 | "@types/node" ">=12" 34 | "@types/promise.allsettled" "^1.0.3" 35 | "@types/tsscmp" "^1.0.0" 36 | axios "^0.21.1" 37 | express "^4.16.4" 38 | please-upgrade-node "^3.2.0" 39 | promise.allsettled "^1.0.2" 40 | raw-body "^2.3.3" 41 | tsscmp "^1.0.6" 42 | 43 | "@slack/logger@>=1.0.0 <3.0.0", "@slack/logger@^2.0.0": 44 | version "2.0.0" 45 | resolved "https://registry.yarnpkg.com/@slack/logger/-/logger-2.0.0.tgz#6a4e1c755849bc0f66dac08a8be54ce790ec0e6b" 46 | integrity sha512-OkIJpiU2fz6HOJujhlhfIGrc8hB4ibqtf7nnbJQDerG0BqwZCfmgtK5sWzZ0TkXVRBKD5MpLrTmCYyMxoMCgPw== 47 | dependencies: 48 | "@types/node" ">=8.9.0" 49 | 50 | "@slack/logger@^3.0.0": 51 | version "3.0.0" 52 | resolved "https://registry.yarnpkg.com/@slack/logger/-/logger-3.0.0.tgz#b736d4e1c112c22a10ffab0c2d364620aedcb714" 53 | integrity sha512-DTuBFbqu4gGfajREEMrkq5jBhcnskinhr4+AnfJEk48zhVeEv3XnUKGIX98B74kxhYsIMfApGGySTn7V3b5yBA== 54 | dependencies: 55 | "@types/node" ">=12.0.0" 56 | 57 | "@slack/oauth@^2.0.0": 58 | version "2.0.1" 59 | resolved "https://registry.yarnpkg.com/@slack/oauth/-/oauth-2.0.1.tgz#56f8f3cd45258465e2c45860f1ca60e307126e30" 60 | integrity sha512-Htiwa70u+uZuWNvYvMjCUuALTl7hMb/1v0sQhrXDDY0dh9tWWUxZCvL6dAR6pxqMCXMjhS3j+tq4o157SGVhRg== 61 | dependencies: 62 | "@slack/logger" "^2.0.0" 63 | "@slack/web-api" "^5.7.0" 64 | "@types/jsonwebtoken" "^8.3.7" 65 | "@types/node" ">=12" 66 | jsonwebtoken "^8.5.1" 67 | lodash.isstring "^4.0.1" 68 | 69 | "@slack/socket-mode@^1.0.0": 70 | version "1.0.2" 71 | resolved "https://registry.yarnpkg.com/@slack/socket-mode/-/socket-mode-1.0.2.tgz#c805a627aa6528b888ad236872d082b40ba4247a" 72 | integrity sha512-Rk5FyfZrSXra5xi8u1ZhnzzqOMUcgCcIxTHP1CqnRPymrrC0j1QqzKh7uhQLw1L4Ah9PKs3ObiJe3rXQJtAd6g== 73 | dependencies: 74 | "@slack/logger" "^3.0.0" 75 | "@slack/web-api" "^6.0.0" 76 | "@types/node" ">=12.0.0" 77 | "@types/p-queue" "^2.3.2" 78 | "@types/ws" "^7.2.5" 79 | eventemitter3 "^3.1.0" 80 | finity "^0.5.4" 81 | p-cancelable "^1.1.0" 82 | p-queue "^2.4.2" 83 | ws "^7.3.1" 84 | 85 | "@slack/types@^1.7.0": 86 | version "1.10.0" 87 | resolved "https://registry.yarnpkg.com/@slack/types/-/types-1.10.0.tgz#cbf7d83e1027f4cbfd13d6b429f120c7fb09127a" 88 | integrity sha512-tA7GG7Tj479vojfV3AoxbckalA48aK6giGjNtgH6ihpLwTyHE3fIgRrvt8TWfLwW8X8dyu7vgmAsGLRG7hWWOg== 89 | 90 | "@slack/types@^2.0.0": 91 | version "2.0.0" 92 | resolved "https://registry.yarnpkg.com/@slack/types/-/types-2.0.0.tgz#7b938ab576cd1d6c9ff9ad67a96f8058d101af10" 93 | integrity sha512-Nu4jWC39mDY5egAX4oElwOypdu8Cx9tmR7bo3ghaHYaC7mkKM1+b+soanW5s2ssu4yOLxMdFExMh6wlR34B6CA== 94 | 95 | "@slack/web-api@^5.7.0": 96 | version "5.15.0" 97 | resolved "https://registry.yarnpkg.com/@slack/web-api/-/web-api-5.15.0.tgz#6bcf1d0a833c0e87e45150c2fd1f9657e3ec0b0b" 98 | integrity sha512-tjQ8Zqv/Fmj9SOL9yIEd7IpTiKfKHi9DKAkfRVeotoX0clMr3SqQtBqO+KZMX27gm7dmgJsQaDKlILyzdCO+IA== 99 | dependencies: 100 | "@slack/logger" ">=1.0.0 <3.0.0" 101 | "@slack/types" "^1.7.0" 102 | "@types/is-stream" "^1.1.0" 103 | "@types/node" ">=8.9.0" 104 | axios "^0.21.1" 105 | eventemitter3 "^3.1.0" 106 | form-data "^2.5.0" 107 | is-stream "^1.1.0" 108 | p-queue "^6.6.1" 109 | p-retry "^4.0.0" 110 | 111 | "@slack/web-api@^6.0.0", "@slack/web-api@^6.2.3": 112 | version "6.2.3" 113 | resolved "https://registry.yarnpkg.com/@slack/web-api/-/web-api-6.2.3.tgz#063ca32987587d1879520badab485796be621e77" 114 | integrity sha512-Qt0JUSuT1RKFYRpMfbP0xKkqWoXGzmEa4N+7H5oB9eH228ABn1sM3LDNW7ZQiLs5jpDuzHAp7dSxAZpb+ZfO/w== 115 | dependencies: 116 | "@slack/logger" "^3.0.0" 117 | "@slack/types" "^2.0.0" 118 | "@types/is-stream" "^1.1.0" 119 | "@types/node" ">=12.0.0" 120 | axios "^0.21.1" 121 | eventemitter3 "^3.1.0" 122 | form-data "^2.5.0" 123 | is-electron "^2.2.0" 124 | is-stream "^1.1.0" 125 | p-queue "^6.6.1" 126 | p-retry "^4.0.0" 127 | 128 | "@types/body-parser@*": 129 | version "1.19.0" 130 | resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.0.tgz#0685b3c47eb3006ffed117cdd55164b61f80538f" 131 | integrity sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ== 132 | dependencies: 133 | "@types/connect" "*" 134 | "@types/node" "*" 135 | 136 | "@types/clone-deep@^4.0.1": 137 | version "4.0.1" 138 | resolved "https://registry.yarnpkg.com/@types/clone-deep/-/clone-deep-4.0.1.tgz#7c488443ab9f571cd343d774551b78e9264ea990" 139 | integrity sha512-bdkCSkyVHsgl3Goe1y16T9k6JuQx7SiDREkq728QjKmTZkGJZuS8R3gGcnGzVuGBP0mssKrzM/GlMOQxtip9cg== 140 | 141 | "@types/connect@*": 142 | version "3.4.34" 143 | resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.34.tgz#170a40223a6d666006d93ca128af2beb1d9b1901" 144 | integrity sha512-ePPA/JuI+X0vb+gSWlPKOY0NdNAie/rPUqX2GUPpbZwiKTkSPhjXWuee47E4MtE54QVzGCQMQkAL6JhV2E1+cQ== 145 | dependencies: 146 | "@types/node" "*" 147 | 148 | "@types/express-serve-static-core@^4.17.18": 149 | version "4.17.19" 150 | resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.19.tgz#00acfc1632e729acac4f1530e9e16f6dd1508a1d" 151 | integrity sha512-DJOSHzX7pCiSElWaGR8kCprwibCB/3yW6vcT8VG3P0SJjnv19gnWG/AZMfM60Xj/YJIp/YCaDHyvzsFVeniARA== 152 | dependencies: 153 | "@types/node" "*" 154 | "@types/qs" "*" 155 | "@types/range-parser" "*" 156 | 157 | "@types/express@^4.16.1": 158 | version "4.17.11" 159 | resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.11.tgz#debe3caa6f8e5fcda96b47bd54e2f40c4ee59545" 160 | integrity sha512-no+R6rW60JEc59977wIxreQVsIEOAYwgCqldrA/vkpCnbD7MqTefO97lmoBe4WE0F156bC4uLSP1XHDOySnChg== 161 | dependencies: 162 | "@types/body-parser" "*" 163 | "@types/express-serve-static-core" "^4.17.18" 164 | "@types/qs" "*" 165 | "@types/serve-static" "*" 166 | 167 | "@types/is-stream@^1.1.0": 168 | version "1.1.0" 169 | resolved "https://registry.yarnpkg.com/@types/is-stream/-/is-stream-1.1.0.tgz#b84d7bb207a210f2af9bed431dc0fbe9c4143be1" 170 | integrity sha512-jkZatu4QVbR60mpIzjINmtS1ZF4a/FqdTUTBeQDVOQ2PYyidtwFKr0B5G6ERukKwliq+7mIXvxyppwzG5EgRYg== 171 | dependencies: 172 | "@types/node" "*" 173 | 174 | "@types/jsonwebtoken@^8.3.7": 175 | version "8.5.1" 176 | resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#56958cb2d80f6d74352bd2e501a018e2506a8a84" 177 | integrity sha512-rNAPdomlIUX0i0cg2+I+Q1wOUr531zHBQ+cV/28PJ39bSPKjahatZZ2LMuhiguETkCgLVzfruw/ZvNMNkKoSzw== 178 | dependencies: 179 | "@types/node" "*" 180 | 181 | "@types/mime@^1": 182 | version "1.3.2" 183 | resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" 184 | integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== 185 | 186 | "@types/node@*", "@types/node@>=12", "@types/node@>=12.0.0", "@types/node@>=8.9.0": 187 | version "15.6.0" 188 | resolved "https://registry.yarnpkg.com/@types/node/-/node-15.6.0.tgz#f0ddca5a61e52627c9dcb771a6039d44694597bc" 189 | integrity sha512-gCYSfQpy+LYhOFTKAeE8BkyGqaxmlFxe+n4DKM6DR0wzw/HISUE/hAmkC/KT8Sw5PCJblqg062b3z9gucv3k0A== 190 | 191 | "@types/p-queue@^2.3.2": 192 | version "2.3.2" 193 | resolved "https://registry.yarnpkg.com/@types/p-queue/-/p-queue-2.3.2.tgz#16bc5fece69ef85efaf2bce8b13f3ebe39c5a1c8" 194 | integrity sha512-eKAv5Ql6k78dh3ULCsSBxX6bFNuGjTmof5Q/T6PiECDq0Yf8IIn46jCyp3RJvCi8owaEmm3DZH1PEImjBMd/vQ== 195 | 196 | "@types/promise.allsettled@^1.0.3": 197 | version "1.0.3" 198 | resolved "https://registry.yarnpkg.com/@types/promise.allsettled/-/promise.allsettled-1.0.3.tgz#6f3166618226a570b98c8250fc78687a912e56d5" 199 | integrity sha512-b/IFHHTkYkTqu41IH9UtpICwqrpKj2oNlb4KHPzFQDMiz+h1BgAeATeO0/XTph4+UkH9W2U0E4B4j64KWOovag== 200 | 201 | "@types/qs@*": 202 | version "6.9.6" 203 | resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.6.tgz#df9c3c8b31a247ec315e6996566be3171df4b3b1" 204 | integrity sha512-0/HnwIfW4ki2D8L8c9GVcG5I72s9jP5GSLVF0VIXDW00kmIpA6O33G7a8n59Tmh7Nz0WUC3rSb7PTY/sdW2JzA== 205 | 206 | "@types/range-parser@*": 207 | version "1.2.3" 208 | resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c" 209 | integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA== 210 | 211 | "@types/retry@^0.12.0": 212 | version "0.12.0" 213 | resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" 214 | integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== 215 | 216 | "@types/serve-static@*": 217 | version "1.13.9" 218 | resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.9.tgz#aacf28a85a05ee29a11fb7c3ead935ac56f33e4e" 219 | integrity sha512-ZFqF6qa48XsPdjXV5Gsz0Zqmux2PerNd3a/ktL45mHpa19cuMi/cL8tcxdAx497yRh+QtYPuofjT9oWw9P7nkA== 220 | dependencies: 221 | "@types/mime" "^1" 222 | "@types/node" "*" 223 | 224 | "@types/strip-bom@^3.0.0": 225 | version "3.0.0" 226 | resolved "https://registry.yarnpkg.com/@types/strip-bom/-/strip-bom-3.0.0.tgz#14a8ec3956c2e81edb7520790aecf21c290aebd2" 227 | integrity sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I= 228 | 229 | "@types/strip-json-comments@0.0.30": 230 | version "0.0.30" 231 | resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1" 232 | integrity sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ== 233 | 234 | "@types/tsscmp@^1.0.0": 235 | version "1.0.0" 236 | resolved "https://registry.yarnpkg.com/@types/tsscmp/-/tsscmp-1.0.0.tgz#761c885a530f9673ae6fda0cae38253ffd46cba6" 237 | integrity sha512-rj18XR6c4Ohds86Lq8MI1NMRrXes4eLo4H06e5bJyKucE1rXGsfBBbFGD2oDC+DSufQCpnU3TTW7QAiwLx+7Yw== 238 | 239 | "@types/ws@^7.2.5": 240 | version "7.4.4" 241 | resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.4.tgz#93e1e00824c1de2608c30e6de4303ab3b4c0c9bc" 242 | integrity sha512-d/7W23JAXPodQNbOZNXvl2K+bqAQrCMwlh/nuQsPSQk6Fq0opHoPrUw43aHsvSbIiQPr8Of2hkFbnz1XBFVyZQ== 243 | dependencies: 244 | "@types/node" "*" 245 | 246 | accepts@~1.3.7: 247 | version "1.3.7" 248 | resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" 249 | integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== 250 | dependencies: 251 | mime-types "~2.1.24" 252 | negotiator "0.6.2" 253 | 254 | anymatch@~3.1.1: 255 | version "3.1.2" 256 | resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" 257 | integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== 258 | dependencies: 259 | normalize-path "^3.0.0" 260 | picomatch "^2.0.4" 261 | 262 | arg@^4.1.0: 263 | version "4.1.3" 264 | resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" 265 | integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== 266 | 267 | array-find-index@^1.0.1: 268 | version "1.0.2" 269 | resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" 270 | integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E= 271 | 272 | array-flatten@1.1.1: 273 | version "1.1.1" 274 | resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" 275 | integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= 276 | 277 | array.prototype.map@^1.0.3: 278 | version "1.0.3" 279 | resolved "https://registry.yarnpkg.com/array.prototype.map/-/array.prototype.map-1.0.3.tgz#1609623618d3d84134a37d4a220030c2bd18420b" 280 | integrity sha512-nNcb30v0wfDyIe26Yif3PcV1JXQp4zEeEfupG7L4SRjnD6HLbO5b2a7eVSba53bOx4YCHYMBHt+Fp4vYstneRA== 281 | dependencies: 282 | call-bind "^1.0.0" 283 | define-properties "^1.1.3" 284 | es-abstract "^1.18.0-next.1" 285 | es-array-method-boxes-properly "^1.0.0" 286 | is-string "^1.0.5" 287 | 288 | asynckit@^0.4.0: 289 | version "0.4.0" 290 | resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" 291 | integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= 292 | 293 | axios@^0.21.1: 294 | version "0.21.1" 295 | resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8" 296 | integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA== 297 | dependencies: 298 | follow-redirects "^1.10.0" 299 | 300 | balanced-match@^1.0.0: 301 | version "1.0.2" 302 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" 303 | integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== 304 | 305 | binary-extensions@^2.0.0: 306 | version "2.2.0" 307 | resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" 308 | integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== 309 | 310 | body-parser@1.19.0, body-parser@^1.19.0: 311 | version "1.19.0" 312 | resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" 313 | integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== 314 | dependencies: 315 | bytes "3.1.0" 316 | content-type "~1.0.4" 317 | debug "2.6.9" 318 | depd "~1.1.2" 319 | http-errors "1.7.2" 320 | iconv-lite "0.4.24" 321 | on-finished "~2.3.0" 322 | qs "6.7.0" 323 | raw-body "2.4.0" 324 | type-is "~1.6.17" 325 | 326 | brace-expansion@^1.1.7: 327 | version "1.1.11" 328 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 329 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== 330 | dependencies: 331 | balanced-match "^1.0.0" 332 | concat-map "0.0.1" 333 | 334 | braces@~3.0.2: 335 | version "3.0.2" 336 | resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" 337 | integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== 338 | dependencies: 339 | fill-range "^7.0.1" 340 | 341 | buffer-equal-constant-time@1.0.1: 342 | version "1.0.1" 343 | resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" 344 | integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk= 345 | 346 | buffer-from@^1.0.0: 347 | version "1.1.1" 348 | resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" 349 | integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== 350 | 351 | bytes@3.1.0: 352 | version "3.1.0" 353 | resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" 354 | integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== 355 | 356 | call-bind@^1.0.0, call-bind@^1.0.2: 357 | version "1.0.2" 358 | resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" 359 | integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== 360 | dependencies: 361 | function-bind "^1.1.1" 362 | get-intrinsic "^1.0.2" 363 | 364 | camelcase-keys@^2.0.0: 365 | version "2.1.0" 366 | resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" 367 | integrity sha1-MIvur/3ygRkFHvodkyITyRuPkuc= 368 | dependencies: 369 | camelcase "^2.0.0" 370 | map-obj "^1.0.0" 371 | 372 | camelcase@^2.0.0: 373 | version "2.1.1" 374 | resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" 375 | integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= 376 | 377 | chokidar@^3.5.1: 378 | version "3.5.1" 379 | resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a" 380 | integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== 381 | dependencies: 382 | anymatch "~3.1.1" 383 | braces "~3.0.2" 384 | glob-parent "~5.1.0" 385 | is-binary-path "~2.1.0" 386 | is-glob "~4.0.1" 387 | normalize-path "~3.0.0" 388 | readdirp "~3.5.0" 389 | optionalDependencies: 390 | fsevents "~2.3.1" 391 | 392 | clone-deep@^4.0.1: 393 | version "4.0.1" 394 | resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" 395 | integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== 396 | dependencies: 397 | is-plain-object "^2.0.4" 398 | kind-of "^6.0.2" 399 | shallow-clone "^3.0.0" 400 | 401 | combined-stream@^1.0.6, combined-stream@^1.0.8: 402 | version "1.0.8" 403 | resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" 404 | integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== 405 | dependencies: 406 | delayed-stream "~1.0.0" 407 | 408 | concat-map@0.0.1: 409 | version "0.0.1" 410 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 411 | integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= 412 | 413 | content-disposition@0.5.3: 414 | version "0.5.3" 415 | resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" 416 | integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== 417 | dependencies: 418 | safe-buffer "5.1.2" 419 | 420 | content-type@~1.0.4: 421 | version "1.0.4" 422 | resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" 423 | integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== 424 | 425 | cookie-signature@1.0.6: 426 | version "1.0.6" 427 | resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" 428 | integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= 429 | 430 | cookie@0.4.0: 431 | version "0.4.0" 432 | resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" 433 | integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== 434 | 435 | create-require@^1.1.0: 436 | version "1.1.1" 437 | resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" 438 | integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== 439 | 440 | cross-fetch@^3.0.6: 441 | version "3.1.4" 442 | resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.4.tgz#9723f3a3a247bf8b89039f3a380a9244e8fa2f39" 443 | integrity sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ== 444 | dependencies: 445 | node-fetch "2.6.1" 446 | 447 | currently-unhandled@^0.4.1: 448 | version "0.4.1" 449 | resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" 450 | integrity sha1-mI3zP+qxke95mmE2nddsF635V+o= 451 | dependencies: 452 | array-find-index "^1.0.1" 453 | 454 | dateformat@~1.0.4-1.2.3: 455 | version "1.0.12" 456 | resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-1.0.12.tgz#9f124b67594c937ff706932e4a642cca8dbbfee9" 457 | integrity sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk= 458 | dependencies: 459 | get-stdin "^4.0.1" 460 | meow "^3.3.0" 461 | 462 | debug@2.6.9: 463 | version "2.6.9" 464 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" 465 | integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== 466 | dependencies: 467 | ms "2.0.0" 468 | 469 | decamelize@^1.1.2: 470 | version "1.2.0" 471 | resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" 472 | integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= 473 | 474 | define-properties@^1.1.3: 475 | version "1.1.3" 476 | resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" 477 | integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== 478 | dependencies: 479 | object-keys "^1.0.12" 480 | 481 | delayed-stream@~1.0.0: 482 | version "1.0.0" 483 | resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" 484 | integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= 485 | 486 | depd@~1.1.2: 487 | version "1.1.2" 488 | resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" 489 | integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= 490 | 491 | destroy@~1.0.4: 492 | version "1.0.4" 493 | resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" 494 | integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= 495 | 496 | diff@^4.0.1: 497 | version "4.0.2" 498 | resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" 499 | integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== 500 | 501 | dotenv@^10.0.0: 502 | version "10.0.0" 503 | resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" 504 | integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== 505 | 506 | dynamic-dedupe@^0.3.0: 507 | version "0.3.0" 508 | resolved "https://registry.yarnpkg.com/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz#06e44c223f5e4e94d78ef9db23a6515ce2f962a1" 509 | integrity sha1-BuRMIj9eTpTXjvnbI6ZRXOL5YqE= 510 | dependencies: 511 | xtend "^4.0.0" 512 | 513 | ecdsa-sig-formatter@1.0.11: 514 | version "1.0.11" 515 | resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" 516 | integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== 517 | dependencies: 518 | safe-buffer "^5.0.1" 519 | 520 | ee-first@1.1.1: 521 | version "1.1.1" 522 | resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" 523 | integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= 524 | 525 | encodeurl@~1.0.2: 526 | version "1.0.2" 527 | resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" 528 | integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= 529 | 530 | error-ex@^1.2.0: 531 | version "1.3.2" 532 | resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" 533 | integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== 534 | dependencies: 535 | is-arrayish "^0.2.1" 536 | 537 | es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2: 538 | version "1.18.0" 539 | resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0.tgz#ab80b359eecb7ede4c298000390bc5ac3ec7b5a4" 540 | integrity sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw== 541 | dependencies: 542 | call-bind "^1.0.2" 543 | es-to-primitive "^1.2.1" 544 | function-bind "^1.1.1" 545 | get-intrinsic "^1.1.1" 546 | has "^1.0.3" 547 | has-symbols "^1.0.2" 548 | is-callable "^1.2.3" 549 | is-negative-zero "^2.0.1" 550 | is-regex "^1.1.2" 551 | is-string "^1.0.5" 552 | object-inspect "^1.9.0" 553 | object-keys "^1.1.1" 554 | object.assign "^4.1.2" 555 | string.prototype.trimend "^1.0.4" 556 | string.prototype.trimstart "^1.0.4" 557 | unbox-primitive "^1.0.0" 558 | 559 | es-array-method-boxes-properly@^1.0.0: 560 | version "1.0.0" 561 | resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" 562 | integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== 563 | 564 | es-get-iterator@^1.0.2: 565 | version "1.1.2" 566 | resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.2.tgz#9234c54aba713486d7ebde0220864af5e2b283f7" 567 | integrity sha512-+DTO8GYwbMCwbywjimwZMHp8AuYXOS2JZFWoi2AlPOS3ebnII9w/NLpNZtA7A0YLaVDw+O7KFCeoIV7OPvM7hQ== 568 | dependencies: 569 | call-bind "^1.0.2" 570 | get-intrinsic "^1.1.0" 571 | has-symbols "^1.0.1" 572 | is-arguments "^1.1.0" 573 | is-map "^2.0.2" 574 | is-set "^2.0.2" 575 | is-string "^1.0.5" 576 | isarray "^2.0.5" 577 | 578 | es-to-primitive@^1.2.1: 579 | version "1.2.1" 580 | resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" 581 | integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== 582 | dependencies: 583 | is-callable "^1.1.4" 584 | is-date-object "^1.0.1" 585 | is-symbol "^1.0.2" 586 | 587 | escape-html@~1.0.3: 588 | version "1.0.3" 589 | resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" 590 | integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= 591 | 592 | etag@~1.8.1: 593 | version "1.8.1" 594 | resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" 595 | integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= 596 | 597 | eventemitter3@^3.1.0: 598 | version "3.1.2" 599 | resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" 600 | integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q== 601 | 602 | eventemitter3@^4.0.4: 603 | version "4.0.7" 604 | resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" 605 | integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== 606 | 607 | express@^4.16.4, express@^4.17.1: 608 | version "4.17.1" 609 | resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" 610 | integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== 611 | dependencies: 612 | accepts "~1.3.7" 613 | array-flatten "1.1.1" 614 | body-parser "1.19.0" 615 | content-disposition "0.5.3" 616 | content-type "~1.0.4" 617 | cookie "0.4.0" 618 | cookie-signature "1.0.6" 619 | debug "2.6.9" 620 | depd "~1.1.2" 621 | encodeurl "~1.0.2" 622 | escape-html "~1.0.3" 623 | etag "~1.8.1" 624 | finalhandler "~1.1.2" 625 | fresh "0.5.2" 626 | merge-descriptors "1.0.1" 627 | methods "~1.1.2" 628 | on-finished "~2.3.0" 629 | parseurl "~1.3.3" 630 | path-to-regexp "0.1.7" 631 | proxy-addr "~2.0.5" 632 | qs "6.7.0" 633 | range-parser "~1.2.1" 634 | safe-buffer "5.1.2" 635 | send "0.17.1" 636 | serve-static "1.14.1" 637 | setprototypeof "1.1.1" 638 | statuses "~1.5.0" 639 | type-is "~1.6.18" 640 | utils-merge "1.0.1" 641 | vary "~1.1.2" 642 | 643 | extract-files@^9.0.0: 644 | version "9.0.0" 645 | resolved "https://registry.yarnpkg.com/extract-files/-/extract-files-9.0.0.tgz#8a7744f2437f81f5ed3250ed9f1550de902fe54a" 646 | integrity sha512-CvdFfHkC95B4bBBk36hcEmvdR2awOdhhVUYH6S/zrVj3477zven/fJMYg7121h4T1xHZC+tetUpubpAhxwI7hQ== 647 | 648 | fill-range@^7.0.1: 649 | version "7.0.1" 650 | resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" 651 | integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== 652 | dependencies: 653 | to-regex-range "^5.0.1" 654 | 655 | finalhandler@~1.1.2: 656 | version "1.1.2" 657 | resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" 658 | integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== 659 | dependencies: 660 | debug "2.6.9" 661 | encodeurl "~1.0.2" 662 | escape-html "~1.0.3" 663 | on-finished "~2.3.0" 664 | parseurl "~1.3.3" 665 | statuses "~1.5.0" 666 | unpipe "~1.0.0" 667 | 668 | find-up@^1.0.0: 669 | version "1.1.2" 670 | resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" 671 | integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= 672 | dependencies: 673 | path-exists "^2.0.0" 674 | pinkie-promise "^2.0.0" 675 | 676 | finity@^0.5.4: 677 | version "0.5.4" 678 | resolved "https://registry.yarnpkg.com/finity/-/finity-0.5.4.tgz#f2a8a9198e8286467328ec32c8bfcc19a2229c11" 679 | integrity sha512-3l+5/1tuw616Lgb0QBimxfdd2TqaDGpfCBpfX6EqtFmqUV3FtQnVEX4Aa62DagYEqnsTIjZcTfbq9msDbXYgyA== 680 | 681 | follow-redirects@^1.10.0: 682 | version "1.14.1" 683 | resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.1.tgz#d9114ded0a1cfdd334e164e6662ad02bfd91ff43" 684 | integrity sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg== 685 | 686 | form-data@^2.5.0: 687 | version "2.5.1" 688 | resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4" 689 | integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA== 690 | dependencies: 691 | asynckit "^0.4.0" 692 | combined-stream "^1.0.6" 693 | mime-types "^2.1.12" 694 | 695 | form-data@^3.0.0: 696 | version "3.0.1" 697 | resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" 698 | integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== 699 | dependencies: 700 | asynckit "^0.4.0" 701 | combined-stream "^1.0.8" 702 | mime-types "^2.1.12" 703 | 704 | forwarded@~0.1.2: 705 | version "0.1.2" 706 | resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" 707 | integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= 708 | 709 | fresh@0.5.2: 710 | version "0.5.2" 711 | resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" 712 | integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= 713 | 714 | fs.realpath@^1.0.0: 715 | version "1.0.0" 716 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 717 | integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= 718 | 719 | fsevents@~2.3.1: 720 | version "2.3.2" 721 | resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" 722 | integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== 723 | 724 | function-bind@^1.1.1: 725 | version "1.1.1" 726 | resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" 727 | integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== 728 | 729 | get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: 730 | version "1.1.1" 731 | resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" 732 | integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== 733 | dependencies: 734 | function-bind "^1.1.1" 735 | has "^1.0.3" 736 | has-symbols "^1.0.1" 737 | 738 | get-stdin@^4.0.1: 739 | version "4.0.1" 740 | resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" 741 | integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= 742 | 743 | glob-parent@~5.1.0: 744 | version "5.1.2" 745 | resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" 746 | integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== 747 | dependencies: 748 | is-glob "^4.0.1" 749 | 750 | glob@^7.1.3: 751 | version "7.1.7" 752 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" 753 | integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== 754 | dependencies: 755 | fs.realpath "^1.0.0" 756 | inflight "^1.0.4" 757 | inherits "2" 758 | minimatch "^3.0.4" 759 | once "^1.3.0" 760 | path-is-absolute "^1.0.0" 761 | 762 | graceful-fs@^4.1.2: 763 | version "4.2.6" 764 | resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" 765 | integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== 766 | 767 | graphql-request@^3.4.0: 768 | version "3.4.0" 769 | resolved "https://registry.yarnpkg.com/graphql-request/-/graphql-request-3.4.0.tgz#3a400cd5511eb3c064b1873afb059196bbea9c2b" 770 | integrity sha512-acrTzidSlwAj8wBNO7Q/UQHS8T+z5qRGquCQRv9J1InwR01BBWV9ObnoE+JS5nCCEj8wSGS0yrDXVDoRiKZuOg== 771 | dependencies: 772 | cross-fetch "^3.0.6" 773 | extract-files "^9.0.0" 774 | form-data "^3.0.0" 775 | 776 | graphql@^15.5.0: 777 | version "15.5.0" 778 | resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.5.0.tgz#39d19494dbe69d1ea719915b578bf920344a69d5" 779 | integrity sha512-OmaM7y0kaK31NKG31q4YbD2beNYa6jBBKtMFT6gLYJljHLJr42IqJ8KX08u3Li/0ifzTU5HjmoOOrwa5BRLeDA== 780 | 781 | has-bigints@^1.0.1: 782 | version "1.0.1" 783 | resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" 784 | integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== 785 | 786 | has-symbols@^1.0.1, has-symbols@^1.0.2: 787 | version "1.0.2" 788 | resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" 789 | integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== 790 | 791 | has@^1.0.3: 792 | version "1.0.3" 793 | resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" 794 | integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== 795 | dependencies: 796 | function-bind "^1.1.1" 797 | 798 | hosted-git-info@^2.1.4: 799 | version "2.8.9" 800 | resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" 801 | integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== 802 | 803 | http-errors@1.7.2: 804 | version "1.7.2" 805 | resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" 806 | integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== 807 | dependencies: 808 | depd "~1.1.2" 809 | inherits "2.0.3" 810 | setprototypeof "1.1.1" 811 | statuses ">= 1.5.0 < 2" 812 | toidentifier "1.0.0" 813 | 814 | http-errors@1.7.3, http-errors@~1.7.2: 815 | version "1.7.3" 816 | resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" 817 | integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== 818 | dependencies: 819 | depd "~1.1.2" 820 | inherits "2.0.4" 821 | setprototypeof "1.1.1" 822 | statuses ">= 1.5.0 < 2" 823 | toidentifier "1.0.0" 824 | 825 | iconv-lite@0.4.24: 826 | version "0.4.24" 827 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" 828 | integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== 829 | dependencies: 830 | safer-buffer ">= 2.1.2 < 3" 831 | 832 | indent-string@^2.1.0: 833 | version "2.1.0" 834 | resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" 835 | integrity sha1-ji1INIdCEhtKghi3oTfppSBJ3IA= 836 | dependencies: 837 | repeating "^2.0.0" 838 | 839 | inflight@^1.0.4: 840 | version "1.0.6" 841 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 842 | integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= 843 | dependencies: 844 | once "^1.3.0" 845 | wrappy "1" 846 | 847 | inherits@2, inherits@2.0.4: 848 | version "2.0.4" 849 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 850 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 851 | 852 | inherits@2.0.3: 853 | version "2.0.3" 854 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 855 | integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= 856 | 857 | ipaddr.js@1.9.1: 858 | version "1.9.1" 859 | resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" 860 | integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== 861 | 862 | is-arguments@^1.1.0: 863 | version "1.1.0" 864 | resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.0.tgz#62353031dfbee07ceb34656a6bde59efecae8dd9" 865 | integrity sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg== 866 | dependencies: 867 | call-bind "^1.0.0" 868 | 869 | is-arrayish@^0.2.1: 870 | version "0.2.1" 871 | resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" 872 | integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= 873 | 874 | is-bigint@^1.0.1: 875 | version "1.0.2" 876 | resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.2.tgz#ffb381442503235ad245ea89e45b3dbff040ee5a" 877 | integrity sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA== 878 | 879 | is-binary-path@~2.1.0: 880 | version "2.1.0" 881 | resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" 882 | integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== 883 | dependencies: 884 | binary-extensions "^2.0.0" 885 | 886 | is-boolean-object@^1.1.0: 887 | version "1.1.1" 888 | resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.1.tgz#3c0878f035cb821228d350d2e1e36719716a3de8" 889 | integrity sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng== 890 | dependencies: 891 | call-bind "^1.0.2" 892 | 893 | is-callable@^1.1.4, is-callable@^1.2.3: 894 | version "1.2.3" 895 | resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e" 896 | integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ== 897 | 898 | is-core-module@^2.2.0: 899 | version "2.4.0" 900 | resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.4.0.tgz#8e9fc8e15027b011418026e98f0e6f4d86305cc1" 901 | integrity sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A== 902 | dependencies: 903 | has "^1.0.3" 904 | 905 | is-date-object@^1.0.1: 906 | version "1.0.4" 907 | resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.4.tgz#550cfcc03afada05eea3dd30981c7b09551f73e5" 908 | integrity sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A== 909 | 910 | is-electron@^2.2.0: 911 | version "2.2.0" 912 | resolved "https://registry.yarnpkg.com/is-electron/-/is-electron-2.2.0.tgz#8943084f09e8b731b3a7a0298a7b5d56f6b7eef0" 913 | integrity sha512-SpMppC2XR3YdxSzczXReBjqs2zGscWQpBIKqwXYBFic0ERaxNVgwLCHwOLZeESfdJQjX0RDvrJ1lBXX2ij+G1Q== 914 | 915 | is-extglob@^2.1.1: 916 | version "2.1.1" 917 | resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" 918 | integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= 919 | 920 | is-finite@^1.0.0: 921 | version "1.1.0" 922 | resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3" 923 | integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== 924 | 925 | is-glob@^4.0.1, is-glob@~4.0.1: 926 | version "4.0.1" 927 | resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" 928 | integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== 929 | dependencies: 930 | is-extglob "^2.1.1" 931 | 932 | is-map@^2.0.2: 933 | version "2.0.2" 934 | resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127" 935 | integrity sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg== 936 | 937 | is-negative-zero@^2.0.1: 938 | version "2.0.1" 939 | resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" 940 | integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== 941 | 942 | is-number-object@^1.0.4: 943 | version "1.0.5" 944 | resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.5.tgz#6edfaeed7950cff19afedce9fbfca9ee6dd289eb" 945 | integrity sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw== 946 | 947 | is-number@^7.0.0: 948 | version "7.0.0" 949 | resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" 950 | integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== 951 | 952 | is-plain-object@^2.0.4: 953 | version "2.0.4" 954 | resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" 955 | integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== 956 | dependencies: 957 | isobject "^3.0.1" 958 | 959 | is-regex@^1.1.2: 960 | version "1.1.3" 961 | resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.3.tgz#d029f9aff6448b93ebbe3f33dac71511fdcbef9f" 962 | integrity sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ== 963 | dependencies: 964 | call-bind "^1.0.2" 965 | has-symbols "^1.0.2" 966 | 967 | is-set@^2.0.2: 968 | version "2.0.2" 969 | resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.2.tgz#90755fa4c2562dc1c5d4024760d6119b94ca18ec" 970 | integrity sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g== 971 | 972 | is-stream@^1.1.0: 973 | version "1.1.0" 974 | resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" 975 | integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= 976 | 977 | is-string@^1.0.5: 978 | version "1.0.6" 979 | resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.6.tgz#3fe5d5992fb0d93404f32584d4b0179a71b54a5f" 980 | integrity sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w== 981 | 982 | is-symbol@^1.0.2, is-symbol@^1.0.3: 983 | version "1.0.4" 984 | resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" 985 | integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== 986 | dependencies: 987 | has-symbols "^1.0.2" 988 | 989 | is-utf8@^0.2.0: 990 | version "0.2.1" 991 | resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" 992 | integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= 993 | 994 | isarray@^2.0.5: 995 | version "2.0.5" 996 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" 997 | integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== 998 | 999 | isobject@^3.0.1: 1000 | version "3.0.1" 1001 | resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" 1002 | integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= 1003 | 1004 | iterate-iterator@^1.0.1: 1005 | version "1.0.1" 1006 | resolved "https://registry.yarnpkg.com/iterate-iterator/-/iterate-iterator-1.0.1.tgz#1693a768c1ddd79c969051459453f082fe82e9f6" 1007 | integrity sha512-3Q6tudGN05kbkDQDI4CqjaBf4qf85w6W6GnuZDtUVYwKgtC1q8yxYX7CZed7N+tLzQqS6roujWvszf13T+n9aw== 1008 | 1009 | iterate-value@^1.0.2: 1010 | version "1.0.2" 1011 | resolved "https://registry.yarnpkg.com/iterate-value/-/iterate-value-1.0.2.tgz#935115bd37d006a52046535ebc8d07e9c9337f57" 1012 | integrity sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ== 1013 | dependencies: 1014 | es-get-iterator "^1.0.2" 1015 | iterate-iterator "^1.0.1" 1016 | 1017 | jsonwebtoken@^8.5.1: 1018 | version "8.5.1" 1019 | resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" 1020 | integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== 1021 | dependencies: 1022 | jws "^3.2.2" 1023 | lodash.includes "^4.3.0" 1024 | lodash.isboolean "^3.0.3" 1025 | lodash.isinteger "^4.0.4" 1026 | lodash.isnumber "^3.0.3" 1027 | lodash.isplainobject "^4.0.6" 1028 | lodash.isstring "^4.0.1" 1029 | lodash.once "^4.0.0" 1030 | ms "^2.1.1" 1031 | semver "^5.6.0" 1032 | 1033 | jwa@^1.4.1: 1034 | version "1.4.1" 1035 | resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" 1036 | integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== 1037 | dependencies: 1038 | buffer-equal-constant-time "1.0.1" 1039 | ecdsa-sig-formatter "1.0.11" 1040 | safe-buffer "^5.0.1" 1041 | 1042 | jws@^3.2.2: 1043 | version "3.2.2" 1044 | resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" 1045 | integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== 1046 | dependencies: 1047 | jwa "^1.4.1" 1048 | safe-buffer "^5.0.1" 1049 | 1050 | kind-of@^6.0.2: 1051 | version "6.0.3" 1052 | resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" 1053 | integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== 1054 | 1055 | load-json-file@^1.0.0: 1056 | version "1.1.0" 1057 | resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" 1058 | integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= 1059 | dependencies: 1060 | graceful-fs "^4.1.2" 1061 | parse-json "^2.2.0" 1062 | pify "^2.0.0" 1063 | pinkie-promise "^2.0.0" 1064 | strip-bom "^2.0.0" 1065 | 1066 | lodash.includes@^4.3.0: 1067 | version "4.3.0" 1068 | resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" 1069 | integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8= 1070 | 1071 | lodash.isboolean@^3.0.3: 1072 | version "3.0.3" 1073 | resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" 1074 | integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY= 1075 | 1076 | lodash.isinteger@^4.0.4: 1077 | version "4.0.4" 1078 | resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" 1079 | integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M= 1080 | 1081 | lodash.isnumber@^3.0.3: 1082 | version "3.0.3" 1083 | resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" 1084 | integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w= 1085 | 1086 | lodash.isplainobject@^4.0.6: 1087 | version "4.0.6" 1088 | resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" 1089 | integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= 1090 | 1091 | lodash.isstring@^4.0.1: 1092 | version "4.0.1" 1093 | resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" 1094 | integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= 1095 | 1096 | lodash.once@^4.0.0: 1097 | version "4.1.1" 1098 | resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" 1099 | integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= 1100 | 1101 | loud-rejection@^1.0.0: 1102 | version "1.6.0" 1103 | resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" 1104 | integrity sha1-W0b4AUft7leIcPCG0Eghz5mOVR8= 1105 | dependencies: 1106 | currently-unhandled "^0.4.1" 1107 | signal-exit "^3.0.0" 1108 | 1109 | make-error@^1.1.1: 1110 | version "1.3.6" 1111 | resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" 1112 | integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== 1113 | 1114 | map-obj@^1.0.0, map-obj@^1.0.1: 1115 | version "1.0.1" 1116 | resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" 1117 | integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= 1118 | 1119 | media-typer@0.3.0: 1120 | version "0.3.0" 1121 | resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" 1122 | integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= 1123 | 1124 | meow@^3.3.0: 1125 | version "3.7.0" 1126 | resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" 1127 | integrity sha1-cstmi0JSKCkKu/qFaJJYcwioAfs= 1128 | dependencies: 1129 | camelcase-keys "^2.0.0" 1130 | decamelize "^1.1.2" 1131 | loud-rejection "^1.0.0" 1132 | map-obj "^1.0.1" 1133 | minimist "^1.1.3" 1134 | normalize-package-data "^2.3.4" 1135 | object-assign "^4.0.1" 1136 | read-pkg-up "^1.0.1" 1137 | redent "^1.0.0" 1138 | trim-newlines "^1.0.0" 1139 | 1140 | merge-descriptors@1.0.1: 1141 | version "1.0.1" 1142 | resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" 1143 | integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= 1144 | 1145 | methods@~1.1.2: 1146 | version "1.1.2" 1147 | resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" 1148 | integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= 1149 | 1150 | mime-db@1.47.0: 1151 | version "1.47.0" 1152 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.47.0.tgz#8cb313e59965d3c05cfbf898915a267af46a335c" 1153 | integrity sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw== 1154 | 1155 | mime-types@^2.1.12, mime-types@~2.1.24: 1156 | version "2.1.30" 1157 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.30.tgz#6e7be8b4c479825f85ed6326695db73f9305d62d" 1158 | integrity sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg== 1159 | dependencies: 1160 | mime-db "1.47.0" 1161 | 1162 | mime@1.6.0: 1163 | version "1.6.0" 1164 | resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" 1165 | integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== 1166 | 1167 | minimatch@^3.0.4: 1168 | version "3.0.4" 1169 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 1170 | integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== 1171 | dependencies: 1172 | brace-expansion "^1.1.7" 1173 | 1174 | minimist@^1.1.3, minimist@^1.2.5: 1175 | version "1.2.5" 1176 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" 1177 | integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== 1178 | 1179 | mkdirp@^1.0.4: 1180 | version "1.0.4" 1181 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" 1182 | integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== 1183 | 1184 | ms@2.0.0: 1185 | version "2.0.0" 1186 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" 1187 | integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= 1188 | 1189 | ms@2.1.1: 1190 | version "2.1.1" 1191 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" 1192 | integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== 1193 | 1194 | ms@^2.1.1: 1195 | version "2.1.3" 1196 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" 1197 | integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== 1198 | 1199 | negotiator@0.6.2: 1200 | version "0.6.2" 1201 | resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" 1202 | integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== 1203 | 1204 | node-fetch@2.6.1: 1205 | version "2.6.1" 1206 | resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" 1207 | integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== 1208 | 1209 | normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: 1210 | version "2.5.0" 1211 | resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" 1212 | integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== 1213 | dependencies: 1214 | hosted-git-info "^2.1.4" 1215 | resolve "^1.10.0" 1216 | semver "2 || 3 || 4 || 5" 1217 | validate-npm-package-license "^3.0.1" 1218 | 1219 | normalize-path@^3.0.0, normalize-path@~3.0.0: 1220 | version "3.0.0" 1221 | resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" 1222 | integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== 1223 | 1224 | object-assign@^4.0.1: 1225 | version "4.1.1" 1226 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" 1227 | integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= 1228 | 1229 | object-inspect@^1.9.0: 1230 | version "1.10.3" 1231 | resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.10.3.tgz#c2aa7d2d09f50c99375704f7a0adf24c5782d369" 1232 | integrity sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw== 1233 | 1234 | object-keys@^1.0.12, object-keys@^1.1.1: 1235 | version "1.1.1" 1236 | resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" 1237 | integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== 1238 | 1239 | object.assign@^4.1.2: 1240 | version "4.1.2" 1241 | resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" 1242 | integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== 1243 | dependencies: 1244 | call-bind "^1.0.0" 1245 | define-properties "^1.1.3" 1246 | has-symbols "^1.0.1" 1247 | object-keys "^1.1.1" 1248 | 1249 | on-finished@~2.3.0: 1250 | version "2.3.0" 1251 | resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" 1252 | integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= 1253 | dependencies: 1254 | ee-first "1.1.1" 1255 | 1256 | once@^1.3.0: 1257 | version "1.4.0" 1258 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 1259 | integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= 1260 | dependencies: 1261 | wrappy "1" 1262 | 1263 | p-cancelable@^1.1.0: 1264 | version "1.1.0" 1265 | resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" 1266 | integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== 1267 | 1268 | p-finally@^1.0.0: 1269 | version "1.0.0" 1270 | resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" 1271 | integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= 1272 | 1273 | p-queue@^2.4.2: 1274 | version "2.4.2" 1275 | resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-2.4.2.tgz#03609826682b743be9a22dba25051bd46724fc34" 1276 | integrity sha512-n8/y+yDJwBjoLQe1GSJbbaYQLTI7QHNZI2+rpmCDbe++WLf9HC3gf6iqj5yfPAV71W4UF3ql5W1+UBPXoXTxng== 1277 | 1278 | p-queue@^6.6.1: 1279 | version "6.6.2" 1280 | resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-6.6.2.tgz#2068a9dcf8e67dd0ec3e7a2bcb76810faa85e426" 1281 | integrity sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ== 1282 | dependencies: 1283 | eventemitter3 "^4.0.4" 1284 | p-timeout "^3.2.0" 1285 | 1286 | p-retry@^4.0.0: 1287 | version "4.5.0" 1288 | resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.5.0.tgz#6685336b3672f9ee8174d3769a660cb5e488521d" 1289 | integrity sha512-5Hwh4aVQSu6BEP+w2zKlVXtFAaYQe1qWuVADSgoeVlLjwe/Q/AMSoRR4MDeaAfu8llT+YNbEijWu/YF3m6avkg== 1290 | dependencies: 1291 | "@types/retry" "^0.12.0" 1292 | retry "^0.12.0" 1293 | 1294 | p-timeout@^3.2.0: 1295 | version "3.2.0" 1296 | resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe" 1297 | integrity sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg== 1298 | dependencies: 1299 | p-finally "^1.0.0" 1300 | 1301 | parse-json@^2.2.0: 1302 | version "2.2.0" 1303 | resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" 1304 | integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= 1305 | dependencies: 1306 | error-ex "^1.2.0" 1307 | 1308 | parseurl@~1.3.3: 1309 | version "1.3.3" 1310 | resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" 1311 | integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== 1312 | 1313 | path-exists@^2.0.0: 1314 | version "2.1.0" 1315 | resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" 1316 | integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= 1317 | dependencies: 1318 | pinkie-promise "^2.0.0" 1319 | 1320 | path-is-absolute@^1.0.0: 1321 | version "1.0.1" 1322 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 1323 | integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= 1324 | 1325 | path-parse@^1.0.6: 1326 | version "1.0.6" 1327 | resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" 1328 | integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== 1329 | 1330 | path-to-regexp@0.1.7: 1331 | version "0.1.7" 1332 | resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" 1333 | integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= 1334 | 1335 | path-type@^1.0.0: 1336 | version "1.1.0" 1337 | resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" 1338 | integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= 1339 | dependencies: 1340 | graceful-fs "^4.1.2" 1341 | pify "^2.0.0" 1342 | pinkie-promise "^2.0.0" 1343 | 1344 | picomatch@^2.0.4, picomatch@^2.2.1: 1345 | version "2.3.0" 1346 | resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" 1347 | integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== 1348 | 1349 | pify@^2.0.0: 1350 | version "2.3.0" 1351 | resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" 1352 | integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= 1353 | 1354 | pinkie-promise@^2.0.0: 1355 | version "2.0.1" 1356 | resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" 1357 | integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= 1358 | dependencies: 1359 | pinkie "^2.0.0" 1360 | 1361 | pinkie@^2.0.0: 1362 | version "2.0.4" 1363 | resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" 1364 | integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= 1365 | 1366 | please-upgrade-node@^3.2.0: 1367 | version "3.2.0" 1368 | resolved "https://registry.yarnpkg.com/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz#aeddd3f994c933e4ad98b99d9a556efa0e2fe942" 1369 | integrity sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg== 1370 | dependencies: 1371 | semver-compare "^1.0.0" 1372 | 1373 | prisma@2.23.0: 1374 | version "2.23.0" 1375 | resolved "https://registry.yarnpkg.com/prisma/-/prisma-2.23.0.tgz#6464cca0e085ed23b1815013a67c868eff07a7d2" 1376 | integrity sha512-3c/lmDy8nsPcEsfCufvCTJUEuwmAcTPbeGg9fL1qjlvS314duLUA/k2nm3n1rq4ImKqzeC5uaKfvI2IoAfwrJA== 1377 | dependencies: 1378 | "@prisma/engines" "2.23.0-36.adf5e8cba3daf12d456d911d72b6e9418681b28b" 1379 | 1380 | promise.allsettled@^1.0.2: 1381 | version "1.0.4" 1382 | resolved "https://registry.yarnpkg.com/promise.allsettled/-/promise.allsettled-1.0.4.tgz#65e71f2a604082ed69c548b68603294090ee6803" 1383 | integrity sha512-o73CbvQh/OnPFShxHcHxk0baXR2a1m4ozb85ha0H14VEoi/EJJLa9mnPfEWJx9RjA9MLfhdjZ8I6HhWtBa64Ag== 1384 | dependencies: 1385 | array.prototype.map "^1.0.3" 1386 | call-bind "^1.0.2" 1387 | define-properties "^1.1.3" 1388 | es-abstract "^1.18.0-next.2" 1389 | get-intrinsic "^1.0.2" 1390 | iterate-value "^1.0.2" 1391 | 1392 | proxy-addr@~2.0.5: 1393 | version "2.0.6" 1394 | resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" 1395 | integrity sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw== 1396 | dependencies: 1397 | forwarded "~0.1.2" 1398 | ipaddr.js "1.9.1" 1399 | 1400 | qs@6.7.0: 1401 | version "6.7.0" 1402 | resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" 1403 | integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== 1404 | 1405 | range-parser@~1.2.1: 1406 | version "1.2.1" 1407 | resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" 1408 | integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== 1409 | 1410 | raw-body@2.4.0: 1411 | version "2.4.0" 1412 | resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" 1413 | integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== 1414 | dependencies: 1415 | bytes "3.1.0" 1416 | http-errors "1.7.2" 1417 | iconv-lite "0.4.24" 1418 | unpipe "1.0.0" 1419 | 1420 | raw-body@^2.3.3: 1421 | version "2.4.1" 1422 | resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.1.tgz#30ac82f98bb5ae8c152e67149dac8d55153b168c" 1423 | integrity sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA== 1424 | dependencies: 1425 | bytes "3.1.0" 1426 | http-errors "1.7.3" 1427 | iconv-lite "0.4.24" 1428 | unpipe "1.0.0" 1429 | 1430 | read-pkg-up@^1.0.1: 1431 | version "1.0.1" 1432 | resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" 1433 | integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= 1434 | dependencies: 1435 | find-up "^1.0.0" 1436 | read-pkg "^1.0.0" 1437 | 1438 | read-pkg@^1.0.0: 1439 | version "1.1.0" 1440 | resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" 1441 | integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= 1442 | dependencies: 1443 | load-json-file "^1.0.0" 1444 | normalize-package-data "^2.3.2" 1445 | path-type "^1.0.0" 1446 | 1447 | readdirp@~3.5.0: 1448 | version "3.5.0" 1449 | resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" 1450 | integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ== 1451 | dependencies: 1452 | picomatch "^2.2.1" 1453 | 1454 | redent@^1.0.0: 1455 | version "1.0.0" 1456 | resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" 1457 | integrity sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94= 1458 | dependencies: 1459 | indent-string "^2.1.0" 1460 | strip-indent "^1.0.1" 1461 | 1462 | repeating@^2.0.0: 1463 | version "2.0.1" 1464 | resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" 1465 | integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= 1466 | dependencies: 1467 | is-finite "^1.0.0" 1468 | 1469 | resolve@^1.0.0, resolve@^1.10.0: 1470 | version "1.20.0" 1471 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" 1472 | integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== 1473 | dependencies: 1474 | is-core-module "^2.2.0" 1475 | path-parse "^1.0.6" 1476 | 1477 | retry@^0.12.0: 1478 | version "0.12.0" 1479 | resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" 1480 | integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= 1481 | 1482 | rimraf@^2.6.1: 1483 | version "2.7.1" 1484 | resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" 1485 | integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== 1486 | dependencies: 1487 | glob "^7.1.3" 1488 | 1489 | safe-buffer@5.1.2: 1490 | version "5.1.2" 1491 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" 1492 | integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== 1493 | 1494 | safe-buffer@^5.0.1: 1495 | version "5.2.1" 1496 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" 1497 | integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== 1498 | 1499 | "safer-buffer@>= 2.1.2 < 3": 1500 | version "2.1.2" 1501 | resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" 1502 | integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== 1503 | 1504 | semver-compare@^1.0.0: 1505 | version "1.0.0" 1506 | resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" 1507 | integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w= 1508 | 1509 | "semver@2 || 3 || 4 || 5", semver@^5.6.0: 1510 | version "5.7.1" 1511 | resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" 1512 | integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== 1513 | 1514 | send@0.17.1: 1515 | version "0.17.1" 1516 | resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" 1517 | integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== 1518 | dependencies: 1519 | debug "2.6.9" 1520 | depd "~1.1.2" 1521 | destroy "~1.0.4" 1522 | encodeurl "~1.0.2" 1523 | escape-html "~1.0.3" 1524 | etag "~1.8.1" 1525 | fresh "0.5.2" 1526 | http-errors "~1.7.2" 1527 | mime "1.6.0" 1528 | ms "2.1.1" 1529 | on-finished "~2.3.0" 1530 | range-parser "~1.2.1" 1531 | statuses "~1.5.0" 1532 | 1533 | serve-static@1.14.1: 1534 | version "1.14.1" 1535 | resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" 1536 | integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== 1537 | dependencies: 1538 | encodeurl "~1.0.2" 1539 | escape-html "~1.0.3" 1540 | parseurl "~1.3.3" 1541 | send "0.17.1" 1542 | 1543 | setprototypeof@1.1.1: 1544 | version "1.1.1" 1545 | resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" 1546 | integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== 1547 | 1548 | shallow-clone@^3.0.0: 1549 | version "3.0.1" 1550 | resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" 1551 | integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== 1552 | dependencies: 1553 | kind-of "^6.0.2" 1554 | 1555 | signal-exit@^3.0.0: 1556 | version "3.0.3" 1557 | resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" 1558 | integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== 1559 | 1560 | source-map-support@^0.5.12, source-map-support@^0.5.17: 1561 | version "0.5.19" 1562 | resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" 1563 | integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== 1564 | dependencies: 1565 | buffer-from "^1.0.0" 1566 | source-map "^0.6.0" 1567 | 1568 | source-map@^0.6.0: 1569 | version "0.6.1" 1570 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" 1571 | integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== 1572 | 1573 | spdx-correct@^3.0.0: 1574 | version "3.1.1" 1575 | resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" 1576 | integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== 1577 | dependencies: 1578 | spdx-expression-parse "^3.0.0" 1579 | spdx-license-ids "^3.0.0" 1580 | 1581 | spdx-exceptions@^2.1.0: 1582 | version "2.3.0" 1583 | resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" 1584 | integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== 1585 | 1586 | spdx-expression-parse@^3.0.0: 1587 | version "3.0.1" 1588 | resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" 1589 | integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== 1590 | dependencies: 1591 | spdx-exceptions "^2.1.0" 1592 | spdx-license-ids "^3.0.0" 1593 | 1594 | spdx-license-ids@^3.0.0: 1595 | version "3.0.9" 1596 | resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.9.tgz#8a595135def9592bda69709474f1cbeea7c2467f" 1597 | integrity sha512-Ki212dKK4ogX+xDo4CtOZBVIwhsKBEfsEEcwmJfLQzirgc2jIWdzg40Unxz/HzEUqM1WFzVlQSMF9kZZ2HboLQ== 1598 | 1599 | "statuses@>= 1.5.0 < 2", statuses@~1.5.0: 1600 | version "1.5.0" 1601 | resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" 1602 | integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= 1603 | 1604 | string.prototype.trimend@^1.0.4: 1605 | version "1.0.4" 1606 | resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" 1607 | integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A== 1608 | dependencies: 1609 | call-bind "^1.0.2" 1610 | define-properties "^1.1.3" 1611 | 1612 | string.prototype.trimstart@^1.0.4: 1613 | version "1.0.4" 1614 | resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" 1615 | integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw== 1616 | dependencies: 1617 | call-bind "^1.0.2" 1618 | define-properties "^1.1.3" 1619 | 1620 | strip-bom@^2.0.0: 1621 | version "2.0.0" 1622 | resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" 1623 | integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= 1624 | dependencies: 1625 | is-utf8 "^0.2.0" 1626 | 1627 | strip-bom@^3.0.0: 1628 | version "3.0.0" 1629 | resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" 1630 | integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= 1631 | 1632 | strip-indent@^1.0.1: 1633 | version "1.0.1" 1634 | resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" 1635 | integrity sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI= 1636 | dependencies: 1637 | get-stdin "^4.0.1" 1638 | 1639 | strip-json-comments@^2.0.0: 1640 | version "2.0.1" 1641 | resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" 1642 | integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= 1643 | 1644 | to-regex-range@^5.0.1: 1645 | version "5.0.1" 1646 | resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" 1647 | integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== 1648 | dependencies: 1649 | is-number "^7.0.0" 1650 | 1651 | toidentifier@1.0.0: 1652 | version "1.0.0" 1653 | resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" 1654 | integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== 1655 | 1656 | tree-kill@^1.2.2: 1657 | version "1.2.2" 1658 | resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" 1659 | integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== 1660 | 1661 | trim-newlines@^1.0.0: 1662 | version "1.0.0" 1663 | resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" 1664 | integrity sha1-WIeWa7WCpFA6QetST301ARgVphM= 1665 | 1666 | ts-node-dev@^1.1.6: 1667 | version "1.1.6" 1668 | resolved "https://registry.yarnpkg.com/ts-node-dev/-/ts-node-dev-1.1.6.tgz#ee2113718cb5a92c1c8f4229123ad6afbeba01f8" 1669 | integrity sha512-RTUi7mHMNQospArGz07KiraQcdgUVNXKsgO2HAi7FoiyPMdTDqdniB6K1dqyaIxT7c9v/VpSbfBZPS6uVpaFLQ== 1670 | dependencies: 1671 | chokidar "^3.5.1" 1672 | dateformat "~1.0.4-1.2.3" 1673 | dynamic-dedupe "^0.3.0" 1674 | minimist "^1.2.5" 1675 | mkdirp "^1.0.4" 1676 | resolve "^1.0.0" 1677 | rimraf "^2.6.1" 1678 | source-map-support "^0.5.12" 1679 | tree-kill "^1.2.2" 1680 | ts-node "^9.0.0" 1681 | tsconfig "^7.0.0" 1682 | 1683 | ts-node@^9.0.0: 1684 | version "9.1.1" 1685 | resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-9.1.1.tgz#51a9a450a3e959401bda5f004a72d54b936d376d" 1686 | integrity sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg== 1687 | dependencies: 1688 | arg "^4.1.0" 1689 | create-require "^1.1.0" 1690 | diff "^4.0.1" 1691 | make-error "^1.1.1" 1692 | source-map-support "^0.5.17" 1693 | yn "3.1.1" 1694 | 1695 | tsconfig@^7.0.0: 1696 | version "7.0.0" 1697 | resolved "https://registry.yarnpkg.com/tsconfig/-/tsconfig-7.0.0.tgz#84538875a4dc216e5c4a5432b3a4dec3d54e91b7" 1698 | integrity sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw== 1699 | dependencies: 1700 | "@types/strip-bom" "^3.0.0" 1701 | "@types/strip-json-comments" "0.0.30" 1702 | strip-bom "^3.0.0" 1703 | strip-json-comments "^2.0.0" 1704 | 1705 | tsscmp@^1.0.6: 1706 | version "1.0.6" 1707 | resolved "https://registry.yarnpkg.com/tsscmp/-/tsscmp-1.0.6.tgz#85b99583ac3589ec4bfef825b5000aa911d605eb" 1708 | integrity sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA== 1709 | 1710 | type-is@~1.6.17, type-is@~1.6.18: 1711 | version "1.6.18" 1712 | resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" 1713 | integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== 1714 | dependencies: 1715 | media-typer "0.3.0" 1716 | mime-types "~2.1.24" 1717 | 1718 | typescript@^4.2.4: 1719 | version "4.2.4" 1720 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.4.tgz#8610b59747de028fda898a8aef0e103f156d0961" 1721 | integrity sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg== 1722 | 1723 | unbox-primitive@^1.0.0: 1724 | version "1.0.1" 1725 | resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" 1726 | integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw== 1727 | dependencies: 1728 | function-bind "^1.1.1" 1729 | has-bigints "^1.0.1" 1730 | has-symbols "^1.0.2" 1731 | which-boxed-primitive "^1.0.2" 1732 | 1733 | unpipe@1.0.0, unpipe@~1.0.0: 1734 | version "1.0.0" 1735 | resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" 1736 | integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= 1737 | 1738 | utils-merge@1.0.1: 1739 | version "1.0.1" 1740 | resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" 1741 | integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= 1742 | 1743 | validate-npm-package-license@^3.0.1: 1744 | version "3.0.4" 1745 | resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" 1746 | integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== 1747 | dependencies: 1748 | spdx-correct "^3.0.0" 1749 | spdx-expression-parse "^3.0.0" 1750 | 1751 | vary@~1.1.2: 1752 | version "1.1.2" 1753 | resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" 1754 | integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= 1755 | 1756 | which-boxed-primitive@^1.0.2: 1757 | version "1.0.2" 1758 | resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" 1759 | integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== 1760 | dependencies: 1761 | is-bigint "^1.0.1" 1762 | is-boolean-object "^1.1.0" 1763 | is-number-object "^1.0.4" 1764 | is-string "^1.0.5" 1765 | is-symbol "^1.0.3" 1766 | 1767 | wrappy@1: 1768 | version "1.0.2" 1769 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 1770 | integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= 1771 | 1772 | ws@^7.3.1: 1773 | version "7.4.5" 1774 | resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.5.tgz#a484dd851e9beb6fdb420027e3885e8ce48986c1" 1775 | integrity sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g== 1776 | 1777 | xtend@^4.0.0: 1778 | version "4.0.2" 1779 | resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" 1780 | integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== 1781 | 1782 | yn@3.1.1: 1783 | version "3.1.1" 1784 | resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" 1785 | integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== 1786 | --------------------------------------------------------------------------------