├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── docker-compose.yml ├── game_map.json ├── game_map2.json ├── images └── app_preview_image.png ├── marketplace.json ├── package-lock.json ├── package.json ├── public ├── index.html ├── sounds │ ├── door │ ├── flag │ ├── key │ └── wall ├── sprites │ ├── door.png │ ├── flag.png │ ├── key.png │ ├── lockeddoor.png │ ├── original │ │ ├── door.png │ │ ├── flag.png │ │ ├── key.png │ │ ├── lockeddoor.png │ │ ├── player.png │ │ └── wall.png │ ├── player.png │ └── wall.png └── static │ ├── css │ └── game.css │ └── js │ └── game.js ├── redis_kaboom_game.gif ├── redis_rpg_map.jpg ├── screenshots ├── screenshot1.png └── screenshot2.png └── src ├── apis └── apis.http ├── data_loader.js └── server.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | *.tmp 4 | *.swp 5 | *.bak 6 | redisdata/ -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:14 2 | WORKDIR /app/ 3 | COPY package.json ./ 4 | RUN npm install 5 | COPY . . 6 | EXPOSE 8080 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This program is free software: you can redistribute it and/or modify 2 | it under the terms of the GNU General Public License as published by 3 | the Free Software Foundation, either version 3 of the License, or 4 | (at your option) any later version. 5 | 6 | This program is distributed in the hope that it will be useful, 7 | but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | GNU General Public License for more details. 10 | 11 | You should have received a copy of the GNU General Public License 12 | along with this program. If not, see . -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Redis Kaboom RPG Game 2 | 3 | This is an RPG maze type game built with [Kaboom.js](https://kaboomjs.com/), [Node.js](https://nodejs.org/) and [Redis](https://redis.io). It makes use of the [Redis JSON](https://redisjson.io) module from [Redis Inc](https://redis.com). 4 | 5 | ![Demo of the game running](https://raw.githubusercontent.com/redis-developer/redis-kaboom-rpg/main/redis_kaboom_game.gif) 6 | 7 | [Watch the video on YouTube!](https://www.youtube.com/watch?v=cowIZWASJNs) 8 | 9 | Since making the video and GIF above, we changed out the sprite images as part of our Hacktoberfest initiative. If you're looking for the originals that you see in the video, they're in this repo in `public/sprites/original`. 10 | 11 | ## Setup 12 | 13 | To run this game, you'll need [Docker](https://www.docker.com/) (or a local Redis instance, version 5 or higher with Redis JSON installed) and [Node.js](https://nodejs.org/) (use the current LTS version). First, clone the repo and install the dependencies: 14 | 15 | ```bash 16 | $ git clone https://github.com/redis-developer/redis-kaboom-rpg.git 17 | $ cd redis-kaboom-rpg 18 | $ npm install 19 | ``` 20 | 21 | ### Docker setup 22 | 23 | With Docker - you need to have Docker installed and there are no other requirements. You can use Docker to get a Redis instance with Redis JSON: 24 | 25 | ```bash 26 | $ docker-compose up -d 27 | ⠿ Network redis-kaboom-rpg_default Created 28 | ⠿ Container redis_kaboom Started 29 | ⠿ Container node_kaboom Started 30 | $ 31 | ``` 32 | 33 | Redis creates a folder named `redisdata` (inside the `redis-kaboom-rpg` folder that you cloned the GitHub repo to) and writes its append-only file there. This ensures that your data is persisted periodically and will still be there if you stop and restart the Docker container. 34 | 35 | Note that when using Docker, there is no need to load the game data as this is done for you. Once the containers are running you should be able to start a game simply by pointing the browser at http://localhost:8080/. 36 | 37 | ### Stopping Redis (Docker) 38 | 39 | If you started Redis using `docker-compose`, stop it as follows when you are done playing the game: 40 | 41 | ```bash 42 | $ docker-compose down 43 | Container node_kaboom Removed 44 | Container redis_kaboom Removed 45 | Network redis-kaboom-rpg_default Removed 46 | $ 47 | ``` 48 | 49 | ### Redis Setup (without Docker) 50 | 51 | Without Docker - you will need Redis 5 or higher, Redis JSON and Node.js (current LTS version recommended) 52 | 53 | This game uses Redis as a data store. The code assumes that Redis is running on localhost port 6379. You can configure an alternative Redis host and port by setting the `REDIS_HOST` and `REDIS_PORT` environment variables. If your Redis instance requires a password, supply that in the `REDIS_PASSWORD` environment variable. You'll need to have Redis JSON installed. 54 | 55 | ### Loading the Game Data 56 | 57 | Next, load the game map into Redis. This stores the map data from the `game_map.json` file in Redis, using Redis JSON: 58 | 59 | ```bash 60 | $ npm run load 61 | 62 | > redis-kaboom-rpg@1.0.0 load 63 | > node src/data_loader.js 64 | 65 | Data loaded! 66 | $ 67 | ``` 68 | 69 | You only need to do this once. Verify that the data loaded by ensuring that the key `kaboom:rooms` exists in Redis and is a Redis JSON document: 70 | 71 | ```bash 72 | 127.0.0.1:6379> type kaboom:rooms 73 | ReJSON-RL 74 | ``` 75 | 76 | ### Starting the Server 77 | 78 | To start the game server: 79 | 80 | ```bash 81 | $ npm run dev 82 | ``` 83 | 84 | Once the server is running, point your browser at `http://localhost:8080`. 85 | 86 | This starts the server using [nodemon](https://www.npmjs.com/package/nodemon), so saving changes to the source code files restarts the server for you automatically. 87 | 88 | If the server logs an error similar to this one, then Redis isn't running on the expected / configured host / port: 89 | 90 | ``` 91 | [ioredis] Unhandled error event: Error: connect ECONNREFUSED 127.0.0.1:6379 92 | at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1146:16) 93 | ``` 94 | 95 | ### Stopping the Server 96 | 97 | To stop the Node.js server, press Ctrl-C. 98 | 99 | 100 | ### Playing the Game 101 | 102 | Press space to start, then use the arrow keys to move your character. Red doors are locked until you have found the appropriate number of keys. Touch a red door to find out how many keys are required, or pass through it if you have enough keys. Green doors are unlocked and don't require keys. 103 | 104 | Find all 3 keys and unlock the door in the room you started in (room 0) to escape. Touching a flag teleports you to a random other room. 105 | 106 | At the end of the game, you'll see how long you took to complete the challenge, and how many times you moved between rooms. 107 | 108 | ## How it Works 109 | 110 | Let's take a look at how the different components of the game architecture fit together. 111 | 112 | ### Project Structure 113 | 114 | The project consists of a Node.js back end that has API routes for some of the game logic and a static server for the front end. 115 | 116 | The back end code lives in the `src` folder, along with the data loader code, used to load the game room map into Redis. It uses the [Express framework](https://expressjs.com/) both to serve the front end HTML / JavaScript / CSS and image files, and also to implement a small API for starting and tracking game events. Redis connectivity is handled using the [ioredis client](https://www.npmjs.com/package/ioredis). 117 | 118 | The front end is written in JavaScript using [Kaboom.js](https://kaboomjs.com/) as the game engine, and the [Bulma CSS framework](https://bulma.io/) for some basic layout. It lives in the `public` folder. 119 | 120 | ### Working with Kaboom.js 121 | 122 | [Kaboom.js](https://kaboomjs.com/) describes itself as "...a JavaScript library that helps you make games fast and fun!". It renders games as a set of scenes in a HTML `` element, the ID of and size of which can be configured, along with some other properties: 123 | 124 | ```javascript 125 | const k = kaboom({ 126 | global: true, // imports all kaboom functions to global namespace. 127 | scale: 3, // pixel scale. 128 | clearColor: [0, 0, 0, 1], // black background. 129 | canvas: document.getElementById('game'), // which canvas to render in. 130 | width: 180, // width of the canvas. 131 | height: 180 // height of the canvas. 132 | }); 133 | ``` 134 | 135 | Each screen in a game is called a "scene" in Kaboom. Our game has three sorts of scene: 136 | 137 | 1. The `start` scene: this is shown when the game is first loaded, and encourages the player to press space to start a new game. 138 | 2. The `play` scene: used to render the room that the player is currently in, and handle player movement and collisions with other game objects (doors, flags, walls, keys). 139 | 3. The `winner` scene: used to dispay game stats once the player has defeated the game by collecting three keys and exiting through the locked door in room 0. 140 | 141 | The `start` and `winner` scenes have static text based layout. Kaboom provides a `scene` function to define a new scene, and other utility functions. Here's the complete definition of the `start` scene, which displays some centered text and waits for the space button to be pressed: 142 | 143 | ```javascript 144 | scene('start', () => { 145 | keysHeld = []; 146 | 147 | add([ 148 | text('press space to begin!', 6), // 6 = font size. 149 | pos(width() / 2, height() / 2), 150 | origin('center'), 151 | ]); 152 | 153 | keyPress('space', () => { 154 | newGame(); // a function run when space is pressed. 155 | }); 156 | }); 157 | ``` 158 | 159 | To start a game, we use the provided `start` function, passing it the name of a scene: 160 | 161 | ```javascript 162 | start('start'); 163 | ``` 164 | 165 | The `start` scene is then rendered. To change to another scene, we add logic that calls the `go` function, providing the name of the next scene: 166 | 167 | ```javascript 168 | scene('start', () => { 169 | ... 170 | 171 | keyPress('space', () => { 172 | go('play', 0); // Go the the 'play' scene, passing room ID 0 as a parameter. 173 | }); 174 | }); 175 | ``` 176 | 177 | There are 31 rooms in the maze for our game, and all of them are rendered using a single scene named `play`. This takes a room ID as a parameter, and uses that to retrieve the room's map from the back end which in turn gets it from Redis. Let's look at features of Kaboom that help enable this... 178 | 179 | First, a scene definition takes a function as its parameter. This defines what's in the scene, plus any logic. Here, our `play` scene has an `async` function parameter and the first thing it does it makes a request to the back end to get the map for the room it's been asked to render: 180 | 181 | ```javascript 182 | scene('play', async (roomNumber) => { 183 | // Get the room details from the server. 184 | const res = await fetch(`/api/room/${gameId}/${roomNumber}`); 185 | const roomDetails = await res.json(); 186 | ``` 187 | 188 | Each room's details contain an encoded tile map where different characters represent different graphics in the room. Kaboom provides sprite loading functionality, allowing us to use images as sprites and lay them out like tiles. Here, I'm loading some images: 189 | 190 | ```javascript 191 | loadSprite('player', 'sprites/player.png'); 192 | loadSprite('wall', 'sprites/wall.png'); 193 | loadSprite('key', 'sprites/key.png'); 194 | loadSprite('flag', 'sprites/flag.png'); 195 | loadSprite('door', 'sprites/door.png'); 196 | loadSprite('lockeddoor', 'sprites/lockeddoor.png'); 197 | ``` 198 | 199 | Kaboom's [`addLevel` function](https://kaboomjs.com/#addLevel) takes the room layout expressed as an array of characters (see the "Using Redis as a Data Store" section for details) and a series of objects describing which sprite to use for each character and any additional properties. It then renders this layout into the canvas and assigns each tile the appropriate properties. For example: 200 | 201 | ```javascript 202 | const roomConf = { 203 | '@': [ 204 | sprite('player'), 205 | 'player' 206 | ], 207 | '=': [ 208 | sprite('wall'), 209 | solid() 210 | ], 211 | 'k': [ 212 | sprite('key'), 213 | 'key', 214 | solid() 215 | ], 216 | 'f': [ 217 | sprite('flag'), 218 | 'flag', 219 | solid() 220 | ] 221 | ``` 222 | 223 | Now, each `@` character in the room layout becomes the player's sprite, each `=` a solid wall and so on. For our game, I chose to represent doors as the numbers 1..9 and give them extra properties such as whether they are locked or not. These are added to the `roomConf` array dynamically: 224 | 225 | ```javascript 226 | [ 227 | // Use the 'lockeddoor' sprite if the door's locked. 228 | sprite(door.keysRequired > 0 ? 'lockeddoor' : 'door'), 229 | 'door', 230 | // Extra properties to store about this door - need 231 | // these when the player touches it to determine what 232 | // to do then. 233 | { 234 | leadsTo: door.leadsTo, 235 | keysRequired: door.keysRequired, 236 | isEnd: door.isEnd || false 237 | }, 238 | solid() 239 | ] 240 | ``` 241 | 242 | Player movement is handled by describing how the player should move (x, y) when each of the cursor keys is pressed: 243 | 244 | ```javascript 245 | const directions = { 246 | 'left': vec2(-1, 0), 247 | 'right': vec2(1, 0), 248 | 'up': vec2(0, -1), 249 | 'down': vec2(0, 1) 250 | }; 251 | ``` 252 | 253 | `vec2` is a Kaboom function. Each direction is then associated with a `keyDown` event handler which calls Kaboom's `move` function on the player's sprite object: 254 | 255 | ``` 256 | for (const direction in directions) { 257 | keyDown(direction, () => { 258 | // Move the player. 259 | player.move(directions[direction].scale(60)); 260 | }); 261 | } 262 | ``` 263 | 264 | The Kaboom `scale` function adjusts the speed at which the movement happens. The real game code also handles `keyPress` events for each cursor - the callback for these deals with tidying up any transient on screen messaging e.g. as the player moves away from a locked door having been told they don't yet hold enough keys. 265 | 266 | We also need to detect collisions between the player and other game objects (keys, flags, doors). Kaboom provides a simple API for this. For example, when the player touches a flag, we provide a function containing the logic describing what to do: 267 | 268 | ```javascript 269 | player.overlaps('flag', async () => { 270 | // Go to a random room number. 271 | const res = await fetch('/api/randomroom'); 272 | const roomDetails = await res.json(); 273 | go('play', roomDetails.room); 274 | }); 275 | ``` 276 | 277 | The code above asks the back end application for a random room number and receives a response that looks like `{room: 22}`. It then tells Kaboom to move to the `play` scene for that room. So, when the player touches a flag... they're teleported to another room (or maybe back to the one they're already in). The game code for this has additional logic that creates a camera spin effect too. 278 | 279 | There's no need to provide code for when the player collides with a wall, as we don't need to take any specific action. Kaboom knows that players can't walk over or through walls as we declared them `solid` and that's all we need to say about them. 280 | 281 | The game tracks keys that the player has found using a global `keysHeld` array, containing the room ID(s) that the key(s) were found in. When a player touches a door, we can then figure out if they have enough keys to open it or not: 282 | 283 | ```javascript 284 | player.overlaps('door', (d) => { 285 | // Wait a short time before revealing what is going to happen. 286 | wait(0.3, ()=> { 287 | // Does opening this door require more keys than the player holds? 288 | if (d.keysRequired && d.keysRequired > keysHeld.length) { 289 | showMsg(`You need ${d.keysRequired - keysHeld.length} more keys!`); 290 | camShake(10); 291 | } else { 292 | // Does this door lead to the end state, or another room? 293 | if (d.isEnd) { 294 | go('winner'); 295 | } else { 296 | go('play', d.leadsTo); 297 | } 298 | } 299 | }); 300 | }); 301 | ``` 302 | 303 | If the player doesn't hold sufficient keys to open the door, Kaboom's `camShake` function is used to shake the camera and provide visual feedback that the door can't be opened. If they do have enough keys, they'll be taken to the next room or the `winner` scene if this door is the end of the maze. Kaboom's `wait` function is equivalent to a `setTimeout` in JavaScript, and is used to provide a small dramatic pause. 304 | 305 | These are the main things you need to know to build a game with Kaboom. The code for the game is in `static/js/game.js` and contains a few more nuances than we covered here.. 306 | 307 | ### Using Redis as a Data Store 308 | 309 | This game uses the following Redis data types and features: 310 | 311 | * **JSON (using Redis JSON)**: The tile map for each level (describing where the walls, doors, keys, flags and player's initial position are) is stored in Redis in a single key using Redis JSON. The data loader uses the `JSON.SET` command to store the data in a Redis key named `kaboom:rooms`. The Node.js back end retrieves the map for a given room with the `JSON.GET` command. Room data is stored as a JSON array in Redis. Each room's data is an object in the array: room 0 is the 0th array element, room 1 the first and so on. We use the `JSON.ARRLEN` command whenever we need to know how many rooms are in the map (for example when choosing a random room to teleport the user to when they touch a flag). Each room's data looks like this: 312 | 313 | ```json 314 | { 315 | "layout": [ 316 | "============", 317 | "= =", 318 | "= =", 319 | "= k =", 320 | "= == =", 321 | "1 f @= 2", 322 | "= == =", 323 | "= =", 324 | "= =", 325 | "= =", 326 | "============" 327 | ], 328 | "doors": { 329 | "1": { 330 | "leadsTo": 5 331 | }, 332 | "2": { 333 | "leadsTo": 3, 334 | "keysRequired": 3, 335 | "isEnd": true 336 | } 337 | } 338 | } 339 | ``` 340 | * The `layout` array contains the tilemap for the room, which Kaboom uses in the front end to render the appropriate tiles. `=` is a solid wall, `@` is the position that the player starts in when they enter the room, `f` is a teleporter flag, and numeric characters are doors to other rooms. 341 | * Each door is further described in the `doors` object. In the example above, door 1 leads to room 5, and door 2 to room 3. Door 2 is locked and the player requires 3 keys to pass through it. Door 2 is also the special `isEnd` door, which represents the escape point from the maze. 342 | * **Streams**: Each new game gets its own Stream. We use the timestamp when the game began as part of the key name, for example `kaboom:moves:1625561580120`. Each time the player enters a new room an entry is written to the Stream (using the `XADD` command). At the end of the game, data from the Stream is used to determine: 343 | * How many times the player entered a room (using the `XLEN` command). 344 | * How long the player took to complete the game (each Stream entry is timestamped, so we can calculate the game duration as the difference between the timestamps of the first and last entries). For this we use the `XRANGE` and `XREVRANGE` commands. 345 | * Each Stream entry looks like this: 346 | 347 | ```bash 348 | 127.0.0.1:6379> xrevrange kaboom:moves:1625561580120 + - count 1 349 | 1) 1) "1625561643258-0" 350 | 2) 1) "roomEntry" 351 | 2) "1" 352 | ``` 353 | * Additionally, we write a "start" event to the Stream when the game starts, so that we get the timestamp of the start of the game in the Stream, rather than waiting until the player first moves to another room to start the game clock. The "start" event will always be the first entry in the Stream, and looks like this: 354 | 355 | ```bash 356 | 127.0.0.1:6379> xrange kaboom:moves:1625561580120 - + count 1 357 | 1) 1) "1625561580122-0" 358 | 2) 1) "event" 359 | 2) "start" 360 | ``` 361 | * **Sets**: Every time the player begins a new game, the code "hides" the keys that the player needs to find to escape. It does this by putting random room numbers into a Redis Set until there are 3 members there, using the `SADD` and `SCARD` commands for this. There needs to be one set per running game, so we use the timestamp that the game was started as part of the key, for example `kaboom:keylocations:1625561580120`. The `SISMEMBER` commmand is used to check if a room number should have a key in it when sending the room map to the front end. If it should, then the `k` (key sprite) character is left in the room map, otherwise it's removed before sending the map to the front end. Each new game has its own set, containing three room numbers like so: 362 | 363 | ```bash 364 | 127.0.0.1:6379> smembers kaboom:keylocations:1625561580120 365 | 1) "5" 366 | 2) "17" 367 | 3) "29" 368 | ``` 369 | * **Key Expiry**: We use the `EXPIRE` command to ensure that keys associated with each game are removed from Redis after a day. This ensures that we don't have uncontrolled data growth in Redis, for example because the player abandons the game. When the player wins the game, keys are tidied up immediately using the `DEL` command. Here, I'm looking at the time to live for a game's Stream key with the `TTL` command: 370 | 371 | ```bash 372 | 127.0.0.1:6379> ttl kaboom:moves:1625561580120 373 | (integer) 86210 374 | ``` 375 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.9" 2 | services: 3 | redis: 4 | container_name: redis_kaboom 5 | image: "redislabs/redismod" 6 | ports: 7 | - 6379:6379 8 | volumes: 9 | - ./redisdata:/data 10 | entrypoint: 11 | redis-server 12 | --loadmodule /usr/lib/redis/modules/rejson.so 13 | --appendonly yes 14 | deploy: 15 | replicas: 1 16 | restart_policy: 17 | condition: on-failure 18 | node: 19 | container_name: node_kaboom 20 | build: . 21 | volumes: 22 | - .:/app 23 | - /app/node_modules 24 | command: sh -c "npm run load && npm run dev" 25 | depends_on: 26 | - redis 27 | ports: 28 | - 8080:8080 29 | environment: 30 | - REDIS_HOST=redis 31 | -------------------------------------------------------------------------------- /game_map.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "layout": [ 4 | "============", 5 | "= =", 6 | "= =", 7 | "= 1", 8 | "= =", 9 | "2 k @ =", 10 | "= =", 11 | "= =", 12 | "= f =", 13 | "= =", 14 | "======3=====" 15 | ], 16 | "doors": { 17 | "1": { 18 | "isEnd": true, 19 | "keysRequired": 3, 20 | "leadsTo": 0 21 | }, 22 | "2": { 23 | "leadsTo": 1 24 | }, 25 | "3": { 26 | "leadsTo": 28 27 | } 28 | } 29 | }, 30 | { 31 | "layout": [ 32 | "======1=====", 33 | "= =", 34 | "= =", 35 | "= =", 36 | "= === 2", 37 | "= = @ =", 38 | "3 = =", 39 | "= =", 40 | "= =", 41 | "= k =", 42 | "===4========" 43 | ], 44 | "doors": { 45 | "1": { 46 | "leadsTo": 2, 47 | "keysRequired": 1 48 | }, 49 | "2": { 50 | "leadsTo": 0 51 | }, 52 | "3": { 53 | "leadsTo": 6 54 | }, 55 | "4": { 56 | "leadsTo": 3 57 | } 58 | } 59 | }, 60 | { 61 | "layout": [ 62 | "============", 63 | "= k =", 64 | "3 = 2", 65 | "= f f f f =", 66 | "= =====", 67 | "= @= =", 68 | "= = ===", 69 | "= =", 70 | "= = =", 71 | "= =", 72 | "=====1======" 73 | ], 74 | "doors": { 75 | "1": { 76 | "leadsTo": 1 77 | }, 78 | "2": { 79 | "leadsTo": 33 80 | }, 81 | "3": { 82 | "leadsTo": 25, 83 | "keysRequired": 1 84 | } 85 | } 86 | }, 87 | { 88 | "layout": [ 89 | "=====1======", 90 | "= =", 91 | "= =", 92 | "= =", 93 | "= =", 94 | "= @ =", 95 | "= =", 96 | "= =", 97 | "= ===== =", 98 | "= k = =", 99 | "======2=====" 100 | ], 101 | "doors": { 102 | "1": { 103 | "leadsTo": 1 104 | }, 105 | "2": { 106 | "leadsTo": 4 107 | } 108 | } 109 | }, 110 | { 111 | "layout": [ 112 | "======1=====", 113 | "= =", 114 | "= =", 115 | "= k =", 116 | "= == =", 117 | "3 f @= =", 118 | "= == =", 119 | "= =", 120 | "= =", 121 | "= =", 122 | "====2=======" 123 | ], 124 | "doors": { 125 | "1": { 126 | "leadsTo": 3 127 | }, 128 | "2": { 129 | "leadsTo": 5 130 | }, 131 | "3": { 132 | "leadsTo": 11 133 | } 134 | } 135 | }, 136 | { 137 | "layout": [ 138 | "======1=====", 139 | "= =", 140 | "= =", 141 | "= =", 142 | "= =", 143 | "= @ =", 144 | "= =", 145 | "= =", 146 | "======== =", 147 | "= k =", 148 | "======2=====" 149 | ], 150 | "doors": { 151 | "1": { 152 | "leadsTo": 4 153 | }, 154 | "2": { 155 | "leadsTo": 38 156 | } 157 | } 158 | }, 159 | { 160 | "layout": [ 161 | "=====1======", 162 | "= =", 163 | "= = =", 164 | "= =", 165 | "= =", 166 | "3 @ 2", 167 | "= == =", 168 | "= k =", 169 | "= = = =", 170 | "= =", 171 | "============" 172 | ], 173 | "doors": { 174 | "1": { 175 | "leadsTo": 22 176 | }, 177 | "2": { 178 | "leadsTo": 1 179 | }, 180 | "3": { 181 | "leadsTo": 7 182 | } 183 | } 184 | }, 185 | { 186 | "layout": [ 187 | "============", 188 | "= =", 189 | "= =", 190 | "= =", 191 | "= =", 192 | "3 @ 2", 193 | "= =", 194 | "= =", 195 | "= ====k =", 196 | "= =", 197 | "======1=====" 198 | ], 199 | "doors": { 200 | "1": { 201 | "leadsTo": 9 202 | }, 203 | "2": { 204 | "leadsTo": 6 205 | }, 206 | "3": { 207 | "leadsTo": 8 208 | } 209 | } 210 | }, 211 | { 212 | "layout": [ 213 | "============", 214 | "= =", 215 | "= = =", 216 | "= =", 217 | "= = =", 218 | "2 @ 1", 219 | "= =", 220 | "= k= =", 221 | "= = =", 222 | "= =", 223 | "============" 224 | ], 225 | "doors": { 226 | "1": { 227 | "leadsTo": 7 228 | }, 229 | "2": { 230 | "leadsTo": 14 231 | } 232 | } 233 | }, 234 | { 235 | "layout": [ 236 | "======1=====", 237 | "= k =", 238 | "= = = =", 239 | "= = =", 240 | "= =", 241 | "= @ =", 242 | "= =", 243 | "= =", 244 | "= =", 245 | "= =", 246 | "======2=====" 247 | ], 248 | "doors": { 249 | "1": { 250 | "leadsTo": 7 251 | }, 252 | "2": { 253 | "leadsTo": 10 254 | } 255 | } 256 | }, 257 | { 258 | "layout": [ 259 | "======1=====", 260 | "= =", 261 | "= =", 262 | "= =", 263 | "= =", 264 | "= @ =", 265 | "= =", 266 | "= =", 267 | "= === =", 268 | "= k =", 269 | "============" 270 | ], 271 | "doors": { 272 | "1": { 273 | "leadsTo": 9 274 | } 275 | } 276 | }, 277 | { 278 | "layout": [ 279 | "============", 280 | "= =", 281 | "= =", 282 | "= =", 283 | "= =", 284 | "= @ 1", 285 | "= =", 286 | "= = = =", 287 | "= = = =", 288 | "= = = k =", 289 | "======2=====" 290 | ], 291 | "doors": { 292 | "1": { 293 | "leadsTo": 4 294 | }, 295 | "2": { 296 | "leadsTo": 12 297 | } 298 | } 299 | }, 300 | { 301 | "layout": [ 302 | "=====1======", 303 | "= k =", 304 | "= =", 305 | "= =", 306 | "= =", 307 | "2 @ =", 308 | "= =", 309 | "= =", 310 | "= =======", 311 | "= =", 312 | "============" 313 | ], 314 | "doors": { 315 | "1": { 316 | "leadsTo": 11 317 | }, 318 | "2": { 319 | "leadsTo": 13 320 | } 321 | } 322 | }, 323 | { 324 | "layout": [ 325 | "============", 326 | "= =", 327 | "= =", 328 | "= === =", 329 | "= =", 330 | "2 @ 1", 331 | "= =", 332 | "= =", 333 | "= f k =", 334 | "= =", 335 | "============" 336 | ], 337 | "doors": { 338 | "1": { 339 | "leadsTo": 12 340 | }, 341 | "2": { 342 | "leadsTo": 26 343 | } 344 | } 345 | }, 346 | { 347 | "layout": [ 348 | "======3=====", 349 | "= =", 350 | "= ==== =", 351 | "= =", 352 | "= =", 353 | "= = @ = 1", 354 | "= =", 355 | "= =", 356 | "= == =", 357 | "= k =", 358 | "======2=====" 359 | ], 360 | "doors": { 361 | "1": { 362 | "leadsTo": 8 363 | }, 364 | "2": { 365 | "leadsTo": 18 366 | }, 367 | "3": { 368 | "leadsTo": 15 369 | } 370 | } 371 | }, 372 | { 373 | "layout": [ 374 | "======1=====", 375 | "= = =", 376 | "= = =", 377 | "= = =", 378 | "= = =", 379 | "3 @ =", 380 | "= =", 381 | "= = =", 382 | "= k = =", 383 | "= = =", 384 | "=====2======" 385 | ], 386 | "doors": { 387 | "1": { 388 | "leadsTo": 16 389 | }, 390 | "2": { 391 | "leadsTo": 14 392 | }, 393 | "3": { 394 | "leadsTo": 21, 395 | "keysRequired": 2 396 | } 397 | } 398 | }, 399 | { 400 | "layout": [ 401 | "============", 402 | "= =", 403 | "= =", 404 | "= =", 405 | "= =", 406 | "= = @ 1", 407 | "= = =", 408 | "= = =", 409 | "= k= =", 410 | "= =", 411 | "======2=====" 412 | ], 413 | "doors": { 414 | "1": { 415 | "leadsTo": 17 416 | }, 417 | "2": { 418 | "leadsTo": 15 419 | } 420 | } 421 | }, 422 | { 423 | "layout": [ 424 | "============", 425 | "= =", 426 | "= =", 427 | "= k =", 428 | "= = = = =", 429 | "2 = @ = 1", 430 | "= = = =", 431 | "= = = =", 432 | "= =", 433 | "= =", 434 | "============" 435 | ], 436 | "doors": { 437 | "1": { 438 | "leadsTo": 24 439 | }, 440 | "2": { 441 | "leadsTo": 16 442 | } 443 | } 444 | }, 445 | { 446 | "layout": [ 447 | "======1=====", 448 | "= =", 449 | "= =", 450 | "= =", 451 | "= =", 452 | "= @= =", 453 | "= =", 454 | "= =", 455 | "= ===k =", 456 | "= =", 457 | "=====2======" 458 | ], 459 | "doors": { 460 | "1": { 461 | "leadsTo": 14 462 | }, 463 | "2": { 464 | "leadsTo": 19 465 | } 466 | } 467 | }, 468 | { 469 | "layout": [ 470 | "=====1======", 471 | "= = =", 472 | "= = =", 473 | "= f =", 474 | "= =", 475 | "= @ =", 476 | "= =", 477 | "= ====== = =", 478 | "= k =", 479 | "= =", 480 | "=====2======" 481 | ], 482 | "doors": { 483 | "1": { 484 | "leadsTo": 18 485 | }, 486 | "2": { 487 | "leadsTo": 20 488 | } 489 | } 490 | }, 491 | { 492 | "layout": [ 493 | "=====1======", 494 | "= =", 495 | "= ==== =", 496 | "= =", 497 | "= =", 498 | "= @ 2", 499 | "= =", 500 | "= =", 501 | "= ==== =", 502 | "= k=", 503 | "============" 504 | ], 505 | "doors": { 506 | "1": { 507 | "leadsTo": 19 508 | }, 509 | "2": { 510 | "leadsTo": 26 511 | } 512 | } 513 | }, 514 | { 515 | "layout": [ 516 | "============", 517 | "= =", 518 | "= ==== =", 519 | "= =", 520 | "= =", 521 | "= @ 1", 522 | "= =", 523 | "= =", 524 | "= ==== k=", 525 | "= =", 526 | "======2=====" 527 | ], 528 | "doors": { 529 | "1": { 530 | "leadsTo": 15, 531 | "keysRequired": 2 532 | }, 533 | "2": { 534 | "leadsTo": 39 535 | } 536 | } 537 | }, 538 | { 539 | "layout": [ 540 | "============", 541 | "= =", 542 | "= =", 543 | "= k =", 544 | "= =", 545 | "2 =@= =", 546 | "= = =", 547 | "= =", 548 | "= =", 549 | "= =", 550 | "======1=====" 551 | ], 552 | "doors": { 553 | "1": { 554 | "leadsTo": 6 555 | }, 556 | "2": { 557 | "leadsTo": 23 558 | } 559 | } 560 | }, 561 | { 562 | "layout": [ 563 | "=====1======", 564 | "= =", 565 | "= =", 566 | "== =", 567 | "= =", 568 | "= @ 2", 569 | "= =", 570 | "= =", 571 | "= = =", 572 | "= k =", 573 | "============" 574 | ], 575 | "doors": { 576 | "1": { 577 | "leadsTo": 24 578 | }, 579 | "2": { 580 | "leadsTo": 22 581 | }, 582 | "3": { 583 | "leadsTo": 41 584 | }, 585 | "4": { 586 | "leadsTo": 39 587 | } 588 | } 589 | }, 590 | { 591 | "layout": [ 592 | "============", 593 | "= =", 594 | "= =", 595 | "= f =", 596 | "= fffff =", 597 | "3 @ f 1", 598 | "= f fff =", 599 | "= f k =", 600 | "= f =", 601 | "= =", 602 | "=====2======" 603 | ], 604 | "doors": { 605 | "1": { 606 | "leadsTo": 25 607 | }, 608 | "2": { 609 | "leadsTo": 23 610 | }, 611 | "3": { 612 | "leadsTo": 17 613 | } 614 | } 615 | }, 616 | { 617 | "layout": [ 618 | "============", 619 | "= k =", 620 | "= = =", 621 | "= = =", 622 | "= = =", 623 | "2 = @ 1", 624 | "= = =", 625 | "= = =", 626 | "= = 3", 627 | "= =", 628 | "============" 629 | ], 630 | "doors": { 631 | "1": { 632 | "leadsTo": 33 633 | }, 634 | "2": { 635 | "leadsTo": 24 636 | }, 637 | "3": { 638 | "leadsTo": 2, 639 | "keysRequired": 1 640 | } 641 | } 642 | }, 643 | { 644 | "layout": [ 645 | "======3=====", 646 | "= =", 647 | "= == =", 648 | "= f =", 649 | "= = =", 650 | "2 @ 1", 651 | "= =", 652 | "= =", 653 | "= = =", 654 | "= k ==== =", 655 | "============" 656 | ], 657 | "doors": { 658 | "1": { 659 | "leadsTo": 13 660 | }, 661 | "2": { 662 | "leadsTo": 20 663 | }, 664 | "3": { 665 | "leadsTo": 27, 666 | "keysRequired": 1 667 | } 668 | } 669 | }, 670 | { 671 | "layout": [ 672 | "============", 673 | "= =", 674 | "= =", 675 | "= =", 676 | "= = =", 677 | "= =k @ =", 678 | "= = = =", 679 | "= =", 680 | "= =", 681 | "= =", 682 | "======1=====" 683 | ], 684 | "doors": { 685 | "1": { 686 | "leadsTo": 26, 687 | "keysRequired": 1 688 | } 689 | } 690 | }, 691 | { 692 | "layout": [ 693 | "=====1======", 694 | "= = =", 695 | "= = =", 696 | "= k =", 697 | "= = =", 698 | "= @ 2", 699 | "= = =", 700 | "= =", 701 | "= = =", 702 | "= =", 703 | "============" 704 | ], 705 | "doors": { 706 | "1": { 707 | "leadsTo": 0 708 | }, 709 | "2": { 710 | "leadsTo": 29 711 | } 712 | } 713 | }, 714 | { 715 | "layout": [ 716 | "============", 717 | "= =", 718 | "= = =", 719 | "= = =", 720 | "= =", 721 | "2 @ 1", 722 | "= =", 723 | "= = =", 724 | "= = k=", 725 | "= f =", 726 | "============" 727 | ], 728 | "doors": { 729 | "1": { 730 | "leadsTo": 30 731 | }, 732 | "2": { 733 | "leadsTo": 28 734 | } 735 | } 736 | }, 737 | { 738 | "layout": [ 739 | "=====1======", 740 | "= =", 741 | "= =", 742 | "=k==========", 743 | "= =", 744 | "3 @ =", 745 | "= =", 746 | "= =", 747 | "= =", 748 | "= =", 749 | "=====2======" 750 | ], 751 | "doors": { 752 | "1": { 753 | "leadsTo": 31 754 | }, 755 | "2": { 756 | "leadsTo": 34 757 | }, 758 | "3": { 759 | "leadsTo": 29 760 | } 761 | } 762 | }, 763 | { 764 | "layout": [ 765 | "=====1======", 766 | "= = = k =", 767 | "= = = =", 768 | "= == =", 769 | "= =", 770 | "= = @ =", 771 | "= =", 772 | "= = =", 773 | "= = = =", 774 | "= =", 775 | "=====2======" 776 | ], 777 | "doors": { 778 | "1": { 779 | "leadsTo": 32 780 | }, 781 | "2": { 782 | "leadsTo": 30 783 | } 784 | } 785 | }, 786 | { 787 | "layout": [ 788 | "======2=====", 789 | "= k =", 790 | "= = = =", 791 | "= = = =", 792 | "= = =", 793 | "= = @ = =", 794 | "= = = =", 795 | "= = =", 796 | "= = = =", 797 | "= =", 798 | "=====1======" 799 | ], 800 | "doors": { 801 | "1": { 802 | "leadsTo": 31 803 | }, 804 | "2": { 805 | "leadsTo": 33 806 | } 807 | } 808 | }, 809 | { 810 | "layout": [ 811 | "============", 812 | "3 =", 813 | "= ==========", 814 | "2 =", 815 | "========== =", 816 | "= @ =", 817 | "= ==========", 818 | "= k =", 819 | "========== =", 820 | "= =", 821 | "=====1======" 822 | ], 823 | "doors": { 824 | "1": { 825 | "leadsTo": 32 826 | }, 827 | "2": { 828 | "leadsTo": 2, 829 | "keysRequired": 1 830 | }, 831 | "3": { 832 | "leadsTo": 25 833 | } 834 | } 835 | }, 836 | { 837 | "layout": [ 838 | "=====1======", 839 | "= =", 840 | "= = = =", 841 | "= = f =", 842 | "= = = =", 843 | "= @ =", 844 | "= =", 845 | "= = = =", 846 | "= = =", 847 | "= = = k =", 848 | "=======2====" 849 | ], 850 | "doors": { 851 | "1": { 852 | "leadsTo": 30 853 | }, 854 | "2": { 855 | "leadsTo": 35 856 | } 857 | } 858 | }, 859 | { 860 | "layout": [ 861 | "======1=====", 862 | "= = =", 863 | "= = =", 864 | "= k = =", 865 | "= = =", 866 | "= = @ =", 867 | "= = =", 868 | "= = =", 869 | "= = = =", 870 | "= = =", 871 | "====2=======" 872 | ], 873 | "doors": { 874 | "1": { 875 | "leadsTo": 34 876 | }, 877 | "2": { 878 | "leadsTo": 36 879 | } 880 | } 881 | }, 882 | { 883 | "layout": [ 884 | "=====1======", 885 | "= =", 886 | "= = = =", 887 | "= = = =", 888 | "= == = =", 889 | "= =f@ = =", 890 | "= ===== =", 891 | "= =", 892 | "= k =", 893 | "= =", 894 | "======2=====" 895 | ], 896 | "doors": { 897 | "1": { 898 | "leadsTo": 35 899 | }, 900 | "2": { 901 | "leadsTo": 37 902 | } 903 | } 904 | }, 905 | { 906 | "layout": [ 907 | "======1=====", 908 | "= =", 909 | "= =", 910 | "= = =", 911 | "= =", 912 | "2 = @ = =", 913 | "= =", 914 | "= = =", 915 | "= f k =", 916 | "= =", 917 | "============" 918 | ], 919 | "doors": { 920 | "1": { 921 | "leadsTo": 36 922 | }, 923 | "2": { 924 | "leadsTo": 38 925 | } 926 | } 927 | }, 928 | { 929 | "layout": [ 930 | "=====2======", 931 | "= =", 932 | "= = = =", 933 | "= =", 934 | "= =", 935 | "3 @ f 1", 936 | "= =", 937 | "= k =", 938 | "= = = =", 939 | "= =", 940 | "============" 941 | ], 942 | "doors": { 943 | "1": { 944 | "leadsTo": 37 945 | }, 946 | "2": { 947 | "leadsTo": 5 948 | }, 949 | "3": { 950 | "leadsTo": 39 951 | } 952 | } 953 | }, 954 | { 955 | "layout": [ 956 | "=====2======", 957 | "= =", 958 | "= = = =", 959 | "= =", 960 | "= =", 961 | "= @ f 1", 962 | "= =", 963 | "= k =", 964 | "= = = =", 965 | "= =", 966 | "============" 967 | ], 968 | "doors": { 969 | "1": { 970 | "leadsTo": 38 971 | }, 972 | "2": { 973 | "leadsTo": 21 974 | } 975 | } 976 | } 977 | ] 978 | -------------------------------------------------------------------------------- /game_map2.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "layout": [ 4 | "=====1======", 5 | "= =", 6 | "= k =", 7 | "= =", 8 | "= =", 9 | "= @ 2", 10 | "= =", 11 | "= =", 12 | "= f =", 13 | "= =", 14 | "======3=====" 15 | ], 16 | "doors": { 17 | "1": { 18 | "leadsTo": 1 19 | }, 20 | "2": { 21 | "leadsTo": 0, 22 | "isEnd": true, 23 | "keysRequired": 3 24 | }, 25 | "3": { 26 | "leadsTo": 31 27 | } 28 | } 29 | }, 30 | { 31 | "layout": [ 32 | "============", 33 | "= =", 34 | "= =", 35 | "= =", 36 | "= === =", 37 | "1 = @ =", 38 | "= = =", 39 | "= =", 40 | "= =", 41 | "= k =", 42 | "======2=====" 43 | ], 44 | "doors": { 45 | "1": { 46 | "leadsTo": 2 47 | }, 48 | "2": { 49 | "leadsTo": 0 50 | } 51 | } 52 | }, 53 | { 54 | "layout": [ 55 | "============", 56 | "= k =", 57 | "= = =", 58 | "= =", 59 | "= =====", 60 | "1 @= 2", 61 | "= = ===", 62 | "= =", 63 | "= = =", 64 | "= =", 65 | "============" 66 | ], 67 | "doors": { 68 | "1": { 69 | "leadsTo": 3 70 | }, 71 | "2": { 72 | "leadsTo": 1 73 | } 74 | } 75 | }, 76 | { 77 | "layout": [ 78 | "=====1======", 79 | "= =", 80 | "= =", 81 | "= =", 82 | "= =", 83 | "2 @ 3", 84 | "= =", 85 | "= =", 86 | "= ===== =", 87 | "= k = =", 88 | "======4=====" 89 | ], 90 | "doors": { 91 | "1": { 92 | "leadsTo": 6 93 | }, 94 | "2": { 95 | "leadsTo": 4 96 | }, 97 | "3": { 98 | "leadsTo": 2 99 | }, 100 | "4": { 101 | "leadsTo": 28, 102 | "keysRequired": 1 103 | } 104 | } 105 | }, 106 | { 107 | "layout": [ 108 | "============", 109 | "= =", 110 | "= =", 111 | "= k =", 112 | "= == =", 113 | "1 f @= 2", 114 | "= == =", 115 | "= =", 116 | "= =", 117 | "= =", 118 | "============" 119 | ], 120 | "doors": { 121 | "1": { 122 | "leadsTo": 5 123 | }, 124 | "2": { 125 | "leadsTo": 3 126 | } 127 | } 128 | }, 129 | { 130 | "layout": [ 131 | "============", 132 | "= =", 133 | "= =", 134 | "= =", 135 | "= =", 136 | "= @ 1", 137 | "= =", 138 | "= =", 139 | "======== =", 140 | "= k =", 141 | "============" 142 | ], 143 | "doors": { 144 | "1": { 145 | "leadsTo": 4 146 | } 147 | } 148 | }, 149 | { 150 | "layout": [ 151 | "=====1======", 152 | "= =", 153 | "= = =", 154 | "= =", 155 | "= =", 156 | "= @ =", 157 | "= == =", 158 | "= k =", 159 | "= = = =", 160 | "= =", 161 | "======2=====" 162 | ], 163 | "doors": { 164 | "1": { 165 | "leadsTo": 7 166 | }, 167 | "2": { 168 | "leadsTo": 3 169 | } 170 | } 171 | }, 172 | { 173 | "layout": [ 174 | "=====1======", 175 | "= =", 176 | "= =", 177 | "= =", 178 | "= =", 179 | "2 @ 4", 180 | "= =", 181 | "= =", 182 | "= ====k =", 183 | "= =", 184 | "======3=====" 185 | ], 186 | "doors": { 187 | "1": { 188 | "leadsTo": 8 189 | }, 190 | "2": { 191 | "leadsTo": 14 192 | }, 193 | "3": { 194 | "leadsTo": 6 195 | }, 196 | "4": { 197 | "leadsTo": 37 198 | } 199 | } 200 | }, 201 | { 202 | "layout": [ 203 | "=====1======", 204 | "= =", 205 | "= = =", 206 | "= =", 207 | "= = =", 208 | "= @ =", 209 | "= =", 210 | "= k= =", 211 | "= = =", 212 | "= =", 213 | "======2=====" 214 | ], 215 | "doors": { 216 | "1": { 217 | "leadsTo": 9 218 | }, 219 | "2": { 220 | "leadsTo": 7 221 | } 222 | } 223 | }, 224 | { 225 | "layout": [ 226 | "============", 227 | "= k = =", 228 | "= = = =", 229 | "= =", 230 | "= =", 231 | "1 @ =", 232 | "= =", 233 | "= =", 234 | "= =", 235 | "= =", 236 | "======2=====" 237 | ], 238 | "doors": { 239 | "1": { 240 | "leadsTo": 10 241 | }, 242 | "2": { 243 | "leadsTo": 8 244 | } 245 | } 246 | }, 247 | { 248 | "layout": [ 249 | "============", 250 | "= =", 251 | "= =", 252 | "= =", 253 | "= =", 254 | "1 @ 2", 255 | "= =", 256 | "= =", 257 | "= === =", 258 | "= k =", 259 | "============" 260 | ], 261 | "doors": { 262 | "1": { 263 | "leadsTo": 11 264 | }, 265 | "2": { 266 | "leadsTo": 9 267 | } 268 | } 269 | }, 270 | { 271 | "layout": [ 272 | "============", 273 | "= =", 274 | "= =", 275 | "= =", 276 | "= =", 277 | "= @ 1", 278 | "= =", 279 | "= = = =", 280 | "= = = =", 281 | "= = = k =", 282 | "======2=====" 283 | ], 284 | "doors": { 285 | "1": { 286 | "leadsTo": 10 287 | }, 288 | "2": { 289 | "leadsTo": 12 290 | } 291 | } 292 | }, 293 | { 294 | "layout": [ 295 | "=====1======", 296 | "= k =", 297 | "= =", 298 | "= =", 299 | "= =", 300 | "= @ =", 301 | "= =", 302 | "= =", 303 | "= =======", 304 | "= =", 305 | "======2=====" 306 | ], 307 | "doors": { 308 | "1": { 309 | "leadsTo": 11 310 | }, 311 | "2": { 312 | "leadsTo": 13 313 | } 314 | } 315 | }, 316 | { 317 | "layout": [ 318 | "=====1======", 319 | "= =", 320 | "= =", 321 | "= === =", 322 | "= =", 323 | "2 @ 3", 324 | "= =", 325 | "= =", 326 | "= fk =", 327 | "= =", 328 | "============" 329 | ], 330 | "doors": { 331 | "1": { 332 | "leadsTo": 12 333 | }, 334 | "2": { 335 | "leadsTo": 15 336 | }, 337 | "3": { 338 | "leadsTo": 14 339 | } 340 | } 341 | }, 342 | { 343 | "layout": [ 344 | "============", 345 | "= =", 346 | "= ==== =", 347 | "= =", 348 | "= =", 349 | "1 = @ = 2", 350 | "= =", 351 | "= =", 352 | "= == =", 353 | "= k =", 354 | "============" 355 | ], 356 | "doors": { 357 | "1": { 358 | "leadsTo": 13 359 | }, 360 | "2": { 361 | "leadsTo": 7 362 | } 363 | } 364 | }, 365 | { 366 | "layout": [ 367 | "============", 368 | "= = =", 369 | "= = =", 370 | "= = =", 371 | "= = =", 372 | "1 @ 2", 373 | "= =", 374 | "= = =", 375 | "= k = =", 376 | "= = =", 377 | "============" 378 | ], 379 | "doors": { 380 | "1": { 381 | "leadsTo": 16 382 | }, 383 | "2": { 384 | "leadsTo": 13 385 | } 386 | } 387 | }, 388 | { 389 | "layout": [ 390 | "============", 391 | "= =", 392 | "= =", 393 | "= =", 394 | "= =", 395 | "1 = @ 2", 396 | "= = =", 397 | "= = =", 398 | "= k= =", 399 | "= =", 400 | "=====3======" 401 | ], 402 | "doors": { 403 | "1": { 404 | "leadsTo": 17 405 | }, 406 | "2": { 407 | "leadsTo": 15 408 | }, 409 | "3": { 410 | "leadsTo": 20 411 | } 412 | } 413 | }, 414 | { 415 | "layout": [ 416 | "============", 417 | "= =", 418 | "= =", 419 | "= k =", 420 | "= = = = =", 421 | "1 = @ = 2", 422 | "= = = =", 423 | "= = = =", 424 | "= =", 425 | "= =", 426 | "============" 427 | ], 428 | "doors": { 429 | "1": { 430 | "leadsTo": 18 431 | }, 432 | "2": { 433 | "leadsTo": 16 434 | } 435 | } 436 | }, 437 | { 438 | "layout": [ 439 | "============", 440 | "= =", 441 | "= =", 442 | "= =", 443 | "= =", 444 | "= @= 1", 445 | "= =", 446 | "= =", 447 | "= ===k =", 448 | "= =", 449 | "=====2======" 450 | ], 451 | "doors": { 452 | "1": { 453 | "leadsTo": 17 454 | }, 455 | "2": { 456 | "leadsTo": 19, 457 | "keysRequired": 1 458 | } 459 | } 460 | }, 461 | { 462 | "layout": [ 463 | "=====1======", 464 | "= = =", 465 | "= = =", 466 | "= f =", 467 | "= =", 468 | "= @ =", 469 | "= =", 470 | "= ====== = =", 471 | "= k =", 472 | "= =", 473 | "============" 474 | ], 475 | "doors": { 476 | "1": { 477 | "leadsTo": 18 478 | } 479 | } 480 | }, 481 | { 482 | "layout": [ 483 | "=====1======", 484 | "= =", 485 | "= ==== =", 486 | "= =", 487 | "= =", 488 | "= @ =", 489 | "= =", 490 | "= =", 491 | "= ==== =", 492 | "= k=", 493 | "=====2======" 494 | ], 495 | "doors": { 496 | "1": { 497 | "leadsTo": 16 498 | }, 499 | "2": { 500 | "leadsTo": 21 501 | } 502 | } 503 | }, 504 | { 505 | "layout": [ 506 | "=====1======", 507 | "= =", 508 | "= ==== =", 509 | "= =", 510 | "= =", 511 | "= @ =", 512 | "= =", 513 | "= =", 514 | "= ==== k=", 515 | "= =", 516 | "======2=====" 517 | ], 518 | "doors": { 519 | "1": { 520 | "leadsTo": 20 521 | }, 522 | "2": { 523 | "leadsTo": 22 524 | } 525 | } 526 | }, 527 | { 528 | "layout": [ 529 | "=====1======", 530 | "= =", 531 | "= =", 532 | "= k =", 533 | "= =", 534 | "= =@= =", 535 | "= = =", 536 | "= =", 537 | "= =", 538 | "= =", 539 | "======2=====" 540 | ], 541 | "doors": { 542 | "1": { 543 | "leadsTo": 21 544 | }, 545 | "2": { 546 | "leadsTo": 23 547 | } 548 | } 549 | }, 550 | { 551 | "layout": [ 552 | "=====1======", 553 | "= =", 554 | "= =", 555 | "== =", 556 | "= =", 557 | "4 @ 2", 558 | "= =", 559 | "= =", 560 | "= = =", 561 | "= k =", 562 | "=====3======" 563 | ], 564 | "doors": { 565 | "1": { 566 | "leadsTo": 22 567 | }, 568 | "2": { 569 | "leadsTo": 24 570 | }, 571 | "3": { 572 | "leadsTo": 41 573 | }, 574 | "4": { 575 | "leadsTo": 39 576 | } 577 | } 578 | }, 579 | { 580 | "layout": [ 581 | "============", 582 | "= =", 583 | "= =", 584 | "= f =", 585 | "= fffff =", 586 | "1 @ f 2", 587 | "= f fff =", 588 | "= f k =", 589 | "= f =", 590 | "= =", 591 | "============" 592 | ], 593 | "doors": { 594 | "1": { 595 | "leadsTo": 23 596 | }, 597 | "2": { 598 | "leadsTo": 25 599 | } 600 | } 601 | }, 602 | { 603 | "layout": [ 604 | "============", 605 | "= k =", 606 | "= = =", 607 | "= = =", 608 | "= = =", 609 | "1 = @ 2", 610 | "= = =", 611 | "= = =", 612 | "= = =", 613 | "= =", 614 | "=====3======" 615 | ], 616 | "doors": { 617 | "1": { 618 | "leadsTo": 24 619 | }, 620 | "2": { 621 | "leadsTo": 26 622 | }, 623 | "3": { 624 | "leadsTo": 42 625 | } 626 | } 627 | }, 628 | { 629 | "layout": [ 630 | "============", 631 | "= =", 632 | "= == =", 633 | "= f =", 634 | "= = =", 635 | "1 @ 2", 636 | "= =", 637 | "= =", 638 | "= = =", 639 | "= k ==== =", 640 | "============" 641 | ], 642 | "doors": { 643 | "1": { 644 | "leadsTo": 25 645 | }, 646 | "2": { 647 | "leadsTo": 27 648 | } 649 | } 650 | }, 651 | { 652 | "layout": [ 653 | "=====1======", 654 | "= =", 655 | "= =", 656 | "= =", 657 | "= = =", 658 | "2 =k @ 3", 659 | "= = = =", 660 | "= =", 661 | "= =", 662 | "= =", 663 | "============" 664 | ], 665 | "doors": { 666 | "1": { 667 | "leadsTo": 28, 668 | "keysRequired": 2 669 | }, 670 | "2": { 671 | "leadsTo": 26 672 | }, 673 | "3": { 674 | "leadsTo": 29 675 | } 676 | } 677 | }, 678 | { 679 | "layout": [ 680 | "=====1======", 681 | "= = =", 682 | "= = =", 683 | "= k =", 684 | "= = =", 685 | "= @ =", 686 | "= = =", 687 | "= =", 688 | "= = =", 689 | "= =", 690 | "======2=====" 691 | ], 692 | "doors": { 693 | "1": { 694 | "leadsTo": 3, 695 | "keysRequired": 1 696 | }, 697 | "2": { 698 | "leadsTo": 27, 699 | "keysRequired": 2 700 | } 701 | } 702 | }, 703 | { 704 | "layout": [ 705 | "============", 706 | "= =", 707 | "= = =", 708 | "= = =", 709 | "= =", 710 | "1 @ 2", 711 | "= =", 712 | "= = =", 713 | "= = k=", 714 | "= f =", 715 | "=====3======" 716 | ], 717 | "doors": { 718 | "1": { 719 | "leadsTo": 27 720 | }, 721 | "2": { 722 | "leadsTo": 31 723 | }, 724 | "3": { 725 | "leadsTo": 30 726 | } 727 | } 728 | }, 729 | { 730 | "layout": [ 731 | "=====1======", 732 | "= =", 733 | "= =", 734 | "=k==========", 735 | "= =", 736 | "= @ =", 737 | "= =", 738 | "= =", 739 | "= =", 740 | "= =", 741 | "=====2======" 742 | ], 743 | "doors": { 744 | "1": { 745 | "leadsTo": 29 746 | }, 747 | "2": { 748 | "leadsTo": 36 749 | } 750 | } 751 | }, 752 | { 753 | "layout": [ 754 | "=====1======", 755 | "= = = k =", 756 | "= = = =", 757 | "= == =", 758 | "= =", 759 | "2 = @ 3", 760 | "= =", 761 | "= = =", 762 | "= = =", 763 | "= = =", 764 | "============" 765 | ], 766 | "doors": { 767 | "1": { 768 | "leadsTo": 0 769 | }, 770 | "2": { 771 | "leadsTo": 29 772 | }, 773 | "3": { 774 | "leadsTo": 32 775 | } 776 | } 777 | }, 778 | { 779 | "layout": [ 780 | "============", 781 | "= = k =", 782 | "= = = =", 783 | "= = = =", 784 | "= = =", 785 | "1 = @ = =", 786 | "= = = =", 787 | "= = =", 788 | "= = = =", 789 | "= =", 790 | "=====2======" 791 | ], 792 | "doors": { 793 | "1": { 794 | "leadsTo": 31 795 | }, 796 | "2": { 797 | "leadsTo": 33 798 | } 799 | } 800 | }, 801 | { 802 | "layout": [ 803 | "=====1======", 804 | "= =", 805 | "= ==========", 806 | "= =", 807 | "========== =", 808 | "= @ =", 809 | "= ==========", 810 | "= k =", 811 | "========== =", 812 | "= =", 813 | "=====2======" 814 | ], 815 | "doors": { 816 | "1": { 817 | "leadsTo": 32 818 | }, 819 | "2": { 820 | "leadsTo": 34 821 | } 822 | } 823 | }, 824 | { 825 | "layout": [ 826 | "=====1======", 827 | "= =", 828 | "= = = =", 829 | "= = f =", 830 | "= = = =", 831 | "2 @ =", 832 | "= =", 833 | "= = = =", 834 | "= = =", 835 | "= = = k =", 836 | "============" 837 | ], 838 | "doors": { 839 | "1": { 840 | "leadsTo": 33, 841 | "keysRequired": 1 842 | }, 843 | "2": { 844 | "leadsTo": 35 845 | } 846 | } 847 | }, 848 | { 849 | "layout": [ 850 | "============", 851 | "= = =", 852 | "= = =", 853 | "= k = =", 854 | "= = =", 855 | "1 = @ 2", 856 | "= = =", 857 | "= = =", 858 | "= = = =", 859 | "= = =", 860 | "============" 861 | ], 862 | "doors": { 863 | "1": { 864 | "leadsTo": 36 865 | }, 866 | "2": { 867 | "leadsTo": 34 868 | } 869 | } 870 | }, 871 | { 872 | "layout": [ 873 | "=====1======", 874 | "= =", 875 | "= = = =", 876 | "= = = =", 877 | "= == = =", 878 | "= =f@ = 2", 879 | "= ===== =", 880 | "= =", 881 | "= k =", 882 | "= =", 883 | "============" 884 | ], 885 | "doors": { 886 | "1": { 887 | "leadsTo": 30 888 | }, 889 | "2": { 890 | "leadsTo": 35 891 | } 892 | } 893 | }, 894 | { 895 | "layout": [ 896 | "============", 897 | "= =", 898 | "= =", 899 | "= = =", 900 | "= =", 901 | "1 = @ = 2", 902 | "= =", 903 | "= = =", 904 | "= f k =", 905 | "= =", 906 | "============" 907 | ], 908 | "doors": { 909 | "1": { 910 | "leadsTo": 7 911 | }, 912 | "2": { 913 | "leadsTo": 38, 914 | "keysRequired": 1 915 | } 916 | } 917 | }, 918 | { 919 | "layout": [ 920 | "============", 921 | "= =", 922 | "= = = =", 923 | "= =", 924 | "= =", 925 | "1 @ f =", 926 | "= =", 927 | "= k =", 928 | "= = = =", 929 | "= =", 930 | "============" 931 | ], 932 | "doors": { 933 | "1": { 934 | "leadsTo": 37 935 | } 936 | } 937 | }, 938 | { 939 | "layout": [ 940 | "============", 941 | "= =", 942 | "= = =", 943 | "= =", 944 | "= = =", 945 | "= @ k 1", 946 | "= = =", 947 | "= =", 948 | "= = =", 949 | "= =", 950 | "=====2======" 951 | ], 952 | "doors": { 953 | "1": { 954 | "leadsTo": 23, 955 | "keysRequired": 2 956 | }, 957 | "2": { 958 | "leadsTo": 40 959 | } 960 | } 961 | }, 962 | { 963 | "layout": [ 964 | "=====1======", 965 | "= =", 966 | "= k = =", 967 | "= =", 968 | "= = =", 969 | "= = @ 2", 970 | "= =", 971 | "= = =", 972 | "= =", 973 | "= = =", 974 | "============" 975 | ], 976 | "doors": { 977 | "1": { 978 | "leadsTo": 39 979 | }, 980 | "2": { 981 | "leadsTo": 41 982 | } 983 | } 984 | }, 985 | { 986 | "layout": [ 987 | "=====2======", 988 | "= =", 989 | "= f =", 990 | "= =", 991 | "= =", 992 | "1 @ =", 993 | "= =", 994 | "= =", 995 | "= k =", 996 | "= =", 997 | "============" 998 | ], 999 | "doors": { 1000 | "1": { 1001 | "leadsTo": 40 1002 | }, 1003 | "2": { 1004 | "leadsTo": 23 1005 | } 1006 | } 1007 | }, 1008 | { 1009 | "layout": [ 1010 | "======1=====", 1011 | "= =", 1012 | "= = = =", 1013 | "= = = =", 1014 | "= = = =", 1015 | "= @ =", 1016 | "= =", 1017 | "= =", 1018 | "= k =", 1019 | "= =", 1020 | "============" 1021 | ], 1022 | "doors": { 1023 | "1": { 1024 | "leadsTo": 25 1025 | } 1026 | } 1027 | } 1028 | ] 1029 | -------------------------------------------------------------------------------- /images/app_preview_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-kaboom-rpg/9626098fc15f3abf7355db7a79561d110fa2fbfa/images/app_preview_image.png -------------------------------------------------------------------------------- /marketplace.json: -------------------------------------------------------------------------------- 1 | { 2 | "app_name": "Redis Kaboom RPG Game", 3 | "description": "A Role playing maze game with Redis, the Kaboom.js game framework, Express.js and Bulma using Redis JSON", 4 | "rank": "280", 5 | "type": "Full App", 6 | "contributed_by": "Redis", 7 | "repo_url": "https://github.com/redis-developer/redis-kaboom-rpg", 8 | "preview_image_url": "https://raw.githubusercontent.com/redis-developer/redis-kaboom-rpg/master/images/app_preview_image.png", 9 | "download_url": "https://github.com/redis-developer/redis-kaboom-rpg/archive/refs/heads/main.zip", 10 | "hosted_url": "", 11 | "quick_deploy": "false", 12 | "deploy_buttons": [ 13 | { 14 | "heroku": "https://heroku.com/deploy?template=https://github.com/redis-developer/redis-kaboom-rpg" 15 | }, 16 | 17 | { 18 | "Google": "https://deploy.cloud.run/?git_repo=https://github.com/redis-developer/redis-kaboom-rpg" 19 | } 20 | ], 21 | 22 | "language": ["JavaScript", "Express"], 23 | "redis_commands": ["JSON.SET", "JSON.GET", "JSON.ARRLEN", "SADD", "SISMEMBER", "SCARD", "SMEMBERS", "XADD", "XRANGE", "XREVRANGE"], 24 | "redis_use_cases": [], 25 | "redis_features": ["JSON"], 26 | "app_image_urls": ["https://raw.githubusercontent.com/redis-developer/redis-kaboom-rpg/main/screenshots/screenshot1.png", "https://raw.githubusercontent.com/redis-developer/redis-kaboom-rpg/main/screenshots/screenshot2.png"], 27 | "youtube_url": "", 28 | "special_tags": [], 29 | "verticals": ["Gaming"], 30 | "markdown": "https://raw.githubusercontent.com/redis-developer/redis-kaboom-rpg/main/README.md" 31 | } 32 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "redis-kaboom-rpg", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "version": "1.0.0", 9 | "license": "MIT", 10 | "dependencies": { 11 | "express": "^4.17.1", 12 | "ioredis": "^4.27.5", 13 | "nodemon": "^2.0.7" 14 | } 15 | }, 16 | "node_modules/@sindresorhus/is": { 17 | "version": "0.14.0", 18 | "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", 19 | "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", 20 | "engines": { 21 | "node": ">=6" 22 | } 23 | }, 24 | "node_modules/@szmarczak/http-timer": { 25 | "version": "1.1.2", 26 | "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", 27 | "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", 28 | "dependencies": { 29 | "defer-to-connect": "^1.0.1" 30 | }, 31 | "engines": { 32 | "node": ">=6" 33 | } 34 | }, 35 | "node_modules/abbrev": { 36 | "version": "1.1.1", 37 | "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", 38 | "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" 39 | }, 40 | "node_modules/accepts": { 41 | "version": "1.3.7", 42 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", 43 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", 44 | "dependencies": { 45 | "mime-types": "~2.1.24", 46 | "negotiator": "0.6.2" 47 | }, 48 | "engines": { 49 | "node": ">= 0.6" 50 | } 51 | }, 52 | "node_modules/ansi-align": { 53 | "version": "3.0.0", 54 | "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", 55 | "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", 56 | "dependencies": { 57 | "string-width": "^3.0.0" 58 | } 59 | }, 60 | "node_modules/ansi-align/node_modules/ansi-regex": { 61 | "version": "4.1.0", 62 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", 63 | "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", 64 | "engines": { 65 | "node": ">=6" 66 | } 67 | }, 68 | "node_modules/ansi-align/node_modules/emoji-regex": { 69 | "version": "7.0.3", 70 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", 71 | "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" 72 | }, 73 | "node_modules/ansi-align/node_modules/is-fullwidth-code-point": { 74 | "version": "2.0.0", 75 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 76 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", 77 | "engines": { 78 | "node": ">=4" 79 | } 80 | }, 81 | "node_modules/ansi-align/node_modules/string-width": { 82 | "version": "3.1.0", 83 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", 84 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", 85 | "dependencies": { 86 | "emoji-regex": "^7.0.1", 87 | "is-fullwidth-code-point": "^2.0.0", 88 | "strip-ansi": "^5.1.0" 89 | }, 90 | "engines": { 91 | "node": ">=6" 92 | } 93 | }, 94 | "node_modules/ansi-align/node_modules/strip-ansi": { 95 | "version": "5.2.0", 96 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", 97 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", 98 | "dependencies": { 99 | "ansi-regex": "^4.1.0" 100 | }, 101 | "engines": { 102 | "node": ">=6" 103 | } 104 | }, 105 | "node_modules/ansi-regex": { 106 | "version": "5.0.0", 107 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", 108 | "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", 109 | "engines": { 110 | "node": ">=8" 111 | } 112 | }, 113 | "node_modules/ansi-styles": { 114 | "version": "4.3.0", 115 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 116 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 117 | "dependencies": { 118 | "color-convert": "^2.0.1" 119 | }, 120 | "engines": { 121 | "node": ">=8" 122 | }, 123 | "funding": { 124 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 125 | } 126 | }, 127 | "node_modules/anymatch": { 128 | "version": "3.1.2", 129 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", 130 | "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", 131 | "dependencies": { 132 | "normalize-path": "^3.0.0", 133 | "picomatch": "^2.0.4" 134 | }, 135 | "engines": { 136 | "node": ">= 8" 137 | } 138 | }, 139 | "node_modules/array-flatten": { 140 | "version": "1.1.1", 141 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 142 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 143 | }, 144 | "node_modules/balanced-match": { 145 | "version": "1.0.2", 146 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 147 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" 148 | }, 149 | "node_modules/binary-extensions": { 150 | "version": "2.2.0", 151 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 152 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", 153 | "engines": { 154 | "node": ">=8" 155 | } 156 | }, 157 | "node_modules/body-parser": { 158 | "version": "1.19.0", 159 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", 160 | "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", 161 | "dependencies": { 162 | "bytes": "3.1.0", 163 | "content-type": "~1.0.4", 164 | "debug": "2.6.9", 165 | "depd": "~1.1.2", 166 | "http-errors": "1.7.2", 167 | "iconv-lite": "0.4.24", 168 | "on-finished": "~2.3.0", 169 | "qs": "6.7.0", 170 | "raw-body": "2.4.0", 171 | "type-is": "~1.6.17" 172 | }, 173 | "engines": { 174 | "node": ">= 0.8" 175 | } 176 | }, 177 | "node_modules/body-parser/node_modules/debug": { 178 | "version": "2.6.9", 179 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 180 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 181 | "dependencies": { 182 | "ms": "2.0.0" 183 | } 184 | }, 185 | "node_modules/body-parser/node_modules/ms": { 186 | "version": "2.0.0", 187 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 188 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 189 | }, 190 | "node_modules/boxen": { 191 | "version": "4.2.0", 192 | "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", 193 | "integrity": "sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==", 194 | "dependencies": { 195 | "ansi-align": "^3.0.0", 196 | "camelcase": "^5.3.1", 197 | "chalk": "^3.0.0", 198 | "cli-boxes": "^2.2.0", 199 | "string-width": "^4.1.0", 200 | "term-size": "^2.1.0", 201 | "type-fest": "^0.8.1", 202 | "widest-line": "^3.1.0" 203 | }, 204 | "engines": { 205 | "node": ">=8" 206 | }, 207 | "funding": { 208 | "url": "https://github.com/sponsors/sindresorhus" 209 | } 210 | }, 211 | "node_modules/brace-expansion": { 212 | "version": "1.1.11", 213 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 214 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 215 | "dependencies": { 216 | "balanced-match": "^1.0.0", 217 | "concat-map": "0.0.1" 218 | } 219 | }, 220 | "node_modules/braces": { 221 | "version": "3.0.2", 222 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 223 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 224 | "dependencies": { 225 | "fill-range": "^7.0.1" 226 | }, 227 | "engines": { 228 | "node": ">=8" 229 | } 230 | }, 231 | "node_modules/bytes": { 232 | "version": "3.1.0", 233 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", 234 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", 235 | "engines": { 236 | "node": ">= 0.8" 237 | } 238 | }, 239 | "node_modules/cacheable-request": { 240 | "version": "6.1.0", 241 | "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", 242 | "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", 243 | "dependencies": { 244 | "clone-response": "^1.0.2", 245 | "get-stream": "^5.1.0", 246 | "http-cache-semantics": "^4.0.0", 247 | "keyv": "^3.0.0", 248 | "lowercase-keys": "^2.0.0", 249 | "normalize-url": "^4.1.0", 250 | "responselike": "^1.0.2" 251 | }, 252 | "engines": { 253 | "node": ">=8" 254 | } 255 | }, 256 | "node_modules/cacheable-request/node_modules/get-stream": { 257 | "version": "5.2.0", 258 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", 259 | "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", 260 | "dependencies": { 261 | "pump": "^3.0.0" 262 | }, 263 | "engines": { 264 | "node": ">=8" 265 | }, 266 | "funding": { 267 | "url": "https://github.com/sponsors/sindresorhus" 268 | } 269 | }, 270 | "node_modules/cacheable-request/node_modules/lowercase-keys": { 271 | "version": "2.0.0", 272 | "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", 273 | "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", 274 | "engines": { 275 | "node": ">=8" 276 | } 277 | }, 278 | "node_modules/camelcase": { 279 | "version": "5.3.1", 280 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", 281 | "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", 282 | "engines": { 283 | "node": ">=6" 284 | } 285 | }, 286 | "node_modules/chalk": { 287 | "version": "3.0.0", 288 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", 289 | "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", 290 | "dependencies": { 291 | "ansi-styles": "^4.1.0", 292 | "supports-color": "^7.1.0" 293 | }, 294 | "engines": { 295 | "node": ">=8" 296 | } 297 | }, 298 | "node_modules/chalk/node_modules/has-flag": { 299 | "version": "4.0.0", 300 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 301 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 302 | "engines": { 303 | "node": ">=8" 304 | } 305 | }, 306 | "node_modules/chalk/node_modules/supports-color": { 307 | "version": "7.2.0", 308 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 309 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 310 | "dependencies": { 311 | "has-flag": "^4.0.0" 312 | }, 313 | "engines": { 314 | "node": ">=8" 315 | } 316 | }, 317 | "node_modules/chokidar": { 318 | "version": "3.5.1", 319 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", 320 | "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", 321 | "dependencies": { 322 | "anymatch": "~3.1.1", 323 | "braces": "~3.0.2", 324 | "glob-parent": "~5.1.0", 325 | "is-binary-path": "~2.1.0", 326 | "is-glob": "~4.0.1", 327 | "normalize-path": "~3.0.0", 328 | "readdirp": "~3.5.0" 329 | }, 330 | "engines": { 331 | "node": ">= 8.10.0" 332 | }, 333 | "optionalDependencies": { 334 | "fsevents": "~2.3.1" 335 | } 336 | }, 337 | "node_modules/ci-info": { 338 | "version": "2.0.0", 339 | "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", 340 | "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" 341 | }, 342 | "node_modules/cli-boxes": { 343 | "version": "2.2.1", 344 | "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", 345 | "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", 346 | "engines": { 347 | "node": ">=6" 348 | }, 349 | "funding": { 350 | "url": "https://github.com/sponsors/sindresorhus" 351 | } 352 | }, 353 | "node_modules/clone-response": { 354 | "version": "1.0.2", 355 | "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", 356 | "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", 357 | "dependencies": { 358 | "mimic-response": "^1.0.0" 359 | } 360 | }, 361 | "node_modules/cluster-key-slot": { 362 | "version": "1.1.0", 363 | "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz", 364 | "integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==", 365 | "engines": { 366 | "node": ">=0.10.0" 367 | } 368 | }, 369 | "node_modules/color-convert": { 370 | "version": "2.0.1", 371 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 372 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 373 | "dependencies": { 374 | "color-name": "~1.1.4" 375 | }, 376 | "engines": { 377 | "node": ">=7.0.0" 378 | } 379 | }, 380 | "node_modules/color-name": { 381 | "version": "1.1.4", 382 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 383 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" 384 | }, 385 | "node_modules/concat-map": { 386 | "version": "0.0.1", 387 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 388 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 389 | }, 390 | "node_modules/configstore": { 391 | "version": "5.0.1", 392 | "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", 393 | "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", 394 | "dependencies": { 395 | "dot-prop": "^5.2.0", 396 | "graceful-fs": "^4.1.2", 397 | "make-dir": "^3.0.0", 398 | "unique-string": "^2.0.0", 399 | "write-file-atomic": "^3.0.0", 400 | "xdg-basedir": "^4.0.0" 401 | }, 402 | "engines": { 403 | "node": ">=8" 404 | } 405 | }, 406 | "node_modules/content-disposition": { 407 | "version": "0.5.3", 408 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", 409 | "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", 410 | "dependencies": { 411 | "safe-buffer": "5.1.2" 412 | }, 413 | "engines": { 414 | "node": ">= 0.6" 415 | } 416 | }, 417 | "node_modules/content-type": { 418 | "version": "1.0.4", 419 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 420 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", 421 | "engines": { 422 | "node": ">= 0.6" 423 | } 424 | }, 425 | "node_modules/cookie": { 426 | "version": "0.4.0", 427 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", 428 | "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", 429 | "engines": { 430 | "node": ">= 0.6" 431 | } 432 | }, 433 | "node_modules/cookie-signature": { 434 | "version": "1.0.6", 435 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 436 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 437 | }, 438 | "node_modules/crypto-random-string": { 439 | "version": "2.0.0", 440 | "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", 441 | "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", 442 | "engines": { 443 | "node": ">=8" 444 | } 445 | }, 446 | "node_modules/debug": { 447 | "version": "4.3.1", 448 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", 449 | "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", 450 | "dependencies": { 451 | "ms": "2.1.2" 452 | }, 453 | "engines": { 454 | "node": ">=6.0" 455 | }, 456 | "peerDependenciesMeta": { 457 | "supports-color": { 458 | "optional": true 459 | } 460 | } 461 | }, 462 | "node_modules/decompress-response": { 463 | "version": "3.3.0", 464 | "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", 465 | "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", 466 | "dependencies": { 467 | "mimic-response": "^1.0.0" 468 | }, 469 | "engines": { 470 | "node": ">=4" 471 | } 472 | }, 473 | "node_modules/deep-extend": { 474 | "version": "0.6.0", 475 | "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", 476 | "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", 477 | "engines": { 478 | "node": ">=4.0.0" 479 | } 480 | }, 481 | "node_modules/defer-to-connect": { 482 | "version": "1.1.3", 483 | "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", 484 | "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" 485 | }, 486 | "node_modules/denque": { 487 | "version": "1.5.0", 488 | "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.0.tgz", 489 | "integrity": "sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ==", 490 | "engines": { 491 | "node": ">=0.10" 492 | } 493 | }, 494 | "node_modules/depd": { 495 | "version": "1.1.2", 496 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 497 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", 498 | "engines": { 499 | "node": ">= 0.6" 500 | } 501 | }, 502 | "node_modules/destroy": { 503 | "version": "1.0.4", 504 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 505 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 506 | }, 507 | "node_modules/dot-prop": { 508 | "version": "5.3.0", 509 | "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", 510 | "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", 511 | "dependencies": { 512 | "is-obj": "^2.0.0" 513 | }, 514 | "engines": { 515 | "node": ">=8" 516 | } 517 | }, 518 | "node_modules/duplexer3": { 519 | "version": "0.1.4", 520 | "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", 521 | "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" 522 | }, 523 | "node_modules/ee-first": { 524 | "version": "1.1.1", 525 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 526 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 527 | }, 528 | "node_modules/emoji-regex": { 529 | "version": "8.0.0", 530 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 531 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" 532 | }, 533 | "node_modules/encodeurl": { 534 | "version": "1.0.2", 535 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 536 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", 537 | "engines": { 538 | "node": ">= 0.8" 539 | } 540 | }, 541 | "node_modules/end-of-stream": { 542 | "version": "1.4.4", 543 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", 544 | "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", 545 | "dependencies": { 546 | "once": "^1.4.0" 547 | } 548 | }, 549 | "node_modules/escape-goat": { 550 | "version": "2.1.1", 551 | "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", 552 | "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", 553 | "engines": { 554 | "node": ">=8" 555 | } 556 | }, 557 | "node_modules/escape-html": { 558 | "version": "1.0.3", 559 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 560 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 561 | }, 562 | "node_modules/etag": { 563 | "version": "1.8.1", 564 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 565 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", 566 | "engines": { 567 | "node": ">= 0.6" 568 | } 569 | }, 570 | "node_modules/express": { 571 | "version": "4.17.1", 572 | "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", 573 | "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", 574 | "dependencies": { 575 | "accepts": "~1.3.7", 576 | "array-flatten": "1.1.1", 577 | "body-parser": "1.19.0", 578 | "content-disposition": "0.5.3", 579 | "content-type": "~1.0.4", 580 | "cookie": "0.4.0", 581 | "cookie-signature": "1.0.6", 582 | "debug": "2.6.9", 583 | "depd": "~1.1.2", 584 | "encodeurl": "~1.0.2", 585 | "escape-html": "~1.0.3", 586 | "etag": "~1.8.1", 587 | "finalhandler": "~1.1.2", 588 | "fresh": "0.5.2", 589 | "merge-descriptors": "1.0.1", 590 | "methods": "~1.1.2", 591 | "on-finished": "~2.3.0", 592 | "parseurl": "~1.3.3", 593 | "path-to-regexp": "0.1.7", 594 | "proxy-addr": "~2.0.5", 595 | "qs": "6.7.0", 596 | "range-parser": "~1.2.1", 597 | "safe-buffer": "5.1.2", 598 | "send": "0.17.1", 599 | "serve-static": "1.14.1", 600 | "setprototypeof": "1.1.1", 601 | "statuses": "~1.5.0", 602 | "type-is": "~1.6.18", 603 | "utils-merge": "1.0.1", 604 | "vary": "~1.1.2" 605 | }, 606 | "engines": { 607 | "node": ">= 0.10.0" 608 | } 609 | }, 610 | "node_modules/express/node_modules/debug": { 611 | "version": "2.6.9", 612 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 613 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 614 | "dependencies": { 615 | "ms": "2.0.0" 616 | } 617 | }, 618 | "node_modules/express/node_modules/ms": { 619 | "version": "2.0.0", 620 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 621 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 622 | }, 623 | "node_modules/fill-range": { 624 | "version": "7.0.1", 625 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 626 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 627 | "dependencies": { 628 | "to-regex-range": "^5.0.1" 629 | }, 630 | "engines": { 631 | "node": ">=8" 632 | } 633 | }, 634 | "node_modules/finalhandler": { 635 | "version": "1.1.2", 636 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", 637 | "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", 638 | "dependencies": { 639 | "debug": "2.6.9", 640 | "encodeurl": "~1.0.2", 641 | "escape-html": "~1.0.3", 642 | "on-finished": "~2.3.0", 643 | "parseurl": "~1.3.3", 644 | "statuses": "~1.5.0", 645 | "unpipe": "~1.0.0" 646 | }, 647 | "engines": { 648 | "node": ">= 0.8" 649 | } 650 | }, 651 | "node_modules/finalhandler/node_modules/debug": { 652 | "version": "2.6.9", 653 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 654 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 655 | "dependencies": { 656 | "ms": "2.0.0" 657 | } 658 | }, 659 | "node_modules/finalhandler/node_modules/ms": { 660 | "version": "2.0.0", 661 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 662 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 663 | }, 664 | "node_modules/forwarded": { 665 | "version": "0.2.0", 666 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", 667 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", 668 | "engines": { 669 | "node": ">= 0.6" 670 | } 671 | }, 672 | "node_modules/fresh": { 673 | "version": "0.5.2", 674 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 675 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", 676 | "engines": { 677 | "node": ">= 0.6" 678 | } 679 | }, 680 | "node_modules/fsevents": { 681 | "version": "2.3.2", 682 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 683 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 684 | "hasInstallScript": true, 685 | "optional": true, 686 | "os": [ 687 | "darwin" 688 | ], 689 | "engines": { 690 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 691 | } 692 | }, 693 | "node_modules/get-stream": { 694 | "version": "4.1.0", 695 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", 696 | "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", 697 | "dependencies": { 698 | "pump": "^3.0.0" 699 | }, 700 | "engines": { 701 | "node": ">=6" 702 | } 703 | }, 704 | "node_modules/glob-parent": { 705 | "version": "5.1.2", 706 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 707 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 708 | "dependencies": { 709 | "is-glob": "^4.0.1" 710 | }, 711 | "engines": { 712 | "node": ">= 6" 713 | } 714 | }, 715 | "node_modules/global-dirs": { 716 | "version": "2.1.0", 717 | "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.1.0.tgz", 718 | "integrity": "sha512-MG6kdOUh/xBnyo9cJFeIKkLEc1AyFq42QTU4XiX51i2NEdxLxLWXIjEjmqKeSuKR7pAZjTqUVoT2b2huxVLgYQ==", 719 | "dependencies": { 720 | "ini": "1.3.7" 721 | }, 722 | "engines": { 723 | "node": ">=8" 724 | }, 725 | "funding": { 726 | "url": "https://github.com/sponsors/sindresorhus" 727 | } 728 | }, 729 | "node_modules/got": { 730 | "version": "9.6.0", 731 | "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", 732 | "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", 733 | "dependencies": { 734 | "@sindresorhus/is": "^0.14.0", 735 | "@szmarczak/http-timer": "^1.1.2", 736 | "cacheable-request": "^6.0.0", 737 | "decompress-response": "^3.3.0", 738 | "duplexer3": "^0.1.4", 739 | "get-stream": "^4.1.0", 740 | "lowercase-keys": "^1.0.1", 741 | "mimic-response": "^1.0.1", 742 | "p-cancelable": "^1.0.0", 743 | "to-readable-stream": "^1.0.0", 744 | "url-parse-lax": "^3.0.0" 745 | }, 746 | "engines": { 747 | "node": ">=8.6" 748 | } 749 | }, 750 | "node_modules/graceful-fs": { 751 | "version": "4.2.6", 752 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", 753 | "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" 754 | }, 755 | "node_modules/has-flag": { 756 | "version": "3.0.0", 757 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 758 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 759 | "engines": { 760 | "node": ">=4" 761 | } 762 | }, 763 | "node_modules/has-yarn": { 764 | "version": "2.1.0", 765 | "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", 766 | "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", 767 | "engines": { 768 | "node": ">=8" 769 | } 770 | }, 771 | "node_modules/http-cache-semantics": { 772 | "version": "4.1.0", 773 | "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", 774 | "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" 775 | }, 776 | "node_modules/http-errors": { 777 | "version": "1.7.2", 778 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", 779 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", 780 | "dependencies": { 781 | "depd": "~1.1.2", 782 | "inherits": "2.0.3", 783 | "setprototypeof": "1.1.1", 784 | "statuses": ">= 1.5.0 < 2", 785 | "toidentifier": "1.0.0" 786 | }, 787 | "engines": { 788 | "node": ">= 0.6" 789 | } 790 | }, 791 | "node_modules/iconv-lite": { 792 | "version": "0.4.24", 793 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 794 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 795 | "dependencies": { 796 | "safer-buffer": ">= 2.1.2 < 3" 797 | }, 798 | "engines": { 799 | "node": ">=0.10.0" 800 | } 801 | }, 802 | "node_modules/ignore-by-default": { 803 | "version": "1.0.1", 804 | "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", 805 | "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=" 806 | }, 807 | "node_modules/import-lazy": { 808 | "version": "2.1.0", 809 | "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", 810 | "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", 811 | "engines": { 812 | "node": ">=4" 813 | } 814 | }, 815 | "node_modules/imurmurhash": { 816 | "version": "0.1.4", 817 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 818 | "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", 819 | "engines": { 820 | "node": ">=0.8.19" 821 | } 822 | }, 823 | "node_modules/inherits": { 824 | "version": "2.0.3", 825 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 826 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 827 | }, 828 | "node_modules/ini": { 829 | "version": "1.3.7", 830 | "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.7.tgz", 831 | "integrity": "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==" 832 | }, 833 | "node_modules/ioredis": { 834 | "version": "4.27.5", 835 | "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.27.5.tgz", 836 | "integrity": "sha512-JJ3HzOzU6kgUk3gKhpx8kxEYn9ruI5TkpOtGvbw/hLyWxfC19T9uCZTgmw4Mci4al4aOCCMfAjYzJ7aqQkLbJg==", 837 | "dependencies": { 838 | "cluster-key-slot": "^1.1.0", 839 | "debug": "^4.3.1", 840 | "denque": "^1.1.0", 841 | "lodash.defaults": "^4.2.0", 842 | "lodash.flatten": "^4.4.0", 843 | "p-map": "^2.1.0", 844 | "redis-commands": "1.7.0", 845 | "redis-errors": "^1.2.0", 846 | "redis-parser": "^3.0.0", 847 | "standard-as-callback": "^2.1.0" 848 | }, 849 | "engines": { 850 | "node": ">=6" 851 | }, 852 | "funding": { 853 | "type": "opencollective", 854 | "url": "https://opencollective.com/ioredis" 855 | } 856 | }, 857 | "node_modules/ipaddr.js": { 858 | "version": "1.9.1", 859 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 860 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", 861 | "engines": { 862 | "node": ">= 0.10" 863 | } 864 | }, 865 | "node_modules/is-binary-path": { 866 | "version": "2.1.0", 867 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 868 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 869 | "dependencies": { 870 | "binary-extensions": "^2.0.0" 871 | }, 872 | "engines": { 873 | "node": ">=8" 874 | } 875 | }, 876 | "node_modules/is-ci": { 877 | "version": "2.0.0", 878 | "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", 879 | "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", 880 | "dependencies": { 881 | "ci-info": "^2.0.0" 882 | }, 883 | "bin": { 884 | "is-ci": "bin.js" 885 | } 886 | }, 887 | "node_modules/is-extglob": { 888 | "version": "2.1.1", 889 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 890 | "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", 891 | "engines": { 892 | "node": ">=0.10.0" 893 | } 894 | }, 895 | "node_modules/is-fullwidth-code-point": { 896 | "version": "3.0.0", 897 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 898 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 899 | "engines": { 900 | "node": ">=8" 901 | } 902 | }, 903 | "node_modules/is-glob": { 904 | "version": "4.0.1", 905 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", 906 | "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", 907 | "dependencies": { 908 | "is-extglob": "^2.1.1" 909 | }, 910 | "engines": { 911 | "node": ">=0.10.0" 912 | } 913 | }, 914 | "node_modules/is-installed-globally": { 915 | "version": "0.3.2", 916 | "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz", 917 | "integrity": "sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==", 918 | "dependencies": { 919 | "global-dirs": "^2.0.1", 920 | "is-path-inside": "^3.0.1" 921 | }, 922 | "engines": { 923 | "node": ">=8" 924 | }, 925 | "funding": { 926 | "url": "https://github.com/sponsors/sindresorhus" 927 | } 928 | }, 929 | "node_modules/is-npm": { 930 | "version": "4.0.0", 931 | "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-4.0.0.tgz", 932 | "integrity": "sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==", 933 | "engines": { 934 | "node": ">=8" 935 | } 936 | }, 937 | "node_modules/is-number": { 938 | "version": "7.0.0", 939 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 940 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 941 | "engines": { 942 | "node": ">=0.12.0" 943 | } 944 | }, 945 | "node_modules/is-obj": { 946 | "version": "2.0.0", 947 | "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", 948 | "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", 949 | "engines": { 950 | "node": ">=8" 951 | } 952 | }, 953 | "node_modules/is-path-inside": { 954 | "version": "3.0.3", 955 | "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", 956 | "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", 957 | "engines": { 958 | "node": ">=8" 959 | } 960 | }, 961 | "node_modules/is-typedarray": { 962 | "version": "1.0.0", 963 | "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", 964 | "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" 965 | }, 966 | "node_modules/is-yarn-global": { 967 | "version": "0.3.0", 968 | "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", 969 | "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==" 970 | }, 971 | "node_modules/json-buffer": { 972 | "version": "3.0.0", 973 | "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", 974 | "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" 975 | }, 976 | "node_modules/keyv": { 977 | "version": "3.1.0", 978 | "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", 979 | "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", 980 | "dependencies": { 981 | "json-buffer": "3.0.0" 982 | } 983 | }, 984 | "node_modules/latest-version": { 985 | "version": "5.1.0", 986 | "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", 987 | "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", 988 | "dependencies": { 989 | "package-json": "^6.3.0" 990 | }, 991 | "engines": { 992 | "node": ">=8" 993 | } 994 | }, 995 | "node_modules/lodash.defaults": { 996 | "version": "4.2.0", 997 | "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", 998 | "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" 999 | }, 1000 | "node_modules/lodash.flatten": { 1001 | "version": "4.4.0", 1002 | "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", 1003 | "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" 1004 | }, 1005 | "node_modules/lowercase-keys": { 1006 | "version": "1.0.1", 1007 | "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", 1008 | "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", 1009 | "engines": { 1010 | "node": ">=0.10.0" 1011 | } 1012 | }, 1013 | "node_modules/make-dir": { 1014 | "version": "3.1.0", 1015 | "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", 1016 | "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", 1017 | "dependencies": { 1018 | "semver": "^6.0.0" 1019 | }, 1020 | "engines": { 1021 | "node": ">=8" 1022 | }, 1023 | "funding": { 1024 | "url": "https://github.com/sponsors/sindresorhus" 1025 | } 1026 | }, 1027 | "node_modules/make-dir/node_modules/semver": { 1028 | "version": "6.3.0", 1029 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", 1030 | "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", 1031 | "bin": { 1032 | "semver": "bin/semver.js" 1033 | } 1034 | }, 1035 | "node_modules/media-typer": { 1036 | "version": "0.3.0", 1037 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 1038 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", 1039 | "engines": { 1040 | "node": ">= 0.6" 1041 | } 1042 | }, 1043 | "node_modules/merge-descriptors": { 1044 | "version": "1.0.1", 1045 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 1046 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 1047 | }, 1048 | "node_modules/methods": { 1049 | "version": "1.1.2", 1050 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 1051 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", 1052 | "engines": { 1053 | "node": ">= 0.6" 1054 | } 1055 | }, 1056 | "node_modules/mime": { 1057 | "version": "1.6.0", 1058 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 1059 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", 1060 | "bin": { 1061 | "mime": "cli.js" 1062 | }, 1063 | "engines": { 1064 | "node": ">=4" 1065 | } 1066 | }, 1067 | "node_modules/mime-db": { 1068 | "version": "1.48.0", 1069 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz", 1070 | "integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==", 1071 | "engines": { 1072 | "node": ">= 0.6" 1073 | } 1074 | }, 1075 | "node_modules/mime-types": { 1076 | "version": "2.1.31", 1077 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz", 1078 | "integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==", 1079 | "dependencies": { 1080 | "mime-db": "1.48.0" 1081 | }, 1082 | "engines": { 1083 | "node": ">= 0.6" 1084 | } 1085 | }, 1086 | "node_modules/mimic-response": { 1087 | "version": "1.0.1", 1088 | "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", 1089 | "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", 1090 | "engines": { 1091 | "node": ">=4" 1092 | } 1093 | }, 1094 | "node_modules/minimatch": { 1095 | "version": "3.0.4", 1096 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 1097 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 1098 | "dependencies": { 1099 | "brace-expansion": "^1.1.7" 1100 | }, 1101 | "engines": { 1102 | "node": "*" 1103 | } 1104 | }, 1105 | "node_modules/minimist": { 1106 | "version": "1.2.5", 1107 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", 1108 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" 1109 | }, 1110 | "node_modules/ms": { 1111 | "version": "2.1.2", 1112 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1113 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 1114 | }, 1115 | "node_modules/negotiator": { 1116 | "version": "0.6.2", 1117 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", 1118 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", 1119 | "engines": { 1120 | "node": ">= 0.6" 1121 | } 1122 | }, 1123 | "node_modules/nodemon": { 1124 | "version": "2.0.7", 1125 | "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.7.tgz", 1126 | "integrity": "sha512-XHzK69Awgnec9UzHr1kc8EomQh4sjTQ8oRf8TsGrSmHDx9/UmiGG9E/mM3BuTfNeFwdNBvrqQq/RHL0xIeyFOA==", 1127 | "hasInstallScript": true, 1128 | "dependencies": { 1129 | "chokidar": "^3.2.2", 1130 | "debug": "^3.2.6", 1131 | "ignore-by-default": "^1.0.1", 1132 | "minimatch": "^3.0.4", 1133 | "pstree.remy": "^1.1.7", 1134 | "semver": "^5.7.1", 1135 | "supports-color": "^5.5.0", 1136 | "touch": "^3.1.0", 1137 | "undefsafe": "^2.0.3", 1138 | "update-notifier": "^4.1.0" 1139 | }, 1140 | "bin": { 1141 | "nodemon": "bin/nodemon.js" 1142 | }, 1143 | "engines": { 1144 | "node": ">=8.10.0" 1145 | }, 1146 | "funding": { 1147 | "type": "opencollective", 1148 | "url": "https://opencollective.com/nodemon" 1149 | } 1150 | }, 1151 | "node_modules/nodemon/node_modules/debug": { 1152 | "version": "3.2.7", 1153 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", 1154 | "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", 1155 | "dependencies": { 1156 | "ms": "^2.1.1" 1157 | } 1158 | }, 1159 | "node_modules/nopt": { 1160 | "version": "1.0.10", 1161 | "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", 1162 | "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", 1163 | "dependencies": { 1164 | "abbrev": "1" 1165 | }, 1166 | "bin": { 1167 | "nopt": "bin/nopt.js" 1168 | }, 1169 | "engines": { 1170 | "node": "*" 1171 | } 1172 | }, 1173 | "node_modules/normalize-path": { 1174 | "version": "3.0.0", 1175 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 1176 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 1177 | "engines": { 1178 | "node": ">=0.10.0" 1179 | } 1180 | }, 1181 | "node_modules/normalize-url": { 1182 | "version": "4.5.1", 1183 | "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", 1184 | "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", 1185 | "engines": { 1186 | "node": ">=8" 1187 | } 1188 | }, 1189 | "node_modules/on-finished": { 1190 | "version": "2.3.0", 1191 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 1192 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 1193 | "dependencies": { 1194 | "ee-first": "1.1.1" 1195 | }, 1196 | "engines": { 1197 | "node": ">= 0.8" 1198 | } 1199 | }, 1200 | "node_modules/once": { 1201 | "version": "1.4.0", 1202 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1203 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 1204 | "dependencies": { 1205 | "wrappy": "1" 1206 | } 1207 | }, 1208 | "node_modules/p-cancelable": { 1209 | "version": "1.1.0", 1210 | "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", 1211 | "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", 1212 | "engines": { 1213 | "node": ">=6" 1214 | } 1215 | }, 1216 | "node_modules/p-map": { 1217 | "version": "2.1.0", 1218 | "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", 1219 | "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", 1220 | "engines": { 1221 | "node": ">=6" 1222 | } 1223 | }, 1224 | "node_modules/package-json": { 1225 | "version": "6.5.0", 1226 | "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", 1227 | "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", 1228 | "dependencies": { 1229 | "got": "^9.6.0", 1230 | "registry-auth-token": "^4.0.0", 1231 | "registry-url": "^5.0.0", 1232 | "semver": "^6.2.0" 1233 | }, 1234 | "engines": { 1235 | "node": ">=8" 1236 | } 1237 | }, 1238 | "node_modules/package-json/node_modules/semver": { 1239 | "version": "6.3.0", 1240 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", 1241 | "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", 1242 | "bin": { 1243 | "semver": "bin/semver.js" 1244 | } 1245 | }, 1246 | "node_modules/parseurl": { 1247 | "version": "1.3.3", 1248 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 1249 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", 1250 | "engines": { 1251 | "node": ">= 0.8" 1252 | } 1253 | }, 1254 | "node_modules/path-to-regexp": { 1255 | "version": "0.1.7", 1256 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 1257 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 1258 | }, 1259 | "node_modules/picomatch": { 1260 | "version": "2.3.0", 1261 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", 1262 | "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", 1263 | "engines": { 1264 | "node": ">=8.6" 1265 | }, 1266 | "funding": { 1267 | "url": "https://github.com/sponsors/jonschlinkert" 1268 | } 1269 | }, 1270 | "node_modules/prepend-http": { 1271 | "version": "2.0.0", 1272 | "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", 1273 | "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", 1274 | "engines": { 1275 | "node": ">=4" 1276 | } 1277 | }, 1278 | "node_modules/proxy-addr": { 1279 | "version": "2.0.7", 1280 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", 1281 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", 1282 | "dependencies": { 1283 | "forwarded": "0.2.0", 1284 | "ipaddr.js": "1.9.1" 1285 | }, 1286 | "engines": { 1287 | "node": ">= 0.10" 1288 | } 1289 | }, 1290 | "node_modules/pstree.remy": { 1291 | "version": "1.1.8", 1292 | "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", 1293 | "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==" 1294 | }, 1295 | "node_modules/pump": { 1296 | "version": "3.0.0", 1297 | "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", 1298 | "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", 1299 | "dependencies": { 1300 | "end-of-stream": "^1.1.0", 1301 | "once": "^1.3.1" 1302 | } 1303 | }, 1304 | "node_modules/pupa": { 1305 | "version": "2.1.1", 1306 | "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", 1307 | "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", 1308 | "dependencies": { 1309 | "escape-goat": "^2.0.0" 1310 | }, 1311 | "engines": { 1312 | "node": ">=8" 1313 | } 1314 | }, 1315 | "node_modules/qs": { 1316 | "version": "6.7.0", 1317 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", 1318 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", 1319 | "engines": { 1320 | "node": ">=0.6" 1321 | } 1322 | }, 1323 | "node_modules/range-parser": { 1324 | "version": "1.2.1", 1325 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 1326 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", 1327 | "engines": { 1328 | "node": ">= 0.6" 1329 | } 1330 | }, 1331 | "node_modules/raw-body": { 1332 | "version": "2.4.0", 1333 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", 1334 | "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", 1335 | "dependencies": { 1336 | "bytes": "3.1.0", 1337 | "http-errors": "1.7.2", 1338 | "iconv-lite": "0.4.24", 1339 | "unpipe": "1.0.0" 1340 | }, 1341 | "engines": { 1342 | "node": ">= 0.8" 1343 | } 1344 | }, 1345 | "node_modules/rc": { 1346 | "version": "1.2.8", 1347 | "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", 1348 | "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", 1349 | "dependencies": { 1350 | "deep-extend": "^0.6.0", 1351 | "ini": "~1.3.0", 1352 | "minimist": "^1.2.0", 1353 | "strip-json-comments": "~2.0.1" 1354 | }, 1355 | "bin": { 1356 | "rc": "cli.js" 1357 | } 1358 | }, 1359 | "node_modules/readdirp": { 1360 | "version": "3.5.0", 1361 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", 1362 | "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", 1363 | "dependencies": { 1364 | "picomatch": "^2.2.1" 1365 | }, 1366 | "engines": { 1367 | "node": ">=8.10.0" 1368 | } 1369 | }, 1370 | "node_modules/redis-commands": { 1371 | "version": "1.7.0", 1372 | "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", 1373 | "integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==" 1374 | }, 1375 | "node_modules/redis-errors": { 1376 | "version": "1.2.0", 1377 | "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", 1378 | "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=", 1379 | "engines": { 1380 | "node": ">=4" 1381 | } 1382 | }, 1383 | "node_modules/redis-parser": { 1384 | "version": "3.0.0", 1385 | "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", 1386 | "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", 1387 | "dependencies": { 1388 | "redis-errors": "^1.0.0" 1389 | }, 1390 | "engines": { 1391 | "node": ">=4" 1392 | } 1393 | }, 1394 | "node_modules/registry-auth-token": { 1395 | "version": "4.2.1", 1396 | "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", 1397 | "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", 1398 | "dependencies": { 1399 | "rc": "^1.2.8" 1400 | }, 1401 | "engines": { 1402 | "node": ">=6.0.0" 1403 | } 1404 | }, 1405 | "node_modules/registry-url": { 1406 | "version": "5.1.0", 1407 | "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", 1408 | "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", 1409 | "dependencies": { 1410 | "rc": "^1.2.8" 1411 | }, 1412 | "engines": { 1413 | "node": ">=8" 1414 | } 1415 | }, 1416 | "node_modules/responselike": { 1417 | "version": "1.0.2", 1418 | "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", 1419 | "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", 1420 | "dependencies": { 1421 | "lowercase-keys": "^1.0.0" 1422 | } 1423 | }, 1424 | "node_modules/safe-buffer": { 1425 | "version": "5.1.2", 1426 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 1427 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 1428 | }, 1429 | "node_modules/safer-buffer": { 1430 | "version": "2.1.2", 1431 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1432 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 1433 | }, 1434 | "node_modules/semver": { 1435 | "version": "5.7.1", 1436 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 1437 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", 1438 | "bin": { 1439 | "semver": "bin/semver" 1440 | } 1441 | }, 1442 | "node_modules/semver-diff": { 1443 | "version": "3.1.1", 1444 | "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", 1445 | "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", 1446 | "dependencies": { 1447 | "semver": "^6.3.0" 1448 | }, 1449 | "engines": { 1450 | "node": ">=8" 1451 | } 1452 | }, 1453 | "node_modules/semver-diff/node_modules/semver": { 1454 | "version": "6.3.0", 1455 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", 1456 | "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", 1457 | "bin": { 1458 | "semver": "bin/semver.js" 1459 | } 1460 | }, 1461 | "node_modules/send": { 1462 | "version": "0.17.1", 1463 | "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", 1464 | "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", 1465 | "dependencies": { 1466 | "debug": "2.6.9", 1467 | "depd": "~1.1.2", 1468 | "destroy": "~1.0.4", 1469 | "encodeurl": "~1.0.2", 1470 | "escape-html": "~1.0.3", 1471 | "etag": "~1.8.1", 1472 | "fresh": "0.5.2", 1473 | "http-errors": "~1.7.2", 1474 | "mime": "1.6.0", 1475 | "ms": "2.1.1", 1476 | "on-finished": "~2.3.0", 1477 | "range-parser": "~1.2.1", 1478 | "statuses": "~1.5.0" 1479 | }, 1480 | "engines": { 1481 | "node": ">= 0.8.0" 1482 | } 1483 | }, 1484 | "node_modules/send/node_modules/debug": { 1485 | "version": "2.6.9", 1486 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 1487 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 1488 | "dependencies": { 1489 | "ms": "2.0.0" 1490 | } 1491 | }, 1492 | "node_modules/send/node_modules/debug/node_modules/ms": { 1493 | "version": "2.0.0", 1494 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 1495 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 1496 | }, 1497 | "node_modules/send/node_modules/ms": { 1498 | "version": "2.1.1", 1499 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 1500 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" 1501 | }, 1502 | "node_modules/serve-static": { 1503 | "version": "1.14.1", 1504 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", 1505 | "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", 1506 | "dependencies": { 1507 | "encodeurl": "~1.0.2", 1508 | "escape-html": "~1.0.3", 1509 | "parseurl": "~1.3.3", 1510 | "send": "0.17.1" 1511 | }, 1512 | "engines": { 1513 | "node": ">= 0.8.0" 1514 | } 1515 | }, 1516 | "node_modules/setprototypeof": { 1517 | "version": "1.1.1", 1518 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", 1519 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" 1520 | }, 1521 | "node_modules/signal-exit": { 1522 | "version": "3.0.3", 1523 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", 1524 | "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" 1525 | }, 1526 | "node_modules/standard-as-callback": { 1527 | "version": "2.1.0", 1528 | "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", 1529 | "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==" 1530 | }, 1531 | "node_modules/statuses": { 1532 | "version": "1.5.0", 1533 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 1534 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", 1535 | "engines": { 1536 | "node": ">= 0.6" 1537 | } 1538 | }, 1539 | "node_modules/string-width": { 1540 | "version": "4.2.2", 1541 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", 1542 | "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", 1543 | "dependencies": { 1544 | "emoji-regex": "^8.0.0", 1545 | "is-fullwidth-code-point": "^3.0.0", 1546 | "strip-ansi": "^6.0.0" 1547 | }, 1548 | "engines": { 1549 | "node": ">=8" 1550 | } 1551 | }, 1552 | "node_modules/strip-ansi": { 1553 | "version": "6.0.0", 1554 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", 1555 | "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", 1556 | "dependencies": { 1557 | "ansi-regex": "^5.0.0" 1558 | }, 1559 | "engines": { 1560 | "node": ">=8" 1561 | } 1562 | }, 1563 | "node_modules/strip-json-comments": { 1564 | "version": "2.0.1", 1565 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", 1566 | "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", 1567 | "engines": { 1568 | "node": ">=0.10.0" 1569 | } 1570 | }, 1571 | "node_modules/supports-color": { 1572 | "version": "5.5.0", 1573 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 1574 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 1575 | "dependencies": { 1576 | "has-flag": "^3.0.0" 1577 | }, 1578 | "engines": { 1579 | "node": ">=4" 1580 | } 1581 | }, 1582 | "node_modules/term-size": { 1583 | "version": "2.2.1", 1584 | "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", 1585 | "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==", 1586 | "engines": { 1587 | "node": ">=8" 1588 | }, 1589 | "funding": { 1590 | "url": "https://github.com/sponsors/sindresorhus" 1591 | } 1592 | }, 1593 | "node_modules/to-readable-stream": { 1594 | "version": "1.0.0", 1595 | "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", 1596 | "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", 1597 | "engines": { 1598 | "node": ">=6" 1599 | } 1600 | }, 1601 | "node_modules/to-regex-range": { 1602 | "version": "5.0.1", 1603 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 1604 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 1605 | "dependencies": { 1606 | "is-number": "^7.0.0" 1607 | }, 1608 | "engines": { 1609 | "node": ">=8.0" 1610 | } 1611 | }, 1612 | "node_modules/toidentifier": { 1613 | "version": "1.0.0", 1614 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", 1615 | "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", 1616 | "engines": { 1617 | "node": ">=0.6" 1618 | } 1619 | }, 1620 | "node_modules/touch": { 1621 | "version": "3.1.0", 1622 | "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", 1623 | "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", 1624 | "dependencies": { 1625 | "nopt": "~1.0.10" 1626 | }, 1627 | "bin": { 1628 | "nodetouch": "bin/nodetouch.js" 1629 | } 1630 | }, 1631 | "node_modules/type-fest": { 1632 | "version": "0.8.1", 1633 | "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", 1634 | "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", 1635 | "engines": { 1636 | "node": ">=8" 1637 | } 1638 | }, 1639 | "node_modules/type-is": { 1640 | "version": "1.6.18", 1641 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 1642 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 1643 | "dependencies": { 1644 | "media-typer": "0.3.0", 1645 | "mime-types": "~2.1.24" 1646 | }, 1647 | "engines": { 1648 | "node": ">= 0.6" 1649 | } 1650 | }, 1651 | "node_modules/typedarray-to-buffer": { 1652 | "version": "3.1.5", 1653 | "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", 1654 | "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", 1655 | "dependencies": { 1656 | "is-typedarray": "^1.0.0" 1657 | } 1658 | }, 1659 | "node_modules/undefsafe": { 1660 | "version": "2.0.3", 1661 | "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz", 1662 | "integrity": "sha512-nrXZwwXrD/T/JXeygJqdCO6NZZ1L66HrxM/Z7mIq2oPanoN0F1nLx3lwJMu6AwJY69hdixaFQOuoYsMjE5/C2A==", 1663 | "dependencies": { 1664 | "debug": "^2.2.0" 1665 | } 1666 | }, 1667 | "node_modules/undefsafe/node_modules/debug": { 1668 | "version": "2.6.9", 1669 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 1670 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 1671 | "dependencies": { 1672 | "ms": "2.0.0" 1673 | } 1674 | }, 1675 | "node_modules/undefsafe/node_modules/ms": { 1676 | "version": "2.0.0", 1677 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 1678 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 1679 | }, 1680 | "node_modules/unique-string": { 1681 | "version": "2.0.0", 1682 | "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", 1683 | "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", 1684 | "dependencies": { 1685 | "crypto-random-string": "^2.0.0" 1686 | }, 1687 | "engines": { 1688 | "node": ">=8" 1689 | } 1690 | }, 1691 | "node_modules/unpipe": { 1692 | "version": "1.0.0", 1693 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 1694 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", 1695 | "engines": { 1696 | "node": ">= 0.8" 1697 | } 1698 | }, 1699 | "node_modules/update-notifier": { 1700 | "version": "4.1.3", 1701 | "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-4.1.3.tgz", 1702 | "integrity": "sha512-Yld6Z0RyCYGB6ckIjffGOSOmHXj1gMeE7aROz4MG+XMkmixBX4jUngrGXNYz7wPKBmtoD4MnBa2Anu7RSKht/A==", 1703 | "dependencies": { 1704 | "boxen": "^4.2.0", 1705 | "chalk": "^3.0.0", 1706 | "configstore": "^5.0.1", 1707 | "has-yarn": "^2.1.0", 1708 | "import-lazy": "^2.1.0", 1709 | "is-ci": "^2.0.0", 1710 | "is-installed-globally": "^0.3.1", 1711 | "is-npm": "^4.0.0", 1712 | "is-yarn-global": "^0.3.0", 1713 | "latest-version": "^5.0.0", 1714 | "pupa": "^2.0.1", 1715 | "semver-diff": "^3.1.1", 1716 | "xdg-basedir": "^4.0.0" 1717 | }, 1718 | "engines": { 1719 | "node": ">=8" 1720 | }, 1721 | "funding": { 1722 | "url": "https://github.com/yeoman/update-notifier?sponsor=1" 1723 | } 1724 | }, 1725 | "node_modules/url-parse-lax": { 1726 | "version": "3.0.0", 1727 | "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", 1728 | "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", 1729 | "dependencies": { 1730 | "prepend-http": "^2.0.0" 1731 | }, 1732 | "engines": { 1733 | "node": ">=4" 1734 | } 1735 | }, 1736 | "node_modules/utils-merge": { 1737 | "version": "1.0.1", 1738 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 1739 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", 1740 | "engines": { 1741 | "node": ">= 0.4.0" 1742 | } 1743 | }, 1744 | "node_modules/vary": { 1745 | "version": "1.1.2", 1746 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 1747 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", 1748 | "engines": { 1749 | "node": ">= 0.8" 1750 | } 1751 | }, 1752 | "node_modules/widest-line": { 1753 | "version": "3.1.0", 1754 | "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", 1755 | "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", 1756 | "dependencies": { 1757 | "string-width": "^4.0.0" 1758 | }, 1759 | "engines": { 1760 | "node": ">=8" 1761 | } 1762 | }, 1763 | "node_modules/wrappy": { 1764 | "version": "1.0.2", 1765 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1766 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 1767 | }, 1768 | "node_modules/write-file-atomic": { 1769 | "version": "3.0.3", 1770 | "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", 1771 | "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", 1772 | "dependencies": { 1773 | "imurmurhash": "^0.1.4", 1774 | "is-typedarray": "^1.0.0", 1775 | "signal-exit": "^3.0.2", 1776 | "typedarray-to-buffer": "^3.1.5" 1777 | } 1778 | }, 1779 | "node_modules/xdg-basedir": { 1780 | "version": "4.0.0", 1781 | "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", 1782 | "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", 1783 | "engines": { 1784 | "node": ">=8" 1785 | } 1786 | } 1787 | }, 1788 | "dependencies": { 1789 | "@sindresorhus/is": { 1790 | "version": "0.14.0", 1791 | "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", 1792 | "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" 1793 | }, 1794 | "@szmarczak/http-timer": { 1795 | "version": "1.1.2", 1796 | "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", 1797 | "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", 1798 | "requires": { 1799 | "defer-to-connect": "^1.0.1" 1800 | } 1801 | }, 1802 | "abbrev": { 1803 | "version": "1.1.1", 1804 | "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", 1805 | "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" 1806 | }, 1807 | "accepts": { 1808 | "version": "1.3.7", 1809 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", 1810 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", 1811 | "requires": { 1812 | "mime-types": "~2.1.24", 1813 | "negotiator": "0.6.2" 1814 | } 1815 | }, 1816 | "ansi-align": { 1817 | "version": "3.0.0", 1818 | "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", 1819 | "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", 1820 | "requires": { 1821 | "string-width": "^3.0.0" 1822 | }, 1823 | "dependencies": { 1824 | "ansi-regex": { 1825 | "version": "4.1.0", 1826 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", 1827 | "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" 1828 | }, 1829 | "emoji-regex": { 1830 | "version": "7.0.3", 1831 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", 1832 | "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" 1833 | }, 1834 | "is-fullwidth-code-point": { 1835 | "version": "2.0.0", 1836 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 1837 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" 1838 | }, 1839 | "string-width": { 1840 | "version": "3.1.0", 1841 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", 1842 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", 1843 | "requires": { 1844 | "emoji-regex": "^7.0.1", 1845 | "is-fullwidth-code-point": "^2.0.0", 1846 | "strip-ansi": "^5.1.0" 1847 | } 1848 | }, 1849 | "strip-ansi": { 1850 | "version": "5.2.0", 1851 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", 1852 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", 1853 | "requires": { 1854 | "ansi-regex": "^4.1.0" 1855 | } 1856 | } 1857 | } 1858 | }, 1859 | "ansi-regex": { 1860 | "version": "5.0.0", 1861 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", 1862 | "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" 1863 | }, 1864 | "ansi-styles": { 1865 | "version": "4.3.0", 1866 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 1867 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 1868 | "requires": { 1869 | "color-convert": "^2.0.1" 1870 | } 1871 | }, 1872 | "anymatch": { 1873 | "version": "3.1.2", 1874 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", 1875 | "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", 1876 | "requires": { 1877 | "normalize-path": "^3.0.0", 1878 | "picomatch": "^2.0.4" 1879 | } 1880 | }, 1881 | "array-flatten": { 1882 | "version": "1.1.1", 1883 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 1884 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 1885 | }, 1886 | "balanced-match": { 1887 | "version": "1.0.2", 1888 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 1889 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" 1890 | }, 1891 | "binary-extensions": { 1892 | "version": "2.2.0", 1893 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 1894 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" 1895 | }, 1896 | "body-parser": { 1897 | "version": "1.19.0", 1898 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", 1899 | "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", 1900 | "requires": { 1901 | "bytes": "3.1.0", 1902 | "content-type": "~1.0.4", 1903 | "debug": "2.6.9", 1904 | "depd": "~1.1.2", 1905 | "http-errors": "1.7.2", 1906 | "iconv-lite": "0.4.24", 1907 | "on-finished": "~2.3.0", 1908 | "qs": "6.7.0", 1909 | "raw-body": "2.4.0", 1910 | "type-is": "~1.6.17" 1911 | }, 1912 | "dependencies": { 1913 | "debug": { 1914 | "version": "2.6.9", 1915 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 1916 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 1917 | "requires": { 1918 | "ms": "2.0.0" 1919 | } 1920 | }, 1921 | "ms": { 1922 | "version": "2.0.0", 1923 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 1924 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 1925 | } 1926 | } 1927 | }, 1928 | "boxen": { 1929 | "version": "4.2.0", 1930 | "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", 1931 | "integrity": "sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==", 1932 | "requires": { 1933 | "ansi-align": "^3.0.0", 1934 | "camelcase": "^5.3.1", 1935 | "chalk": "^3.0.0", 1936 | "cli-boxes": "^2.2.0", 1937 | "string-width": "^4.1.0", 1938 | "term-size": "^2.1.0", 1939 | "type-fest": "^0.8.1", 1940 | "widest-line": "^3.1.0" 1941 | } 1942 | }, 1943 | "brace-expansion": { 1944 | "version": "1.1.11", 1945 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 1946 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 1947 | "requires": { 1948 | "balanced-match": "^1.0.0", 1949 | "concat-map": "0.0.1" 1950 | } 1951 | }, 1952 | "braces": { 1953 | "version": "3.0.2", 1954 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 1955 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 1956 | "requires": { 1957 | "fill-range": "^7.0.1" 1958 | } 1959 | }, 1960 | "bytes": { 1961 | "version": "3.1.0", 1962 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", 1963 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" 1964 | }, 1965 | "cacheable-request": { 1966 | "version": "6.1.0", 1967 | "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", 1968 | "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", 1969 | "requires": { 1970 | "clone-response": "^1.0.2", 1971 | "get-stream": "^5.1.0", 1972 | "http-cache-semantics": "^4.0.0", 1973 | "keyv": "^3.0.0", 1974 | "lowercase-keys": "^2.0.0", 1975 | "normalize-url": "^4.1.0", 1976 | "responselike": "^1.0.2" 1977 | }, 1978 | "dependencies": { 1979 | "get-stream": { 1980 | "version": "5.2.0", 1981 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", 1982 | "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", 1983 | "requires": { 1984 | "pump": "^3.0.0" 1985 | } 1986 | }, 1987 | "lowercase-keys": { 1988 | "version": "2.0.0", 1989 | "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", 1990 | "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" 1991 | } 1992 | } 1993 | }, 1994 | "camelcase": { 1995 | "version": "5.3.1", 1996 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", 1997 | "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" 1998 | }, 1999 | "chalk": { 2000 | "version": "3.0.0", 2001 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", 2002 | "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", 2003 | "requires": { 2004 | "ansi-styles": "^4.1.0", 2005 | "supports-color": "^7.1.0" 2006 | }, 2007 | "dependencies": { 2008 | "has-flag": { 2009 | "version": "4.0.0", 2010 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 2011 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" 2012 | }, 2013 | "supports-color": { 2014 | "version": "7.2.0", 2015 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 2016 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 2017 | "requires": { 2018 | "has-flag": "^4.0.0" 2019 | } 2020 | } 2021 | } 2022 | }, 2023 | "chokidar": { 2024 | "version": "3.5.1", 2025 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", 2026 | "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", 2027 | "requires": { 2028 | "anymatch": "~3.1.1", 2029 | "braces": "~3.0.2", 2030 | "fsevents": "~2.3.1", 2031 | "glob-parent": "~5.1.0", 2032 | "is-binary-path": "~2.1.0", 2033 | "is-glob": "~4.0.1", 2034 | "normalize-path": "~3.0.0", 2035 | "readdirp": "~3.5.0" 2036 | } 2037 | }, 2038 | "ci-info": { 2039 | "version": "2.0.0", 2040 | "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", 2041 | "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" 2042 | }, 2043 | "cli-boxes": { 2044 | "version": "2.2.1", 2045 | "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", 2046 | "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==" 2047 | }, 2048 | "clone-response": { 2049 | "version": "1.0.2", 2050 | "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", 2051 | "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", 2052 | "requires": { 2053 | "mimic-response": "^1.0.0" 2054 | } 2055 | }, 2056 | "cluster-key-slot": { 2057 | "version": "1.1.0", 2058 | "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz", 2059 | "integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==" 2060 | }, 2061 | "color-convert": { 2062 | "version": "2.0.1", 2063 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 2064 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 2065 | "requires": { 2066 | "color-name": "~1.1.4" 2067 | } 2068 | }, 2069 | "color-name": { 2070 | "version": "1.1.4", 2071 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 2072 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" 2073 | }, 2074 | "concat-map": { 2075 | "version": "0.0.1", 2076 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 2077 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 2078 | }, 2079 | "configstore": { 2080 | "version": "5.0.1", 2081 | "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", 2082 | "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", 2083 | "requires": { 2084 | "dot-prop": "^5.2.0", 2085 | "graceful-fs": "^4.1.2", 2086 | "make-dir": "^3.0.0", 2087 | "unique-string": "^2.0.0", 2088 | "write-file-atomic": "^3.0.0", 2089 | "xdg-basedir": "^4.0.0" 2090 | } 2091 | }, 2092 | "content-disposition": { 2093 | "version": "0.5.3", 2094 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", 2095 | "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", 2096 | "requires": { 2097 | "safe-buffer": "5.1.2" 2098 | } 2099 | }, 2100 | "content-type": { 2101 | "version": "1.0.4", 2102 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 2103 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 2104 | }, 2105 | "cookie": { 2106 | "version": "0.4.0", 2107 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", 2108 | "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" 2109 | }, 2110 | "cookie-signature": { 2111 | "version": "1.0.6", 2112 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 2113 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 2114 | }, 2115 | "crypto-random-string": { 2116 | "version": "2.0.0", 2117 | "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", 2118 | "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==" 2119 | }, 2120 | "debug": { 2121 | "version": "4.3.1", 2122 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", 2123 | "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", 2124 | "requires": { 2125 | "ms": "2.1.2" 2126 | } 2127 | }, 2128 | "decompress-response": { 2129 | "version": "3.3.0", 2130 | "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", 2131 | "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", 2132 | "requires": { 2133 | "mimic-response": "^1.0.0" 2134 | } 2135 | }, 2136 | "deep-extend": { 2137 | "version": "0.6.0", 2138 | "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", 2139 | "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" 2140 | }, 2141 | "defer-to-connect": { 2142 | "version": "1.1.3", 2143 | "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", 2144 | "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" 2145 | }, 2146 | "denque": { 2147 | "version": "1.5.0", 2148 | "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.0.tgz", 2149 | "integrity": "sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ==" 2150 | }, 2151 | "depd": { 2152 | "version": "1.1.2", 2153 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 2154 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 2155 | }, 2156 | "destroy": { 2157 | "version": "1.0.4", 2158 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 2159 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 2160 | }, 2161 | "dot-prop": { 2162 | "version": "5.3.0", 2163 | "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", 2164 | "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", 2165 | "requires": { 2166 | "is-obj": "^2.0.0" 2167 | } 2168 | }, 2169 | "duplexer3": { 2170 | "version": "0.1.4", 2171 | "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", 2172 | "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" 2173 | }, 2174 | "ee-first": { 2175 | "version": "1.1.1", 2176 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 2177 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 2178 | }, 2179 | "emoji-regex": { 2180 | "version": "8.0.0", 2181 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 2182 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" 2183 | }, 2184 | "encodeurl": { 2185 | "version": "1.0.2", 2186 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 2187 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 2188 | }, 2189 | "end-of-stream": { 2190 | "version": "1.4.4", 2191 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", 2192 | "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", 2193 | "requires": { 2194 | "once": "^1.4.0" 2195 | } 2196 | }, 2197 | "escape-goat": { 2198 | "version": "2.1.1", 2199 | "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", 2200 | "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==" 2201 | }, 2202 | "escape-html": { 2203 | "version": "1.0.3", 2204 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 2205 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 2206 | }, 2207 | "etag": { 2208 | "version": "1.8.1", 2209 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 2210 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 2211 | }, 2212 | "express": { 2213 | "version": "4.17.1", 2214 | "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", 2215 | "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", 2216 | "requires": { 2217 | "accepts": "~1.3.7", 2218 | "array-flatten": "1.1.1", 2219 | "body-parser": "1.19.0", 2220 | "content-disposition": "0.5.3", 2221 | "content-type": "~1.0.4", 2222 | "cookie": "0.4.0", 2223 | "cookie-signature": "1.0.6", 2224 | "debug": "2.6.9", 2225 | "depd": "~1.1.2", 2226 | "encodeurl": "~1.0.2", 2227 | "escape-html": "~1.0.3", 2228 | "etag": "~1.8.1", 2229 | "finalhandler": "~1.1.2", 2230 | "fresh": "0.5.2", 2231 | "merge-descriptors": "1.0.1", 2232 | "methods": "~1.1.2", 2233 | "on-finished": "~2.3.0", 2234 | "parseurl": "~1.3.3", 2235 | "path-to-regexp": "0.1.7", 2236 | "proxy-addr": "~2.0.5", 2237 | "qs": "6.7.0", 2238 | "range-parser": "~1.2.1", 2239 | "safe-buffer": "5.1.2", 2240 | "send": "0.17.1", 2241 | "serve-static": "1.14.1", 2242 | "setprototypeof": "1.1.1", 2243 | "statuses": "~1.5.0", 2244 | "type-is": "~1.6.18", 2245 | "utils-merge": "1.0.1", 2246 | "vary": "~1.1.2" 2247 | }, 2248 | "dependencies": { 2249 | "debug": { 2250 | "version": "2.6.9", 2251 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 2252 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 2253 | "requires": { 2254 | "ms": "2.0.0" 2255 | } 2256 | }, 2257 | "ms": { 2258 | "version": "2.0.0", 2259 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 2260 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 2261 | } 2262 | } 2263 | }, 2264 | "fill-range": { 2265 | "version": "7.0.1", 2266 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 2267 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 2268 | "requires": { 2269 | "to-regex-range": "^5.0.1" 2270 | } 2271 | }, 2272 | "finalhandler": { 2273 | "version": "1.1.2", 2274 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", 2275 | "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", 2276 | "requires": { 2277 | "debug": "2.6.9", 2278 | "encodeurl": "~1.0.2", 2279 | "escape-html": "~1.0.3", 2280 | "on-finished": "~2.3.0", 2281 | "parseurl": "~1.3.3", 2282 | "statuses": "~1.5.0", 2283 | "unpipe": "~1.0.0" 2284 | }, 2285 | "dependencies": { 2286 | "debug": { 2287 | "version": "2.6.9", 2288 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 2289 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 2290 | "requires": { 2291 | "ms": "2.0.0" 2292 | } 2293 | }, 2294 | "ms": { 2295 | "version": "2.0.0", 2296 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 2297 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 2298 | } 2299 | } 2300 | }, 2301 | "forwarded": { 2302 | "version": "0.2.0", 2303 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", 2304 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" 2305 | }, 2306 | "fresh": { 2307 | "version": "0.5.2", 2308 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 2309 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 2310 | }, 2311 | "fsevents": { 2312 | "version": "2.3.2", 2313 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 2314 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 2315 | "optional": true 2316 | }, 2317 | "get-stream": { 2318 | "version": "4.1.0", 2319 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", 2320 | "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", 2321 | "requires": { 2322 | "pump": "^3.0.0" 2323 | } 2324 | }, 2325 | "glob-parent": { 2326 | "version": "5.1.2", 2327 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 2328 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 2329 | "requires": { 2330 | "is-glob": "^4.0.1" 2331 | } 2332 | }, 2333 | "global-dirs": { 2334 | "version": "2.1.0", 2335 | "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.1.0.tgz", 2336 | "integrity": "sha512-MG6kdOUh/xBnyo9cJFeIKkLEc1AyFq42QTU4XiX51i2NEdxLxLWXIjEjmqKeSuKR7pAZjTqUVoT2b2huxVLgYQ==", 2337 | "requires": { 2338 | "ini": "1.3.7" 2339 | } 2340 | }, 2341 | "got": { 2342 | "version": "9.6.0", 2343 | "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", 2344 | "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", 2345 | "requires": { 2346 | "@sindresorhus/is": "^0.14.0", 2347 | "@szmarczak/http-timer": "^1.1.2", 2348 | "cacheable-request": "^6.0.0", 2349 | "decompress-response": "^3.3.0", 2350 | "duplexer3": "^0.1.4", 2351 | "get-stream": "^4.1.0", 2352 | "lowercase-keys": "^1.0.1", 2353 | "mimic-response": "^1.0.1", 2354 | "p-cancelable": "^1.0.0", 2355 | "to-readable-stream": "^1.0.0", 2356 | "url-parse-lax": "^3.0.0" 2357 | } 2358 | }, 2359 | "graceful-fs": { 2360 | "version": "4.2.6", 2361 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", 2362 | "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" 2363 | }, 2364 | "has-flag": { 2365 | "version": "3.0.0", 2366 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 2367 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" 2368 | }, 2369 | "has-yarn": { 2370 | "version": "2.1.0", 2371 | "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", 2372 | "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==" 2373 | }, 2374 | "http-cache-semantics": { 2375 | "version": "4.1.0", 2376 | "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", 2377 | "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" 2378 | }, 2379 | "http-errors": { 2380 | "version": "1.7.2", 2381 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", 2382 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", 2383 | "requires": { 2384 | "depd": "~1.1.2", 2385 | "inherits": "2.0.3", 2386 | "setprototypeof": "1.1.1", 2387 | "statuses": ">= 1.5.0 < 2", 2388 | "toidentifier": "1.0.0" 2389 | } 2390 | }, 2391 | "iconv-lite": { 2392 | "version": "0.4.24", 2393 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 2394 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 2395 | "requires": { 2396 | "safer-buffer": ">= 2.1.2 < 3" 2397 | } 2398 | }, 2399 | "ignore-by-default": { 2400 | "version": "1.0.1", 2401 | "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", 2402 | "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=" 2403 | }, 2404 | "import-lazy": { 2405 | "version": "2.1.0", 2406 | "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", 2407 | "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=" 2408 | }, 2409 | "imurmurhash": { 2410 | "version": "0.1.4", 2411 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 2412 | "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" 2413 | }, 2414 | "inherits": { 2415 | "version": "2.0.3", 2416 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 2417 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 2418 | }, 2419 | "ini": { 2420 | "version": "1.3.7", 2421 | "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.7.tgz", 2422 | "integrity": "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==" 2423 | }, 2424 | "ioredis": { 2425 | "version": "4.27.5", 2426 | "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.27.5.tgz", 2427 | "integrity": "sha512-JJ3HzOzU6kgUk3gKhpx8kxEYn9ruI5TkpOtGvbw/hLyWxfC19T9uCZTgmw4Mci4al4aOCCMfAjYzJ7aqQkLbJg==", 2428 | "requires": { 2429 | "cluster-key-slot": "^1.1.0", 2430 | "debug": "^4.3.1", 2431 | "denque": "^1.1.0", 2432 | "lodash.defaults": "^4.2.0", 2433 | "lodash.flatten": "^4.4.0", 2434 | "p-map": "^2.1.0", 2435 | "redis-commands": "1.7.0", 2436 | "redis-errors": "^1.2.0", 2437 | "redis-parser": "^3.0.0", 2438 | "standard-as-callback": "^2.1.0" 2439 | } 2440 | }, 2441 | "ipaddr.js": { 2442 | "version": "1.9.1", 2443 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 2444 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" 2445 | }, 2446 | "is-binary-path": { 2447 | "version": "2.1.0", 2448 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 2449 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 2450 | "requires": { 2451 | "binary-extensions": "^2.0.0" 2452 | } 2453 | }, 2454 | "is-ci": { 2455 | "version": "2.0.0", 2456 | "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", 2457 | "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", 2458 | "requires": { 2459 | "ci-info": "^2.0.0" 2460 | } 2461 | }, 2462 | "is-extglob": { 2463 | "version": "2.1.1", 2464 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 2465 | "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" 2466 | }, 2467 | "is-fullwidth-code-point": { 2468 | "version": "3.0.0", 2469 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 2470 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" 2471 | }, 2472 | "is-glob": { 2473 | "version": "4.0.1", 2474 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", 2475 | "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", 2476 | "requires": { 2477 | "is-extglob": "^2.1.1" 2478 | } 2479 | }, 2480 | "is-installed-globally": { 2481 | "version": "0.3.2", 2482 | "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz", 2483 | "integrity": "sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==", 2484 | "requires": { 2485 | "global-dirs": "^2.0.1", 2486 | "is-path-inside": "^3.0.1" 2487 | } 2488 | }, 2489 | "is-npm": { 2490 | "version": "4.0.0", 2491 | "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-4.0.0.tgz", 2492 | "integrity": "sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==" 2493 | }, 2494 | "is-number": { 2495 | "version": "7.0.0", 2496 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 2497 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" 2498 | }, 2499 | "is-obj": { 2500 | "version": "2.0.0", 2501 | "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", 2502 | "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" 2503 | }, 2504 | "is-path-inside": { 2505 | "version": "3.0.3", 2506 | "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", 2507 | "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==" 2508 | }, 2509 | "is-typedarray": { 2510 | "version": "1.0.0", 2511 | "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", 2512 | "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" 2513 | }, 2514 | "is-yarn-global": { 2515 | "version": "0.3.0", 2516 | "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", 2517 | "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==" 2518 | }, 2519 | "json-buffer": { 2520 | "version": "3.0.0", 2521 | "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", 2522 | "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" 2523 | }, 2524 | "keyv": { 2525 | "version": "3.1.0", 2526 | "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", 2527 | "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", 2528 | "requires": { 2529 | "json-buffer": "3.0.0" 2530 | } 2531 | }, 2532 | "latest-version": { 2533 | "version": "5.1.0", 2534 | "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", 2535 | "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", 2536 | "requires": { 2537 | "package-json": "^6.3.0" 2538 | } 2539 | }, 2540 | "lodash.defaults": { 2541 | "version": "4.2.0", 2542 | "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", 2543 | "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" 2544 | }, 2545 | "lodash.flatten": { 2546 | "version": "4.4.0", 2547 | "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", 2548 | "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" 2549 | }, 2550 | "lowercase-keys": { 2551 | "version": "1.0.1", 2552 | "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", 2553 | "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" 2554 | }, 2555 | "make-dir": { 2556 | "version": "3.1.0", 2557 | "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", 2558 | "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", 2559 | "requires": { 2560 | "semver": "^6.0.0" 2561 | }, 2562 | "dependencies": { 2563 | "semver": { 2564 | "version": "6.3.0", 2565 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", 2566 | "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" 2567 | } 2568 | } 2569 | }, 2570 | "media-typer": { 2571 | "version": "0.3.0", 2572 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 2573 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 2574 | }, 2575 | "merge-descriptors": { 2576 | "version": "1.0.1", 2577 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 2578 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 2579 | }, 2580 | "methods": { 2581 | "version": "1.1.2", 2582 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 2583 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 2584 | }, 2585 | "mime": { 2586 | "version": "1.6.0", 2587 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 2588 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" 2589 | }, 2590 | "mime-db": { 2591 | "version": "1.48.0", 2592 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz", 2593 | "integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==" 2594 | }, 2595 | "mime-types": { 2596 | "version": "2.1.31", 2597 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz", 2598 | "integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==", 2599 | "requires": { 2600 | "mime-db": "1.48.0" 2601 | } 2602 | }, 2603 | "mimic-response": { 2604 | "version": "1.0.1", 2605 | "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", 2606 | "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" 2607 | }, 2608 | "minimatch": { 2609 | "version": "3.0.4", 2610 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 2611 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 2612 | "requires": { 2613 | "brace-expansion": "^1.1.7" 2614 | } 2615 | }, 2616 | "minimist": { 2617 | "version": "1.2.5", 2618 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", 2619 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" 2620 | }, 2621 | "ms": { 2622 | "version": "2.1.2", 2623 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 2624 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 2625 | }, 2626 | "negotiator": { 2627 | "version": "0.6.2", 2628 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", 2629 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" 2630 | }, 2631 | "nodemon": { 2632 | "version": "2.0.7", 2633 | "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.7.tgz", 2634 | "integrity": "sha512-XHzK69Awgnec9UzHr1kc8EomQh4sjTQ8oRf8TsGrSmHDx9/UmiGG9E/mM3BuTfNeFwdNBvrqQq/RHL0xIeyFOA==", 2635 | "requires": { 2636 | "chokidar": "^3.2.2", 2637 | "debug": "^3.2.6", 2638 | "ignore-by-default": "^1.0.1", 2639 | "minimatch": "^3.0.4", 2640 | "pstree.remy": "^1.1.7", 2641 | "semver": "^5.7.1", 2642 | "supports-color": "^5.5.0", 2643 | "touch": "^3.1.0", 2644 | "undefsafe": "^2.0.3", 2645 | "update-notifier": "^4.1.0" 2646 | }, 2647 | "dependencies": { 2648 | "debug": { 2649 | "version": "3.2.7", 2650 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", 2651 | "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", 2652 | "requires": { 2653 | "ms": "^2.1.1" 2654 | } 2655 | } 2656 | } 2657 | }, 2658 | "nopt": { 2659 | "version": "1.0.10", 2660 | "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", 2661 | "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", 2662 | "requires": { 2663 | "abbrev": "1" 2664 | } 2665 | }, 2666 | "normalize-path": { 2667 | "version": "3.0.0", 2668 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 2669 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" 2670 | }, 2671 | "normalize-url": { 2672 | "version": "4.5.1", 2673 | "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", 2674 | "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==" 2675 | }, 2676 | "on-finished": { 2677 | "version": "2.3.0", 2678 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 2679 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 2680 | "requires": { 2681 | "ee-first": "1.1.1" 2682 | } 2683 | }, 2684 | "once": { 2685 | "version": "1.4.0", 2686 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 2687 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 2688 | "requires": { 2689 | "wrappy": "1" 2690 | } 2691 | }, 2692 | "p-cancelable": { 2693 | "version": "1.1.0", 2694 | "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", 2695 | "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" 2696 | }, 2697 | "p-map": { 2698 | "version": "2.1.0", 2699 | "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", 2700 | "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==" 2701 | }, 2702 | "package-json": { 2703 | "version": "6.5.0", 2704 | "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", 2705 | "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", 2706 | "requires": { 2707 | "got": "^9.6.0", 2708 | "registry-auth-token": "^4.0.0", 2709 | "registry-url": "^5.0.0", 2710 | "semver": "^6.2.0" 2711 | }, 2712 | "dependencies": { 2713 | "semver": { 2714 | "version": "6.3.0", 2715 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", 2716 | "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" 2717 | } 2718 | } 2719 | }, 2720 | "parseurl": { 2721 | "version": "1.3.3", 2722 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 2723 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" 2724 | }, 2725 | "path-to-regexp": { 2726 | "version": "0.1.7", 2727 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 2728 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 2729 | }, 2730 | "picomatch": { 2731 | "version": "2.3.0", 2732 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", 2733 | "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==" 2734 | }, 2735 | "prepend-http": { 2736 | "version": "2.0.0", 2737 | "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", 2738 | "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" 2739 | }, 2740 | "proxy-addr": { 2741 | "version": "2.0.7", 2742 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", 2743 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", 2744 | "requires": { 2745 | "forwarded": "0.2.0", 2746 | "ipaddr.js": "1.9.1" 2747 | } 2748 | }, 2749 | "pstree.remy": { 2750 | "version": "1.1.8", 2751 | "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", 2752 | "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==" 2753 | }, 2754 | "pump": { 2755 | "version": "3.0.0", 2756 | "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", 2757 | "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", 2758 | "requires": { 2759 | "end-of-stream": "^1.1.0", 2760 | "once": "^1.3.1" 2761 | } 2762 | }, 2763 | "pupa": { 2764 | "version": "2.1.1", 2765 | "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", 2766 | "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", 2767 | "requires": { 2768 | "escape-goat": "^2.0.0" 2769 | } 2770 | }, 2771 | "qs": { 2772 | "version": "6.7.0", 2773 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", 2774 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" 2775 | }, 2776 | "range-parser": { 2777 | "version": "1.2.1", 2778 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 2779 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" 2780 | }, 2781 | "raw-body": { 2782 | "version": "2.4.0", 2783 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", 2784 | "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", 2785 | "requires": { 2786 | "bytes": "3.1.0", 2787 | "http-errors": "1.7.2", 2788 | "iconv-lite": "0.4.24", 2789 | "unpipe": "1.0.0" 2790 | } 2791 | }, 2792 | "rc": { 2793 | "version": "1.2.8", 2794 | "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", 2795 | "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", 2796 | "requires": { 2797 | "deep-extend": "^0.6.0", 2798 | "ini": "~1.3.0", 2799 | "minimist": "^1.2.0", 2800 | "strip-json-comments": "~2.0.1" 2801 | } 2802 | }, 2803 | "readdirp": { 2804 | "version": "3.5.0", 2805 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", 2806 | "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", 2807 | "requires": { 2808 | "picomatch": "^2.2.1" 2809 | } 2810 | }, 2811 | "redis-commands": { 2812 | "version": "1.7.0", 2813 | "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", 2814 | "integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==" 2815 | }, 2816 | "redis-errors": { 2817 | "version": "1.2.0", 2818 | "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", 2819 | "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=" 2820 | }, 2821 | "redis-parser": { 2822 | "version": "3.0.0", 2823 | "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", 2824 | "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", 2825 | "requires": { 2826 | "redis-errors": "^1.0.0" 2827 | } 2828 | }, 2829 | "registry-auth-token": { 2830 | "version": "4.2.1", 2831 | "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", 2832 | "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", 2833 | "requires": { 2834 | "rc": "^1.2.8" 2835 | } 2836 | }, 2837 | "registry-url": { 2838 | "version": "5.1.0", 2839 | "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", 2840 | "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", 2841 | "requires": { 2842 | "rc": "^1.2.8" 2843 | } 2844 | }, 2845 | "responselike": { 2846 | "version": "1.0.2", 2847 | "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", 2848 | "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", 2849 | "requires": { 2850 | "lowercase-keys": "^1.0.0" 2851 | } 2852 | }, 2853 | "safe-buffer": { 2854 | "version": "5.1.2", 2855 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 2856 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 2857 | }, 2858 | "safer-buffer": { 2859 | "version": "2.1.2", 2860 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 2861 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 2862 | }, 2863 | "semver": { 2864 | "version": "5.7.1", 2865 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 2866 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" 2867 | }, 2868 | "semver-diff": { 2869 | "version": "3.1.1", 2870 | "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", 2871 | "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", 2872 | "requires": { 2873 | "semver": "^6.3.0" 2874 | }, 2875 | "dependencies": { 2876 | "semver": { 2877 | "version": "6.3.0", 2878 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", 2879 | "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" 2880 | } 2881 | } 2882 | }, 2883 | "send": { 2884 | "version": "0.17.1", 2885 | "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", 2886 | "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", 2887 | "requires": { 2888 | "debug": "2.6.9", 2889 | "depd": "~1.1.2", 2890 | "destroy": "~1.0.4", 2891 | "encodeurl": "~1.0.2", 2892 | "escape-html": "~1.0.3", 2893 | "etag": "~1.8.1", 2894 | "fresh": "0.5.2", 2895 | "http-errors": "~1.7.2", 2896 | "mime": "1.6.0", 2897 | "ms": "2.1.1", 2898 | "on-finished": "~2.3.0", 2899 | "range-parser": "~1.2.1", 2900 | "statuses": "~1.5.0" 2901 | }, 2902 | "dependencies": { 2903 | "debug": { 2904 | "version": "2.6.9", 2905 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 2906 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 2907 | "requires": { 2908 | "ms": "2.0.0" 2909 | }, 2910 | "dependencies": { 2911 | "ms": { 2912 | "version": "2.0.0", 2913 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 2914 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 2915 | } 2916 | } 2917 | }, 2918 | "ms": { 2919 | "version": "2.1.1", 2920 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 2921 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" 2922 | } 2923 | } 2924 | }, 2925 | "serve-static": { 2926 | "version": "1.14.1", 2927 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", 2928 | "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", 2929 | "requires": { 2930 | "encodeurl": "~1.0.2", 2931 | "escape-html": "~1.0.3", 2932 | "parseurl": "~1.3.3", 2933 | "send": "0.17.1" 2934 | } 2935 | }, 2936 | "setprototypeof": { 2937 | "version": "1.1.1", 2938 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", 2939 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" 2940 | }, 2941 | "signal-exit": { 2942 | "version": "3.0.3", 2943 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", 2944 | "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" 2945 | }, 2946 | "standard-as-callback": { 2947 | "version": "2.1.0", 2948 | "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", 2949 | "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==" 2950 | }, 2951 | "statuses": { 2952 | "version": "1.5.0", 2953 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 2954 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 2955 | }, 2956 | "string-width": { 2957 | "version": "4.2.2", 2958 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", 2959 | "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", 2960 | "requires": { 2961 | "emoji-regex": "^8.0.0", 2962 | "is-fullwidth-code-point": "^3.0.0", 2963 | "strip-ansi": "^6.0.0" 2964 | } 2965 | }, 2966 | "strip-ansi": { 2967 | "version": "6.0.0", 2968 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", 2969 | "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", 2970 | "requires": { 2971 | "ansi-regex": "^5.0.0" 2972 | } 2973 | }, 2974 | "strip-json-comments": { 2975 | "version": "2.0.1", 2976 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", 2977 | "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" 2978 | }, 2979 | "supports-color": { 2980 | "version": "5.5.0", 2981 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 2982 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 2983 | "requires": { 2984 | "has-flag": "^3.0.0" 2985 | } 2986 | }, 2987 | "term-size": { 2988 | "version": "2.2.1", 2989 | "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", 2990 | "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==" 2991 | }, 2992 | "to-readable-stream": { 2993 | "version": "1.0.0", 2994 | "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", 2995 | "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==" 2996 | }, 2997 | "to-regex-range": { 2998 | "version": "5.0.1", 2999 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 3000 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 3001 | "requires": { 3002 | "is-number": "^7.0.0" 3003 | } 3004 | }, 3005 | "toidentifier": { 3006 | "version": "1.0.0", 3007 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", 3008 | "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" 3009 | }, 3010 | "touch": { 3011 | "version": "3.1.0", 3012 | "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", 3013 | "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", 3014 | "requires": { 3015 | "nopt": "~1.0.10" 3016 | } 3017 | }, 3018 | "type-fest": { 3019 | "version": "0.8.1", 3020 | "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", 3021 | "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" 3022 | }, 3023 | "type-is": { 3024 | "version": "1.6.18", 3025 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 3026 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 3027 | "requires": { 3028 | "media-typer": "0.3.0", 3029 | "mime-types": "~2.1.24" 3030 | } 3031 | }, 3032 | "typedarray-to-buffer": { 3033 | "version": "3.1.5", 3034 | "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", 3035 | "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", 3036 | "requires": { 3037 | "is-typedarray": "^1.0.0" 3038 | } 3039 | }, 3040 | "undefsafe": { 3041 | "version": "2.0.3", 3042 | "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz", 3043 | "integrity": "sha512-nrXZwwXrD/T/JXeygJqdCO6NZZ1L66HrxM/Z7mIq2oPanoN0F1nLx3lwJMu6AwJY69hdixaFQOuoYsMjE5/C2A==", 3044 | "requires": { 3045 | "debug": "^2.2.0" 3046 | }, 3047 | "dependencies": { 3048 | "debug": { 3049 | "version": "2.6.9", 3050 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 3051 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 3052 | "requires": { 3053 | "ms": "2.0.0" 3054 | } 3055 | }, 3056 | "ms": { 3057 | "version": "2.0.0", 3058 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 3059 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 3060 | } 3061 | } 3062 | }, 3063 | "unique-string": { 3064 | "version": "2.0.0", 3065 | "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", 3066 | "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", 3067 | "requires": { 3068 | "crypto-random-string": "^2.0.0" 3069 | } 3070 | }, 3071 | "unpipe": { 3072 | "version": "1.0.0", 3073 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 3074 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 3075 | }, 3076 | "update-notifier": { 3077 | "version": "4.1.3", 3078 | "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-4.1.3.tgz", 3079 | "integrity": "sha512-Yld6Z0RyCYGB6ckIjffGOSOmHXj1gMeE7aROz4MG+XMkmixBX4jUngrGXNYz7wPKBmtoD4MnBa2Anu7RSKht/A==", 3080 | "requires": { 3081 | "boxen": "^4.2.0", 3082 | "chalk": "^3.0.0", 3083 | "configstore": "^5.0.1", 3084 | "has-yarn": "^2.1.0", 3085 | "import-lazy": "^2.1.0", 3086 | "is-ci": "^2.0.0", 3087 | "is-installed-globally": "^0.3.1", 3088 | "is-npm": "^4.0.0", 3089 | "is-yarn-global": "^0.3.0", 3090 | "latest-version": "^5.0.0", 3091 | "pupa": "^2.0.1", 3092 | "semver-diff": "^3.1.1", 3093 | "xdg-basedir": "^4.0.0" 3094 | } 3095 | }, 3096 | "url-parse-lax": { 3097 | "version": "3.0.0", 3098 | "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", 3099 | "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", 3100 | "requires": { 3101 | "prepend-http": "^2.0.0" 3102 | } 3103 | }, 3104 | "utils-merge": { 3105 | "version": "1.0.1", 3106 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 3107 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 3108 | }, 3109 | "vary": { 3110 | "version": "1.1.2", 3111 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 3112 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 3113 | }, 3114 | "widest-line": { 3115 | "version": "3.1.0", 3116 | "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", 3117 | "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", 3118 | "requires": { 3119 | "string-width": "^4.0.0" 3120 | } 3121 | }, 3122 | "wrappy": { 3123 | "version": "1.0.2", 3124 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 3125 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 3126 | }, 3127 | "write-file-atomic": { 3128 | "version": "3.0.3", 3129 | "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", 3130 | "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", 3131 | "requires": { 3132 | "imurmurhash": "^0.1.4", 3133 | "is-typedarray": "^1.0.0", 3134 | "signal-exit": "^3.0.2", 3135 | "typedarray-to-buffer": "^3.1.5" 3136 | } 3137 | }, 3138 | "xdg-basedir": { 3139 | "version": "4.0.0", 3140 | "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", 3141 | "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==" 3142 | } 3143 | } 3144 | } 3145 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "redis-kaboom-rpg", 3 | "version": "1.0.0", 4 | "description": "RPG type game example with Redis and Kaboom.js", 5 | "main": "src/server.js", 6 | "scripts": { 7 | "start": "node src/server.js", 8 | "dev": "node ./node_modules/nodemon/bin/nodemon.js", 9 | "load": "node src/data_loader.js", 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "keywords": [ 13 | "Redis" 14 | ], 15 | "author": "Simon Prickett", 16 | "license": "MIT", 17 | "dependencies": { 18 | "express": "^4.17.1", 19 | "ioredis": "^4.27.5", 20 | "nodemon": "^2.0.7" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Redis Kaboom RPG Game 9 | 10 | 11 |
12 |
13 |
14 |

Redis Kaboom RPG Game

15 |
16 | 17 |
18 |

Source code: https://github.com/redis-developer/redis-kaboom-rpg.

19 |
20 |
21 |
22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /public/sounds/door: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-kaboom-rpg/9626098fc15f3abf7355db7a79561d110fa2fbfa/public/sounds/door -------------------------------------------------------------------------------- /public/sounds/flag: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-kaboom-rpg/9626098fc15f3abf7355db7a79561d110fa2fbfa/public/sounds/flag -------------------------------------------------------------------------------- /public/sounds/key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-kaboom-rpg/9626098fc15f3abf7355db7a79561d110fa2fbfa/public/sounds/key -------------------------------------------------------------------------------- /public/sounds/wall: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-kaboom-rpg/9626098fc15f3abf7355db7a79561d110fa2fbfa/public/sounds/wall -------------------------------------------------------------------------------- /public/sprites/door.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-kaboom-rpg/9626098fc15f3abf7355db7a79561d110fa2fbfa/public/sprites/door.png -------------------------------------------------------------------------------- /public/sprites/flag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-kaboom-rpg/9626098fc15f3abf7355db7a79561d110fa2fbfa/public/sprites/flag.png -------------------------------------------------------------------------------- /public/sprites/key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-kaboom-rpg/9626098fc15f3abf7355db7a79561d110fa2fbfa/public/sprites/key.png -------------------------------------------------------------------------------- /public/sprites/lockeddoor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-kaboom-rpg/9626098fc15f3abf7355db7a79561d110fa2fbfa/public/sprites/lockeddoor.png -------------------------------------------------------------------------------- /public/sprites/original/door.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-kaboom-rpg/9626098fc15f3abf7355db7a79561d110fa2fbfa/public/sprites/original/door.png -------------------------------------------------------------------------------- /public/sprites/original/flag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-kaboom-rpg/9626098fc15f3abf7355db7a79561d110fa2fbfa/public/sprites/original/flag.png -------------------------------------------------------------------------------- /public/sprites/original/key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-kaboom-rpg/9626098fc15f3abf7355db7a79561d110fa2fbfa/public/sprites/original/key.png -------------------------------------------------------------------------------- /public/sprites/original/lockeddoor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-kaboom-rpg/9626098fc15f3abf7355db7a79561d110fa2fbfa/public/sprites/original/lockeddoor.png -------------------------------------------------------------------------------- /public/sprites/original/player.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-kaboom-rpg/9626098fc15f3abf7355db7a79561d110fa2fbfa/public/sprites/original/player.png -------------------------------------------------------------------------------- /public/sprites/original/wall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-kaboom-rpg/9626098fc15f3abf7355db7a79561d110fa2fbfa/public/sprites/original/wall.png -------------------------------------------------------------------------------- /public/sprites/player.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-kaboom-rpg/9626098fc15f3abf7355db7a79561d110fa2fbfa/public/sprites/player.png -------------------------------------------------------------------------------- /public/sprites/wall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-kaboom-rpg/9626098fc15f3abf7355db7a79561d110fa2fbfa/public/sprites/wall.png -------------------------------------------------------------------------------- /public/static/css/game.css: -------------------------------------------------------------------------------- 1 | * { 2 | padding: 0; 3 | margin: 0; 4 | box-sizing: border-box; 5 | } 6 | 7 | body { 8 | background-color: #000000; 9 | height: 100vh; 10 | } 11 | 12 | p { 13 | color: #ffffff; 14 | } 15 | 16 | a:link, a:visited { 17 | color: #ffff00; 18 | } 19 | 20 | .title { 21 | color: #ffffff; 22 | } 23 | 24 | .canvas-holder { 25 | width: 100%; 26 | text-align:center; 27 | margin: auto; 28 | } -------------------------------------------------------------------------------- /public/static/js/game.js: -------------------------------------------------------------------------------- 1 | window.onload = function () { 2 | // Initialize Kaboom... 3 | const k = kaboom({ 4 | global: true, 5 | scale: 3, 6 | clearColor: [0, 0, 0, 1], 7 | canvas: document.getElementById('game'), 8 | width: 180, 9 | height: 180 10 | }); 11 | 12 | // Load the various sprite graphics. 13 | loadRoot('/'); 14 | loadSprite('player', 'sprites/player.png'); 15 | loadSprite('wall', 'sprites/wall.png'); 16 | loadSprite('key', 'sprites/key.png'); 17 | loadSprite('flag', 'sprites/flag.png'); 18 | loadSprite('door', 'sprites/door.png'); 19 | loadSprite('lockeddoor', 'sprites/lockeddoor.png'); 20 | 21 | // Load the various sound effects 22 | loadSound("key", "sounds/key"); 23 | loadSound("wall", "sounds/wall"); 24 | loadSound("flag", "sounds/flag"); 25 | loadSound("door","sounds/door"); 26 | 27 | // Globals to remember which rooms the player found 28 | // keys in and the ID of the game they're playing. 29 | let keysHeld = []; 30 | let gameId; 31 | 32 | // Render a particular room... 33 | scene('play', async (roomNumber) => { 34 | // Get the room details from the server. 35 | const res = await fetch(`/api/room/${gameId}/${roomNumber}`); 36 | const roomDetails = await res.json(); 37 | 38 | let popupMsg = null; 39 | let keysHeldMsg = null; 40 | 41 | // Show a message e.g. one to tell the player how many 42 | // keys they need to open a locked door. 43 | const showMsg = (msg) => { 44 | popupMsg = add([ 45 | text(msg, 6), 46 | pos(width() / 2, 10), 47 | origin('center') 48 | ]); 49 | }; 50 | 51 | // Update the keys held message at the bottom of the screen. 52 | const updateKeysHeld = () => { 53 | if (keysHeldMsg) { 54 | destroy(keysHeldMsg); 55 | } 56 | 57 | keysHeldMsg = add([ 58 | text(`Room ${roomNumber}. Keys held: ${keysHeld.length}`, 6), 59 | pos(80, 150), 60 | origin('center') 61 | ]); 62 | }; 63 | 64 | // Mapping between characters in the room layout and sprites. 65 | const roomConf = { 66 | width: roomDetails.layout[0].length, 67 | height: roomDetails.layout.length, 68 | pos: vec2(20, 20), 69 | '@': [ 70 | sprite('player'), 71 | 'player' 72 | ], 73 | '=': [ 74 | sprite('wall'), 75 | "wall", 76 | solid() 77 | ], 78 | 'k': [ 79 | sprite('key'), 80 | 'key', 81 | solid() 82 | ], 83 | 'f': [ 84 | sprite('flag'), 85 | 'flag', 86 | solid() 87 | ] 88 | }; 89 | 90 | // Mapping for each door, determines whether to show a locked 91 | // or unlocked door... 92 | for (const doorId in roomDetails.doors) { 93 | const door = roomDetails.doors[doorId]; 94 | 95 | roomConf[doorId] = [ 96 | sprite(door.keysRequired > 0 ? 'lockeddoor' : 'door'), 97 | 'door', 98 | // Extra properties to store about this door - need 99 | // these when the player touches it to determine what 100 | // to do then. 101 | { 102 | leadsTo: door.leadsTo, 103 | keysRequired: door.keysRequired, 104 | isEnd: door.isEnd || false 105 | }, 106 | solid() 107 | ]; 108 | } 109 | 110 | addLevel(roomDetails.layout, roomConf); 111 | updateKeysHeld(); 112 | 113 | // Delete any key in this room if the player already collected it. 114 | const keys = get('key'); 115 | if (keys.length > 0 && keysHeld.includes(roomNumber)) { 116 | destroy(keys[0]); 117 | } 118 | 119 | const player = get('player')[0]; 120 | 121 | const directions = { 122 | 'left': vec2(-1, 0), 123 | 'right': vec2(1, 0), 124 | 'up': vec2(0, -1), 125 | 'down': vec2(0, 1) 126 | }; 127 | 128 | // Map key presses to player movement actions. 129 | for (const direction in directions) { 130 | keyPress(direction, () => { 131 | // Destroy any popup message 1/2 a second after 132 | // the player starts to move again. 133 | if (popupMsg) { 134 | wait(0.5, () => { 135 | if (popupMsg) { 136 | destroy(popupMsg); 137 | popupMsg = null; 138 | } 139 | }); 140 | } 141 | }); 142 | keyDown(direction, () => { 143 | // Move the player. 144 | player.move(directions[direction].scale(60)); 145 | }); 146 | } 147 | 148 | 149 | // What to do when the player touches a door. 150 | player.overlaps('door', (d) => { 151 | wait(0.3, () => { 152 | // Does opening this door require more keys than the player holds? 153 | if (d.keysRequired && d.keysRequired > keysHeld.length) { 154 | showMsg(`You need ${d.keysRequired - keysHeld.length} more keys!`); 155 | camShake(10); 156 | } else { 157 | // Does this door lead to the end state, or another room? 158 | play('door'); 159 | if (d.isEnd) { 160 | go('winner'); 161 | } else { 162 | go('play', d.leadsTo); 163 | } 164 | } 165 | }); 166 | }); 167 | 168 | player.overlaps("wall", (e) => { 169 | play("wall"); 170 | }); 171 | 172 | // What to do when the player touches a key. 173 | player.overlaps('key', (k) => { 174 | play("key"); 175 | destroy(k); 176 | showMsg('You got a key!'); 177 | // Remember the player has this key, so we don't 178 | // render it next time they enter this room and 179 | // so we know they can unlock some doors now. 180 | keysHeld.push(roomNumber); 181 | updateKeysHeld(); 182 | }); 183 | 184 | // What to do when the player touches a flag. 185 | player.overlaps('flag', async () => { 186 | play('flag'); 187 | // Go to a random room number and spin the 188 | // camera around and around. 189 | let angle = 0.1; 190 | const timer = setInterval(async () => { 191 | camRot(angle); 192 | angle += 0.1; 193 | 194 | if (angle >= 6.0) { 195 | // Stop spinning and go to the new room. 196 | camRot(0); 197 | clearInterval(timer); 198 | 199 | const res = await fetch('/api/randomroom'); 200 | const roomDetails = await res.json(); 201 | 202 | go('play', roomDetails.room); 203 | } 204 | }, 10); 205 | }); 206 | 207 | // Update the player position etc - run every frame. 208 | player.action(() => { 209 | player.resolve(); 210 | }); 211 | }); 212 | 213 | // Get a new game ID and start a new game. 214 | const newGame = async () => { 215 | const res = await fetch('/api/newgame'); 216 | const newGameResponse = await res.json(); 217 | 218 | gameId = newGameResponse.gameId; 219 | 220 | // New game always starts in room 0. 221 | go('play', 0); 222 | } 223 | 224 | // Display a message telling the player how to start 225 | // a new game. 226 | scene('start', () => { 227 | keysHeld = []; 228 | 229 | add([ 230 | text('press space to begin!', 6), 231 | pos(width() / 2, height() / 2), 232 | origin('center'), 233 | ]); 234 | 235 | keyPress('space', () => { 236 | newGame(); 237 | }); 238 | }); 239 | 240 | // This is the scene for when the player solves the 241 | // puzzle and escapes the maze with all the keys. 242 | scene('winner', async () => { 243 | // Reset for next game. 244 | keysHeld = []; 245 | 246 | // Get the number of times a room was entered and the 247 | // overall elapsed time for this game. 248 | const res = await fetch(`/api/endgame/${gameId}`); 249 | const { roomEntries, elapsedTime } = await res.json(); 250 | 251 | add([ 252 | text(`you escaped in:\n\n${roomEntries} moves.\n\n${elapsedTime} seconds.\n\nspace restarts!`, 6), 253 | pos(width() / 2, height() / 2), 254 | origin('center'), 255 | ]); 256 | 257 | keyPress('space', () => { 258 | newGame(); 259 | }); 260 | }); 261 | 262 | start('start'); 263 | }; -------------------------------------------------------------------------------- /redis_kaboom_game.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-kaboom-rpg/9626098fc15f3abf7355db7a79561d110fa2fbfa/redis_kaboom_game.gif -------------------------------------------------------------------------------- /redis_rpg_map.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-kaboom-rpg/9626098fc15f3abf7355db7a79561d110fa2fbfa/redis_rpg_map.jpg -------------------------------------------------------------------------------- /screenshots/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-kaboom-rpg/9626098fc15f3abf7355db7a79561d110fa2fbfa/screenshots/screenshot1.png -------------------------------------------------------------------------------- /screenshots/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-kaboom-rpg/9626098fc15f3abf7355db7a79561d110fa2fbfa/screenshots/screenshot2.png -------------------------------------------------------------------------------- /src/apis/apis.http: -------------------------------------------------------------------------------- 1 | ### 2 | # Api to get gameIds of active games 3 | # {API} /api/activegames 4 | # {Returns} { gameIds: String[], length: Number } 5 | 6 | GET http://localhost:8080/api/activegames 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/data_loader.js: -------------------------------------------------------------------------------- 1 | const Redis = require('ioredis'); 2 | 3 | const REDIS_HOST = process.env.REDIS_HOST || '127.0.0.1'; 4 | const REDIS_PORT = process.env.REDIS_PORT || 6379; 5 | const REDIS_PASSWORD = process.env.REDIS_PASSWORD; 6 | 7 | const loadGameData = async () => { 8 | // Connect to Redis... 9 | const redis = new Redis({ 10 | host: REDIS_HOST, 11 | port: REDIS_PORT, 12 | password: REDIS_PASSWORD 13 | }); 14 | 15 | // Where we'll store the room data in Redis. 16 | const GAME_MAP_KEY = 'kaboom:rooms'; 17 | 18 | // Load the room data from JSON file. 19 | const gameData = require('../game_map.json'); 20 | 21 | // Delete any previous data in Redis and store the room data 22 | // as a JSON document. 23 | await redis.del(GAME_MAP_KEY); 24 | await redis.call('JSON.SET', GAME_MAP_KEY, '.', JSON.stringify(gameData)); 25 | 26 | console.log('Data loaded!'); 27 | 28 | redis.quit(); 29 | }; 30 | 31 | loadGameData(); 32 | -------------------------------------------------------------------------------- /src/server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const path = require('path'); 3 | const Redis = require('ioredis'); 4 | 5 | // Redis configuration. 6 | const PORT = process.env.PORT||8080; 7 | const REDIS_HOST = process.env.REDIS_HOST || '127.0.0.1'; 8 | const REDIS_PORT = process.env.REDIS_PORT || 6379; 9 | const REDIS_PASSWORD = process.env.REDIS_PASSWORD; 10 | 11 | const app = express(); 12 | 13 | // Connect to Redis. 14 | const redis = new Redis({ 15 | host: REDIS_HOST, 16 | port: REDIS_PORT, 17 | password: REDIS_PASSWORD 18 | }); 19 | 20 | // Keep our Redis keys in a namespace. 21 | const getRedisKeyName = n => `kaboom:${n}`; 22 | 23 | // We'll use this key a lot to get data about rooms, stored 24 | // in Redis as a JSON document. 25 | const ROOM_KEY_NAME = getRedisKeyName('rooms'); 26 | 27 | // Serve the front end statically from the 'public' folder. 28 | app.use(express.static(path.join(__dirname, '../public'))); 29 | 30 | // Start a new game. 31 | app.get('/api/newgame', async (req, res) => { 32 | const gameId = Date.now(); 33 | const gameMovesKey = getRedisKeyName(`moves:${gameId}`); 34 | 35 | // Start a new stream for this game and set a long expiry in case 36 | // the user abandons it. 37 | await redis.xadd(gameMovesKey, '*', 'event', 'start'); 38 | redis.expire(gameMovesKey, 86400); 39 | 40 | // Pick 3 random room numbers to place the keys in for this game. 41 | let keysPlaced = 0; 42 | 43 | // We'll store these in a Redis set, so we'll need a key for that... 44 | const keyLocationsKey = getRedisKeyName(`keylocations:${gameId}`); 45 | 46 | // Figure out how many rooms are available so we know what the range 47 | // of room numbers to pick from is. 48 | const numRooms = await redis.call('JSON.ARRLEN', ROOM_KEY_NAME, '.'); 49 | 50 | do { 51 | const roomNumber = Math.floor(Math.random() * (numRooms - 1)); 52 | await redis.sadd(keyLocationsKey, roomNumber); 53 | keysPlaced = await redis.scard(keyLocationsKey); 54 | } while (keysPlaced < 3); 55 | 56 | // Set a long expiry on the key locations key in case the user 57 | // abandons the game. 58 | redis.expire(keyLocationsKey, 86400); 59 | 60 | console.log(`Started game ${gameId}.`); 61 | 62 | res.json({ gameId: gameId }); 63 | }); 64 | 65 | // Get JSON array of all currently active game IDs. 66 | app.get('/api/activegames', async (req, res) => { 67 | 68 | // Scan through all keys in the stream starting with "kaboom:moves". 69 | const stream = redis.scanStream({ 70 | match: 'kaboom:moves:*' 71 | }); 72 | 73 | const gameIds = []; 74 | 75 | stream.on('data', (keys) => { 76 | // Extract the gameId from the key and append to gameIds array. 77 | keys.forEach((key) => gameIds.push(key.split(':')[2])); 78 | }); 79 | 80 | stream.on('end', () => { 81 | res.status(200).json({ 82 | data: { 83 | gameIds, 84 | length: gameIds.length 85 | }, 86 | status: 'success', 87 | }) 88 | }) 89 | }); 90 | 91 | // Get details for a specified room number from Redis. 92 | app.get('/api/room/:gameId/:roomNumber', async (req, res) => { 93 | const { gameId, roomNumber } = req.params; 94 | 95 | const minRoomNumber = 0; 96 | const roomCount = JSON.parse(await redis.call('JSON.ARRLEN', ROOM_KEY_NAME, '.')); 97 | const maxRoomNumber = roomCount - 1; 98 | const roomNumberInteger = parseInt(roomNumber); 99 | 100 | if (roomNumberInteger < minRoomNumber || roomNumberInteger > maxRoomNumber) { 101 | console.log(`/api/room/:gameId/:roomNumber called with invalid room number of ${roomNumber}`) 102 | return res.status(400).send('Invalid room number') 103 | } 104 | 105 | // Store this movement in Redis. 106 | redis.xadd(getRedisKeyName(`moves:${gameId}`), '*', 'roomEntry', roomNumber); 107 | 108 | // Get the room details for this room. 109 | const roomDetails = JSON.parse(await redis.call('JSON.GET', ROOM_KEY_NAME, `[${roomNumber}]`)); 110 | 111 | // Does this room have a key in it for this specific game? 112 | const roomHasKey = await redis.sismember(getRedisKeyName(`keylocations:${gameId}`), roomNumber); 113 | 114 | if (roomHasKey === 0) { 115 | // No key here, so remove the 'k' placeholder from the room map. 116 | // String.replaceAll not available until Node 15... 117 | roomDetails.layout = roomDetails.layout.map(row => row.split('k').join(' ')); 118 | } 119 | 120 | res.json(roomDetails); 121 | }); 122 | 123 | // Get details for a specified room number. 124 | app.get('/api/randomroom/', async (req, res) => { 125 | // Figure out how many rooms are available. 126 | const numRooms = await redis.call('JSON.ARRLEN', ROOM_KEY_NAME, '.'); 127 | 128 | // Get a random number from room 0 to room (numRooms - 1). 129 | res.json({ room: Math.floor(Math.random() * (numRooms - 1)) }); 130 | }); 131 | 132 | // End the current game and get the stats. 133 | app.get('/api/endgame/:gameId', async (req, res) => { 134 | const { gameId } = req.params; 135 | 136 | // Check the format of the gameId in the request 137 | if (isNaN(gameId) || !/^[1-9]+[0-9]*$/.test(gameId)) { 138 | console.log(`/api/endgame/:gameId called with invalid gameId: ${gameId}.`); 139 | return res.status(400).send('Invalid gameId specified, should be a whole number greater than zero'); 140 | } 141 | 142 | // Check for gameIds that could only exist in the future 143 | if (gameId > Date.now()) { 144 | console.log(`/api/endgame/:gameId called with future gameId: ${gameId}.`); 145 | return res.status(400).send('Time travelling not allowed, this game hasn\'t started yet!'); 146 | } 147 | 148 | const gameMovesKey = getRedisKeyName(`moves:${gameId}`); 149 | 150 | // Does this gameMovesKey (still) exist? 151 | const gameMovesKeyExists = await redis.exists(gameMovesKey); 152 | if (!gameMovesKeyExists) { 153 | console.log(`Request for invalid or completed gameId: ${gameId}.`); 154 | return res.status(400).send('Game not found, invalid gameId or game has ended'); 155 | } 156 | 157 | // How many times did they enter a room (length of stream minus 1 for 158 | // the start event). 159 | const roomEntries = await redis.xlen(gameMovesKey) - 1; 160 | 161 | // Get the first and last entries in the stream, and the overall 162 | // elapsed game time will be the difference between the timestamp 163 | // components of their IDs. 164 | const streamStartAndEnd = await Promise.all([ 165 | redis.xrange(gameMovesKey, '-', '+', 'COUNT', 1), 166 | redis.xrevrange(gameMovesKey, '+', '-', 'COUNT', 1), 167 | ]); 168 | 169 | // Parse out the timestamps from the Redis return values. 170 | const startTimeStamp = parseInt(streamStartAndEnd[0][0][0].split('-')[0], 10); 171 | const endTimeStamp = parseInt(streamStartAndEnd[1][0][0].split('-')[0], 10); 172 | const elapsedTime = Math.floor((endTimeStamp - startTimeStamp) / 1000); 173 | 174 | // Tidy up, delete the stream and key locations keys as 175 | // we don't need them any more. 176 | redis.del(gameMovesKey); 177 | redis.del(getRedisKeyName(`keylocations:${gameId}`)); 178 | 179 | console.log(`Game ${gameId} has ended.`); 180 | 181 | res.json({ roomEntries, elapsedTime }); 182 | }); 183 | 184 | // Start the server. 185 | app.listen(PORT, () => { 186 | console.log(`Redis Kaboom RPG server listening on port ${PORT}, Redis at ${REDIS_HOST}:${REDIS_PORT}.`); 187 | }); 188 | --------------------------------------------------------------------------------