├── .github └── workflows │ └── ci.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── assets ├── socket.io-cluster-redis.excalidraw ├── socket.io-cluster-redis.png ├── socket.io-cluster.excalidraw └── socket.io-cluster.png ├── index.d.ts ├── index.js ├── package-lock.json ├── package.json └── test ├── fixtures ├── connection.js ├── cors.js ├── failing.js └── util.js └── index.js /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | pull_request: 6 | schedule: 7 | - cron: '0 0 * * 0' 8 | 9 | jobs: 10 | test-node: 11 | runs-on: ubuntu-latest 12 | timeout-minutes: 10 13 | 14 | strategy: 15 | matrix: 16 | node-version: 17 | - 10 18 | - 18 19 | 20 | steps: 21 | - name: Checkout repository 22 | uses: actions/checkout@v3 23 | 24 | - name: Use Node.js ${{ matrix.node-version }} 25 | uses: actions/setup-node@v3 26 | with: 27 | node-version: ${{ matrix.node-version }} 28 | 29 | - name: Install dependencies 30 | run: npm ci 31 | 32 | - name: Run tests 33 | run: npm test 34 | env: 35 | CI: true 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .nyc_output 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # History 2 | 3 | - [1.0.4](#104-2023-08-12) (Aug 2023) 4 | - [1.0.3](#103-2023-02-24) (Feb 2023) 5 | - [1.0.2](#102-2022-11-01) (Nov 2022) 6 | - [1.0.1](#101-2021-07-01) (Jul 2021) 7 | - [1.0.0](#100-2021-01-25) (Jan 2021) 8 | 9 | # Release notes 10 | 11 | ## [1.0.4](https://github.com/socketio/socket.io-sticky/compare/1.0.3...1.0.4) (2023-08-12) 12 | 13 | 14 | ### Bug Fixes 15 | 16 | * cleanup sid to worker map on worker exit ([4b430e7](https://github.com/socketio/socket.io-sticky/commit/4b430e7e9383006e4641e40e8a5082156ed862a4)) 17 | * properly detect requests without body ([ac7bb8e](https://github.com/socketio/socket.io-sticky/commit/ac7bb8e851d6a458736281568ed6946c512e4251)) 18 | 19 | 20 | 21 | ## [1.0.3](https://github.com/socketio/socket.io-sticky/compare/1.0.2...1.0.3) (2023-02-24) 22 | 23 | 24 | ### Bug Fixes 25 | 26 | * handle case with no available worker ([e8b4203](https://github.com/socketio/socket.io-sticky/commit/e8b4203d18fc9601e05af3457baba49fafdb15f0)) 27 | 28 | 29 | 30 | ## [1.0.2](https://github.com/socketio/socket.io-sticky/compare/1.0.1...1.0.2) (2022-11-01) 31 | 32 | 33 | ### Bug Fixes 34 | 35 | * properly handle large request body ([a124d0b](https://github.com/socketio/socket.io-sticky/commit/a124d0beb5c1b78be5b75f10153859a9e4672862)) 36 | 37 | 38 | 39 | ## [1.0.1](https://github.com/socketio/socket.io-sticky/compare/1.0.0...1.0.1) (2021-07-01) 40 | 41 | 42 | ### Bug Fixes 43 | 44 | * check if the socket exists in the worker ([7069fbc](https://github.com/socketio/socket.io-sticky/commit/7069fbc5bfbc845556f4a5cb8fd8240a1ef24b0e)) 45 | 46 | 47 | ## 1.0.0 (2021-01-25) 48 | 49 | Initial release 50 | 51 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2021 Damien Arrachequesne (@darrachequesne) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | 'Software'), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sticky sessions for Socket.IO 2 | 3 | A simple and performant way to use [Socket.IO](https://socket.io/) within a [cluster](http://nodejs.org/docs/latest/api/cluster.html). 4 | 5 | Unlike other packages like [sticky-session](https://github.com/indutny/sticky-session), the routing is based on the session ID (the `sid` query parameter). 6 | 7 | ![Cluster diagram](./assets/socket.io-cluster.png) 8 | 9 | See also: 10 | 11 | - [sticky-session](https://github.com/indutny/sticky-session) (routing based on `connection.remoteAddress`) 12 | - [socketio-sticky-session](https://github.com/wzrdtales/socket-io-sticky-session) (routing based on the `x-forwarded-for` header) 13 | 14 | **Table of contents** 15 | 16 | - [Installation](#installation) 17 | - [Usage](#usage) 18 | - [How it works](#how-it-works) 19 | - [Notes](#notes) 20 | - [License](#license) 21 | 22 | ## Installation 23 | 24 | ``` 25 | npm install @socket.io/sticky 26 | ``` 27 | 28 | ## Usage 29 | 30 | ```js 31 | const cluster = require("cluster"); 32 | const http = require("http"); 33 | const { Server } = require("socket.io"); 34 | const numCPUs = require("os").cpus().length; 35 | const { setupMaster, setupWorker } = require("@socket.io/sticky"); 36 | const { createAdapter, setupPrimary } = require("@socket.io/cluster-adapter"); 37 | 38 | if (cluster.isMaster) { 39 | console.log(`Master ${process.pid} is running`); 40 | 41 | const httpServer = http.createServer(); 42 | 43 | setupMaster(httpServer, { 44 | loadBalancingMethod: "least-connection", // either "random", "round-robin" or "least-connection" 45 | }); 46 | 47 | setupPrimary(); 48 | 49 | httpServer.listen(3000); 50 | 51 | for (let i = 0; i < numCPUs; i++) { 52 | cluster.fork(); 53 | } 54 | 55 | cluster.on("exit", (worker) => { 56 | console.log(`Worker ${worker.process.pid} died`); 57 | cluster.fork(); 58 | }); 59 | } else { 60 | console.log(`Worker ${process.pid} started`); 61 | 62 | const httpServer = http.createServer(); 63 | const io = new Server(httpServer); 64 | io.adapter(createAdapter()); 65 | setupWorker(io); 66 | 67 | io.on("connection", (socket) => { 68 | /* ... */ 69 | }); 70 | } 71 | ``` 72 | 73 | ## How it works 74 | 75 | The first HTTP request (without `sid` query parameter) is forwarded to a random worker (based on the `loadBalancingMethod` option). 76 | 77 | The underlying Engine.IO server creates a new session and emits a `connection` event with the session ID. The worker sends this session ID to the master, which stores the relationship between the worker ID and the session ID. 78 | 79 | For subsequent requests, the `sid` query parameter is extracted by the master process, which then forwards the handle to the right worker. 80 | 81 | ## Notes 82 | 83 | - this package is not needed if you only use WebSockets (which might be a sensible choice as of 2021) 84 | 85 | ```js 86 | // client-side 87 | const socket = io({ 88 | transports: ["websocket"] // HTTP long-polling is disabled 89 | }); 90 | ``` 91 | 92 | - in a multi-server setup, you will need to use another adapter, like the [Redis adapter](https://socket.io/docs/v4/redis-adapter/) 93 | 94 | ![Cluster diagram with Redis](./assets/socket.io-cluster-redis.png) 95 | 96 | - this module is not compatible with an HTTPS server 97 | 98 | For more information, please see [this issue](https://github.com/socketio/socket.io-sticky/issues/3). 99 | 100 | ## License 101 | 102 | MIT 103 | -------------------------------------------------------------------------------- /assets/socket.io-cluster-redis.excalidraw: -------------------------------------------------------------------------------- 1 | { 2 | "type": "excalidraw", 3 | "version": 2, 4 | "source": "https://excalidraw.com", 5 | "elements": [ 6 | { 7 | "type": "text", 8 | "version": 264, 9 | "versionNonce": 1085373869, 10 | "isDeleted": false, 11 | "id": "5hUB5ALUlsn26W0PzU4fM", 12 | "fillStyle": "hachure", 13 | "strokeWidth": 1, 14 | "strokeStyle": "solid", 15 | "roughness": 1, 16 | "opacity": 100, 17 | "angle": 0, 18 | "x": 639, 19 | "y": -20.5, 20 | "strokeColor": "#000000", 21 | "backgroundColor": "transparent", 22 | "width": 78, 23 | "height": 26, 24 | "seed": 28708370, 25 | "groupIds": [], 26 | "strokeSharpness": "sharp", 27 | "boundElementIds": [ 28 | "_wBO22vaQplcoKyBXbWRC" 29 | ], 30 | "fontSize": 20, 31 | "fontFamily": 1, 32 | "text": "worker 1", 33 | "baseline": 18, 34 | "textAlign": "center", 35 | "verticalAlign": "middle" 36 | }, 37 | { 38 | "type": "rectangle", 39 | "version": 178, 40 | "versionNonce": 1203989197, 41 | "isDeleted": false, 42 | "id": "lmQ4o4New7xuXQLwavuSn", 43 | "fillStyle": "hachure", 44 | "strokeWidth": 1, 45 | "strokeStyle": "solid", 46 | "roughness": 1, 47 | "opacity": 100, 48 | "angle": 0, 49 | "x": 428, 50 | "y": -101, 51 | "strokeColor": "#000000", 52 | "backgroundColor": "transparent", 53 | "width": 345.00000000000006, 54 | "height": 311, 55 | "seed": 1594950354, 56 | "groupIds": [], 57 | "strokeSharpness": "sharp", 58 | "boundElementIds": [ 59 | "_wBO22vaQplcoKyBXbWRC", 60 | "BZVwnsrGk9G-X87ZHkh-6", 61 | "eU1gfEXnHSjxc-pEgv43A", 62 | "XZpY0rnxgeDlxu5b8fgRQ", 63 | "4mjxZzapHnLuRx7KU2JeH", 64 | "zkLKnuWAYxJGCPBZp9UAB" 65 | ] 66 | }, 67 | { 68 | "type": "text", 69 | "version": 113, 70 | "versionNonce": 1651458061, 71 | "isDeleted": false, 72 | "id": "ZQsZmj4NaTubBHMkVG2dl", 73 | "fillStyle": "hachure", 74 | "strokeWidth": 1, 75 | "strokeStyle": "solid", 76 | "roughness": 1, 77 | "opacity": 100, 78 | "angle": 0, 79 | "x": 447, 80 | "y": -90, 81 | "strokeColor": "#000000", 82 | "backgroundColor": "transparent", 83 | "width": 66, 84 | "height": 26, 85 | "seed": 126533902, 86 | "groupIds": [], 87 | "strokeSharpness": "sharp", 88 | "boundElementIds": [], 89 | "fontSize": 20, 90 | "fontFamily": 1, 91 | "text": "Host A", 92 | "baseline": 18, 93 | "textAlign": "left", 94 | "verticalAlign": "top" 95 | }, 96 | { 97 | "type": "rectangle", 98 | "version": 222, 99 | "versionNonce": 117774787, 100 | "isDeleted": false, 101 | "id": "RRrk3Vsl-pM8Z1r8Fj3Vu", 102 | "fillStyle": "hachure", 103 | "strokeWidth": 1, 104 | "strokeStyle": "solid", 105 | "roughness": 1, 106 | "opacity": 100, 107 | "angle": 0, 108 | "x": 626.5, 109 | "y": -36, 110 | "strokeColor": "#000000", 111 | "backgroundColor": "transparent", 112 | "width": 129, 113 | "height": 56, 114 | "seed": 1013161166, 115 | "groupIds": [], 116 | "strokeSharpness": "sharp", 117 | "boundElementIds": [ 118 | "use4Bp2hbb77Fq5njtwBi" 119 | ] 120 | }, 121 | { 122 | "type": "diamond", 123 | "version": 349, 124 | "versionNonce": 2102462467, 125 | "isDeleted": false, 126 | "id": "k0pJTVL4F3HHsfRPlE-gO", 127 | "fillStyle": "hachure", 128 | "strokeWidth": 2, 129 | "strokeStyle": "solid", 130 | "roughness": 0, 131 | "opacity": 100, 132 | "angle": 0, 133 | "x": 733, 134 | "y": -14, 135 | "strokeColor": "#000000", 136 | "backgroundColor": "transparent", 137 | "width": 16, 138 | "height": 18, 139 | "seed": 1260350118, 140 | "groupIds": [], 141 | "strokeSharpness": "sharp", 142 | "boundElementIds": [ 143 | "Sp9AvxDh8gwRvSC53VFKe", 144 | "XZpY0rnxgeDlxu5b8fgRQ" 145 | ] 146 | }, 147 | { 148 | "type": "ellipse", 149 | "version": 110, 150 | "versionNonce": 44665507, 151 | "isDeleted": false, 152 | "id": "EQmjbilyrf3OcSwGbMZrg", 153 | "fillStyle": "hachure", 154 | "strokeWidth": 2, 155 | "strokeStyle": "solid", 156 | "roughness": 0, 157 | "opacity": 100, 158 | "angle": 0, 159 | "x": 873, 160 | "y": 197, 161 | "strokeColor": "#000000", 162 | "backgroundColor": "transparent", 163 | "width": 92, 164 | "height": 81, 165 | "seed": 1885795942, 166 | "groupIds": [], 167 | "strokeSharpness": "sharp", 168 | "boundElementIds": [ 169 | "xDobZ6graJnZZP8g59wJ4", 170 | "eU1gfEXnHSjxc-pEgv43A", 171 | "4mjxZzapHnLuRx7KU2JeH", 172 | "XZpY0rnxgeDlxu5b8fgRQ", 173 | "CKKC2fK8c7VggdJKNATSf", 174 | "H6kPRLfMhpJHuNpq28DBh" 175 | ] 176 | }, 177 | { 178 | "type": "text", 179 | "version": 55, 180 | "versionNonce": 320845955, 181 | "isDeleted": false, 182 | "id": "wV6Y3XyIP5TbX50EF6xs6", 183 | "fillStyle": "hachure", 184 | "strokeWidth": 2, 185 | "strokeStyle": "solid", 186 | "roughness": 0, 187 | "opacity": 100, 188 | "angle": 0, 189 | "x": 893.5, 190 | "y": 224.5, 191 | "strokeColor": "#000000", 192 | "backgroundColor": "transparent", 193 | "width": 51, 194 | "height": 26, 195 | "seed": 1433614630, 196 | "groupIds": [], 197 | "strokeSharpness": "sharp", 198 | "boundElementIds": [], 199 | "fontSize": 20, 200 | "fontFamily": 1, 201 | "text": "Redis", 202 | "baseline": 18, 203 | "textAlign": "center", 204 | "verticalAlign": "middle" 205 | }, 206 | { 207 | "type": "arrow", 208 | "version": 237, 209 | "versionNonce": 1968322851, 210 | "isDeleted": false, 211 | "id": "eU1gfEXnHSjxc-pEgv43A", 212 | "fillStyle": "hachure", 213 | "strokeWidth": 2, 214 | "strokeStyle": "solid", 215 | "roughness": 0, 216 | "opacity": 100, 217 | "angle": 0, 218 | "x": 782, 219 | "y": 160, 220 | "strokeColor": "#000000", 221 | "backgroundColor": "transparent", 222 | "width": 79, 223 | "height": 50, 224 | "seed": 1145880934, 225 | "groupIds": [], 226 | "strokeSharpness": "round", 227 | "boundElementIds": [], 228 | "startBinding": { 229 | "elementId": "lmQ4o4New7xuXQLwavuSn", 230 | "focus": -0.03541452449843372, 231 | "gap": 9 232 | }, 233 | "endBinding": { 234 | "elementId": "EQmjbilyrf3OcSwGbMZrg", 235 | "focus": -0.18462564038762336, 236 | "gap": 19.293806306612936 237 | }, 238 | "points": [ 239 | [ 240 | 0, 241 | 0 242 | ], 243 | [ 244 | 79, 245 | 50 246 | ] 247 | ], 248 | "lastCommittedPoint": null, 249 | "startArrowhead": "arrow", 250 | "endArrowhead": "arrow" 251 | }, 252 | { 253 | "type": "text", 254 | "version": 302, 255 | "versionNonce": 1588664205, 256 | "isDeleted": false, 257 | "id": "qfQdcJHnwYnCMtLCV51X8", 258 | "fillStyle": "hachure", 259 | "strokeWidth": 1, 260 | "strokeStyle": "solid", 261 | "roughness": 1, 262 | "opacity": 100, 263 | "angle": 0, 264 | "x": 633, 265 | "y": 48.5, 266 | "strokeColor": "#000000", 267 | "backgroundColor": "transparent", 268 | "width": 90, 269 | "height": 26, 270 | "seed": 1535426147, 271 | "groupIds": [], 272 | "strokeSharpness": "sharp", 273 | "boundElementIds": [ 274 | "_wBO22vaQplcoKyBXbWRC", 275 | "2DIFacJXJtC5QIuMuo3pK" 276 | ], 277 | "fontSize": 20, 278 | "fontFamily": 1, 279 | "text": "worker 2", 280 | "baseline": 18, 281 | "textAlign": "center", 282 | "verticalAlign": "middle" 283 | }, 284 | { 285 | "type": "rectangle", 286 | "version": 255, 287 | "versionNonce": 1916253123, 288 | "isDeleted": false, 289 | "id": "IRd1nPQbv0PQdJQn_yLOs", 290 | "fillStyle": "hachure", 291 | "strokeWidth": 1, 292 | "strokeStyle": "solid", 293 | "roughness": 1, 294 | "opacity": 100, 295 | "angle": 0, 296 | "x": 626.5, 297 | "y": 33, 298 | "strokeColor": "#000000", 299 | "backgroundColor": "transparent", 300 | "width": 129, 301 | "height": 56, 302 | "seed": 452398413, 303 | "groupIds": [], 304 | "strokeSharpness": "sharp", 305 | "boundElementIds": [ 306 | "2DIFacJXJtC5QIuMuo3pK", 307 | "NhqDM6wVMhgbRvXrQJQge" 308 | ] 309 | }, 310 | { 311 | "type": "diamond", 312 | "version": 380, 313 | "versionNonce": 891330029, 314 | "isDeleted": false, 315 | "id": "tnpfCYwrVzd694_kUfzPn", 316 | "fillStyle": "hachure", 317 | "strokeWidth": 2, 318 | "strokeStyle": "solid", 319 | "roughness": 0, 320 | "opacity": 100, 321 | "angle": 0, 322 | "x": 733, 323 | "y": 55, 324 | "strokeColor": "#000000", 325 | "backgroundColor": "transparent", 326 | "width": 16, 327 | "height": 18, 328 | "seed": 2110061059, 329 | "groupIds": [], 330 | "strokeSharpness": "sharp", 331 | "boundElementIds": [ 332 | "Sp9AvxDh8gwRvSC53VFKe", 333 | "4mjxZzapHnLuRx7KU2JeH" 334 | ] 335 | }, 336 | { 337 | "type": "text", 338 | "version": 287, 339 | "versionNonce": 556942179, 340 | "isDeleted": false, 341 | "id": "ENOSqQ4visNbCN7ZMZwxP", 342 | "fillStyle": "hachure", 343 | "strokeWidth": 1, 344 | "strokeStyle": "solid", 345 | "roughness": 1, 346 | "opacity": 100, 347 | "angle": 0, 348 | "x": 634.5, 349 | "y": 114.5, 350 | "strokeColor": "#000000", 351 | "backgroundColor": "transparent", 352 | "width": 89, 353 | "height": 26, 354 | "seed": 1916984429, 355 | "groupIds": [], 356 | "strokeSharpness": "sharp", 357 | "boundElementIds": [ 358 | "_wBO22vaQplcoKyBXbWRC" 359 | ], 360 | "fontSize": 20, 361 | "fontFamily": 1, 362 | "text": "worker 3", 363 | "baseline": 18, 364 | "textAlign": "center", 365 | "verticalAlign": "middle" 366 | }, 367 | { 368 | "type": "rectangle", 369 | "version": 240, 370 | "versionNonce": 1955616845, 371 | "isDeleted": false, 372 | "id": "IqdB8EO7s50UY1EU9TVVP", 373 | "fillStyle": "hachure", 374 | "strokeWidth": 1, 375 | "strokeStyle": "solid", 376 | "roughness": 1, 377 | "opacity": 100, 378 | "angle": 0, 379 | "x": 627.5, 380 | "y": 99, 381 | "strokeColor": "#000000", 382 | "backgroundColor": "transparent", 383 | "width": 129, 384 | "height": 56, 385 | "seed": 1832463587, 386 | "groupIds": [], 387 | "strokeSharpness": "sharp", 388 | "boundElementIds": [ 389 | "NhqDM6wVMhgbRvXrQJQge" 390 | ] 391 | }, 392 | { 393 | "type": "diamond", 394 | "version": 371, 395 | "versionNonce": 364044397, 396 | "isDeleted": false, 397 | "id": "zCwNorsYWY3fLtRl9zxB1", 398 | "fillStyle": "hachure", 399 | "strokeWidth": 2, 400 | "strokeStyle": "solid", 401 | "roughness": 0, 402 | "opacity": 100, 403 | "angle": 0, 404 | "x": 734, 405 | "y": 121, 406 | "strokeColor": "#000000", 407 | "backgroundColor": "transparent", 408 | "width": 16, 409 | "height": 18, 410 | "seed": 2145830605, 411 | "groupIds": [], 412 | "strokeSharpness": "sharp", 413 | "boundElementIds": [ 414 | "Sp9AvxDh8gwRvSC53VFKe", 415 | "eU1gfEXnHSjxc-pEgv43A" 416 | ] 417 | }, 418 | { 419 | "type": "text", 420 | "version": 19, 421 | "versionNonce": 1368196771, 422 | "isDeleted": false, 423 | "id": "D8P7Bclj4xdmqfd3xlM9w", 424 | "fillStyle": "hachure", 425 | "strokeWidth": 1, 426 | "strokeStyle": "solid", 427 | "roughness": 1, 428 | "opacity": 100, 429 | "angle": 0, 430 | "x": 476, 431 | "y": 45, 432 | "strokeColor": "#000000", 433 | "backgroundColor": "transparent", 434 | "width": 67, 435 | "height": 26, 436 | "seed": 415223619, 437 | "groupIds": [], 438 | "strokeSharpness": "sharp", 439 | "boundElementIds": [], 440 | "fontSize": 20, 441 | "fontFamily": 1, 442 | "text": "master", 443 | "baseline": 18, 444 | "textAlign": "center", 445 | "verticalAlign": "middle" 446 | }, 447 | { 448 | "type": "rectangle", 449 | "version": 251, 450 | "versionNonce": 514222349, 451 | "isDeleted": false, 452 | "id": "SlTRvTrZRavsPt44aEe3R", 453 | "fillStyle": "hachure", 454 | "strokeWidth": 1, 455 | "strokeStyle": "solid", 456 | "roughness": 1, 457 | "opacity": 100, 458 | "angle": 0, 459 | "x": 442.5, 460 | "y": 32, 461 | "strokeColor": "#000000", 462 | "backgroundColor": "transparent", 463 | "width": 129, 464 | "height": 56, 465 | "seed": 813326061, 466 | "groupIds": [], 467 | "strokeSharpness": "sharp", 468 | "boundElementIds": [ 469 | "use4Bp2hbb77Fq5njtwBi", 470 | "2DIFacJXJtC5QIuMuo3pK", 471 | "NhqDM6wVMhgbRvXrQJQge" 472 | ] 473 | }, 474 | { 475 | "type": "arrow", 476 | "version": 29, 477 | "versionNonce": 1644165603, 478 | "isDeleted": false, 479 | "id": "use4Bp2hbb77Fq5njtwBi", 480 | "fillStyle": "hachure", 481 | "strokeWidth": 1, 482 | "strokeStyle": "solid", 483 | "roughness": 1, 484 | "opacity": 100, 485 | "angle": 0, 486 | "x": 581, 487 | "y": 53, 488 | "strokeColor": "#000000", 489 | "backgroundColor": "transparent", 490 | "width": 34, 491 | "height": 48, 492 | "seed": 1423358221, 493 | "groupIds": [], 494 | "strokeSharpness": "round", 495 | "boundElementIds": [], 496 | "startBinding": { 497 | "elementId": "SlTRvTrZRavsPt44aEe3R", 498 | "focus": 0.8186758893280632, 499 | "gap": 9.5 500 | }, 501 | "endBinding": { 502 | "elementId": "RRrk3Vsl-pM8Z1r8Fj3Vu", 503 | "focus": 0.7919960474308301, 504 | "gap": 11.5 505 | }, 506 | "points": [ 507 | [ 508 | 0, 509 | 0 510 | ], 511 | [ 512 | 34, 513 | -48 514 | ] 515 | ], 516 | "lastCommittedPoint": null, 517 | "startArrowhead": null, 518 | "endArrowhead": "arrow" 519 | }, 520 | { 521 | "type": "arrow", 522 | "version": 85, 523 | "versionNonce": 964643277, 524 | "isDeleted": false, 525 | "id": "2DIFacJXJtC5QIuMuo3pK", 526 | "fillStyle": "hachure", 527 | "strokeWidth": 1, 528 | "strokeStyle": "solid", 529 | "roughness": 1, 530 | "opacity": 100, 531 | "angle": 0, 532 | "x": 583.706318583712, 533 | "y": 59.504354361072174, 534 | "strokeColor": "#000000", 535 | "backgroundColor": "transparent", 536 | "width": 32.383044731616906, 537 | "height": 0.6793771122768604, 538 | "seed": 1764672259, 539 | "groupIds": [], 540 | "strokeSharpness": "round", 541 | "boundElementIds": [], 542 | "startBinding": { 543 | "elementId": "SlTRvTrZRavsPt44aEe3R", 544 | "focus": -0.07170941580148099, 545 | "gap": 12.206318583712005 546 | }, 547 | "endBinding": { 548 | "elementId": "IRd1nPQbv0PQdJQn_yLOs", 549 | "focus": -0.025731879981101647, 550 | "gap": 10.41063668467109 551 | }, 552 | "points": [ 553 | [ 554 | 0, 555 | 0 556 | ], 557 | [ 558 | 32.383044731616906, 559 | 0.6793771122768604 560 | ] 561 | ], 562 | "lastCommittedPoint": null, 563 | "startArrowhead": null, 564 | "endArrowhead": "arrow" 565 | }, 566 | { 567 | "type": "arrow", 568 | "version": 140, 569 | "versionNonce": 407684483, 570 | "isDeleted": false, 571 | "id": "NhqDM6wVMhgbRvXrQJQge", 572 | "fillStyle": "hachure", 573 | "strokeWidth": 1, 574 | "strokeStyle": "solid", 575 | "roughness": 1, 576 | "opacity": 100, 577 | "angle": 0, 578 | "x": 585.8084776341915, 579 | "y": 71.66031144386157, 580 | "strokeColor": "#000000", 581 | "backgroundColor": "transparent", 582 | "width": 32.40004203654814, 583 | "height": 43.76823068360791, 584 | "seed": 1285959341, 585 | "groupIds": [], 586 | "strokeSharpness": "round", 587 | "boundElementIds": [], 588 | "startBinding": { 589 | "elementId": "SlTRvTrZRavsPt44aEe3R", 590 | "focus": -0.8234063011749806, 591 | "gap": 14.30847763419149 592 | }, 593 | "endBinding": { 594 | "elementId": "IqdB8EO7s50UY1EU9TVVP", 595 | "focus": -0.7653120403381738, 596 | "gap": 9.291480329260366 597 | }, 598 | "points": [ 599 | [ 600 | 0, 601 | 0 602 | ], 603 | [ 604 | 32.40004203654814, 605 | 43.76823068360791 606 | ] 607 | ], 608 | "lastCommittedPoint": null, 609 | "startArrowhead": null, 610 | "endArrowhead": "arrow" 611 | }, 612 | { 613 | "type": "arrow", 614 | "version": 301, 615 | "versionNonce": 247399085, 616 | "isDeleted": false, 617 | "id": "4mjxZzapHnLuRx7KU2JeH", 618 | "fillStyle": "hachure", 619 | "strokeWidth": 2, 620 | "strokeStyle": "solid", 621 | "roughness": 0, 622 | "opacity": 100, 623 | "angle": 0, 624 | "x": 782.5, 625 | "y": 93.5, 626 | "strokeColor": "#000000", 627 | "backgroundColor": "transparent", 628 | "width": 95, 629 | "height": 105, 630 | "seed": 1192703469, 631 | "groupIds": [], 632 | "strokeSharpness": "round", 633 | "boundElementIds": [], 634 | "startBinding": { 635 | "elementId": "lmQ4o4New7xuXQLwavuSn", 636 | "focus": -0.4684506613957731, 637 | "gap": 9.5 638 | }, 639 | "endBinding": { 640 | "elementId": "EQmjbilyrf3OcSwGbMZrg", 641 | "focus": -0.10566589454963482, 642 | "gap": 13.70698078356314 643 | }, 644 | "points": [ 645 | [ 646 | 0, 647 | 0 648 | ], 649 | [ 650 | 95, 651 | 105 652 | ] 653 | ], 654 | "lastCommittedPoint": null, 655 | "startArrowhead": "arrow", 656 | "endArrowhead": "arrow" 657 | }, 658 | { 659 | "type": "arrow", 660 | "version": 351, 661 | "versionNonce": 356767395, 662 | "isDeleted": false, 663 | "id": "XZpY0rnxgeDlxu5b8fgRQ", 664 | "fillStyle": "hachure", 665 | "strokeWidth": 2, 666 | "strokeStyle": "solid", 667 | "roughness": 0, 668 | "opacity": 100, 669 | "angle": 0, 670 | "x": 791.5, 671 | "y": 15, 672 | "strokeColor": "#000000", 673 | "backgroundColor": "transparent", 674 | "width": 111, 675 | "height": 169, 676 | "seed": 1996215427, 677 | "groupIds": [], 678 | "strokeSharpness": "round", 679 | "boundElementIds": [], 680 | "startBinding": { 681 | "elementId": "lmQ4o4New7xuXQLwavuSn", 682 | "focus": -0.789940318445263, 683 | "gap": 18.5 684 | }, 685 | "endBinding": { 686 | "elementId": "EQmjbilyrf3OcSwGbMZrg", 687 | "focus": 0.35077038369679053, 688 | "gap": 15.061067030244878 689 | }, 690 | "points": [ 691 | [ 692 | 0, 693 | 0 694 | ], 695 | [ 696 | 111, 697 | 169 698 | ] 699 | ], 700 | "lastCommittedPoint": null, 701 | "startArrowhead": "arrow", 702 | "endArrowhead": "arrow" 703 | }, 704 | { 705 | "type": "text", 706 | "version": 321, 707 | "versionNonce": 1856436877, 708 | "isDeleted": false, 709 | "id": "-t5tOJW9p_LjgKO1rENdv", 710 | "fillStyle": "hachure", 711 | "strokeWidth": 1, 712 | "strokeStyle": "solid", 713 | "roughness": 1, 714 | "opacity": 100, 715 | "angle": 0, 716 | "x": 640.5, 717 | "y": 307, 718 | "strokeColor": "#000000", 719 | "backgroundColor": "transparent", 720 | "width": 78, 721 | "height": 26, 722 | "seed": 1081021827, 723 | "groupIds": [], 724 | "strokeSharpness": "sharp", 725 | "boundElementIds": [ 726 | "_wBO22vaQplcoKyBXbWRC" 727 | ], 728 | "fontSize": 20, 729 | "fontFamily": 1, 730 | "text": "worker 1", 731 | "baseline": 18, 732 | "textAlign": "center", 733 | "verticalAlign": "middle" 734 | }, 735 | { 736 | "type": "rectangle", 737 | "version": 235, 738 | "versionNonce": 1685650659, 739 | "isDeleted": false, 740 | "id": "Yp5Kpa1JPtKUjJlgEDAwU", 741 | "fillStyle": "hachure", 742 | "strokeWidth": 1, 743 | "strokeStyle": "solid", 744 | "roughness": 1, 745 | "opacity": 100, 746 | "angle": 0, 747 | "x": 429.5, 748 | "y": 226.5, 749 | "strokeColor": "#000000", 750 | "backgroundColor": "transparent", 751 | "width": 345.00000000000006, 752 | "height": 311, 753 | "seed": 299448365, 754 | "groupIds": [], 755 | "strokeSharpness": "sharp", 756 | "boundElementIds": [ 757 | "_wBO22vaQplcoKyBXbWRC", 758 | "BZVwnsrGk9G-X87ZHkh-6", 759 | "CKKC2fK8c7VggdJKNATSf", 760 | "H6kPRLfMhpJHuNpq28DBh", 761 | "s6bEUrOJ4kLZeJLk9dJST", 762 | "SkslE91eN8Chjc7oKry2J" 763 | ] 764 | }, 765 | { 766 | "type": "text", 767 | "version": 170, 768 | "versionNonce": 845291757, 769 | "isDeleted": false, 770 | "id": "YNewOdVcj3FK_bEFPz2gX", 771 | "fillStyle": "hachure", 772 | "strokeWidth": 1, 773 | "strokeStyle": "solid", 774 | "roughness": 1, 775 | "opacity": 100, 776 | "angle": 0, 777 | "x": 449.5, 778 | "y": 238.5, 779 | "strokeColor": "#000000", 780 | "backgroundColor": "transparent", 781 | "width": 68, 782 | "height": 26, 783 | "seed": 2107075875, 784 | "groupIds": [], 785 | "strokeSharpness": "sharp", 786 | "boundElementIds": [], 787 | "fontSize": 20, 788 | "fontFamily": 1, 789 | "text": "Host B", 790 | "baseline": 18, 791 | "textAlign": "left", 792 | "verticalAlign": "top" 793 | }, 794 | { 795 | "type": "rectangle", 796 | "version": 281, 797 | "versionNonce": 889090147, 798 | "isDeleted": false, 799 | "id": "6QhcK7EmeSnlNA6mELnWw", 800 | "fillStyle": "hachure", 801 | "strokeWidth": 1, 802 | "strokeStyle": "solid", 803 | "roughness": 1, 804 | "opacity": 100, 805 | "angle": 0, 806 | "x": 628, 807 | "y": 291.5, 808 | "strokeColor": "#000000", 809 | "backgroundColor": "transparent", 810 | "width": 129, 811 | "height": 56, 812 | "seed": 1539025549, 813 | "groupIds": [], 814 | "strokeSharpness": "sharp", 815 | "boundElementIds": [ 816 | "MEmprCJaM-n292s9xtiMc" 817 | ] 818 | }, 819 | { 820 | "type": "diamond", 821 | "version": 404, 822 | "versionNonce": 1957264205, 823 | "isDeleted": false, 824 | "id": "yi99XpjStdyF5QoSSbpua", 825 | "fillStyle": "hachure", 826 | "strokeWidth": 2, 827 | "strokeStyle": "solid", 828 | "roughness": 0, 829 | "opacity": 100, 830 | "angle": 0, 831 | "x": 734.5, 832 | "y": 313.5, 833 | "strokeColor": "#000000", 834 | "backgroundColor": "transparent", 835 | "width": 16, 836 | "height": 18, 837 | "seed": 252956867, 838 | "groupIds": [], 839 | "strokeSharpness": "sharp", 840 | "boundElementIds": [ 841 | "Sp9AvxDh8gwRvSC53VFKe", 842 | "XZpY0rnxgeDlxu5b8fgRQ" 843 | ] 844 | }, 845 | { 846 | "type": "text", 847 | "version": 359, 848 | "versionNonce": 557345795, 849 | "isDeleted": false, 850 | "id": "m_-rmUgV8rzVPm9wJLnWj", 851 | "fillStyle": "hachure", 852 | "strokeWidth": 1, 853 | "strokeStyle": "solid", 854 | "roughness": 1, 855 | "opacity": 100, 856 | "angle": 0, 857 | "x": 634.5, 858 | "y": 376, 859 | "strokeColor": "#000000", 860 | "backgroundColor": "transparent", 861 | "width": 90, 862 | "height": 26, 863 | "seed": 2036388077, 864 | "groupIds": [], 865 | "strokeSharpness": "sharp", 866 | "boundElementIds": [ 867 | "_wBO22vaQplcoKyBXbWRC", 868 | "wi3_pEzGG6Yyne5m3_yJi" 869 | ], 870 | "fontSize": 20, 871 | "fontFamily": 1, 872 | "text": "worker 2", 873 | "baseline": 18, 874 | "textAlign": "center", 875 | "verticalAlign": "middle" 876 | }, 877 | { 878 | "type": "rectangle", 879 | "version": 314, 880 | "versionNonce": 2111211949, 881 | "isDeleted": false, 882 | "id": "ROA9477OMY_jpRWcfxUw6", 883 | "fillStyle": "hachure", 884 | "strokeWidth": 1, 885 | "strokeStyle": "solid", 886 | "roughness": 1, 887 | "opacity": 100, 888 | "angle": 0, 889 | "x": 628, 890 | "y": 360.5, 891 | "strokeColor": "#000000", 892 | "backgroundColor": "transparent", 893 | "width": 129, 894 | "height": 56, 895 | "seed": 1719844963, 896 | "groupIds": [], 897 | "strokeSharpness": "sharp", 898 | "boundElementIds": [ 899 | "wi3_pEzGG6Yyne5m3_yJi", 900 | "BwLeUURxsJb0kYyPr25hY" 901 | ] 902 | }, 903 | { 904 | "type": "diamond", 905 | "version": 437, 906 | "versionNonce": 1475474339, 907 | "isDeleted": false, 908 | "id": "ZpKpnER4tz6BC8v3Ioci4", 909 | "fillStyle": "hachure", 910 | "strokeWidth": 2, 911 | "strokeStyle": "solid", 912 | "roughness": 0, 913 | "opacity": 100, 914 | "angle": 0, 915 | "x": 734.5, 916 | "y": 382.5, 917 | "strokeColor": "#000000", 918 | "backgroundColor": "transparent", 919 | "width": 16, 920 | "height": 18, 921 | "seed": 1213344589, 922 | "groupIds": [], 923 | "strokeSharpness": "sharp", 924 | "boundElementIds": [ 925 | "Sp9AvxDh8gwRvSC53VFKe", 926 | "4mjxZzapHnLuRx7KU2JeH" 927 | ] 928 | }, 929 | { 930 | "type": "text", 931 | "version": 344, 932 | "versionNonce": 1326631949, 933 | "isDeleted": false, 934 | "id": "lnKKGJYWby9w4kCUJ_3IF", 935 | "fillStyle": "hachure", 936 | "strokeWidth": 1, 937 | "strokeStyle": "solid", 938 | "roughness": 1, 939 | "opacity": 100, 940 | "angle": 0, 941 | "x": 636, 942 | "y": 442, 943 | "strokeColor": "#000000", 944 | "backgroundColor": "transparent", 945 | "width": 89, 946 | "height": 26, 947 | "seed": 1035123715, 948 | "groupIds": [], 949 | "strokeSharpness": "sharp", 950 | "boundElementIds": [ 951 | "_wBO22vaQplcoKyBXbWRC" 952 | ], 953 | "fontSize": 20, 954 | "fontFamily": 1, 955 | "text": "worker 3", 956 | "baseline": 18, 957 | "textAlign": "center", 958 | "verticalAlign": "middle" 959 | }, 960 | { 961 | "type": "rectangle", 962 | "version": 299, 963 | "versionNonce": 2035910467, 964 | "isDeleted": false, 965 | "id": "RcoOUjN-3ibbIV71_ozA6", 966 | "fillStyle": "hachure", 967 | "strokeWidth": 1, 968 | "strokeStyle": "solid", 969 | "roughness": 1, 970 | "opacity": 100, 971 | "angle": 0, 972 | "x": 629, 973 | "y": 426.5, 974 | "strokeColor": "#000000", 975 | "backgroundColor": "transparent", 976 | "width": 129, 977 | "height": 56, 978 | "seed": 954808749, 979 | "groupIds": [], 980 | "strokeSharpness": "sharp", 981 | "boundElementIds": [ 982 | "BwLeUURxsJb0kYyPr25hY" 983 | ] 984 | }, 985 | { 986 | "type": "diamond", 987 | "version": 427, 988 | "versionNonce": 139969133, 989 | "isDeleted": false, 990 | "id": "D01DM9Gzt47JacAhP--Cs", 991 | "fillStyle": "hachure", 992 | "strokeWidth": 2, 993 | "strokeStyle": "solid", 994 | "roughness": 0, 995 | "opacity": 100, 996 | "angle": 0, 997 | "x": 735.5, 998 | "y": 448.5, 999 | "strokeColor": "#000000", 1000 | "backgroundColor": "transparent", 1001 | "width": 16, 1002 | "height": 18, 1003 | "seed": 279069603, 1004 | "groupIds": [], 1005 | "strokeSharpness": "sharp", 1006 | "boundElementIds": [ 1007 | "Sp9AvxDh8gwRvSC53VFKe", 1008 | "eU1gfEXnHSjxc-pEgv43A" 1009 | ] 1010 | }, 1011 | { 1012 | "type": "text", 1013 | "version": 75, 1014 | "versionNonce": 466462435, 1015 | "isDeleted": false, 1016 | "id": "_Tyw0FsCuxYBWnTNAl2Ey", 1017 | "fillStyle": "hachure", 1018 | "strokeWidth": 1, 1019 | "strokeStyle": "solid", 1020 | "roughness": 1, 1021 | "opacity": 100, 1022 | "angle": 0, 1023 | "x": 477.5, 1024 | "y": 372.5, 1025 | "strokeColor": "#000000", 1026 | "backgroundColor": "transparent", 1027 | "width": 67, 1028 | "height": 26, 1029 | "seed": 1951366157, 1030 | "groupIds": [], 1031 | "strokeSharpness": "sharp", 1032 | "boundElementIds": [], 1033 | "fontSize": 20, 1034 | "fontFamily": 1, 1035 | "text": "master", 1036 | "baseline": 18, 1037 | "textAlign": "center", 1038 | "verticalAlign": "middle" 1039 | }, 1040 | { 1041 | "type": "rectangle", 1042 | "version": 314, 1043 | "versionNonce": 242150605, 1044 | "isDeleted": false, 1045 | "id": "AEMAAc1yR1kMKbtgszrCZ", 1046 | "fillStyle": "hachure", 1047 | "strokeWidth": 1, 1048 | "strokeStyle": "solid", 1049 | "roughness": 1, 1050 | "opacity": 100, 1051 | "angle": 0, 1052 | "x": 444, 1053 | "y": 359.5, 1054 | "strokeColor": "#000000", 1055 | "backgroundColor": "transparent", 1056 | "width": 129, 1057 | "height": 56, 1058 | "seed": 1467995971, 1059 | "groupIds": [], 1060 | "strokeSharpness": "sharp", 1061 | "boundElementIds": [ 1062 | "MEmprCJaM-n292s9xtiMc", 1063 | "wi3_pEzGG6Yyne5m3_yJi", 1064 | "BwLeUURxsJb0kYyPr25hY" 1065 | ] 1066 | }, 1067 | { 1068 | "type": "arrow", 1069 | "version": 204, 1070 | "versionNonce": 98357891, 1071 | "isDeleted": false, 1072 | "id": "MEmprCJaM-n292s9xtiMc", 1073 | "fillStyle": "hachure", 1074 | "strokeWidth": 1, 1075 | "strokeStyle": "solid", 1076 | "roughness": 1, 1077 | "opacity": 100, 1078 | "angle": 0, 1079 | "x": 582.5, 1080 | "y": 380.5, 1081 | "strokeColor": "#000000", 1082 | "backgroundColor": "transparent", 1083 | "width": 34, 1084 | "height": 48, 1085 | "seed": 1115583085, 1086 | "groupIds": [], 1087 | "strokeSharpness": "round", 1088 | "boundElementIds": [], 1089 | "startBinding": { 1090 | "elementId": "AEMAAc1yR1kMKbtgszrCZ", 1091 | "focus": 0.8186758893280632, 1092 | "gap": 9.5 1093 | }, 1094 | "endBinding": { 1095 | "elementId": "6QhcK7EmeSnlNA6mELnWw", 1096 | "focus": 0.7919960474308301, 1097 | "gap": 11.5 1098 | }, 1099 | "points": [ 1100 | [ 1101 | 0, 1102 | 0 1103 | ], 1104 | [ 1105 | 34, 1106 | -48 1107 | ] 1108 | ], 1109 | "lastCommittedPoint": null, 1110 | "startArrowhead": null, 1111 | "endArrowhead": "arrow" 1112 | }, 1113 | { 1114 | "type": "arrow", 1115 | "version": 260, 1116 | "versionNonce": 1894894381, 1117 | "isDeleted": false, 1118 | "id": "wi3_pEzGG6Yyne5m3_yJi", 1119 | "fillStyle": "hachure", 1120 | "strokeWidth": 1, 1121 | "strokeStyle": "solid", 1122 | "roughness": 1, 1123 | "opacity": 100, 1124 | "angle": 0, 1125 | "x": 585.2063185837119, 1126 | "y": 387.00435436107216, 1127 | "strokeColor": "#000000", 1128 | "backgroundColor": "transparent", 1129 | "width": 32.383044731616906, 1130 | "height": 0.6793771122768604, 1131 | "seed": 11138787, 1132 | "groupIds": [], 1133 | "strokeSharpness": "round", 1134 | "boundElementIds": [], 1135 | "startBinding": { 1136 | "elementId": "AEMAAc1yR1kMKbtgszrCZ", 1137 | "focus": -0.071709415801483, 1138 | "gap": 12.20631858371189 1139 | }, 1140 | "endBinding": { 1141 | "elementId": "ROA9477OMY_jpRWcfxUw6", 1142 | "focus": -0.02573187998110361, 1143 | "gap": 10.410636684671204 1144 | }, 1145 | "points": [ 1146 | [ 1147 | 0, 1148 | 0 1149 | ], 1150 | [ 1151 | 32.383044731616906, 1152 | 0.6793771122768604 1153 | ] 1154 | ], 1155 | "lastCommittedPoint": null, 1156 | "startArrowhead": null, 1157 | "endArrowhead": "arrow" 1158 | }, 1159 | { 1160 | "type": "arrow", 1161 | "version": 315, 1162 | "versionNonce": 675645987, 1163 | "isDeleted": false, 1164 | "id": "BwLeUURxsJb0kYyPr25hY", 1165 | "fillStyle": "hachure", 1166 | "strokeWidth": 1, 1167 | "strokeStyle": "solid", 1168 | "roughness": 1, 1169 | "opacity": 100, 1170 | "angle": 0, 1171 | "x": 587.3084776341916, 1172 | "y": 399.1603114438616, 1173 | "strokeColor": "#000000", 1174 | "backgroundColor": "transparent", 1175 | "width": 32.40004203654814, 1176 | "height": 43.76823068360791, 1177 | "seed": 809475277, 1178 | "groupIds": [], 1179 | "strokeSharpness": "round", 1180 | "boundElementIds": [], 1181 | "startBinding": { 1182 | "elementId": "AEMAAc1yR1kMKbtgszrCZ", 1183 | "focus": -0.8234063011749818, 1184 | "gap": 14.308477634191604 1185 | }, 1186 | "endBinding": { 1187 | "elementId": "RcoOUjN-3ibbIV71_ozA6", 1188 | "focus": -0.7653120403381727, 1189 | "gap": 9.291480329260253 1190 | }, 1191 | "points": [ 1192 | [ 1193 | 0, 1194 | 0 1195 | ], 1196 | [ 1197 | 32.40004203654814, 1198 | 43.76823068360791 1199 | ] 1200 | ], 1201 | "lastCommittedPoint": null, 1202 | "startArrowhead": null, 1203 | "endArrowhead": "arrow" 1204 | }, 1205 | { 1206 | "type": "text", 1207 | "version": 46, 1208 | "versionNonce": 273276301, 1209 | "isDeleted": false, 1210 | "id": "YWZMjOMxTqcywTzWOi2Fd", 1211 | "fillStyle": "hachure", 1212 | "strokeWidth": 1, 1213 | "strokeStyle": "solid", 1214 | "roughness": 1, 1215 | "opacity": 100, 1216 | "angle": 0, 1217 | "x": 678, 1218 | "y": 170, 1219 | "strokeColor": "#000000", 1220 | "backgroundColor": "transparent", 1221 | "width": 16, 1222 | "height": 26, 1223 | "seed": 542615715, 1224 | "groupIds": [], 1225 | "strokeSharpness": "sharp", 1226 | "boundElementIds": [], 1227 | "fontSize": 20, 1228 | "fontFamily": 1, 1229 | "text": "...", 1230 | "baseline": 18, 1231 | "textAlign": "left", 1232 | "verticalAlign": "top" 1233 | }, 1234 | { 1235 | "type": "text", 1236 | "version": 73, 1237 | "versionNonce": 1475759555, 1238 | "isDeleted": false, 1239 | "id": "ft8BfHmWU6cvt65MLclAF", 1240 | "fillStyle": "hachure", 1241 | "strokeWidth": 1, 1242 | "strokeStyle": "solid", 1243 | "roughness": 1, 1244 | "opacity": 100, 1245 | "angle": 0, 1246 | "x": 681, 1247 | "y": 503, 1248 | "strokeColor": "#000000", 1249 | "backgroundColor": "transparent", 1250 | "width": 16, 1251 | "height": 26, 1252 | "seed": 202755715, 1253 | "groupIds": [], 1254 | "strokeSharpness": "sharp", 1255 | "boundElementIds": [], 1256 | "fontSize": 20, 1257 | "fontFamily": 1, 1258 | "text": "...", 1259 | "baseline": 18, 1260 | "textAlign": "left", 1261 | "verticalAlign": "top" 1262 | }, 1263 | { 1264 | "type": "arrow", 1265 | "version": 382, 1266 | "versionNonce": 61907949, 1267 | "isDeleted": false, 1268 | "id": "CKKC2fK8c7VggdJKNATSf", 1269 | "fillStyle": "hachure", 1270 | "strokeWidth": 2, 1271 | "strokeStyle": "solid", 1272 | "roughness": 0, 1273 | "opacity": 100, 1274 | "angle": 0, 1275 | "x": 864, 1276 | "y": 261, 1277 | "strokeColor": "#000000", 1278 | "backgroundColor": "transparent", 1279 | "width": 77, 1280 | "height": 39, 1281 | "seed": 1155983491, 1282 | "groupIds": [], 1283 | "strokeSharpness": "round", 1284 | "boundElementIds": [], 1285 | "startBinding": { 1286 | "elementId": "EQmjbilyrf3OcSwGbMZrg", 1287 | "focus": 0.0932539046701243, 1288 | "gap": 14.753203494349187 1289 | }, 1290 | "endBinding": { 1291 | "elementId": "Yp5Kpa1JPtKUjJlgEDAwU", 1292 | "focus": 0.04817924175177798, 1293 | "gap": 12.5 1294 | }, 1295 | "points": [ 1296 | [ 1297 | 0, 1298 | 0 1299 | ], 1300 | [ 1301 | -77, 1302 | 39 1303 | ] 1304 | ], 1305 | "lastCommittedPoint": null, 1306 | "startArrowhead": "arrow", 1307 | "endArrowhead": "arrow" 1308 | }, 1309 | { 1310 | "type": "arrow", 1311 | "version": 440, 1312 | "versionNonce": 1470302275, 1313 | "isDeleted": false, 1314 | "id": "H6kPRLfMhpJHuNpq28DBh", 1315 | "fillStyle": "hachure", 1316 | "strokeWidth": 2, 1317 | "strokeStyle": "solid", 1318 | "roughness": 0, 1319 | "opacity": 100, 1320 | "angle": 0, 1321 | "x": 879.5, 1322 | "y": 284.5, 1323 | "strokeColor": "#000000", 1324 | "backgroundColor": "transparent", 1325 | "width": 87, 1326 | "height": 88, 1327 | "seed": 1138358531, 1328 | "groupIds": [], 1329 | "strokeSharpness": "round", 1330 | "boundElementIds": [], 1331 | "startBinding": { 1332 | "elementId": "EQmjbilyrf3OcSwGbMZrg", 1333 | "focus": -0.1142231163839854, 1334 | "gap": 18.76824885115778 1335 | }, 1336 | "endBinding": { 1337 | "elementId": "Yp5Kpa1JPtKUjJlgEDAwU", 1338 | "focus": 0.5551491718480589, 1339 | "gap": 18 1340 | }, 1341 | "points": [ 1342 | [ 1343 | 0, 1344 | 0 1345 | ], 1346 | [ 1347 | -87, 1348 | 88 1349 | ] 1350 | ], 1351 | "lastCommittedPoint": null, 1352 | "startArrowhead": "arrow", 1353 | "endArrowhead": "arrow" 1354 | }, 1355 | { 1356 | "type": "arrow", 1357 | "version": 494, 1358 | "versionNonce": 164316941, 1359 | "isDeleted": false, 1360 | "id": "s6bEUrOJ4kLZeJLk9dJST", 1361 | "fillStyle": "hachure", 1362 | "strokeWidth": 2, 1363 | "strokeStyle": "solid", 1364 | "roughness": 0, 1365 | "opacity": 100, 1366 | "angle": 0, 1367 | "x": 901.5, 1368 | "y": 297, 1369 | "strokeColor": "#000000", 1370 | "backgroundColor": "transparent", 1371 | "width": 109, 1372 | "height": 153, 1373 | "seed": 1853494147, 1374 | "groupIds": [], 1375 | "strokeSharpness": "round", 1376 | "boundElementIds": [], 1377 | "startBinding": null, 1378 | "endBinding": { 1379 | "elementId": "Yp5Kpa1JPtKUjJlgEDAwU", 1380 | "focus": 0.8434889945087904, 1381 | "gap": 18 1382 | }, 1383 | "points": [ 1384 | [ 1385 | 0, 1386 | 0 1387 | ], 1388 | [ 1389 | -109, 1390 | 153 1391 | ] 1392 | ], 1393 | "lastCommittedPoint": null, 1394 | "startArrowhead": "arrow", 1395 | "endArrowhead": "arrow" 1396 | }, 1397 | { 1398 | "id": "2JYwvUtBLBneWYjVgsiLM", 1399 | "type": "text", 1400 | "x": 164, 1401 | "y": 186, 1402 | "width": 150, 1403 | "height": 52, 1404 | "angle": 0, 1405 | "strokeColor": "#000000", 1406 | "backgroundColor": "transparent", 1407 | "fillStyle": "hachure", 1408 | "strokeWidth": 2, 1409 | "strokeStyle": "solid", 1410 | "roughness": 1, 1411 | "opacity": 100, 1412 | "groupIds": [], 1413 | "strokeSharpness": "sharp", 1414 | "seed": 1208666435, 1415 | "version": 168, 1416 | "versionNonce": 1663726979, 1417 | "isDeleted": false, 1418 | "boundElementIds": null, 1419 | "text": "load-balancer\n(sticky session)", 1420 | "fontSize": 20, 1421 | "fontFamily": 1, 1422 | "textAlign": "center", 1423 | "verticalAlign": "top", 1424 | "baseline": 44 1425 | }, 1426 | { 1427 | "id": "EBAsIqbyBZbbwEKEu4HSG", 1428 | "type": "diamond", 1429 | "x": 134, 1430 | "y": 138, 1431 | "width": 207, 1432 | "height": 149, 1433 | "angle": 0, 1434 | "strokeColor": "#000000", 1435 | "backgroundColor": "transparent", 1436 | "fillStyle": "hachure", 1437 | "strokeWidth": 2, 1438 | "strokeStyle": "solid", 1439 | "roughness": 1, 1440 | "opacity": 100, 1441 | "groupIds": [], 1442 | "strokeSharpness": "sharp", 1443 | "seed": 1371271043, 1444 | "version": 100, 1445 | "versionNonce": 439852355, 1446 | "isDeleted": false, 1447 | "boundElementIds": [ 1448 | "zkLKnuWAYxJGCPBZp9UAB", 1449 | "SkslE91eN8Chjc7oKry2J" 1450 | ] 1451 | }, 1452 | { 1453 | "id": "zkLKnuWAYxJGCPBZp9UAB", 1454 | "type": "arrow", 1455 | "x": 315, 1456 | "y": 166, 1457 | "width": 91, 1458 | "height": 80, 1459 | "angle": 0, 1460 | "strokeColor": "#000000", 1461 | "backgroundColor": "transparent", 1462 | "fillStyle": "hachure", 1463 | "strokeWidth": 1, 1464 | "strokeStyle": "solid", 1465 | "roughness": 1, 1466 | "opacity": 100, 1467 | "groupIds": [], 1468 | "strokeSharpness": "round", 1469 | "seed": 1134589091, 1470 | "version": 46, 1471 | "versionNonce": 2036575267, 1472 | "isDeleted": false, 1473 | "boundElementIds": null, 1474 | "points": [ 1475 | [ 1476 | 0, 1477 | 0 1478 | ], 1479 | [ 1480 | 91, 1481 | -80 1482 | ] 1483 | ], 1484 | "lastCommittedPoint": null, 1485 | "startBinding": { 1486 | "elementId": "EBAsIqbyBZbbwEKEu4HSG", 1487 | "focus": 0.290360646065344, 1488 | "gap": 22.55056739709275 1489 | }, 1490 | "endBinding": { 1491 | "elementId": "lmQ4o4New7xuXQLwavuSn", 1492 | "focus": 0.45414214414768966, 1493 | "gap": 22 1494 | }, 1495 | "startArrowhead": "arrow", 1496 | "endArrowhead": "arrow" 1497 | }, 1498 | { 1499 | "id": "SkslE91eN8Chjc7oKry2J", 1500 | "type": "arrow", 1501 | "x": 306.8498551402241, 1502 | "y": 257.2574737574905, 1503 | "width": 98.01361482879145, 1504 | "height": 128.02140059301632, 1505 | "angle": 0, 1506 | "strokeColor": "#000000", 1507 | "backgroundColor": "transparent", 1508 | "fillStyle": "hachure", 1509 | "strokeWidth": 1, 1510 | "strokeStyle": "solid", 1511 | "roughness": 1, 1512 | "opacity": 100, 1513 | "groupIds": [], 1514 | "strokeSharpness": "round", 1515 | "seed": 237887981, 1516 | "version": 93, 1517 | "versionNonce": 2089684077, 1518 | "isDeleted": false, 1519 | "boundElementIds": null, 1520 | "points": [ 1521 | [ 1522 | 0, 1523 | 0 1524 | ], 1525 | [ 1526 | 98.01361482879145, 1527 | 128.02140059301632 1528 | ] 1529 | ], 1530 | "lastCommittedPoint": null, 1531 | "startBinding": { 1532 | "elementId": "EBAsIqbyBZbbwEKEu4HSG", 1533 | "focus": -0.3389698812804046, 1534 | "gap": 16.374991628092786 1535 | }, 1536 | "endBinding": { 1537 | "elementId": "Yp5Kpa1JPtKUjJlgEDAwU", 1538 | "focus": -0.6847742800267905, 1539 | "gap": 24.63653003098443 1540 | }, 1541 | "startArrowhead": "arrow", 1542 | "endArrowhead": "arrow" 1543 | } 1544 | ], 1545 | "appState": { 1546 | "gridSize": null, 1547 | "viewBackgroundColor": "#ffffff" 1548 | } 1549 | } -------------------------------------------------------------------------------- /assets/socket.io-cluster-redis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/socketio/socket.io-sticky/518bd0b989e20fadecbbb6b225578bfca82ad963/assets/socket.io-cluster-redis.png -------------------------------------------------------------------------------- /assets/socket.io-cluster.excalidraw: -------------------------------------------------------------------------------- 1 | { 2 | "type": "excalidraw", 3 | "version": 2, 4 | "source": "https://excalidraw.com", 5 | "elements": [ 6 | { 7 | "type": "text", 8 | "version": 287, 9 | "versionNonce": 624942413, 10 | "isDeleted": false, 11 | "id": "5hUB5ALUlsn26W0PzU4fM", 12 | "fillStyle": "hachure", 13 | "strokeWidth": 1, 14 | "strokeStyle": "solid", 15 | "roughness": 1, 16 | "opacity": 100, 17 | "angle": 0, 18 | "x": 1047, 19 | "y": 23.5, 20 | "strokeColor": "#000000", 21 | "backgroundColor": "transparent", 22 | "width": 78, 23 | "height": 26, 24 | "seed": 28708370, 25 | "groupIds": [], 26 | "strokeSharpness": "sharp", 27 | "boundElementIds": [ 28 | "_wBO22vaQplcoKyBXbWRC" 29 | ], 30 | "fontSize": 20, 31 | "fontFamily": 1, 32 | "text": "worker 1", 33 | "baseline": 18, 34 | "textAlign": "center", 35 | "verticalAlign": "middle" 36 | }, 37 | { 38 | "type": "rectangle", 39 | "version": 193, 40 | "versionNonce": 1953704131, 41 | "isDeleted": false, 42 | "id": "lmQ4o4New7xuXQLwavuSn", 43 | "fillStyle": "hachure", 44 | "strokeWidth": 1, 45 | "strokeStyle": "solid", 46 | "roughness": 1, 47 | "opacity": 100, 48 | "angle": 0, 49 | "x": 821, 50 | "y": -57, 51 | "strokeColor": "#000000", 52 | "backgroundColor": "transparent", 53 | "width": 345.00000000000006, 54 | "height": 311, 55 | "seed": 1594950354, 56 | "groupIds": [], 57 | "strokeSharpness": "sharp", 58 | "boundElementIds": [ 59 | "_wBO22vaQplcoKyBXbWRC", 60 | "BZVwnsrGk9G-X87ZHkh-6", 61 | "eU1gfEXnHSjxc-pEgv43A", 62 | "XZpY0rnxgeDlxu5b8fgRQ", 63 | "4mjxZzapHnLuRx7KU2JeH", 64 | "mV8ZNfAcYrxGLJ7b9a_kn" 65 | ] 66 | }, 67 | { 68 | "type": "text", 69 | "version": 132, 70 | "versionNonce": 1364241325, 71 | "isDeleted": false, 72 | "id": "ZQsZmj4NaTubBHMkVG2dl", 73 | "fillStyle": "hachure", 74 | "strokeWidth": 1, 75 | "strokeStyle": "solid", 76 | "roughness": 1, 77 | "opacity": 100, 78 | "angle": 0, 79 | "x": 841, 80 | "y": -47, 81 | "strokeColor": "#000000", 82 | "backgroundColor": "transparent", 83 | "width": 43, 84 | "height": 26, 85 | "seed": 126533902, 86 | "groupIds": [], 87 | "strokeSharpness": "sharp", 88 | "boundElementIds": [], 89 | "fontSize": 20, 90 | "fontFamily": 1, 91 | "text": "Host", 92 | "baseline": 18, 93 | "textAlign": "left", 94 | "verticalAlign": "top" 95 | }, 96 | { 97 | "type": "rectangle", 98 | "version": 237, 99 | "versionNonce": 1477038563, 100 | "isDeleted": false, 101 | "id": "RRrk3Vsl-pM8Z1r8Fj3Vu", 102 | "fillStyle": "hachure", 103 | "strokeWidth": 1, 104 | "strokeStyle": "solid", 105 | "roughness": 1, 106 | "opacity": 100, 107 | "angle": 0, 108 | "x": 1019.5, 109 | "y": 8, 110 | "strokeColor": "#000000", 111 | "backgroundColor": "transparent", 112 | "width": 129, 113 | "height": 56, 114 | "seed": 1013161166, 115 | "groupIds": [], 116 | "strokeSharpness": "sharp", 117 | "boundElementIds": [ 118 | "use4Bp2hbb77Fq5njtwBi" 119 | ] 120 | }, 121 | { 122 | "type": "text", 123 | "version": 328, 124 | "versionNonce": 414893379, 125 | "isDeleted": false, 126 | "id": "qfQdcJHnwYnCMtLCV51X8", 127 | "fillStyle": "hachure", 128 | "strokeWidth": 1, 129 | "strokeStyle": "solid", 130 | "roughness": 1, 131 | "opacity": 100, 132 | "angle": 0, 133 | "x": 1043, 134 | "y": 94.5, 135 | "strokeColor": "#000000", 136 | "backgroundColor": "transparent", 137 | "width": 90, 138 | "height": 26, 139 | "seed": 1535426147, 140 | "groupIds": [], 141 | "strokeSharpness": "sharp", 142 | "boundElementIds": [ 143 | "_wBO22vaQplcoKyBXbWRC", 144 | "2DIFacJXJtC5QIuMuo3pK" 145 | ], 146 | "fontSize": 20, 147 | "fontFamily": 1, 148 | "text": "worker 2", 149 | "baseline": 18, 150 | "textAlign": "center", 151 | "verticalAlign": "middle" 152 | }, 153 | { 154 | "type": "rectangle", 155 | "version": 270, 156 | "versionNonce": 498267427, 157 | "isDeleted": false, 158 | "id": "IRd1nPQbv0PQdJQn_yLOs", 159 | "fillStyle": "hachure", 160 | "strokeWidth": 1, 161 | "strokeStyle": "solid", 162 | "roughness": 1, 163 | "opacity": 100, 164 | "angle": 0, 165 | "x": 1019.5, 166 | "y": 77, 167 | "strokeColor": "#000000", 168 | "backgroundColor": "transparent", 169 | "width": 129, 170 | "height": 56, 171 | "seed": 452398413, 172 | "groupIds": [], 173 | "strokeSharpness": "sharp", 174 | "boundElementIds": [ 175 | "2DIFacJXJtC5QIuMuo3pK", 176 | "NhqDM6wVMhgbRvXrQJQge" 177 | ] 178 | }, 179 | { 180 | "type": "text", 181 | "version": 316, 182 | "versionNonce": 935612109, 183 | "isDeleted": false, 184 | "id": "ENOSqQ4visNbCN7ZMZwxP", 185 | "fillStyle": "hachure", 186 | "strokeWidth": 1, 187 | "strokeStyle": "solid", 188 | "roughness": 1, 189 | "opacity": 100, 190 | "angle": 0, 191 | "x": 1040.5, 192 | "y": 161.5, 193 | "strokeColor": "#000000", 194 | "backgroundColor": "transparent", 195 | "width": 89, 196 | "height": 26, 197 | "seed": 1916984429, 198 | "groupIds": [], 199 | "strokeSharpness": "sharp", 200 | "boundElementIds": [ 201 | "_wBO22vaQplcoKyBXbWRC" 202 | ], 203 | "fontSize": 20, 204 | "fontFamily": 1, 205 | "text": "worker 3", 206 | "baseline": 18, 207 | "textAlign": "center", 208 | "verticalAlign": "middle" 209 | }, 210 | { 211 | "type": "rectangle", 212 | "version": 255, 213 | "versionNonce": 1264339043, 214 | "isDeleted": false, 215 | "id": "IqdB8EO7s50UY1EU9TVVP", 216 | "fillStyle": "hachure", 217 | "strokeWidth": 1, 218 | "strokeStyle": "solid", 219 | "roughness": 1, 220 | "opacity": 100, 221 | "angle": 0, 222 | "x": 1020.5, 223 | "y": 143, 224 | "strokeColor": "#000000", 225 | "backgroundColor": "transparent", 226 | "width": 129, 227 | "height": 56, 228 | "seed": 1832463587, 229 | "groupIds": [], 230 | "strokeSharpness": "sharp", 231 | "boundElementIds": [ 232 | "NhqDM6wVMhgbRvXrQJQge" 233 | ] 234 | }, 235 | { 236 | "id": "D8P7Bclj4xdmqfd3xlM9w", 237 | "type": "text", 238 | "x": 869, 239 | "y": 89, 240 | "width": 67, 241 | "height": 26, 242 | "angle": 0, 243 | "strokeColor": "#000000", 244 | "backgroundColor": "transparent", 245 | "fillStyle": "hachure", 246 | "strokeWidth": 1, 247 | "strokeStyle": "solid", 248 | "roughness": 1, 249 | "opacity": 100, 250 | "groupIds": [], 251 | "strokeSharpness": "sharp", 252 | "seed": 415223619, 253 | "version": 33, 254 | "versionNonce": 856131619, 255 | "isDeleted": false, 256 | "boundElementIds": null, 257 | "text": "master", 258 | "fontSize": 20, 259 | "fontFamily": 1, 260 | "textAlign": "center", 261 | "verticalAlign": "middle", 262 | "baseline": 18 263 | }, 264 | { 265 | "type": "rectangle", 266 | "version": 268, 267 | "versionNonce": 847141059, 268 | "isDeleted": false, 269 | "id": "SlTRvTrZRavsPt44aEe3R", 270 | "fillStyle": "hachure", 271 | "strokeWidth": 1, 272 | "strokeStyle": "solid", 273 | "roughness": 1, 274 | "opacity": 100, 275 | "angle": 0, 276 | "x": 835.5, 277 | "y": 76, 278 | "strokeColor": "#000000", 279 | "backgroundColor": "transparent", 280 | "width": 129, 281 | "height": 56, 282 | "seed": 813326061, 283 | "groupIds": [], 284 | "strokeSharpness": "sharp", 285 | "boundElementIds": [ 286 | "use4Bp2hbb77Fq5njtwBi", 287 | "2DIFacJXJtC5QIuMuo3pK", 288 | "NhqDM6wVMhgbRvXrQJQge" 289 | ] 290 | }, 291 | { 292 | "id": "use4Bp2hbb77Fq5njtwBi", 293 | "type": "arrow", 294 | "x": 974, 295 | "y": 97, 296 | "width": 34, 297 | "height": 48, 298 | "angle": 0, 299 | "strokeColor": "#000000", 300 | "backgroundColor": "transparent", 301 | "fillStyle": "hachure", 302 | "strokeWidth": 1, 303 | "strokeStyle": "solid", 304 | "roughness": 1, 305 | "opacity": 100, 306 | "groupIds": [], 307 | "strokeSharpness": "round", 308 | "seed": 1423358221, 309 | "version": 73, 310 | "versionNonce": 1550083949, 311 | "isDeleted": false, 312 | "boundElementIds": null, 313 | "points": [ 314 | [ 315 | 0, 316 | 0 317 | ], 318 | [ 319 | 34, 320 | -48 321 | ] 322 | ], 323 | "lastCommittedPoint": null, 324 | "startBinding": { 325 | "elementId": "SlTRvTrZRavsPt44aEe3R", 326 | "focus": 0.8186758893280632, 327 | "gap": 9.5 328 | }, 329 | "endBinding": { 330 | "elementId": "RRrk3Vsl-pM8Z1r8Fj3Vu", 331 | "focus": 0.7919960474308301, 332 | "gap": 11.5 333 | }, 334 | "startArrowhead": null, 335 | "endArrowhead": "arrow" 336 | }, 337 | { 338 | "id": "2DIFacJXJtC5QIuMuo3pK", 339 | "type": "arrow", 340 | "x": 976.706318583712, 341 | "y": 103.50435436107217, 342 | "width": 32.383044731616906, 343 | "height": 0.6793771122768604, 344 | "angle": 0, 345 | "strokeColor": "#000000", 346 | "backgroundColor": "transparent", 347 | "fillStyle": "hachure", 348 | "strokeWidth": 2, 349 | "strokeStyle": "solid", 350 | "roughness": 1, 351 | "opacity": 100, 352 | "groupIds": [], 353 | "strokeSharpness": "round", 354 | "seed": 1764672259, 355 | "version": 144, 356 | "versionNonce": 1995450765, 357 | "isDeleted": false, 358 | "boundElementIds": null, 359 | "points": [ 360 | [ 361 | 0, 362 | 0 363 | ], 364 | [ 365 | 32.383044731616906, 366 | 0.6793771122768604 367 | ] 368 | ], 369 | "lastCommittedPoint": null, 370 | "startBinding": { 371 | "elementId": "SlTRvTrZRavsPt44aEe3R", 372 | "focus": -0.07170941580148152, 373 | "gap": 12.206318583712005 374 | }, 375 | "endBinding": { 376 | "elementId": "IRd1nPQbv0PQdJQn_yLOs", 377 | "focus": -0.025731879981102438, 378 | "gap": 10.41063668467109 379 | }, 380 | "startArrowhead": null, 381 | "endArrowhead": "arrow" 382 | }, 383 | { 384 | "id": "NhqDM6wVMhgbRvXrQJQge", 385 | "type": "arrow", 386 | "x": 978.8084776341915, 387 | "y": 115.66031144386157, 388 | "width": 32.40004203654814, 389 | "height": 43.76823068360791, 390 | "angle": 0, 391 | "strokeColor": "#000000", 392 | "backgroundColor": "transparent", 393 | "fillStyle": "hachure", 394 | "strokeWidth": 1, 395 | "strokeStyle": "solid", 396 | "roughness": 1, 397 | "opacity": 100, 398 | "groupIds": [], 399 | "strokeSharpness": "round", 400 | "seed": 1285959341, 401 | "version": 184, 402 | "versionNonce": 2109917421, 403 | "isDeleted": false, 404 | "boundElementIds": null, 405 | "points": [ 406 | [ 407 | 0, 408 | 0 409 | ], 410 | [ 411 | 32.40004203654814, 412 | 43.76823068360791 413 | ] 414 | ], 415 | "lastCommittedPoint": null, 416 | "startBinding": { 417 | "elementId": "SlTRvTrZRavsPt44aEe3R", 418 | "focus": -0.8234063011749806, 419 | "gap": 14.30847763419149 420 | }, 421 | "endBinding": { 422 | "elementId": "IqdB8EO7s50UY1EU9TVVP", 423 | "focus": -0.7653120403381738, 424 | "gap": 9.291480329260366 425 | }, 426 | "startArrowhead": null, 427 | "endArrowhead": "arrow" 428 | }, 429 | { 430 | "id": "YWZMjOMxTqcywTzWOi2Fd", 431 | "type": "text", 432 | "x": 1071, 433 | "y": 214, 434 | "width": 16, 435 | "height": 26, 436 | "angle": 0, 437 | "strokeColor": "#000000", 438 | "backgroundColor": "transparent", 439 | "fillStyle": "hachure", 440 | "strokeWidth": 1, 441 | "strokeStyle": "solid", 442 | "roughness": 1, 443 | "opacity": 100, 444 | "groupIds": [], 445 | "strokeSharpness": "sharp", 446 | "seed": 542615715, 447 | "version": 60, 448 | "versionNonce": 464314019, 449 | "isDeleted": false, 450 | "boundElementIds": null, 451 | "text": "...", 452 | "fontSize": 20, 453 | "fontFamily": 1, 454 | "textAlign": "left", 455 | "verticalAlign": "top", 456 | "baseline": 18 457 | }, 458 | { 459 | "id": "9grXh8d6z3-WENQLWSBP6", 460 | "type": "rectangle", 461 | "x": 416, 462 | "y": 83, 463 | "width": 140, 464 | "height": 49, 465 | "angle": 0, 466 | "strokeColor": "#000000", 467 | "backgroundColor": "transparent", 468 | "fillStyle": "hachure", 469 | "strokeWidth": 1, 470 | "strokeStyle": "solid", 471 | "roughness": 1, 472 | "opacity": 100, 473 | "groupIds": [], 474 | "strokeSharpness": "sharp", 475 | "seed": 1667334019, 476 | "version": 56, 477 | "versionNonce": 799901037, 478 | "isDeleted": false, 479 | "boundElementIds": null 480 | }, 481 | { 482 | "id": "uw4DcwoucyYZxQuvW-XdC", 483 | "type": "text", 484 | "x": 461, 485 | "y": 94.5, 486 | "width": 50, 487 | "height": 26, 488 | "angle": 0, 489 | "strokeColor": "#000000", 490 | "backgroundColor": "transparent", 491 | "fillStyle": "hachure", 492 | "strokeWidth": 1, 493 | "strokeStyle": "solid", 494 | "roughness": 1, 495 | "opacity": 100, 496 | "groupIds": [], 497 | "strokeSharpness": "sharp", 498 | "seed": 1216901411, 499 | "version": 36, 500 | "versionNonce": 266965987, 501 | "isDeleted": false, 502 | "boundElementIds": null, 503 | "text": "client", 504 | "fontSize": 20, 505 | "fontFamily": 1, 506 | "textAlign": "center", 507 | "verticalAlign": "middle", 508 | "baseline": 18 509 | }, 510 | { 511 | "id": "mV8ZNfAcYrxGLJ7b9a_kn", 512 | "type": "arrow", 513 | "x": 574, 514 | "y": 105, 515 | "width": 229.6901474025101, 516 | "height": 1.3929567589983378, 517 | "angle": 0, 518 | "strokeColor": "#000000", 519 | "backgroundColor": "transparent", 520 | "fillStyle": "hachure", 521 | "strokeWidth": 2, 522 | "strokeStyle": "solid", 523 | "roughness": 1, 524 | "opacity": 100, 525 | "groupIds": [], 526 | "strokeSharpness": "round", 527 | "seed": 846315459, 528 | "version": 58, 529 | "versionNonce": 1002623629, 530 | "isDeleted": false, 531 | "boundElementIds": null, 532 | "points": [ 533 | [ 534 | 0, 535 | 0 536 | ], 537 | [ 538 | 229.6901474025101, 539 | 1.3929567589983378 540 | ] 541 | ], 542 | "lastCommittedPoint": null, 543 | "startBinding": null, 544 | "endBinding": { 545 | "elementId": "lmQ4o4New7xuXQLwavuSn", 546 | "focus": -0.05777248853197267, 547 | "gap": 17.309852597489908 548 | }, 549 | "startArrowhead": "arrow", 550 | "endArrowhead": "arrow" 551 | } 552 | ], 553 | "appState": { 554 | "gridSize": null, 555 | "viewBackgroundColor": "#ffffff" 556 | } 557 | } -------------------------------------------------------------------------------- /assets/socket.io-cluster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/socketio/socket.io-sticky/518bd0b989e20fadecbbb6b225578bfca82ad963/assets/socket.io-cluster.png -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import { Server as HttpServer } from "http"; 3 | import { Server } from "socket.io"; 4 | 5 | interface Options { 6 | loadBalancingMethod?: "random" | "round-robin" | "least-connection"; 7 | } 8 | 9 | export declare function setupMaster(httpServer: HttpServer, opts?: Options): void; 10 | export declare function setupWorker(io: Server): void; 11 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const cluster = require("cluster"); 2 | const { randomBytes } = require("crypto"); 3 | 4 | const randomId = () => randomBytes(8).toString("hex"); 5 | 6 | function destroySocket() { 7 | this.destroy(); 8 | } 9 | 10 | const setupMaster = (httpServer, opts) => { 11 | if (!cluster.isMaster) { 12 | throw new Error("not master"); 13 | } 14 | 15 | const options = Object.assign( 16 | { 17 | loadBalancingMethod: "least-connection", // either "random", "round-robin" or "least-connection" 18 | }, 19 | opts 20 | ); 21 | 22 | const sessionIdToWorker = new Map(); 23 | const sidRegex = /sid=([\w\-]{20})/; 24 | let currentIndex = 0; // for round-robin load balancing 25 | 26 | const computeWorkerId = (data) => { 27 | const match = sidRegex.exec(data); 28 | if (match) { 29 | const sid = match[1]; 30 | const workerId = sessionIdToWorker.get(sid); 31 | if (workerId && cluster.workers[workerId]) { 32 | return workerId; 33 | } 34 | } 35 | switch (options.loadBalancingMethod) { 36 | case "random": { 37 | const workerIds = Object.keys(cluster.workers); 38 | return workerIds[Math.floor(Math.random() * workerIds.length)]; 39 | } 40 | case "round-robin": { 41 | const workerIds = Object.keys(cluster.workers); 42 | currentIndex++; 43 | if (currentIndex >= workerIds.length) { 44 | currentIndex = 0; 45 | } 46 | return workerIds[currentIndex]; 47 | } 48 | case "least-connection": 49 | let leastActiveWorker; 50 | for (const id in cluster.workers) { 51 | const worker = cluster.workers[id]; 52 | if (leastActiveWorker === undefined) { 53 | leastActiveWorker = worker; 54 | } else { 55 | const c1 = worker.clientsCount || 0; 56 | const c2 = leastActiveWorker.clientsCount || 0; 57 | if (c1 < c2) { 58 | leastActiveWorker = worker; 59 | } 60 | } 61 | } 62 | return leastActiveWorker.id; 63 | } 64 | }; 65 | 66 | httpServer.on("connection", (socket) => { 67 | let workerId, connectionId; 68 | 69 | const sendCallback = (err) => { 70 | if (err) { 71 | socket.destroy(); 72 | } 73 | }; 74 | 75 | socket.on("data", (buffer) => { 76 | if (Object.keys(cluster.workers).length === 0) { 77 | if (workerId) { 78 | // the socket was already assigned to a worker, so we destroy it directly 79 | socket.destroy(); 80 | } else { 81 | socket.on("error", destroySocket); 82 | socket.once("finish", destroySocket); 83 | 84 | socket.end(`HTTP/1.1 503 Service Unavailable\r\n 85 | Connection: 'close'\r\n 86 | Content-Length: 0\r\n 87 | \r\n`); 88 | } 89 | return; 90 | } 91 | const data = buffer.toString(); 92 | if (workerId && connectionId) { 93 | cluster.workers[workerId].send( 94 | { type: "sticky:http-chunk", data, connectionId }, 95 | sendCallback 96 | ); 97 | return; 98 | } 99 | workerId = computeWorkerId(data); 100 | 101 | const head = data.substring(0, data.indexOf("\r\n\r\n")).toLowerCase(); 102 | const mayHaveMultipleChunks = 103 | head.includes("content-length:") || head.includes("transfer-encoding:"); 104 | 105 | if (mayHaveMultipleChunks) { 106 | connectionId = randomId(); 107 | } 108 | cluster.workers[workerId].send( 109 | { type: "sticky:connection", data, connectionId }, 110 | socket, 111 | { 112 | keepOpen: mayHaveMultipleChunks, 113 | }, 114 | sendCallback 115 | ); 116 | }); 117 | }); 118 | 119 | // this is needed to properly detect the end of the HTTP request body 120 | httpServer.on("request", (req) => { 121 | req.on("data", () => {}); 122 | }); 123 | 124 | cluster.on("message", (worker, { type, data }) => { 125 | switch (type) { 126 | case "sticky:connection": 127 | sessionIdToWorker.set(data, worker.id); 128 | if (options.loadBalancingMethod === "least-connection") { 129 | worker.clientsCount = (worker.clientsCount || 0) + 1; 130 | } 131 | break; 132 | case "sticky:disconnection": 133 | sessionIdToWorker.delete(data); 134 | if (options.loadBalancingMethod === "least-connection") { 135 | worker.clientsCount--; 136 | } 137 | break; 138 | } 139 | }); 140 | 141 | cluster.on("exit", (worker) => { 142 | sessionIdToWorker.forEach((value, key) => { 143 | if (value === worker.id) { 144 | sessionIdToWorker.delete(key); 145 | } 146 | }); 147 | }); 148 | }; 149 | 150 | const setupWorker = (io) => { 151 | if (!cluster.isWorker) { 152 | throw new Error("not worker"); 153 | } 154 | 155 | // store connections that may receive multiple chunks 156 | const sockets = new Map(); 157 | 158 | process.on("message", ({ type, data, connectionId }, socket) => { 159 | switch (type) { 160 | case "sticky:connection": 161 | if (!socket) { 162 | // might happen if the socket is closed during the transfer to the worker 163 | // see https://nodejs.org/api/child_process.html#child_process_example_sending_a_socket_object 164 | return; 165 | } 166 | io.httpServer.emit("connection", socket); // inject connection 167 | socket.emit("data", Buffer.from(data)); // republish first chunk 168 | socket.resume(); 169 | 170 | if (connectionId) { 171 | sockets.set(connectionId, socket); 172 | 173 | socket.on("close", () => { 174 | sockets.delete(connectionId); 175 | }); 176 | } 177 | 178 | break; 179 | 180 | case "sticky:http-chunk": { 181 | const socket = sockets.get(connectionId); 182 | if (socket) { 183 | socket.emit("data", Buffer.from(data)); 184 | } 185 | } 186 | } 187 | }); 188 | 189 | const ignoreError = () => {}; // the next request will fail anyway 190 | 191 | io.engine.on("connection", (socket) => { 192 | process.send({ type: "sticky:connection", data: socket.id }, ignoreError); 193 | 194 | socket.once("close", () => { 195 | process.send( 196 | { type: "sticky:disconnection", data: socket.id }, 197 | ignoreError 198 | ); 199 | }); 200 | }); 201 | }; 202 | 203 | module.exports = { 204 | setupMaster, 205 | setupWorker, 206 | }; 207 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@socket.io/sticky", 3 | "version": "1.0.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@babel/code-frame": { 8 | "version": "7.12.11", 9 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", 10 | "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", 11 | "dev": true, 12 | "requires": { 13 | "@babel/highlight": "^7.10.4" 14 | } 15 | }, 16 | "@babel/core": { 17 | "version": "7.12.10", 18 | "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.10.tgz", 19 | "integrity": "sha512-eTAlQKq65zHfkHZV0sIVODCPGVgoo1HdBlbSLi9CqOzuZanMv2ihzY+4paiKr1mH+XmYESMAmJ/dpZ68eN6d8w==", 20 | "dev": true, 21 | "requires": { 22 | "@babel/code-frame": "^7.10.4", 23 | "@babel/generator": "^7.12.10", 24 | "@babel/helper-module-transforms": "^7.12.1", 25 | "@babel/helpers": "^7.12.5", 26 | "@babel/parser": "^7.12.10", 27 | "@babel/template": "^7.12.7", 28 | "@babel/traverse": "^7.12.10", 29 | "@babel/types": "^7.12.10", 30 | "convert-source-map": "^1.7.0", 31 | "debug": "^4.1.0", 32 | "gensync": "^1.0.0-beta.1", 33 | "json5": "^2.1.2", 34 | "lodash": "^4.17.19", 35 | "semver": "^5.4.1", 36 | "source-map": "^0.5.0" 37 | }, 38 | "dependencies": { 39 | "semver": { 40 | "version": "5.7.1", 41 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 42 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", 43 | "dev": true 44 | } 45 | } 46 | }, 47 | "@babel/generator": { 48 | "version": "7.12.11", 49 | "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.11.tgz", 50 | "integrity": "sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA==", 51 | "dev": true, 52 | "requires": { 53 | "@babel/types": "^7.12.11", 54 | "jsesc": "^2.5.1", 55 | "source-map": "^0.5.0" 56 | } 57 | }, 58 | "@babel/helper-function-name": { 59 | "version": "7.12.11", 60 | "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz", 61 | "integrity": "sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA==", 62 | "dev": true, 63 | "requires": { 64 | "@babel/helper-get-function-arity": "^7.12.10", 65 | "@babel/template": "^7.12.7", 66 | "@babel/types": "^7.12.11" 67 | } 68 | }, 69 | "@babel/helper-get-function-arity": { 70 | "version": "7.12.10", 71 | "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz", 72 | "integrity": "sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag==", 73 | "dev": true, 74 | "requires": { 75 | "@babel/types": "^7.12.10" 76 | } 77 | }, 78 | "@babel/helper-member-expression-to-functions": { 79 | "version": "7.12.7", 80 | "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.7.tgz", 81 | "integrity": "sha512-DCsuPyeWxeHgh1Dus7APn7iza42i/qXqiFPWyBDdOFtvS581JQePsc1F/nD+fHrcswhLlRc2UpYS1NwERxZhHw==", 82 | "dev": true, 83 | "requires": { 84 | "@babel/types": "^7.12.7" 85 | } 86 | }, 87 | "@babel/helper-module-imports": { 88 | "version": "7.12.5", 89 | "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", 90 | "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", 91 | "dev": true, 92 | "requires": { 93 | "@babel/types": "^7.12.5" 94 | } 95 | }, 96 | "@babel/helper-module-transforms": { 97 | "version": "7.12.1", 98 | "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", 99 | "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", 100 | "dev": true, 101 | "requires": { 102 | "@babel/helper-module-imports": "^7.12.1", 103 | "@babel/helper-replace-supers": "^7.12.1", 104 | "@babel/helper-simple-access": "^7.12.1", 105 | "@babel/helper-split-export-declaration": "^7.11.0", 106 | "@babel/helper-validator-identifier": "^7.10.4", 107 | "@babel/template": "^7.10.4", 108 | "@babel/traverse": "^7.12.1", 109 | "@babel/types": "^7.12.1", 110 | "lodash": "^4.17.19" 111 | } 112 | }, 113 | "@babel/helper-optimise-call-expression": { 114 | "version": "7.12.10", 115 | "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.10.tgz", 116 | "integrity": "sha512-4tpbU0SrSTjjt65UMWSrUOPZTsgvPgGG4S8QSTNHacKzpS51IVWGDj0yCwyeZND/i+LSN2g/O63jEXEWm49sYQ==", 117 | "dev": true, 118 | "requires": { 119 | "@babel/types": "^7.12.10" 120 | } 121 | }, 122 | "@babel/helper-replace-supers": { 123 | "version": "7.12.11", 124 | "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.11.tgz", 125 | "integrity": "sha512-q+w1cqmhL7R0FNzth/PLLp2N+scXEK/L2AHbXUyydxp828F4FEa5WcVoqui9vFRiHDQErj9Zof8azP32uGVTRA==", 126 | "dev": true, 127 | "requires": { 128 | "@babel/helper-member-expression-to-functions": "^7.12.7", 129 | "@babel/helper-optimise-call-expression": "^7.12.10", 130 | "@babel/traverse": "^7.12.10", 131 | "@babel/types": "^7.12.11" 132 | } 133 | }, 134 | "@babel/helper-simple-access": { 135 | "version": "7.12.1", 136 | "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", 137 | "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", 138 | "dev": true, 139 | "requires": { 140 | "@babel/types": "^7.12.1" 141 | } 142 | }, 143 | "@babel/helper-split-export-declaration": { 144 | "version": "7.12.11", 145 | "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz", 146 | "integrity": "sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g==", 147 | "dev": true, 148 | "requires": { 149 | "@babel/types": "^7.12.11" 150 | } 151 | }, 152 | "@babel/helper-validator-identifier": { 153 | "version": "7.12.11", 154 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", 155 | "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", 156 | "dev": true 157 | }, 158 | "@babel/helpers": { 159 | "version": "7.12.5", 160 | "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.5.tgz", 161 | "integrity": "sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA==", 162 | "dev": true, 163 | "requires": { 164 | "@babel/template": "^7.10.4", 165 | "@babel/traverse": "^7.12.5", 166 | "@babel/types": "^7.12.5" 167 | } 168 | }, 169 | "@babel/highlight": { 170 | "version": "7.10.4", 171 | "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", 172 | "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", 173 | "dev": true, 174 | "requires": { 175 | "@babel/helper-validator-identifier": "^7.10.4", 176 | "chalk": "^2.0.0", 177 | "js-tokens": "^4.0.0" 178 | }, 179 | "dependencies": { 180 | "ansi-styles": { 181 | "version": "3.2.1", 182 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 183 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 184 | "dev": true, 185 | "requires": { 186 | "color-convert": "^1.9.0" 187 | } 188 | }, 189 | "chalk": { 190 | "version": "2.4.2", 191 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 192 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 193 | "dev": true, 194 | "requires": { 195 | "ansi-styles": "^3.2.1", 196 | "escape-string-regexp": "^1.0.5", 197 | "supports-color": "^5.3.0" 198 | } 199 | }, 200 | "color-convert": { 201 | "version": "1.9.3", 202 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 203 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 204 | "dev": true, 205 | "requires": { 206 | "color-name": "1.1.3" 207 | } 208 | }, 209 | "color-name": { 210 | "version": "1.1.3", 211 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 212 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 213 | "dev": true 214 | }, 215 | "escape-string-regexp": { 216 | "version": "1.0.5", 217 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 218 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 219 | "dev": true 220 | }, 221 | "has-flag": { 222 | "version": "3.0.0", 223 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 224 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 225 | "dev": true 226 | }, 227 | "supports-color": { 228 | "version": "5.5.0", 229 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 230 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 231 | "dev": true, 232 | "requires": { 233 | "has-flag": "^3.0.0" 234 | } 235 | } 236 | } 237 | }, 238 | "@babel/parser": { 239 | "version": "7.12.11", 240 | "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.11.tgz", 241 | "integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==", 242 | "dev": true 243 | }, 244 | "@babel/template": { 245 | "version": "7.12.7", 246 | "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", 247 | "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==", 248 | "dev": true, 249 | "requires": { 250 | "@babel/code-frame": "^7.10.4", 251 | "@babel/parser": "^7.12.7", 252 | "@babel/types": "^7.12.7" 253 | } 254 | }, 255 | "@babel/traverse": { 256 | "version": "7.12.12", 257 | "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.12.tgz", 258 | "integrity": "sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w==", 259 | "dev": true, 260 | "requires": { 261 | "@babel/code-frame": "^7.12.11", 262 | "@babel/generator": "^7.12.11", 263 | "@babel/helper-function-name": "^7.12.11", 264 | "@babel/helper-split-export-declaration": "^7.12.11", 265 | "@babel/parser": "^7.12.11", 266 | "@babel/types": "^7.12.12", 267 | "debug": "^4.1.0", 268 | "globals": "^11.1.0", 269 | "lodash": "^4.17.19" 270 | } 271 | }, 272 | "@babel/types": { 273 | "version": "7.12.12", 274 | "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", 275 | "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", 276 | "dev": true, 277 | "requires": { 278 | "@babel/helper-validator-identifier": "^7.12.11", 279 | "lodash": "^4.17.19", 280 | "to-fast-properties": "^2.0.0" 281 | } 282 | }, 283 | "@istanbuljs/load-nyc-config": { 284 | "version": "1.1.0", 285 | "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", 286 | "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", 287 | "dev": true, 288 | "requires": { 289 | "camelcase": "^5.3.1", 290 | "find-up": "^4.1.0", 291 | "get-package-type": "^0.1.0", 292 | "js-yaml": "^3.13.1", 293 | "resolve-from": "^5.0.0" 294 | }, 295 | "dependencies": { 296 | "find-up": { 297 | "version": "4.1.0", 298 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", 299 | "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", 300 | "dev": true, 301 | "requires": { 302 | "locate-path": "^5.0.0", 303 | "path-exists": "^4.0.0" 304 | } 305 | }, 306 | "locate-path": { 307 | "version": "5.0.0", 308 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", 309 | "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", 310 | "dev": true, 311 | "requires": { 312 | "p-locate": "^4.1.0" 313 | } 314 | }, 315 | "p-limit": { 316 | "version": "2.3.0", 317 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", 318 | "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", 319 | "dev": true, 320 | "requires": { 321 | "p-try": "^2.0.0" 322 | } 323 | }, 324 | "p-locate": { 325 | "version": "4.1.0", 326 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", 327 | "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", 328 | "dev": true, 329 | "requires": { 330 | "p-limit": "^2.2.0" 331 | } 332 | } 333 | } 334 | }, 335 | "@istanbuljs/schema": { 336 | "version": "0.1.2", 337 | "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", 338 | "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", 339 | "dev": true 340 | }, 341 | "@types/component-emitter": { 342 | "version": "1.2.10", 343 | "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.10.tgz", 344 | "integrity": "sha512-bsjleuRKWmGqajMerkzox19aGbscQX5rmmvvXl3wlIp5gMG1HgkiwPxsN5p070fBDKTNSPgojVbuY1+HWMbFhg==", 345 | "dev": true 346 | }, 347 | "@types/cookie": { 348 | "version": "0.4.0", 349 | "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.0.tgz", 350 | "integrity": "sha512-y7mImlc/rNkvCRmg8gC3/lj87S7pTUIJ6QGjwHR9WQJcFs+ZMTOaoPrkdFA/YdbuqVEmEbb5RdhVxMkAcgOnpg==", 351 | "dev": true 352 | }, 353 | "@types/cors": { 354 | "version": "2.8.9", 355 | "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.9.tgz", 356 | "integrity": "sha512-zurD1ibz21BRlAOIKP8yhrxlqKx6L9VCwkB5kMiP6nZAhoF5MvC7qS1qPA7nRcr1GJolfkQC7/EAL4hdYejLtg==", 357 | "dev": true 358 | }, 359 | "@types/node": { 360 | "version": "14.14.22", 361 | "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.22.tgz", 362 | "integrity": "sha512-g+f/qj/cNcqKkc3tFqlXOYjrmZA+jNBiDzbP3kH+B+otKFqAdPgVTGP1IeKRdMml/aE69as5S4FqtxAbl+LaMw==", 363 | "dev": true 364 | }, 365 | "@ungap/promise-all-settled": { 366 | "version": "1.1.2", 367 | "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", 368 | "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", 369 | "dev": true 370 | }, 371 | "accepts": { 372 | "version": "1.3.7", 373 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", 374 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", 375 | "dev": true, 376 | "requires": { 377 | "mime-types": "~2.1.24", 378 | "negotiator": "0.6.2" 379 | } 380 | }, 381 | "aggregate-error": { 382 | "version": "3.1.0", 383 | "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", 384 | "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", 385 | "dev": true, 386 | "requires": { 387 | "clean-stack": "^2.0.0", 388 | "indent-string": "^4.0.0" 389 | } 390 | }, 391 | "ansi-colors": { 392 | "version": "4.1.1", 393 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", 394 | "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", 395 | "dev": true 396 | }, 397 | "ansi-regex": { 398 | "version": "3.0.0", 399 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", 400 | "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", 401 | "dev": true 402 | }, 403 | "ansi-styles": { 404 | "version": "4.3.0", 405 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 406 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 407 | "dev": true, 408 | "requires": { 409 | "color-convert": "^2.0.1" 410 | } 411 | }, 412 | "anymatch": { 413 | "version": "3.1.1", 414 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", 415 | "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", 416 | "dev": true, 417 | "requires": { 418 | "normalize-path": "^3.0.0", 419 | "picomatch": "^2.0.4" 420 | } 421 | }, 422 | "append-transform": { 423 | "version": "2.0.0", 424 | "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", 425 | "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", 426 | "dev": true, 427 | "requires": { 428 | "default-require-extensions": "^3.0.0" 429 | } 430 | }, 431 | "archy": { 432 | "version": "1.0.0", 433 | "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", 434 | "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", 435 | "dev": true 436 | }, 437 | "argparse": { 438 | "version": "1.0.10", 439 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 440 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 441 | "dev": true, 442 | "requires": { 443 | "sprintf-js": "~1.0.2" 444 | } 445 | }, 446 | "backo2": { 447 | "version": "1.0.2", 448 | "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", 449 | "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=", 450 | "dev": true 451 | }, 452 | "balanced-match": { 453 | "version": "1.0.0", 454 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 455 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 456 | "dev": true 457 | }, 458 | "base64-arraybuffer": { 459 | "version": "0.1.4", 460 | "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz", 461 | "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI=", 462 | "dev": true 463 | }, 464 | "base64id": { 465 | "version": "2.0.0", 466 | "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", 467 | "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", 468 | "dev": true 469 | }, 470 | "binary-extensions": { 471 | "version": "2.2.0", 472 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 473 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", 474 | "dev": true 475 | }, 476 | "brace-expansion": { 477 | "version": "1.1.11", 478 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 479 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 480 | "dev": true, 481 | "requires": { 482 | "balanced-match": "^1.0.0", 483 | "concat-map": "0.0.1" 484 | } 485 | }, 486 | "braces": { 487 | "version": "3.0.2", 488 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 489 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 490 | "dev": true, 491 | "requires": { 492 | "fill-range": "^7.0.1" 493 | } 494 | }, 495 | "browser-stdout": { 496 | "version": "1.3.1", 497 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", 498 | "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", 499 | "dev": true 500 | }, 501 | "caching-transform": { 502 | "version": "4.0.0", 503 | "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", 504 | "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", 505 | "dev": true, 506 | "requires": { 507 | "hasha": "^5.0.0", 508 | "make-dir": "^3.0.0", 509 | "package-hash": "^4.0.0", 510 | "write-file-atomic": "^3.0.0" 511 | } 512 | }, 513 | "camelcase": { 514 | "version": "5.3.1", 515 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", 516 | "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", 517 | "dev": true 518 | }, 519 | "chalk": { 520 | "version": "4.1.0", 521 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", 522 | "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", 523 | "dev": true, 524 | "requires": { 525 | "ansi-styles": "^4.1.0", 526 | "supports-color": "^7.1.0" 527 | } 528 | }, 529 | "chokidar": { 530 | "version": "3.4.3", 531 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", 532 | "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", 533 | "dev": true, 534 | "requires": { 535 | "anymatch": "~3.1.1", 536 | "braces": "~3.0.2", 537 | "fsevents": "~2.1.2", 538 | "glob-parent": "~5.1.0", 539 | "is-binary-path": "~2.1.0", 540 | "is-glob": "~4.0.1", 541 | "normalize-path": "~3.0.0", 542 | "readdirp": "~3.5.0" 543 | } 544 | }, 545 | "clean-stack": { 546 | "version": "2.2.0", 547 | "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", 548 | "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", 549 | "dev": true 550 | }, 551 | "cliui": { 552 | "version": "5.0.0", 553 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", 554 | "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", 555 | "dev": true, 556 | "requires": { 557 | "string-width": "^3.1.0", 558 | "strip-ansi": "^5.2.0", 559 | "wrap-ansi": "^5.1.0" 560 | }, 561 | "dependencies": { 562 | "ansi-regex": { 563 | "version": "4.1.0", 564 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", 565 | "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", 566 | "dev": true 567 | }, 568 | "string-width": { 569 | "version": "3.1.0", 570 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", 571 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", 572 | "dev": true, 573 | "requires": { 574 | "emoji-regex": "^7.0.1", 575 | "is-fullwidth-code-point": "^2.0.0", 576 | "strip-ansi": "^5.1.0" 577 | } 578 | }, 579 | "strip-ansi": { 580 | "version": "5.2.0", 581 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", 582 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", 583 | "dev": true, 584 | "requires": { 585 | "ansi-regex": "^4.1.0" 586 | } 587 | } 588 | } 589 | }, 590 | "color-convert": { 591 | "version": "2.0.1", 592 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 593 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 594 | "dev": true, 595 | "requires": { 596 | "color-name": "~1.1.4" 597 | } 598 | }, 599 | "color-name": { 600 | "version": "1.1.4", 601 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 602 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 603 | "dev": true 604 | }, 605 | "commondir": { 606 | "version": "1.0.1", 607 | "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", 608 | "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", 609 | "dev": true 610 | }, 611 | "component-emitter": { 612 | "version": "1.3.0", 613 | "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", 614 | "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", 615 | "dev": true 616 | }, 617 | "concat-map": { 618 | "version": "0.0.1", 619 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 620 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 621 | "dev": true 622 | }, 623 | "convert-source-map": { 624 | "version": "1.7.0", 625 | "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", 626 | "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", 627 | "dev": true, 628 | "requires": { 629 | "safe-buffer": "~5.1.1" 630 | }, 631 | "dependencies": { 632 | "safe-buffer": { 633 | "version": "5.1.2", 634 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 635 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", 636 | "dev": true 637 | } 638 | } 639 | }, 640 | "cookie": { 641 | "version": "0.4.1", 642 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", 643 | "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", 644 | "dev": true 645 | }, 646 | "cors": { 647 | "version": "2.8.5", 648 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", 649 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", 650 | "dev": true, 651 | "requires": { 652 | "object-assign": "^4", 653 | "vary": "^1" 654 | } 655 | }, 656 | "cross-spawn": { 657 | "version": "7.0.3", 658 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", 659 | "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", 660 | "dev": true, 661 | "requires": { 662 | "path-key": "^3.1.0", 663 | "shebang-command": "^2.0.0", 664 | "which": "^2.0.1" 665 | } 666 | }, 667 | "debug": { 668 | "version": "4.2.0", 669 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", 670 | "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", 671 | "dev": true, 672 | "requires": { 673 | "ms": "2.1.2" 674 | } 675 | }, 676 | "decamelize": { 677 | "version": "1.2.0", 678 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", 679 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", 680 | "dev": true 681 | }, 682 | "default-require-extensions": { 683 | "version": "3.0.0", 684 | "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", 685 | "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", 686 | "dev": true, 687 | "requires": { 688 | "strip-bom": "^4.0.0" 689 | } 690 | }, 691 | "diff": { 692 | "version": "4.0.2", 693 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", 694 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", 695 | "dev": true 696 | }, 697 | "emoji-regex": { 698 | "version": "7.0.3", 699 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", 700 | "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", 701 | "dev": true 702 | }, 703 | "engine.io": { 704 | "version": "4.1.0", 705 | "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-4.1.0.tgz", 706 | "integrity": "sha512-vW7EAtn0HDQ4MtT5QbmCHF17TaYLONv2/JwdYsq9USPRZVM4zG7WB3k0Nc321z8EuSOlhGokrYlYx4176QhD0A==", 707 | "dev": true, 708 | "requires": { 709 | "accepts": "~1.3.4", 710 | "base64id": "2.0.0", 711 | "cookie": "~0.4.1", 712 | "cors": "~2.8.5", 713 | "debug": "~4.3.1", 714 | "engine.io-parser": "~4.0.0", 715 | "ws": "~7.4.2" 716 | }, 717 | "dependencies": { 718 | "debug": { 719 | "version": "4.3.1", 720 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", 721 | "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", 722 | "dev": true, 723 | "requires": { 724 | "ms": "2.1.2" 725 | } 726 | } 727 | } 728 | }, 729 | "engine.io-client": { 730 | "version": "4.1.0", 731 | "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-4.1.0.tgz", 732 | "integrity": "sha512-OUmn4m71/lW3ixICv4h3DuBRuh3ri0w3cDuepjsrINSbbqbni4Xw1shTFiKhl0v58lEtNpwJTpSKJJ3fondu5Q==", 733 | "dev": true, 734 | "requires": { 735 | "base64-arraybuffer": "0.1.4", 736 | "component-emitter": "~1.3.0", 737 | "debug": "~4.3.1", 738 | "engine.io-parser": "~4.0.1", 739 | "has-cors": "1.1.0", 740 | "parseqs": "0.0.6", 741 | "parseuri": "0.0.6", 742 | "ws": "~7.4.2", 743 | "xmlhttprequest-ssl": "~1.5.4", 744 | "yeast": "0.1.2" 745 | }, 746 | "dependencies": { 747 | "debug": { 748 | "version": "4.3.1", 749 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", 750 | "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", 751 | "dev": true, 752 | "requires": { 753 | "ms": "2.1.2" 754 | } 755 | } 756 | } 757 | }, 758 | "engine.io-parser": { 759 | "version": "4.0.2", 760 | "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-4.0.2.tgz", 761 | "integrity": "sha512-sHfEQv6nmtJrq6TKuIz5kyEKH/qSdK56H/A+7DnAuUPWosnIZAS2NHNcPLmyjtY3cGS/MqJdZbUjW97JU72iYg==", 762 | "dev": true, 763 | "requires": { 764 | "base64-arraybuffer": "0.1.4" 765 | } 766 | }, 767 | "es6-error": { 768 | "version": "4.1.1", 769 | "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", 770 | "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", 771 | "dev": true 772 | }, 773 | "escape-string-regexp": { 774 | "version": "4.0.0", 775 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 776 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 777 | "dev": true 778 | }, 779 | "esprima": { 780 | "version": "4.0.1", 781 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 782 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", 783 | "dev": true 784 | }, 785 | "fill-range": { 786 | "version": "7.0.1", 787 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 788 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 789 | "dev": true, 790 | "requires": { 791 | "to-regex-range": "^5.0.1" 792 | } 793 | }, 794 | "find-cache-dir": { 795 | "version": "3.3.1", 796 | "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", 797 | "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", 798 | "dev": true, 799 | "requires": { 800 | "commondir": "^1.0.1", 801 | "make-dir": "^3.0.2", 802 | "pkg-dir": "^4.1.0" 803 | } 804 | }, 805 | "find-up": { 806 | "version": "5.0.0", 807 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", 808 | "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", 809 | "dev": true, 810 | "requires": { 811 | "locate-path": "^6.0.0", 812 | "path-exists": "^4.0.0" 813 | } 814 | }, 815 | "flat": { 816 | "version": "5.0.2", 817 | "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", 818 | "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", 819 | "dev": true 820 | }, 821 | "foreground-child": { 822 | "version": "2.0.0", 823 | "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", 824 | "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", 825 | "dev": true, 826 | "requires": { 827 | "cross-spawn": "^7.0.0", 828 | "signal-exit": "^3.0.2" 829 | } 830 | }, 831 | "fromentries": { 832 | "version": "1.3.2", 833 | "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", 834 | "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", 835 | "dev": true 836 | }, 837 | "fs.realpath": { 838 | "version": "1.0.0", 839 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 840 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 841 | "dev": true 842 | }, 843 | "fsevents": { 844 | "version": "2.1.3", 845 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", 846 | "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", 847 | "dev": true, 848 | "optional": true 849 | }, 850 | "gensync": { 851 | "version": "1.0.0-beta.2", 852 | "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", 853 | "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", 854 | "dev": true 855 | }, 856 | "get-caller-file": { 857 | "version": "2.0.5", 858 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 859 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 860 | "dev": true 861 | }, 862 | "get-package-type": { 863 | "version": "0.1.0", 864 | "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", 865 | "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", 866 | "dev": true 867 | }, 868 | "glob": { 869 | "version": "7.1.6", 870 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", 871 | "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", 872 | "dev": true, 873 | "requires": { 874 | "fs.realpath": "^1.0.0", 875 | "inflight": "^1.0.4", 876 | "inherits": "2", 877 | "minimatch": "^3.0.4", 878 | "once": "^1.3.0", 879 | "path-is-absolute": "^1.0.0" 880 | } 881 | }, 882 | "glob-parent": { 883 | "version": "5.1.1", 884 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", 885 | "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", 886 | "dev": true, 887 | "requires": { 888 | "is-glob": "^4.0.1" 889 | } 890 | }, 891 | "globals": { 892 | "version": "11.12.0", 893 | "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", 894 | "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", 895 | "dev": true 896 | }, 897 | "graceful-fs": { 898 | "version": "4.2.4", 899 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", 900 | "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", 901 | "dev": true 902 | }, 903 | "growl": { 904 | "version": "1.10.5", 905 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", 906 | "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", 907 | "dev": true 908 | }, 909 | "has-cors": { 910 | "version": "1.1.0", 911 | "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", 912 | "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=", 913 | "dev": true 914 | }, 915 | "has-flag": { 916 | "version": "4.0.0", 917 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 918 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 919 | "dev": true 920 | }, 921 | "hasha": { 922 | "version": "5.2.2", 923 | "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", 924 | "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", 925 | "dev": true, 926 | "requires": { 927 | "is-stream": "^2.0.0", 928 | "type-fest": "^0.8.0" 929 | } 930 | }, 931 | "he": { 932 | "version": "1.2.0", 933 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", 934 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", 935 | "dev": true 936 | }, 937 | "html-escaper": { 938 | "version": "2.0.2", 939 | "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", 940 | "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", 941 | "dev": true 942 | }, 943 | "imurmurhash": { 944 | "version": "0.1.4", 945 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 946 | "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", 947 | "dev": true 948 | }, 949 | "indent-string": { 950 | "version": "4.0.0", 951 | "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", 952 | "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", 953 | "dev": true 954 | }, 955 | "inflight": { 956 | "version": "1.0.6", 957 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 958 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 959 | "dev": true, 960 | "requires": { 961 | "once": "^1.3.0", 962 | "wrappy": "1" 963 | } 964 | }, 965 | "inherits": { 966 | "version": "2.0.4", 967 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 968 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 969 | "dev": true 970 | }, 971 | "is-binary-path": { 972 | "version": "2.1.0", 973 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 974 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 975 | "dev": true, 976 | "requires": { 977 | "binary-extensions": "^2.0.0" 978 | } 979 | }, 980 | "is-extglob": { 981 | "version": "2.1.1", 982 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 983 | "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", 984 | "dev": true 985 | }, 986 | "is-fullwidth-code-point": { 987 | "version": "2.0.0", 988 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 989 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", 990 | "dev": true 991 | }, 992 | "is-glob": { 993 | "version": "4.0.1", 994 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", 995 | "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", 996 | "dev": true, 997 | "requires": { 998 | "is-extglob": "^2.1.1" 999 | } 1000 | }, 1001 | "is-number": { 1002 | "version": "7.0.0", 1003 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 1004 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 1005 | "dev": true 1006 | }, 1007 | "is-plain-obj": { 1008 | "version": "2.1.0", 1009 | "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", 1010 | "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", 1011 | "dev": true 1012 | }, 1013 | "is-stream": { 1014 | "version": "2.0.0", 1015 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", 1016 | "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", 1017 | "dev": true 1018 | }, 1019 | "is-typedarray": { 1020 | "version": "1.0.0", 1021 | "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", 1022 | "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", 1023 | "dev": true 1024 | }, 1025 | "is-windows": { 1026 | "version": "1.0.2", 1027 | "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", 1028 | "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", 1029 | "dev": true 1030 | }, 1031 | "isexe": { 1032 | "version": "2.0.0", 1033 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 1034 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 1035 | "dev": true 1036 | }, 1037 | "istanbul-lib-coverage": { 1038 | "version": "3.0.0", 1039 | "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", 1040 | "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", 1041 | "dev": true 1042 | }, 1043 | "istanbul-lib-hook": { 1044 | "version": "3.0.0", 1045 | "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", 1046 | "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", 1047 | "dev": true, 1048 | "requires": { 1049 | "append-transform": "^2.0.0" 1050 | } 1051 | }, 1052 | "istanbul-lib-instrument": { 1053 | "version": "4.0.3", 1054 | "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", 1055 | "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", 1056 | "dev": true, 1057 | "requires": { 1058 | "@babel/core": "^7.7.5", 1059 | "@istanbuljs/schema": "^0.1.2", 1060 | "istanbul-lib-coverage": "^3.0.0", 1061 | "semver": "^6.3.0" 1062 | } 1063 | }, 1064 | "istanbul-lib-processinfo": { 1065 | "version": "2.0.2", 1066 | "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz", 1067 | "integrity": "sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw==", 1068 | "dev": true, 1069 | "requires": { 1070 | "archy": "^1.0.0", 1071 | "cross-spawn": "^7.0.0", 1072 | "istanbul-lib-coverage": "^3.0.0-alpha.1", 1073 | "make-dir": "^3.0.0", 1074 | "p-map": "^3.0.0", 1075 | "rimraf": "^3.0.0", 1076 | "uuid": "^3.3.3" 1077 | } 1078 | }, 1079 | "istanbul-lib-report": { 1080 | "version": "3.0.0", 1081 | "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", 1082 | "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", 1083 | "dev": true, 1084 | "requires": { 1085 | "istanbul-lib-coverage": "^3.0.0", 1086 | "make-dir": "^3.0.0", 1087 | "supports-color": "^7.1.0" 1088 | } 1089 | }, 1090 | "istanbul-lib-source-maps": { 1091 | "version": "4.0.0", 1092 | "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", 1093 | "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", 1094 | "dev": true, 1095 | "requires": { 1096 | "debug": "^4.1.1", 1097 | "istanbul-lib-coverage": "^3.0.0", 1098 | "source-map": "^0.6.1" 1099 | }, 1100 | "dependencies": { 1101 | "source-map": { 1102 | "version": "0.6.1", 1103 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 1104 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 1105 | "dev": true 1106 | } 1107 | } 1108 | }, 1109 | "istanbul-reports": { 1110 | "version": "3.0.2", 1111 | "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", 1112 | "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", 1113 | "dev": true, 1114 | "requires": { 1115 | "html-escaper": "^2.0.0", 1116 | "istanbul-lib-report": "^3.0.0" 1117 | } 1118 | }, 1119 | "js-tokens": { 1120 | "version": "4.0.0", 1121 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 1122 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 1123 | "dev": true 1124 | }, 1125 | "js-yaml": { 1126 | "version": "3.14.0", 1127 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", 1128 | "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", 1129 | "dev": true, 1130 | "requires": { 1131 | "argparse": "^1.0.7", 1132 | "esprima": "^4.0.0" 1133 | } 1134 | }, 1135 | "jsesc": { 1136 | "version": "2.5.2", 1137 | "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", 1138 | "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", 1139 | "dev": true 1140 | }, 1141 | "json5": { 1142 | "version": "2.1.3", 1143 | "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", 1144 | "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", 1145 | "dev": true, 1146 | "requires": { 1147 | "minimist": "^1.2.5" 1148 | } 1149 | }, 1150 | "locate-path": { 1151 | "version": "6.0.0", 1152 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", 1153 | "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", 1154 | "dev": true, 1155 | "requires": { 1156 | "p-locate": "^5.0.0" 1157 | } 1158 | }, 1159 | "lodash": { 1160 | "version": "4.17.20", 1161 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", 1162 | "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", 1163 | "dev": true 1164 | }, 1165 | "lodash.flattendeep": { 1166 | "version": "4.4.0", 1167 | "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", 1168 | "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", 1169 | "dev": true 1170 | }, 1171 | "log-symbols": { 1172 | "version": "4.0.0", 1173 | "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", 1174 | "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", 1175 | "dev": true, 1176 | "requires": { 1177 | "chalk": "^4.0.0" 1178 | } 1179 | }, 1180 | "make-dir": { 1181 | "version": "3.1.0", 1182 | "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", 1183 | "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", 1184 | "dev": true, 1185 | "requires": { 1186 | "semver": "^6.0.0" 1187 | } 1188 | }, 1189 | "mime-db": { 1190 | "version": "1.45.0", 1191 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.45.0.tgz", 1192 | "integrity": "sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w==", 1193 | "dev": true 1194 | }, 1195 | "mime-types": { 1196 | "version": "2.1.28", 1197 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.28.tgz", 1198 | "integrity": "sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ==", 1199 | "dev": true, 1200 | "requires": { 1201 | "mime-db": "1.45.0" 1202 | } 1203 | }, 1204 | "minimatch": { 1205 | "version": "3.0.4", 1206 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 1207 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 1208 | "dev": true, 1209 | "requires": { 1210 | "brace-expansion": "^1.1.7" 1211 | } 1212 | }, 1213 | "minimist": { 1214 | "version": "1.2.5", 1215 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", 1216 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", 1217 | "dev": true 1218 | }, 1219 | "mocha": { 1220 | "version": "8.2.1", 1221 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.2.1.tgz", 1222 | "integrity": "sha512-cuLBVfyFfFqbNR0uUKbDGXKGk+UDFe6aR4os78XIrMQpZl/nv7JYHcvP5MFIAb374b2zFXsdgEGwmzMtP0Xg8w==", 1223 | "dev": true, 1224 | "requires": { 1225 | "@ungap/promise-all-settled": "1.1.2", 1226 | "ansi-colors": "4.1.1", 1227 | "browser-stdout": "1.3.1", 1228 | "chokidar": "3.4.3", 1229 | "debug": "4.2.0", 1230 | "diff": "4.0.2", 1231 | "escape-string-regexp": "4.0.0", 1232 | "find-up": "5.0.0", 1233 | "glob": "7.1.6", 1234 | "growl": "1.10.5", 1235 | "he": "1.2.0", 1236 | "js-yaml": "3.14.0", 1237 | "log-symbols": "4.0.0", 1238 | "minimatch": "3.0.4", 1239 | "ms": "2.1.2", 1240 | "nanoid": "3.1.12", 1241 | "serialize-javascript": "5.0.1", 1242 | "strip-json-comments": "3.1.1", 1243 | "supports-color": "7.2.0", 1244 | "which": "2.0.2", 1245 | "wide-align": "1.1.3", 1246 | "workerpool": "6.0.2", 1247 | "yargs": "13.3.2", 1248 | "yargs-parser": "13.1.2", 1249 | "yargs-unparser": "2.0.0" 1250 | } 1251 | }, 1252 | "ms": { 1253 | "version": "2.1.2", 1254 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1255 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 1256 | "dev": true 1257 | }, 1258 | "nanoid": { 1259 | "version": "3.1.12", 1260 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.12.tgz", 1261 | "integrity": "sha512-1qstj9z5+x491jfiC4Nelk+f8XBad7LN20PmyWINJEMRSf3wcAjAWysw1qaA8z6NSKe2sjq1hRSDpBH5paCb6A==", 1262 | "dev": true 1263 | }, 1264 | "negotiator": { 1265 | "version": "0.6.2", 1266 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", 1267 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", 1268 | "dev": true 1269 | }, 1270 | "node-preload": { 1271 | "version": "0.2.1", 1272 | "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", 1273 | "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", 1274 | "dev": true, 1275 | "requires": { 1276 | "process-on-spawn": "^1.0.0" 1277 | } 1278 | }, 1279 | "normalize-path": { 1280 | "version": "3.0.0", 1281 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 1282 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 1283 | "dev": true 1284 | }, 1285 | "nyc": { 1286 | "version": "15.1.0", 1287 | "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", 1288 | "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", 1289 | "dev": true, 1290 | "requires": { 1291 | "@istanbuljs/load-nyc-config": "^1.0.0", 1292 | "@istanbuljs/schema": "^0.1.2", 1293 | "caching-transform": "^4.0.0", 1294 | "convert-source-map": "^1.7.0", 1295 | "decamelize": "^1.2.0", 1296 | "find-cache-dir": "^3.2.0", 1297 | "find-up": "^4.1.0", 1298 | "foreground-child": "^2.0.0", 1299 | "get-package-type": "^0.1.0", 1300 | "glob": "^7.1.6", 1301 | "istanbul-lib-coverage": "^3.0.0", 1302 | "istanbul-lib-hook": "^3.0.0", 1303 | "istanbul-lib-instrument": "^4.0.0", 1304 | "istanbul-lib-processinfo": "^2.0.2", 1305 | "istanbul-lib-report": "^3.0.0", 1306 | "istanbul-lib-source-maps": "^4.0.0", 1307 | "istanbul-reports": "^3.0.2", 1308 | "make-dir": "^3.0.0", 1309 | "node-preload": "^0.2.1", 1310 | "p-map": "^3.0.0", 1311 | "process-on-spawn": "^1.0.0", 1312 | "resolve-from": "^5.0.0", 1313 | "rimraf": "^3.0.0", 1314 | "signal-exit": "^3.0.2", 1315 | "spawn-wrap": "^2.0.0", 1316 | "test-exclude": "^6.0.0", 1317 | "yargs": "^15.0.2" 1318 | }, 1319 | "dependencies": { 1320 | "ansi-regex": { 1321 | "version": "5.0.0", 1322 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", 1323 | "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", 1324 | "dev": true 1325 | }, 1326 | "cliui": { 1327 | "version": "6.0.0", 1328 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", 1329 | "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", 1330 | "dev": true, 1331 | "requires": { 1332 | "string-width": "^4.2.0", 1333 | "strip-ansi": "^6.0.0", 1334 | "wrap-ansi": "^6.2.0" 1335 | } 1336 | }, 1337 | "emoji-regex": { 1338 | "version": "8.0.0", 1339 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 1340 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 1341 | "dev": true 1342 | }, 1343 | "find-up": { 1344 | "version": "4.1.0", 1345 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", 1346 | "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", 1347 | "dev": true, 1348 | "requires": { 1349 | "locate-path": "^5.0.0", 1350 | "path-exists": "^4.0.0" 1351 | } 1352 | }, 1353 | "is-fullwidth-code-point": { 1354 | "version": "3.0.0", 1355 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 1356 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 1357 | "dev": true 1358 | }, 1359 | "locate-path": { 1360 | "version": "5.0.0", 1361 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", 1362 | "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", 1363 | "dev": true, 1364 | "requires": { 1365 | "p-locate": "^4.1.0" 1366 | } 1367 | }, 1368 | "p-limit": { 1369 | "version": "2.3.0", 1370 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", 1371 | "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", 1372 | "dev": true, 1373 | "requires": { 1374 | "p-try": "^2.0.0" 1375 | } 1376 | }, 1377 | "p-locate": { 1378 | "version": "4.1.0", 1379 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", 1380 | "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", 1381 | "dev": true, 1382 | "requires": { 1383 | "p-limit": "^2.2.0" 1384 | } 1385 | }, 1386 | "string-width": { 1387 | "version": "4.2.0", 1388 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", 1389 | "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", 1390 | "dev": true, 1391 | "requires": { 1392 | "emoji-regex": "^8.0.0", 1393 | "is-fullwidth-code-point": "^3.0.0", 1394 | "strip-ansi": "^6.0.0" 1395 | } 1396 | }, 1397 | "strip-ansi": { 1398 | "version": "6.0.0", 1399 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", 1400 | "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", 1401 | "dev": true, 1402 | "requires": { 1403 | "ansi-regex": "^5.0.0" 1404 | } 1405 | }, 1406 | "wrap-ansi": { 1407 | "version": "6.2.0", 1408 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", 1409 | "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", 1410 | "dev": true, 1411 | "requires": { 1412 | "ansi-styles": "^4.0.0", 1413 | "string-width": "^4.1.0", 1414 | "strip-ansi": "^6.0.0" 1415 | } 1416 | }, 1417 | "yargs": { 1418 | "version": "15.4.1", 1419 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", 1420 | "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", 1421 | "dev": true, 1422 | "requires": { 1423 | "cliui": "^6.0.0", 1424 | "decamelize": "^1.2.0", 1425 | "find-up": "^4.1.0", 1426 | "get-caller-file": "^2.0.1", 1427 | "require-directory": "^2.1.1", 1428 | "require-main-filename": "^2.0.0", 1429 | "set-blocking": "^2.0.0", 1430 | "string-width": "^4.2.0", 1431 | "which-module": "^2.0.0", 1432 | "y18n": "^4.0.0", 1433 | "yargs-parser": "^18.1.2" 1434 | } 1435 | }, 1436 | "yargs-parser": { 1437 | "version": "18.1.3", 1438 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", 1439 | "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", 1440 | "dev": true, 1441 | "requires": { 1442 | "camelcase": "^5.0.0", 1443 | "decamelize": "^1.2.0" 1444 | } 1445 | } 1446 | } 1447 | }, 1448 | "object-assign": { 1449 | "version": "4.1.1", 1450 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 1451 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", 1452 | "dev": true 1453 | }, 1454 | "once": { 1455 | "version": "1.4.0", 1456 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1457 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 1458 | "dev": true, 1459 | "requires": { 1460 | "wrappy": "1" 1461 | } 1462 | }, 1463 | "p-limit": { 1464 | "version": "3.1.0", 1465 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", 1466 | "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", 1467 | "dev": true, 1468 | "requires": { 1469 | "yocto-queue": "^0.1.0" 1470 | } 1471 | }, 1472 | "p-locate": { 1473 | "version": "5.0.0", 1474 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", 1475 | "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", 1476 | "dev": true, 1477 | "requires": { 1478 | "p-limit": "^3.0.2" 1479 | } 1480 | }, 1481 | "p-map": { 1482 | "version": "3.0.0", 1483 | "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", 1484 | "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", 1485 | "dev": true, 1486 | "requires": { 1487 | "aggregate-error": "^3.0.0" 1488 | } 1489 | }, 1490 | "p-try": { 1491 | "version": "2.2.0", 1492 | "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", 1493 | "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", 1494 | "dev": true 1495 | }, 1496 | "package-hash": { 1497 | "version": "4.0.0", 1498 | "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", 1499 | "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", 1500 | "dev": true, 1501 | "requires": { 1502 | "graceful-fs": "^4.1.15", 1503 | "hasha": "^5.0.0", 1504 | "lodash.flattendeep": "^4.4.0", 1505 | "release-zalgo": "^1.0.0" 1506 | } 1507 | }, 1508 | "parseqs": { 1509 | "version": "0.0.6", 1510 | "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz", 1511 | "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==", 1512 | "dev": true 1513 | }, 1514 | "parseuri": { 1515 | "version": "0.0.6", 1516 | "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz", 1517 | "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==", 1518 | "dev": true 1519 | }, 1520 | "path-exists": { 1521 | "version": "4.0.0", 1522 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 1523 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 1524 | "dev": true 1525 | }, 1526 | "path-is-absolute": { 1527 | "version": "1.0.1", 1528 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1529 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 1530 | "dev": true 1531 | }, 1532 | "path-key": { 1533 | "version": "3.1.1", 1534 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 1535 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 1536 | "dev": true 1537 | }, 1538 | "picomatch": { 1539 | "version": "2.2.2", 1540 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", 1541 | "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", 1542 | "dev": true 1543 | }, 1544 | "pkg-dir": { 1545 | "version": "4.2.0", 1546 | "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", 1547 | "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", 1548 | "dev": true, 1549 | "requires": { 1550 | "find-up": "^4.0.0" 1551 | }, 1552 | "dependencies": { 1553 | "find-up": { 1554 | "version": "4.1.0", 1555 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", 1556 | "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", 1557 | "dev": true, 1558 | "requires": { 1559 | "locate-path": "^5.0.0", 1560 | "path-exists": "^4.0.0" 1561 | } 1562 | }, 1563 | "locate-path": { 1564 | "version": "5.0.0", 1565 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", 1566 | "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", 1567 | "dev": true, 1568 | "requires": { 1569 | "p-locate": "^4.1.0" 1570 | } 1571 | }, 1572 | "p-limit": { 1573 | "version": "2.3.0", 1574 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", 1575 | "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", 1576 | "dev": true, 1577 | "requires": { 1578 | "p-try": "^2.0.0" 1579 | } 1580 | }, 1581 | "p-locate": { 1582 | "version": "4.1.0", 1583 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", 1584 | "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", 1585 | "dev": true, 1586 | "requires": { 1587 | "p-limit": "^2.2.0" 1588 | } 1589 | } 1590 | } 1591 | }, 1592 | "prettier": { 1593 | "version": "2.2.1", 1594 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", 1595 | "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", 1596 | "dev": true 1597 | }, 1598 | "process-on-spawn": { 1599 | "version": "1.0.0", 1600 | "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", 1601 | "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", 1602 | "dev": true, 1603 | "requires": { 1604 | "fromentries": "^1.2.0" 1605 | } 1606 | }, 1607 | "randombytes": { 1608 | "version": "2.1.0", 1609 | "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", 1610 | "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", 1611 | "dev": true, 1612 | "requires": { 1613 | "safe-buffer": "^5.1.0" 1614 | } 1615 | }, 1616 | "readdirp": { 1617 | "version": "3.5.0", 1618 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", 1619 | "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", 1620 | "dev": true, 1621 | "requires": { 1622 | "picomatch": "^2.2.1" 1623 | } 1624 | }, 1625 | "release-zalgo": { 1626 | "version": "1.0.0", 1627 | "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", 1628 | "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", 1629 | "dev": true, 1630 | "requires": { 1631 | "es6-error": "^4.0.1" 1632 | } 1633 | }, 1634 | "require-directory": { 1635 | "version": "2.1.1", 1636 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 1637 | "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", 1638 | "dev": true 1639 | }, 1640 | "require-main-filename": { 1641 | "version": "2.0.0", 1642 | "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", 1643 | "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", 1644 | "dev": true 1645 | }, 1646 | "resolve-from": { 1647 | "version": "5.0.0", 1648 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", 1649 | "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", 1650 | "dev": true 1651 | }, 1652 | "rimraf": { 1653 | "version": "3.0.2", 1654 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", 1655 | "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", 1656 | "dev": true, 1657 | "requires": { 1658 | "glob": "^7.1.3" 1659 | } 1660 | }, 1661 | "safe-buffer": { 1662 | "version": "5.2.1", 1663 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 1664 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 1665 | "dev": true 1666 | }, 1667 | "semver": { 1668 | "version": "6.3.0", 1669 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", 1670 | "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", 1671 | "dev": true 1672 | }, 1673 | "serialize-javascript": { 1674 | "version": "5.0.1", 1675 | "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", 1676 | "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", 1677 | "dev": true, 1678 | "requires": { 1679 | "randombytes": "^2.1.0" 1680 | } 1681 | }, 1682 | "set-blocking": { 1683 | "version": "2.0.0", 1684 | "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", 1685 | "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", 1686 | "dev": true 1687 | }, 1688 | "shebang-command": { 1689 | "version": "2.0.0", 1690 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 1691 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 1692 | "dev": true, 1693 | "requires": { 1694 | "shebang-regex": "^3.0.0" 1695 | } 1696 | }, 1697 | "shebang-regex": { 1698 | "version": "3.0.0", 1699 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 1700 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 1701 | "dev": true 1702 | }, 1703 | "signal-exit": { 1704 | "version": "3.0.3", 1705 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", 1706 | "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", 1707 | "dev": true 1708 | }, 1709 | "socket.io": { 1710 | "version": "3.1.0", 1711 | "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-3.1.0.tgz", 1712 | "integrity": "sha512-Aqg2dlRh6xSJvRYK31ksG65q4kmBOqU4g+1ukhPcoT6wNGYoIwSYPlCPuRwOO9pgLUajojGFztl6+V2opmKcww==", 1713 | "dev": true, 1714 | "requires": { 1715 | "@types/cookie": "^0.4.0", 1716 | "@types/cors": "^2.8.8", 1717 | "@types/node": "^14.14.10", 1718 | "accepts": "~1.3.4", 1719 | "base64id": "~2.0.0", 1720 | "debug": "~4.3.1", 1721 | "engine.io": "~4.1.0", 1722 | "socket.io-adapter": "~2.1.0", 1723 | "socket.io-parser": "~4.0.3" 1724 | }, 1725 | "dependencies": { 1726 | "debug": { 1727 | "version": "4.3.1", 1728 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", 1729 | "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", 1730 | "dev": true, 1731 | "requires": { 1732 | "ms": "2.1.2" 1733 | } 1734 | } 1735 | } 1736 | }, 1737 | "socket.io-adapter": { 1738 | "version": "2.1.0", 1739 | "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.1.0.tgz", 1740 | "integrity": "sha512-+vDov/aTsLjViYTwS9fPy5pEtTkrbEKsw2M+oVSoFGw6OD1IpvlV1VPhUzNbofCQ8oyMbdYJqDtGdmHQK6TdPg==", 1741 | "dev": true 1742 | }, 1743 | "socket.io-client": { 1744 | "version": "3.1.0", 1745 | "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-3.1.0.tgz", 1746 | "integrity": "sha512-T4qPOL80KnoBwkdR70zMpiR6aH6zv3ZqLNriofHqsO9wvQllNTOez0mpV4GdVqo1Y55Z+h8YOlBo7c8pOxDlHw==", 1747 | "dev": true, 1748 | "requires": { 1749 | "@types/component-emitter": "^1.2.10", 1750 | "backo2": "~1.0.2", 1751 | "component-emitter": "~1.3.0", 1752 | "debug": "~4.3.1", 1753 | "engine.io-client": "~4.1.0", 1754 | "parseuri": "0.0.6", 1755 | "socket.io-parser": "~4.0.4" 1756 | }, 1757 | "dependencies": { 1758 | "debug": { 1759 | "version": "4.3.1", 1760 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", 1761 | "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", 1762 | "dev": true, 1763 | "requires": { 1764 | "ms": "2.1.2" 1765 | } 1766 | } 1767 | } 1768 | }, 1769 | "socket.io-parser": { 1770 | "version": "4.0.4", 1771 | "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz", 1772 | "integrity": "sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==", 1773 | "dev": true, 1774 | "requires": { 1775 | "@types/component-emitter": "^1.2.10", 1776 | "component-emitter": "~1.3.0", 1777 | "debug": "~4.3.1" 1778 | }, 1779 | "dependencies": { 1780 | "debug": { 1781 | "version": "4.3.1", 1782 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", 1783 | "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", 1784 | "dev": true, 1785 | "requires": { 1786 | "ms": "2.1.2" 1787 | } 1788 | } 1789 | } 1790 | }, 1791 | "source-map": { 1792 | "version": "0.5.7", 1793 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", 1794 | "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", 1795 | "dev": true 1796 | }, 1797 | "spawn-wrap": { 1798 | "version": "2.0.0", 1799 | "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", 1800 | "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", 1801 | "dev": true, 1802 | "requires": { 1803 | "foreground-child": "^2.0.0", 1804 | "is-windows": "^1.0.2", 1805 | "make-dir": "^3.0.0", 1806 | "rimraf": "^3.0.0", 1807 | "signal-exit": "^3.0.2", 1808 | "which": "^2.0.1" 1809 | } 1810 | }, 1811 | "sprintf-js": { 1812 | "version": "1.0.3", 1813 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 1814 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", 1815 | "dev": true 1816 | }, 1817 | "string-width": { 1818 | "version": "2.1.1", 1819 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", 1820 | "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", 1821 | "dev": true, 1822 | "requires": { 1823 | "is-fullwidth-code-point": "^2.0.0", 1824 | "strip-ansi": "^4.0.0" 1825 | } 1826 | }, 1827 | "strip-ansi": { 1828 | "version": "4.0.0", 1829 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", 1830 | "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", 1831 | "dev": true, 1832 | "requires": { 1833 | "ansi-regex": "^3.0.0" 1834 | } 1835 | }, 1836 | "strip-bom": { 1837 | "version": "4.0.0", 1838 | "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", 1839 | "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", 1840 | "dev": true 1841 | }, 1842 | "strip-json-comments": { 1843 | "version": "3.1.1", 1844 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 1845 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 1846 | "dev": true 1847 | }, 1848 | "supports-color": { 1849 | "version": "7.2.0", 1850 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 1851 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 1852 | "dev": true, 1853 | "requires": { 1854 | "has-flag": "^4.0.0" 1855 | } 1856 | }, 1857 | "test-exclude": { 1858 | "version": "6.0.0", 1859 | "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", 1860 | "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", 1861 | "dev": true, 1862 | "requires": { 1863 | "@istanbuljs/schema": "^0.1.2", 1864 | "glob": "^7.1.4", 1865 | "minimatch": "^3.0.4" 1866 | } 1867 | }, 1868 | "to-fast-properties": { 1869 | "version": "2.0.0", 1870 | "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", 1871 | "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", 1872 | "dev": true 1873 | }, 1874 | "to-regex-range": { 1875 | "version": "5.0.1", 1876 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 1877 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 1878 | "dev": true, 1879 | "requires": { 1880 | "is-number": "^7.0.0" 1881 | } 1882 | }, 1883 | "type-fest": { 1884 | "version": "0.8.1", 1885 | "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", 1886 | "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", 1887 | "dev": true 1888 | }, 1889 | "typedarray-to-buffer": { 1890 | "version": "3.1.5", 1891 | "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", 1892 | "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", 1893 | "dev": true, 1894 | "requires": { 1895 | "is-typedarray": "^1.0.0" 1896 | } 1897 | }, 1898 | "uuid": { 1899 | "version": "3.4.0", 1900 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", 1901 | "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", 1902 | "dev": true 1903 | }, 1904 | "vary": { 1905 | "version": "1.1.2", 1906 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 1907 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", 1908 | "dev": true 1909 | }, 1910 | "which": { 1911 | "version": "2.0.2", 1912 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 1913 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 1914 | "dev": true, 1915 | "requires": { 1916 | "isexe": "^2.0.0" 1917 | } 1918 | }, 1919 | "which-module": { 1920 | "version": "2.0.0", 1921 | "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", 1922 | "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", 1923 | "dev": true 1924 | }, 1925 | "wide-align": { 1926 | "version": "1.1.3", 1927 | "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", 1928 | "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", 1929 | "dev": true, 1930 | "requires": { 1931 | "string-width": "^1.0.2 || 2" 1932 | } 1933 | }, 1934 | "workerpool": { 1935 | "version": "6.0.2", 1936 | "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.2.tgz", 1937 | "integrity": "sha512-DSNyvOpFKrNusaaUwk+ej6cBj1bmhLcBfj80elGk+ZIo5JSkq+unB1dLKEOcNfJDZgjGICfhQ0Q5TbP0PvF4+Q==", 1938 | "dev": true 1939 | }, 1940 | "wrap-ansi": { 1941 | "version": "5.1.0", 1942 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", 1943 | "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", 1944 | "dev": true, 1945 | "requires": { 1946 | "ansi-styles": "^3.2.0", 1947 | "string-width": "^3.0.0", 1948 | "strip-ansi": "^5.0.0" 1949 | }, 1950 | "dependencies": { 1951 | "ansi-regex": { 1952 | "version": "4.1.0", 1953 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", 1954 | "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", 1955 | "dev": true 1956 | }, 1957 | "ansi-styles": { 1958 | "version": "3.2.1", 1959 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 1960 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 1961 | "dev": true, 1962 | "requires": { 1963 | "color-convert": "^1.9.0" 1964 | } 1965 | }, 1966 | "color-convert": { 1967 | "version": "1.9.3", 1968 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 1969 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 1970 | "dev": true, 1971 | "requires": { 1972 | "color-name": "1.1.3" 1973 | } 1974 | }, 1975 | "color-name": { 1976 | "version": "1.1.3", 1977 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 1978 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 1979 | "dev": true 1980 | }, 1981 | "string-width": { 1982 | "version": "3.1.0", 1983 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", 1984 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", 1985 | "dev": true, 1986 | "requires": { 1987 | "emoji-regex": "^7.0.1", 1988 | "is-fullwidth-code-point": "^2.0.0", 1989 | "strip-ansi": "^5.1.0" 1990 | } 1991 | }, 1992 | "strip-ansi": { 1993 | "version": "5.2.0", 1994 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", 1995 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", 1996 | "dev": true, 1997 | "requires": { 1998 | "ansi-regex": "^4.1.0" 1999 | } 2000 | } 2001 | } 2002 | }, 2003 | "wrappy": { 2004 | "version": "1.0.2", 2005 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 2006 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 2007 | "dev": true 2008 | }, 2009 | "write-file-atomic": { 2010 | "version": "3.0.3", 2011 | "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", 2012 | "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", 2013 | "dev": true, 2014 | "requires": { 2015 | "imurmurhash": "^0.1.4", 2016 | "is-typedarray": "^1.0.0", 2017 | "signal-exit": "^3.0.2", 2018 | "typedarray-to-buffer": "^3.1.5" 2019 | } 2020 | }, 2021 | "ws": { 2022 | "version": "7.4.2", 2023 | "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.2.tgz", 2024 | "integrity": "sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA==", 2025 | "dev": true 2026 | }, 2027 | "xmlhttprequest-ssl": { 2028 | "version": "1.5.5", 2029 | "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", 2030 | "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=", 2031 | "dev": true 2032 | }, 2033 | "y18n": { 2034 | "version": "4.0.1", 2035 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", 2036 | "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", 2037 | "dev": true 2038 | }, 2039 | "yargs": { 2040 | "version": "13.3.2", 2041 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", 2042 | "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", 2043 | "dev": true, 2044 | "requires": { 2045 | "cliui": "^5.0.0", 2046 | "find-up": "^3.0.0", 2047 | "get-caller-file": "^2.0.1", 2048 | "require-directory": "^2.1.1", 2049 | "require-main-filename": "^2.0.0", 2050 | "set-blocking": "^2.0.0", 2051 | "string-width": "^3.0.0", 2052 | "which-module": "^2.0.0", 2053 | "y18n": "^4.0.0", 2054 | "yargs-parser": "^13.1.2" 2055 | }, 2056 | "dependencies": { 2057 | "ansi-regex": { 2058 | "version": "4.1.0", 2059 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", 2060 | "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", 2061 | "dev": true 2062 | }, 2063 | "find-up": { 2064 | "version": "3.0.0", 2065 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", 2066 | "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", 2067 | "dev": true, 2068 | "requires": { 2069 | "locate-path": "^3.0.0" 2070 | } 2071 | }, 2072 | "locate-path": { 2073 | "version": "3.0.0", 2074 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", 2075 | "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", 2076 | "dev": true, 2077 | "requires": { 2078 | "p-locate": "^3.0.0", 2079 | "path-exists": "^3.0.0" 2080 | } 2081 | }, 2082 | "p-limit": { 2083 | "version": "2.3.0", 2084 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", 2085 | "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", 2086 | "dev": true, 2087 | "requires": { 2088 | "p-try": "^2.0.0" 2089 | } 2090 | }, 2091 | "p-locate": { 2092 | "version": "3.0.0", 2093 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", 2094 | "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", 2095 | "dev": true, 2096 | "requires": { 2097 | "p-limit": "^2.0.0" 2098 | } 2099 | }, 2100 | "path-exists": { 2101 | "version": "3.0.0", 2102 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", 2103 | "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", 2104 | "dev": true 2105 | }, 2106 | "string-width": { 2107 | "version": "3.1.0", 2108 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", 2109 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", 2110 | "dev": true, 2111 | "requires": { 2112 | "emoji-regex": "^7.0.1", 2113 | "is-fullwidth-code-point": "^2.0.0", 2114 | "strip-ansi": "^5.1.0" 2115 | } 2116 | }, 2117 | "strip-ansi": { 2118 | "version": "5.2.0", 2119 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", 2120 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", 2121 | "dev": true, 2122 | "requires": { 2123 | "ansi-regex": "^4.1.0" 2124 | } 2125 | } 2126 | } 2127 | }, 2128 | "yargs-parser": { 2129 | "version": "13.1.2", 2130 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", 2131 | "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", 2132 | "dev": true, 2133 | "requires": { 2134 | "camelcase": "^5.0.0", 2135 | "decamelize": "^1.2.0" 2136 | } 2137 | }, 2138 | "yargs-unparser": { 2139 | "version": "2.0.0", 2140 | "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", 2141 | "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", 2142 | "dev": true, 2143 | "requires": { 2144 | "camelcase": "^6.0.0", 2145 | "decamelize": "^4.0.0", 2146 | "flat": "^5.0.2", 2147 | "is-plain-obj": "^2.1.0" 2148 | }, 2149 | "dependencies": { 2150 | "camelcase": { 2151 | "version": "6.2.0", 2152 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", 2153 | "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", 2154 | "dev": true 2155 | }, 2156 | "decamelize": { 2157 | "version": "4.0.0", 2158 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", 2159 | "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", 2160 | "dev": true 2161 | } 2162 | } 2163 | }, 2164 | "yeast": { 2165 | "version": "0.1.2", 2166 | "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", 2167 | "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=", 2168 | "dev": true 2169 | }, 2170 | "yocto-queue": { 2171 | "version": "0.1.0", 2172 | "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", 2173 | "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", 2174 | "dev": true 2175 | } 2176 | } 2177 | } 2178 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@socket.io/sticky", 3 | "version": "1.0.4", 4 | "description": "An alternative to the sticky-session package (https://github.com/indutny/sticky-session)", 5 | "main": "index.js", 6 | "types": "index.d.ts", 7 | "scripts": { 8 | "format:check": "prettier --check \"index.js\" \"test/**/*.js\"", 9 | "format:fix": "prettier --write \"index.js\" \"test/**/*.js\"", 10 | "test": "npm run format:check && nyc mocha --timeout 5000" 11 | }, 12 | "files": [ 13 | "index.js", 14 | "index.d.ts" 15 | ], 16 | "author": "Damien Arrachequesne ", 17 | "license": "MIT", 18 | "repository": { 19 | "type": "git", 20 | "url": "git+https://github.com/socketio/socket.io-sticky.git" 21 | }, 22 | "homepage": "https://github.com/socketio/socket.io-sticky#readme", 23 | "devDependencies": { 24 | "mocha": "^8.2.1", 25 | "nyc": "^15.1.0", 26 | "prettier": "^2.2.1", 27 | "socket.io": "^3.1.0", 28 | "socket.io-client": "^3.1.0" 29 | }, 30 | "keywords": [ 31 | "socket.io", 32 | "websocket", 33 | "cluster", 34 | "node.js" 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /test/fixtures/connection.js: -------------------------------------------------------------------------------- 1 | const cluster = require("cluster"); 2 | const http = require("http"); 3 | const { Server } = require("socket.io"); 4 | const ioc = require("socket.io-client"); 5 | const { setupMaster, setupWorker } = require("../.."); 6 | 7 | if (cluster.isWorker) { 8 | const httpServer = http.createServer(); 9 | const io = new Server(httpServer, { 10 | maxHttpBufferSize: 1.4 * 1e6, // to account for the base64 encoding of the binary payload 11 | }); 12 | setupWorker(io); 13 | 14 | io.on("connection", (socket) => { 15 | socket.on("foo", (val) => { 16 | socket.emit("bar", val); 17 | }); 18 | }); 19 | 20 | return; 21 | } 22 | 23 | const WORKER_COUNT = 3; 24 | 25 | for (let i = 0; i < WORKER_COUNT; i++) { 26 | cluster.fork(); 27 | } 28 | 29 | const httpServer = http.createServer(); 30 | 31 | setupMaster(httpServer, { 32 | loadBalancingMethod: process.env.LB_METHOD || "least-connection", 33 | }); 34 | 35 | const waitFor = (emitter, event) => { 36 | return new Promise((resolve) => { 37 | emitter.once(event, resolve); 38 | }); 39 | }; 40 | 41 | httpServer.listen(async () => { 42 | const port = httpServer.address().port; 43 | 44 | const socket = ioc(`http://localhost:${port}`, { 45 | transports: process.env.TRANSPORT 46 | ? [process.env.TRANSPORT] 47 | : ["polling", "websocket"], 48 | }); 49 | 50 | await waitFor(socket, "connect"); 51 | 52 | socket.disconnect().connect(); 53 | await waitFor(socket, "connect"); 54 | 55 | socket.disconnect().connect(); 56 | await waitFor(socket, "connect"); 57 | 58 | socket.emit("foo", "hello"); 59 | await waitFor(socket, "bar"); 60 | 61 | socket.emit("foo", Buffer.allocUnsafe(1e6)); 62 | await waitFor(socket, "bar"); 63 | 64 | // cleanup 65 | for (const id in cluster.workers) { 66 | cluster.workers[id].kill(); 67 | } 68 | httpServer.close(); 69 | socket.disconnect(); 70 | }); 71 | -------------------------------------------------------------------------------- /test/fixtures/cors.js: -------------------------------------------------------------------------------- 1 | const cluster = require("cluster"); 2 | const { createServer, request } = require("http"); 3 | const { Server } = require("socket.io"); 4 | const { setupMaster, setupWorker } = require("../.."); 5 | const assert = require("assert").strict; 6 | const { sendRequest } = require("./util"); 7 | 8 | if (cluster.isWorker) { 9 | const httpServer = createServer(); 10 | const io = new Server(httpServer, { 11 | cors: { 12 | origin: true, 13 | }, 14 | }); 15 | setupWorker(io); 16 | 17 | io.on("connection", (socket) => { 18 | socket.on("foo", (val) => { 19 | socket.emit("bar", val); 20 | }); 21 | }); 22 | 23 | return; 24 | } 25 | 26 | const WORKER_COUNT = 3; 27 | 28 | for (let i = 0; i < WORKER_COUNT; i++) { 29 | cluster.fork(); 30 | } 31 | 32 | const httpServer = createServer(); 33 | 34 | setupMaster(httpServer, { 35 | loadBalancingMethod: process.env.LB_METHOD || "least-connection", 36 | }); 37 | 38 | const waitFor = (emitter, event) => { 39 | return new Promise((resolve) => { 40 | emitter.once(event, resolve); 41 | }); 42 | }; 43 | 44 | httpServer.listen(async () => { 45 | const port = httpServer.address().port; 46 | 47 | const res = await sendRequest({ 48 | port, 49 | method: "options", 50 | path: "/socket.io/", 51 | headers: { 52 | origin: "https://example.com", 53 | }, 54 | }); 55 | 56 | assert.equal(res.statusCode, 204); 57 | 58 | assert.equal( 59 | res.headers["access-control-allow-origin"], 60 | "https://example.com" 61 | ); 62 | 63 | // cleanup 64 | for (const id in cluster.workers) { 65 | cluster.workers[id].kill(); 66 | } 67 | httpServer.close(); 68 | }); 69 | -------------------------------------------------------------------------------- /test/fixtures/failing.js: -------------------------------------------------------------------------------- 1 | const cluster = require("cluster"); 2 | const { createServer, Agent } = require("http"); 3 | const { Server } = require("socket.io"); 4 | const { setupMaster, setupWorker } = require("../.."); 5 | const assert = require("assert").strict; 6 | const { sendRequest } = require("./util"); 7 | 8 | if (cluster.isWorker) { 9 | const httpServer = createServer(); 10 | const io = new Server(httpServer); 11 | setupWorker(io); 12 | 13 | io.on("connection", (socket) => { 14 | socket.on("foo", (val) => { 15 | socket.emit("bar", val); 16 | }); 17 | }); 18 | 19 | return; 20 | } 21 | 22 | const WORKER_COUNT = 3; 23 | 24 | for (let i = 0; i < WORKER_COUNT; i++) { 25 | cluster.fork(); 26 | } 27 | 28 | const httpServer = createServer(); 29 | 30 | setupMaster(httpServer, { 31 | loadBalancingMethod: process.env.LB_METHOD || "least-connection", 32 | }); 33 | 34 | httpServer.listen(async () => { 35 | const port = httpServer.address().port; 36 | 37 | const agent1 = new Agent({ keepAlive: true }); 38 | const agent2 = new Agent({ keepAlive: true }); 39 | 40 | // Engine.IO handshake on connection 1 41 | const res1 = await sendRequest({ 42 | agent: agent1, 43 | port, 44 | method: "get", 45 | path: "/socket.io/?EIO=4&transport=polling", 46 | }); 47 | 48 | assert.equal(res1.statusCode, 200); 49 | 50 | const sid = JSON.parse(res1.body.toString().substring(1)).sid; 51 | 52 | // Engine.IO handshake on connection 2 53 | const res2 = await sendRequest({ 54 | agent: agent2, 55 | port, 56 | method: "get", 57 | path: "/socket.io/?EIO=4&transport=polling", 58 | }); 59 | 60 | assert.equal(res2.statusCode, 200); 61 | 62 | // Socket.IO handshake on connection 2 63 | const res3 = await sendRequest( 64 | { 65 | agent: agent2, 66 | port, 67 | method: "post", 68 | path: `/socket.io/?EIO=4&transport=polling&sid=${sid}`, 69 | }, 70 | "40" 71 | ); 72 | 73 | // FIXME "session ID unknown" 74 | assert.equal(res3.statusCode, 400); 75 | 76 | // cleanup 77 | for (const id in cluster.workers) { 78 | cluster.workers[id].kill(); 79 | } 80 | httpServer.close(); 81 | }); 82 | -------------------------------------------------------------------------------- /test/fixtures/util.js: -------------------------------------------------------------------------------- 1 | const { request } = require("http"); 2 | 3 | module.exports.sendRequest = function sendRequest(options, data) { 4 | return new Promise((resolve) => { 5 | if (data) { 6 | options.headers = options.headers || {}; 7 | options.headers["content-length"] = Buffer.byteLength(data); 8 | } 9 | 10 | const req = request(options, (res) => { 11 | const chunks = []; 12 | 13 | res.on("data", (chunk) => { 14 | chunks.push(chunk); 15 | }); 16 | 17 | res.on("end", () => { 18 | res.body = Buffer.concat(chunks); 19 | resolve(res); 20 | }); 21 | }); 22 | 23 | if (data) { 24 | req.write(data); 25 | } 26 | 27 | req.end(); 28 | }); 29 | }; 30 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | const { join } = require("path"); 2 | const { exec } = require("child_process"); 3 | const { createServer } = require("http"); 4 | const { setupMaster } = require("../index"); 5 | const ioc = require("socket.io-client"); 6 | 7 | const fixture = (filename) => { 8 | return ( 9 | '"' + process.execPath + '" "' + join(__dirname, "fixtures", filename) + '"' 10 | ); 11 | }; 12 | 13 | function waitFor(emitter, event) { 14 | return new Promise((resolve) => { 15 | emitter.once(event, resolve); 16 | }); 17 | } 18 | 19 | function success(done, httpServer, socket) { 20 | httpServer.close(); 21 | socket.disconnect(); 22 | done(); 23 | } 24 | 25 | describe("@socket.io/sticky", () => { 26 | it("should work with least-connection load-balancing", (done) => { 27 | exec(fixture("connection.js"), done); 28 | }); 29 | 30 | it("should work with round-robin load-balancing", (done) => { 31 | exec(fixture("connection.js"), { env: { LB_METHOD: "round-robin" } }, done); 32 | }); 33 | 34 | it("should work with random load-balancing", (done) => { 35 | exec(fixture("connection.js"), { env: { LB_METHOD: "random" } }, done); 36 | }); 37 | 38 | it("should work with WebSocket only", (done) => { 39 | exec(fixture("connection.js"), { env: { TRANSPORT: "websocket" } }, done); 40 | }); 41 | 42 | it("should work with HTTP long-polling only", (done) => { 43 | exec(fixture("connection.js"), { env: { TRANSPORT: "polling" } }, done); 44 | }); 45 | 46 | it("should work with CORS", (done) => { 47 | exec(fixture("cors.js"), done); 48 | }); 49 | 50 | it("should return a 503 error when no worker is available (polling)", (done) => { 51 | const httpServer = createServer(); 52 | 53 | setupMaster(httpServer, { 54 | loadBalancingMethod: "least-connection", 55 | }); 56 | 57 | httpServer.listen(async () => { 58 | const { port } = httpServer.address(); 59 | 60 | const socket = ioc(`http://localhost:${port}`, { 61 | transports: ["polling"], 62 | }); 63 | 64 | await waitFor(socket, "connect_error"); 65 | 66 | success(done, httpServer, socket); 67 | }); 68 | }); 69 | 70 | it("should return a 503 error when no worker is available (websocket)", (done) => { 71 | const httpServer = createServer(); 72 | 73 | setupMaster(httpServer, { 74 | loadBalancingMethod: "least-connection", 75 | }); 76 | 77 | httpServer.listen(async () => { 78 | const { port } = httpServer.address(); 79 | 80 | const socket = ioc(`http://localhost:${port}`, { 81 | transports: ["websocket"], 82 | }); 83 | 84 | await waitFor(socket, "connect_error"); 85 | 86 | success(done, httpServer, socket); 87 | }); 88 | }); 89 | }); 90 | --------------------------------------------------------------------------------