From f2e3d162ab4b294aa53091bf3b0aa38551de7bf5 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Mon, 14 Jun 2021 07:59:12 +0200 Subject: [PATCH] Initial commit --- .github/workflows/ci.yml | 42 + .gitignore | 3 + CHANGELOG.md | 0 LICENSE | 7 + README.md | 141 +++ assets/emitter.excalidraw | 1380 +++++++++++++++++++++ assets/emitter.png | Bin 0 -> 102189 bytes docker-compose.yml | 9 + lib/index.ts | 533 +++++++++ lib/typed-events.ts | 37 + package-lock.json | 2377 +++++++++++++++++++++++++++++++++++++ package.json | 48 + test/index.ts | 278 +++++ test/util.ts | 13 + tsconfig.json | 12 + 15 files changed, 4880 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 .gitignore create mode 100644 CHANGELOG.md create mode 100644 LICENSE create mode 100644 README.md create mode 100644 assets/emitter.excalidraw create mode 100644 assets/emitter.png create mode 100644 docker-compose.yml create mode 100644 lib/index.ts create mode 100644 lib/typed-events.ts create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 test/index.ts create mode 100644 test/util.ts create mode 100644 tsconfig.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..7f12b300 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,42 @@ +name: CI + +on: + push: + pull_request: + schedule: + - cron: '0 0 * * 0' + +jobs: + test-node: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [12.x, 14.x, 16.x] + + services: + postgres: + image: postgres + env: + POSTGRES_PASSWORD: changeit + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + + - run: npm ci + + - run: npm test + env: + CI: true + timeout-minutes: 10 diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..044c5480 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules +dist/ +.nyc_output/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..e69de29b diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..afc17bc3 --- /dev/null +++ b/LICENSE @@ -0,0 +1,7 @@ +Copyright (c) 2021 Damien Arrachequesne (@darrachequesne) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 00000000..048416b5 --- /dev/null +++ b/README.md @@ -0,0 +1,141 @@ +# Socket.IO Postgres emitter + +The `@socket.io/postgres-emitter` package allows you to easily communicate with a group of Socket.IO servers from another Node.js process (server-side). + +![Emitter diagram](./assets/emitter.png) + +It must be used in conjunction with [`@socket.io/posgres-adapter`](https://github.com/socketio/socket.io-posgres-adapter/). + +Supported features: + +- [broadcasting](https://socket.io/docs/v4/broadcasting-events/) +- [utility methods](https://socket.io/docs/v4/server-instance/#Utility-methods) + - [`socketsJoin`](https://socket.io/docs/v4/server-instance/#socketsJoin) + - [`socketsLeave`](https://socket.io/docs/v4/server-instance/#socketsLeave) + - [`disconnectSockets`](https://socket.io/docs/v4/server-instance/#disconnectSockets) + - [`serverSideEmit`](https://socket.io/docs/v4/server-instance/#serverSideEmit) + +**Table of contents** + +- [Installation](#installation) +- [Usage](#usage) +- [API](#api) +- [Known errors](#known-errors) +- [License](#license) + +## Installation + +``` +npm install @socket.io/postgres-emitter pg +``` + +For TypeScript users, you might also need `@types/pg`. + +## Usage + +```js +const { Emitter } = require("@socket.io/postgres-emitter"); +const { Pool } = require("pg"); + +const pool = new Pool({ + user: "postgres", + host: "localhost", + database: "postgres", + password: "changeit", + port: 5432, +}); + +const io = new Emitter(pool); + +setInterval(() => { + io.emit("ping", new Date()); +}, 1000); +``` + +## API + +### `Emitter(pool[, nsp][, opts])` + +```js +const io = new Emitter(pool); +``` + +The `pool` argument is a [Pool object](https://node-postgres.com/api/pool) from the `pg` package. + +### `Emitter#to(room:string):BroadcastOperator` +### `Emitter#in(room:string):BroadcastOperator` + +Specifies a specific `room` that you want to emit to. + +```js +io.to("room1").emit("hello"); +``` + +### `Emitter#except(room:string):BroadcastOperator` + +Specifies a specific `room` that you want to exclude from broadcasting. + +```js +io.except("room2").emit("hello"); +``` + +### `Emitter#of(namespace:string):Emitter` + +Specifies a specific namespace that you want to emit to. + +```js +const customNamespace = io.of("/custom"); + +customNamespace.emit("hello"); +``` + +### `Emitter#socketsJoin(rooms:string|string[])` + +Makes the matching socket instances join the specified rooms: + +```js +// make all Socket instances join the "room1" room +io.socketsJoin("room1"); + +// make all Socket instances of the "admin" namespace in the "room1" room join the "room2" room +io.of("/admin").in("room1").socketsJoin("room2"); +``` + +### `Emitter#socketsLeave(rooms:string|string[])` + +Makes the matching socket instances leave the specified rooms: + +```js +// make all Socket instances leave the "room1" room +io.socketsLeave("room1"); + +// make all Socket instances of the "admin" namespace in the "room1" room leave the "room2" room +io.of("/admin").in("room1").socketsLeave("room2"); +``` + +### `Emitter#disconnectSockets(close:boolean)` + +Makes the matching socket instances disconnect: + +```js +// make all Socket instances disconnect +io.disconnectSockets(); + +// make all Socket instances of the "admin" namespace in the "room1" room disconnect +io.of("/admin").in("room1").disconnectSockets(); + +// this also works with a single socket ID +io.of("/admin").in(theSocketId).disconnectSockets(); +``` + +### `Emitter#serverSideEmit(ev:string[,...args:any[]])` + +Emits an event that will be received by each Socket.IO server of the cluster. + +```js +io.serverSideEmit("ping"); +``` + +## License + +[MIT](LICENSE) diff --git a/assets/emitter.excalidraw b/assets/emitter.excalidraw new file mode 100644 index 00000000..d80d011f --- /dev/null +++ b/assets/emitter.excalidraw @@ -0,0 +1,1380 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "type": "text", + "version": 124, + "versionNonce": 379934487, + "isDeleted": false, + "id": "5hUB5ALUlsn26W0PzU4fM", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 894, + "y": 3.5, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 64, + "height": 25, + "seed": 28708370, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElementIds": [], + "fontSize": 20, + "fontFamily": 1, + "text": "socket", + "baseline": 18, + "textAlign": "center", + "verticalAlign": "middle" + }, + { + "type": "rectangle", + "version": 124, + "versionNonce": 307268057, + "isDeleted": false, + "id": "lmQ4o4New7xuXQLwavuSn", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 824, + "y": -80, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 277, + "height": 311, + "seed": 1594950354, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElementIds": [ + "_wBO22vaQplcoKyBXbWRC", + "BZVwnsrGk9G-X87ZHkh-6" + ] + }, + { + "type": "text", + "version": 65, + "versionNonce": 1614306359, + "isDeleted": false, + "id": "ZQsZmj4NaTubBHMkVG2dl", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 843, + "y": -69, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 85, + "height": 26, + "seed": 126533902, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElementIds": [], + "fontSize": 20, + "fontFamily": 1, + "text": "Server A", + "baseline": 18, + "textAlign": "left", + "verticalAlign": "top" + }, + { + "type": "arrow", + "version": 143, + "versionNonce": 754409079, + "isDeleted": false, + "id": "ABQydsvmkN5ptLyYQaUA3", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 837.8983868047594, + "y": 21.868707241881623, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 251.33111393617446, + "height": 0.7613046474941143, + "seed": 1466702734, + "groupIds": [], + "strokeSharpness": "round", + "boundElementIds": [], + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -251.33111393617446, + -0.7613046474941143 + ] + ] + }, + { + "type": "rectangle", + "version": 150, + "versionNonce": 1198374231, + "isDeleted": false, + "id": "x54ljUV2PW8AfubZ6fiVJ", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 436, + "y": -8, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 129, + "height": 56, + "seed": 486293390, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElementIds": [] + }, + { + "type": "arrow", + "version": 159, + "versionNonce": 563246999, + "isDeleted": false, + "id": "zdzgdf3hgOYX0SgjEtyIZ", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 837.2434176281095, + "y": 87.19281457587147, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 247.23231148719788, + "height": 2.2114410393964476, + "seed": 1674715794, + "groupIds": [], + "strokeSharpness": "round", + "boundElementIds": [], + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -247.23231148719788, + 2.2114410393964476 + ] + ] + }, + { + "type": "text", + "version": 127, + "versionNonce": 530184823, + "isDeleted": false, + "id": "dXknKeuYe3X3K-0Hw9P95", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 484, + "y": 9, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 40, + "height": 20, + "seed": 1858283854, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElementIds": [], + "fontSize": 16, + "fontFamily": 1, + "text": "client", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top" + }, + { + "type": "rectangle", + "version": 170, + "versionNonce": 988957817, + "isDeleted": false, + "id": "Ce1Lw4MMOtiunstd3FPJv", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 437.5, + "y": 67, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 129, + "height": 56, + "seed": 568384654, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElementIds": [] + }, + { + "type": "text", + "version": 148, + "versionNonce": 1568773015, + "isDeleted": false, + "id": "rcCUGk-XM0jKzcGaeO0iS", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 484.5, + "y": 84, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 40, + "height": 20, + "seed": 244546386, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElementIds": [], + "fontSize": 16, + "fontFamily": 1, + "text": "client", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top" + }, + { + "type": "rectangle", + "version": 211, + "versionNonce": 1673811289, + "isDeleted": false, + "id": "4iido5zQ7QhoIfnOzWp3h", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 437.5, + "y": 142, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 129, + "height": 56, + "seed": 1055485070, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElementIds": [] + }, + { + "type": "text", + "version": 189, + "versionNonce": 1767442615, + "isDeleted": false, + "id": "D1E2DkimaDb8hGxIfXKmq", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 484.5, + "y": 159, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 40, + "height": 20, + "seed": 270265170, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElementIds": [], + "fontSize": 16, + "fontFamily": 1, + "text": "client", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top" + }, + { + "type": "rectangle", + "version": 182, + "versionNonce": 992100921, + "isDeleted": false, + "id": "RRrk3Vsl-pM8Z1r8Fj3Vu", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 860.5, + "y": -8, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 129, + "height": 56, + "seed": 1013161166, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElementIds": [] + }, + { + "type": "text", + "version": 165, + "versionNonce": 818207191, + "isDeleted": false, + "id": "8pCtm42TpakWdZ7WNS4VN", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 893, + "y": 73.5, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 64, + "height": 25, + "seed": 684338382, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElementIds": [], + "fontSize": 20, + "fontFamily": 1, + "text": "socket", + "baseline": 18, + "textAlign": "center", + "verticalAlign": "middle" + }, + { + "type": "rectangle", + "version": 223, + "versionNonce": 1307546393, + "isDeleted": false, + "id": "thsI1AfZ_VshmC8wdQoT_", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 859.5, + "y": 62, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 129, + "height": 56, + "seed": 1104563986, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElementIds": [] + }, + { + "type": "text", + "version": 186, + "versionNonce": 2027521783, + "isDeleted": false, + "id": "dfFxeVTIg6OH8ny7WuBsb", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 890, + "y": 150.5, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 64, + "height": 25, + "seed": 1000469902, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElementIds": [], + "fontSize": 20, + "fontFamily": 1, + "text": "socket", + "baseline": 18, + "textAlign": "center", + "verticalAlign": "middle" + }, + { + "type": "rectangle", + "version": 244, + "versionNonce": 1084213241, + "isDeleted": false, + "id": "Ejm4QTgpRy-0064kg5DDC", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 856.5, + "y": 139, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 129, + "height": 56, + "seed": 1070363218, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElementIds": [] + }, + { + "type": "arrow", + "version": 194, + "versionNonce": 1101884599, + "isDeleted": false, + "id": "yn0_EJ_FjGmr2PHYTCPsC", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 837.6161557435989, + "y": 166.89427948030175, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 247.23231148719788, + "height": 2.2114410393964476, + "seed": 1559186084, + "groupIds": [], + "strokeSharpness": "round", + "boundElementIds": [], + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -247.23231148719788, + 2.2114410393964476 + ] + ] + }, + { + "type": "text", + "version": 169, + "versionNonce": 1563844825, + "isDeleted": false, + "id": "2KQuRzgUL-iSoMHZQ9zbS", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 892.5, + "y": 406, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 64, + "height": 25, + "seed": 1479277478, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElementIds": [], + "fontSize": 20, + "fontFamily": 1, + "text": "socket", + "baseline": 18, + "textAlign": "center", + "verticalAlign": "middle" + }, + { + "type": "rectangle", + "version": 200, + "versionNonce": 8353079, + "isDeleted": false, + "id": "dJhDWOnAJOszWt_UNEXdt", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 822.5, + "y": 322.5, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 277, + "height": 280, + "seed": 224360890, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElementIds": [ + "qmYaJfZ9NO1RK7YHGQGo6", + "GsCkqpyPppqmnpvbaTChx", + "x_nMpLlFEV43XGOAM6Gxj" + ] + }, + { + "type": "text", + "version": 106, + "versionNonce": 1632034233, + "isDeleted": false, + "id": "lyh4RgaTTCZNLUjl519k9", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 841.5, + "y": 333.5, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 87, + "height": 26, + "seed": 364484326, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElementIds": [], + "fontSize": 20, + "fontFamily": 1, + "text": "Server B", + "baseline": 18, + "textAlign": "left", + "verticalAlign": "top" + }, + { + "type": "arrow", + "version": 181, + "versionNonce": 1223474647, + "isDeleted": false, + "id": "x7ujWlTTvv0aN7XIFTWjr", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 842.3983868047594, + "y": 425.3687072418816, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 251.33111393617446, + "height": 0.7613046474941143, + "seed": 1836855930, + "groupIds": [], + "strokeSharpness": "round", + "boundElementIds": [], + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -251.33111393617446, + -0.7613046474941143 + ] + ] + }, + { + "type": "rectangle", + "version": 188, + "versionNonce": 200588953, + "isDeleted": false, + "id": "cqdTPTcZefvtqeNEAMTBe", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 440.5, + "y": 395.5, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 129, + "height": 56, + "seed": 1567738406, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElementIds": [] + }, + { + "type": "arrow", + "version": 197, + "versionNonce": 174385911, + "isDeleted": false, + "id": "59kripFevaDD2Mo2bkYk-", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 841.7434176281095, + "y": 490.69281457587147, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 247.23231148719788, + "height": 2.2114410393964476, + "seed": 1124324154, + "groupIds": [], + "strokeSharpness": "round", + "boundElementIds": [], + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -247.23231148719788, + 2.2114410393964476 + ] + ] + }, + { + "type": "text", + "version": 165, + "versionNonce": 1811121017, + "isDeleted": false, + "id": "U0x2FIFxg4BZgOIK6sVnW", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 488.5, + "y": 412.5, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 40, + "height": 20, + "seed": 1044485478, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElementIds": [], + "fontSize": 16, + "fontFamily": 1, + "text": "client", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top" + }, + { + "type": "rectangle", + "version": 208, + "versionNonce": 563301527, + "isDeleted": false, + "id": "NU9potS0F6f8sxY5IT0Lt", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 442, + "y": 470.5, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 129, + "height": 56, + "seed": 1884904442, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElementIds": [] + }, + { + "type": "text", + "version": 186, + "versionNonce": 1837542489, + "isDeleted": false, + "id": "IpJJ20xja0yqXQC_netfw", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 489, + "y": 487.5, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 40, + "height": 20, + "seed": 1635121318, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElementIds": [], + "fontSize": 16, + "fontFamily": 1, + "text": "client", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top" + }, + { + "type": "rectangle", + "version": 227, + "versionNonce": 348889527, + "isDeleted": false, + "id": "scSxnujNYgELyMUDbnTNS", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 859, + "y": 394.5, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 129, + "height": 56, + "seed": 303703418, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElementIds": [] + }, + { + "type": "text", + "version": 210, + "versionNonce": 679590201, + "isDeleted": false, + "id": "Lyv2NwV0SfYm5kvp9sJEn", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 891.5, + "y": 476, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 64, + "height": 25, + "seed": 1344309030, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElementIds": [], + "fontSize": 20, + "fontFamily": 1, + "text": "socket", + "baseline": 18, + "textAlign": "center", + "verticalAlign": "middle" + }, + { + "type": "rectangle", + "version": 268, + "versionNonce": 1685869271, + "isDeleted": false, + "id": "e3D2rl_rbVQwQUKshOG8E", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 858, + "y": 464.5, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 129, + "height": 56, + "seed": 627795514, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElementIds": [] + }, + { + "type": "diamond", + "version": 248, + "versionNonce": 1850223129, + "isDeleted": false, + "id": "k0pJTVL4F3HHsfRPlE-gO", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1149, + "y": 66, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 46, + "height": 46, + "seed": 1260350118, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElementIds": [ + "Sp9AvxDh8gwRvSC53VFKe" + ] + }, + { + "type": "text", + "version": 239, + "versionNonce": 1348441349, + "isDeleted": false, + "id": "DiLMkDsU2SrPef3STL9fw", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1114.5, + "y": 22.5, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 176, + "height": 26, + "seed": 1810644198, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElementIds": [], + "fontSize": 20, + "fontFamily": 1, + "text": "Postgres adapter", + "baseline": 18, + "textAlign": "center", + "verticalAlign": "top" + }, + { + "type": "arrow", + "version": 19, + "versionNonce": 399343353, + "isDeleted": false, + "id": "Sp9AvxDh8gwRvSC53VFKe", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1129, + "y": 89, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 109, + "height": 1, + "seed": 714162918, + "groupIds": [], + "strokeSharpness": "round", + "boundElementIds": [], + "startBinding": { + "elementId": "k0pJTVL4F3HHsfRPlE-gO", + "focus": -0.01715197447147986, + "gap": 14.142135623730947 + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -109, + -1 + ] + ] + }, + { + "type": "arrow", + "version": 35, + "versionNonce": 889768215, + "isDeleted": false, + "id": "_wBO22vaQplcoKyBXbWRC", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1126, + "y": 83, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 105, + "height": 57, + "seed": 1243541542, + "groupIds": [], + "strokeSharpness": "round", + "boundElementIds": [], + "startBinding": { + "elementId": "lmQ4o4New7xuXQLwavuSn", + "focus": 0.35224176368590543, + "gap": 25 + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -105, + -57 + ] + ] + }, + { + "type": "arrow", + "version": 50, + "versionNonce": 328546265, + "isDeleted": false, + "id": "BZVwnsrGk9G-X87ZHkh-6", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1128, + "y": 96, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 95, + "height": 62, + "seed": 1890534970, + "groupIds": [], + "strokeSharpness": "round", + "boundElementIds": [], + "startBinding": { + "elementId": "lmQ4o4New7xuXQLwavuSn", + "focus": -0.522635330379503, + "gap": 27 + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -95, + 62 + ] + ] + }, + { + "type": "diamond", + "version": 355, + "versionNonce": 90017335, + "isDeleted": false, + "id": "vJwd2LS9grrvUFlbCugEG", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1149.25, + "y": 467, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 46, + "height": 46, + "seed": 1072510330, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElementIds": [ + "x_nMpLlFEV43XGOAM6Gxj" + ] + }, + { + "type": "arrow", + "version": 239, + "versionNonce": 891072343, + "isDeleted": false, + "id": "x_nMpLlFEV43XGOAM6Gxj", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1123.25, + "y": 489, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 109, + "height": 1, + "seed": 1180464698, + "groupIds": [], + "strokeSharpness": "round", + "boundElementIds": [], + "startBinding": { + "elementId": "dJhDWOnAJOszWt_UNEXdt", + "focus": -0.17704646556482773, + "gap": 23.75 + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -109, + -1 + ] + ] + }, + { + "type": "arrow", + "version": 230, + "versionNonce": 1003083161, + "isDeleted": false, + "id": "qmYaJfZ9NO1RK7YHGQGo6", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1119.9214748277186, + "y": 479.7229508196721, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 104.67147482771861, + "height": 53.72295081967212, + "seed": 880321126, + "groupIds": [], + "strokeSharpness": "round", + "boundElementIds": [], + "startBinding": { + "elementId": "dJhDWOnAJOszWt_UNEXdt", + "focus": 0.304824173970933, + "gap": 20.421474827718612 + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -104.67147482771861, + -53.72295081967212 + ] + ] + }, + { + "type": "ellipse", + "version": 150, + "versionNonce": 1527992587, + "isDeleted": false, + "id": "EQmjbilyrf3OcSwGbMZrg", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1203, + "y": 221, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 138.0000000000001, + "height": 94.00000000000001, + "seed": 1885795942, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElementIds": [ + "xDobZ6graJnZZP8g59wJ4", + "ZwSlmk3G5YWvU1BN3Aoh_", + "eU1gfEXnHSjxc-pEgv43A" + ] + }, + { + "type": "text", + "version": 32, + "versionNonce": 1329091685, + "isDeleted": false, + "id": "wV6Y3XyIP5TbX50EF6xs6", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1217.5, + "y": 256.5, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 114, + "height": 26, + "seed": 1433614630, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElementIds": [], + "fontSize": 20, + "fontFamily": 1, + "text": "PostgreSQL", + "baseline": 18, + "textAlign": "center", + "verticalAlign": "middle" + }, + { + "type": "arrow", + "version": 205, + "versionNonce": 1081064421, + "isDeleted": false, + "id": "eU1gfEXnHSjxc-pEgv43A", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1231.3032386555233, + "y": 209.73280760837395, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 38.30323865552327, + "height": 92.73280760837395, + "seed": 1145880934, + "groupIds": [], + "strokeSharpness": "round", + "boundElementIds": [], + "startBinding": { + "elementId": "EQmjbilyrf3OcSwGbMZrg", + "gap": 18.76496421842689, + "focus": -0.23200003253698098 + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -38.30323865552327, + -92.73280760837395 + ] + ] + }, + { + "type": "arrow", + "version": 186, + "versionNonce": 256792869, + "isDeleted": false, + "id": "xDobZ6graJnZZP8g59wJ4", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1245.605826516406, + "y": 329.3085913454021, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 39.321068840273256, + "height": 89.69140865459792, + "seed": 1443544058, + "groupIds": [], + "strokeSharpness": "round", + "boundElementIds": [], + "startBinding": { + "elementId": "EQmjbilyrf3OcSwGbMZrg", + "focus": -0.006717816566995138, + "gap": 17.320930477764534 + }, + "endBinding": { + "elementId": "8mohQ2rfKubVo6W8gvlz7", + "focus": -0.15011582418406128, + "gap": 11.75 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -39.321068840273256, + 89.69140865459792 + ] + ] + }, + { + "type": "rectangle", + "version": 32, + "versionNonce": 559233719, + "isDeleted": false, + "id": "vJCC3fZPhzuOSUDm-IELz", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1424, + "y": 236, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 126, + "height": 54, + "seed": 1521447142, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElementIds": [] + }, + { + "type": "text", + "version": 12, + "versionNonce": 901394489, + "isDeleted": false, + "id": "85ta6aPywjSaHanhmwnmS", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1459.5, + "y": 252.5, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 55, + "height": 21, + "seed": 1601504934, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElementIds": [], + "fontSize": 16, + "fontFamily": 1, + "text": "emitter", + "baseline": 15, + "textAlign": "center", + "verticalAlign": "middle" + }, + { + "type": "arrow", + "version": 140, + "versionNonce": 666542667, + "isDeleted": false, + "id": "ZwSlmk3G5YWvU1BN3Aoh_", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1405, + "y": 266, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 45.91567192209823, + "height": 0.7121352093494124, + "seed": 14898170, + "groupIds": [], + "strokeSharpness": "round", + "boundElementIds": [], + "startBinding": null, + "endBinding": { + "elementId": "EQmjbilyrf3OcSwGbMZrg", + "gap": 18.159861146516803, + "focus": -0.08641975308641975 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -45.91567192209823, + -0.7121352093494124 + ] + ] + }, + { + "type": "text", + "version": 302, + "versionNonce": 372963243, + "isDeleted": false, + "id": "8mohQ2rfKubVo6W8gvlz7", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1121.5, + "y": 430.75, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 176, + "height": 26, + "seed": 382986699, + "groupIds": [], + "strokeSharpness": "sharp", + "boundElementIds": [ + "xDobZ6graJnZZP8g59wJ4" + ], + "fontSize": 20, + "fontFamily": 1, + "text": "Postgres adapter", + "baseline": 18, + "textAlign": "center", + "verticalAlign": "top" + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + } +} \ No newline at end of file diff --git a/assets/emitter.png b/assets/emitter.png new file mode 100644 index 0000000000000000000000000000000000000000..1fd451293d0e696cba7551c3dc6e2c900528685c GIT binary patch literal 102189 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 Mwxey*t)_(hAAwDxoB#j- literal 0 HcmV?d00001 diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..2e832215 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,9 @@ +version: "3" + +services: + postgres: + image: postgres:12 + ports: + - "5432:5432" + environment: + POSTGRES_PASSWORD: "changeit" diff --git a/lib/index.ts b/lib/index.ts new file mode 100644 index 00000000..1c6cc9f5 --- /dev/null +++ b/lib/index.ts @@ -0,0 +1,533 @@ +import debugModule from "debug"; +import type { + DefaultEventsMap, + EventNames, + EventParams, + EventsMap, + TypedEventBroadcaster, +} from "./typed-events"; +import { encode } from "@msgpack/msgpack"; + +const debug = debugModule("socket.io-postgres-emitter"); +const EMITTER_UID = "emitter"; + +const hasBinary = (obj: any, toJSON?: boolean): boolean => { + if (!obj || typeof obj !== "object") { + return false; + } + + if (obj instanceof ArrayBuffer || ArrayBuffer.isView(obj)) { + return true; + } + + if (Array.isArray(obj)) { + for (let i = 0, l = obj.length; i < l; i++) { + if (hasBinary(obj[i])) { + return true; + } + } + return false; + } + + for (const key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key) && hasBinary(obj[key])) { + return true; + } + } + + if (obj.toJSON && typeof obj.toJSON === "function" && !toJSON) { + return hasBinary(obj.toJSON(), true); + } + + return false; +}; + +/** + * Event types, for messages between nodes + */ + +enum EventType { + INITIAL_HEARTBEAT = 1, + HEARTBEAT, + BROADCAST, + SOCKETS_JOIN, + SOCKETS_LEAVE, + DISCONNECT_SOCKETS, + FETCH_SOCKETS, + FETCH_SOCKETS_RESPONSE, + SERVER_SIDE_EMIT, + SERVER_SIDE_EMIT_RESPONSE, +} + +interface BroadcastFlags { + volatile?: boolean; + compress?: boolean; +} + +export interface PostgresEmitterOptions { + /** + * The prefix of the notification channel + * @default "socket.io" + */ + channelPrefix: string; + /** + * The name of the table for payloads over the 8000 bytes limit or containing binary data + */ + tableName: string; + /** + * The threshold for the payload size in bytes (see https://www.postgresql.org/docs/current/sql-notify.html) + * @default 8000 + */ + payloadThreshold: number; +} + +export class Emitter< + EmitEvents extends EventsMap = DefaultEventsMap, + ServerSideEvents extends EventsMap = DefaultEventsMap +> { + public readonly channel: string; + public readonly tableName: string; + public payloadThreshold: number; + + constructor( + readonly pool: any, + readonly nsp: string = "/", + opts: Partial = {} + ) { + const channelPrefix = opts.channelPrefix || "socket.io"; + this.channel = `${channelPrefix}#${nsp}`; + this.tableName = opts.tableName || "socket_io_attachments"; + this.payloadThreshold = opts.payloadThreshold || 8000; + } + + /** + * Return a new emitter for the given namespace. + * + * @param nsp - namespace + * @public + */ + public of(nsp: string): Emitter { + return new Emitter(this.pool, (nsp[0] !== "/" ? "/" : "") + nsp); + } + + /** + * Emits to all clients. + * + * @return Always true + * @public + */ + public emit>( + ev: Ev, + ...args: EventParams + ): true { + return new BroadcastOperator(this).emit( + ev, + ...args + ); + } + + /** + * Targets a room when emitting. + * + * @param room + * @return BroadcastOperator + * @public + */ + public to( + room: string | string[] + ): BroadcastOperator { + return new BroadcastOperator(this).to(room); + } + + /** + * Targets a room when emitting. + * + * @param room + * @return BroadcastOperator + * @public + */ + public in( + room: string | string[] + ): BroadcastOperator { + return new BroadcastOperator(this).in(room); + } + + /** + * Excludes a room when emitting. + * + * @param room + * @return BroadcastOperator + * @public + */ + public except( + room: string | string[] + ): BroadcastOperator { + return new BroadcastOperator(this).except(room); + } + + /** + * Sets a modifier for a subsequent event emission that the event data may be lost if the client is not ready to + * receive messages (because of network slowness or other issues, or because they’re connected through long polling + * and is in the middle of a request-response cycle). + * + * @return BroadcastOperator + * @public + */ + public get volatile(): BroadcastOperator { + return new BroadcastOperator(this).volatile; + } + + /** + * Sets the compress flag. + * + * @param compress - if `true`, compresses the sending data + * @return BroadcastOperator + * @public + */ + public compress( + compress: boolean + ): BroadcastOperator { + return new BroadcastOperator(this).compress(compress); + } + + /** + * Makes the matching socket instances join the specified rooms + * + * @param rooms + * @public + */ + public socketsJoin(rooms: string | string[]): void { + return new BroadcastOperator(this).socketsJoin(rooms); + } + + /** + * Makes the matching socket instances leave the specified rooms + * + * @param rooms + * @public + */ + public socketsLeave(rooms: string | string[]): void { + return new BroadcastOperator(this).socketsLeave(rooms); + } + + /** + * Makes the matching socket instances disconnect + * + * @param close - whether to close the underlying connection + * @public + */ + public disconnectSockets(close: boolean = false): void { + return new BroadcastOperator(this).disconnectSockets(close); + } + + /** + * Send a packet to the Socket.IO servers in the cluster + * + * @param ev - the event name + * @param args - any number of serializable arguments + */ + public serverSideEmit>( + ev: Ev, + ...args: EventParams + ): void { + return new BroadcastOperator( + this + ).serverSideEmit(ev, ...args); + } +} + +export const RESERVED_EVENTS: ReadonlySet = new Set([ + "connect", + "connect_error", + "disconnect", + "disconnecting", + "newListener", + "removeListener", +]); + +export class BroadcastOperator< + EmitEvents extends EventsMap, + ServerSideEvents extends EventsMap +> implements TypedEventBroadcaster { + constructor( + private readonly emitter: Emitter, + private readonly rooms: Set = new Set(), + private readonly exceptRooms: Set = new Set(), + private readonly flags: BroadcastFlags = {} + ) {} + + /** + * Targets a room when emitting. + * + * @param room + * @return a new BroadcastOperator instance + * @public + */ + public to( + room: string | string[] + ): BroadcastOperator { + const rooms = new Set(this.rooms); + if (Array.isArray(room)) { + room.forEach((r) => rooms.add(r)); + } else { + rooms.add(room); + } + return new BroadcastOperator( + this.emitter, + rooms, + this.exceptRooms, + this.flags + ); + } + + /** + * Targets a room when emitting. + * + * @param room + * @return a new BroadcastOperator instance + * @public + */ + public in( + room: string | string[] + ): BroadcastOperator { + return this.to(room); + } + + /** + * Excludes a room when emitting. + * + * @param room + * @return a new BroadcastOperator instance + * @public + */ + public except( + room: string | string[] + ): BroadcastOperator { + const exceptRooms = new Set(this.exceptRooms); + if (Array.isArray(room)) { + room.forEach((r) => exceptRooms.add(r)); + } else { + exceptRooms.add(room); + } + return new BroadcastOperator( + this.emitter, + this.rooms, + exceptRooms, + this.flags + ); + } + + /** + * Sets the compress flag. + * + * @param compress - if `true`, compresses the sending data + * @return a new BroadcastOperator instance + * @public + */ + public compress( + compress: boolean + ): BroadcastOperator { + const flags = Object.assign({}, this.flags, { compress }); + return new BroadcastOperator( + this.emitter, + this.rooms, + this.exceptRooms, + flags + ); + } + + /** + * Sets a modifier for a subsequent event emission that the event data may be lost if the client is not ready to + * receive messages (because of network slowness or other issues, or because they’re connected through long polling + * and is in the middle of a request-response cycle). + * + * @return a new BroadcastOperator instance + * @public + */ + public get volatile(): BroadcastOperator { + const flags = Object.assign({}, this.flags, { volatile: true }); + return new BroadcastOperator( + this.emitter, + this.rooms, + this.exceptRooms, + flags + ); + } + + private async publish(document: any) { + document.uid = EMITTER_UID; + + try { + if ( + [ + EventType.BROADCAST, + EventType.SERVER_SIDE_EMIT, + EventType.SERVER_SIDE_EMIT_RESPONSE, + ].includes(document.type) && + hasBinary(document) + ) { + return await this.publishWithAttachment(document); + } + + const payload = JSON.stringify(document); + if (Buffer.byteLength(payload) > this.emitter.payloadThreshold) { + return await this.publishWithAttachment(document); + } + + debug( + "sending event of type %s to channel %s", + document.type, + this.emitter.channel + ); + await this.emitter.pool.query( + `NOTIFY "${this.emitter.channel}", '${payload}'` + ); + } catch (err) { + // @ts-ignore + this.emit("error", err); + } + } + + private async publishWithAttachment(document: any) { + const payload = encode(document); + + debug( + "sending event of type %s with attachment to channel %s", + document.type, + this.emitter.channel + ); + const result = await this.emitter.pool.query( + `INSERT INTO ${this.emitter.tableName} (payload) VALUES ($1) RETURNING id;`, + [payload] + ); + const attachmentId = result.rows[0].id; + const headerPayload = JSON.stringify({ + uid: document.uid, + type: document.type, + attachmentId, + }); + this.emitter.pool.query( + `NOTIFY "${this.emitter.channel}", '${headerPayload}'` + ); + } + + /** + * Emits to all clients. + * + * @return Always true + * @public + */ + public emit>( + ev: Ev, + ...args: EventParams + ): true { + if (RESERVED_EVENTS.has(ev)) { + throw new Error(`"${ev}" is a reserved event name`); + } + + // set up packet object + const data = [ev, ...args]; + const packet = { + type: 2, // EVENT + data: data, + nsp: this.emitter.nsp, + }; + + const opts = { + rooms: [...this.rooms], + flags: this.flags, + except: [...this.exceptRooms], + }; + + this.publish({ + type: EventType.BROADCAST, + data: { + packet, + opts, + }, + }); + + return true; + } + + /** + * Makes the matching socket instances join the specified rooms + * + * @param rooms + * @public + */ + public socketsJoin(rooms: string | string[]): void { + this.publish({ + type: EventType.SOCKETS_JOIN, + data: { + opts: { + rooms: [...this.rooms], + except: [...this.exceptRooms], + }, + rooms: Array.isArray(rooms) ? rooms : [rooms], + }, + }); + } + + /** + * Makes the matching socket instances leave the specified rooms + * + * @param rooms + * @public + */ + public socketsLeave(rooms: string | string[]): void { + this.publish({ + type: EventType.SOCKETS_LEAVE, + data: { + opts: { + rooms: [...this.rooms], + except: [...this.exceptRooms], + }, + rooms: Array.isArray(rooms) ? rooms : [rooms], + }, + }); + } + + /** + * Makes the matching socket instances disconnect + * + * @param close - whether to close the underlying connection + * @public + */ + public disconnectSockets(close: boolean = false): void { + this.publish({ + type: EventType.DISCONNECT_SOCKETS, + data: { + opts: { + rooms: [...this.rooms], + except: [...this.exceptRooms], + }, + close, + }, + }); + } + + /** + * Send a packet to the Socket.IO servers in the cluster + * + * @param ev - the event name + * @param args - any number of serializable arguments + */ + public serverSideEmit>( + ev: Ev, + ...args: EventParams + ): void { + const withAck = args.length && typeof args[args.length - 1] === "function"; + + if (withAck) { + throw new Error("Acknowledgements are not supported"); + } + + this.publish({ + type: EventType.SERVER_SIDE_EMIT, + data: { + packet: [ev, ...args], + }, + }); + } +} diff --git a/lib/typed-events.ts b/lib/typed-events.ts new file mode 100644 index 00000000..33f7bb8b --- /dev/null +++ b/lib/typed-events.ts @@ -0,0 +1,37 @@ +/** + * An events map is an interface that maps event names to their value, which + * represents the type of the `on` listener. + */ +export interface EventsMap { + [event: string]: any; +} + +/** + * The default events map, used if no EventsMap is given. Using this EventsMap + * is equivalent to accepting all event names, and any data. + */ +export interface DefaultEventsMap { + [event: string]: (...args: any[]) => void; +} + +/** + * Returns a union type containing all the keys of an event map. + */ +export type EventNames = keyof Map & (string | symbol); + +/** The tuple type representing the parameters of an event listener */ +export type EventParams< + Map extends EventsMap, + Ev extends EventNames +> = Parameters; + +/** + * Interface for classes that aren't `EventEmitter`s, but still expose a + * strictly typed `emit` method. + */ +export interface TypedEventBroadcaster { + emit>( + ev: Ev, + ...args: EventParams + ): boolean; +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..12141c56 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2377 @@ +{ + "name": "@socket.io/postgres-emitter", + "version": "0.0.1", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/code-frame": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.12.13" + } + }, + "@babel/compat-data": { + "version": "7.13.11", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.13.11.tgz", + "integrity": "sha512-BwKEkO+2a67DcFeS3RLl0Z3Gs2OvdXewuWjc1Hfokhb5eQWP9YRYH1/+VrVZvql2CfjOiNGqSAFOYt4lsqTHzg==", + "dev": true + }, + "@babel/core": { + "version": "7.13.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.13.10.tgz", + "integrity": "sha512-bfIYcT0BdKeAZrovpMqX2Mx5NrgAckGbwT982AkdS5GNfn3KMGiprlBAtmBcFZRUmpaufS6WZFP8trvx8ptFDw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@babel/generator": "^7.13.9", + "@babel/helper-compilation-targets": "^7.13.10", + "@babel/helper-module-transforms": "^7.13.0", + "@babel/helpers": "^7.13.10", + "@babel/parser": "^7.13.10", + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.13.0", + "@babel/types": "^7.13.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "lodash": "^4.17.19", + "semver": "^6.3.0", + "source-map": "^0.5.0" + } + }, + "@babel/generator": { + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.13.9.tgz", + "integrity": "sha512-mHOOmY0Axl/JCTkxTU6Lf5sWOg/v8nUa+Xkt4zMTftX0wqmb6Sh7J8gvcehBw7q0AhrhAR+FDacKjCZ2X8K+Sw==", + "dev": true, + "requires": { + "@babel/types": "^7.13.0", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.13.10", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.10.tgz", + "integrity": "sha512-/Xju7Qg1GQO4mHZ/Kcs6Au7gfafgZnwm+a7sy/ow/tV1sHeraRUHbjdat8/UvDor4Tez+siGKDk6zIKtCPKVJA==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.13.8", + "@babel/helper-validator-option": "^7.12.17", + "browserslist": "^4.14.5", + "semver": "^6.3.0" + } + }, + "@babel/helper-function-name": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", + "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", + "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.0.tgz", + "integrity": "sha512-yvRf8Ivk62JwisqV1rFRMxiSMDGnN6KH1/mDMmIrij4jztpQNRoHqqMG3U6apYbGRPJpgPalhva9Yd06HlUxJQ==", + "dev": true, + "requires": { + "@babel/types": "^7.13.0" + } + }, + "@babel/helper-module-imports": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.13.tgz", + "integrity": "sha512-NGmfvRp9Rqxy0uHSSVP+SRIW1q31a7Ji10cLBcqSDUngGentY4FRiHOFZFE1CLU5eiL0oE8reH7Tg1y99TDM/g==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-module-transforms": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.13.0.tgz", + "integrity": "sha512-Ls8/VBwH577+pw7Ku1QkUWIyRRNHpYlts7+qSqBBFCW3I8QteB9DxfcZ5YJpOwH6Ihe/wn8ch7fMGOP1OhEIvw==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-replace-supers": "^7.13.0", + "@babel/helper-simple-access": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/helper-validator-identifier": "^7.12.11", + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.13.0", + "@babel/types": "^7.13.0", + "lodash": "^4.17.19" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz", + "integrity": "sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-replace-supers": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.13.0.tgz", + "integrity": "sha512-Segd5me1+Pz+rmN/NFBOplMbZG3SqRJOBlY+mA0SxAv6rjj7zJqr1AVr3SfzUVTLCv7ZLU5FycOM/SBGuLPbZw==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.13.0", + "@babel/helper-optimise-call-expression": "^7.12.13", + "@babel/traverse": "^7.13.0", + "@babel/types": "^7.13.0" + } + }, + "@babel/helper-simple-access": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.13.tgz", + "integrity": "sha512-0ski5dyYIHEfwpWGx5GPWhH35j342JaflmCeQmsPWcrOQDtCN6C1zKAVRFVbK53lPW2c9TsuLLSUDf0tIGJ5hA==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", + "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", + "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.12.17", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz", + "integrity": "sha512-TopkMDmLzq8ngChwRlyjR6raKD6gMSae4JdYDB8bByKreQgG0RBTuKe9LRxW3wFtUnjxOPRKBDwEH6Mg5KeDfw==", + "dev": true + }, + "@babel/helpers": { + "version": "7.13.10", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.13.10.tgz", + "integrity": "sha512-4VO883+MWPDUVRF3PhiLBUFHoX/bsLTGFpFK/HqvvfBZz2D57u9XzPVNFVBTc0PW/CWR9BXTOKt8NF4DInUHcQ==", + "dev": true, + "requires": { + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.13.0", + "@babel/types": "^7.13.0" + } + }, + "@babel/highlight": { + "version": "7.13.10", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.10.tgz", + "integrity": "sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.13.11", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.11.tgz", + "integrity": "sha512-PhuoqeHoO9fc4ffMEVk4qb/w/s2iOSWohvbHxLtxui0eBg3Lg5gN1U8wp1V1u61hOWkPQJJyJzGH6Y+grwkq8Q==", + "dev": true + }, + "@babel/template": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", + "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "@babel/traverse": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.13.0.tgz", + "integrity": "sha512-xys5xi5JEhzC3RzEmSGrs/b3pJW/o87SypZ+G/PhaE7uqVQNv/jlmVIBXuoh5atqQ434LfXV+sf23Oxj0bchJQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@babel/generator": "^7.13.0", + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/parser": "^7.13.0", + "@babel/types": "^7.13.0", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.19" + } + }, + "@babel/types": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.0.tgz", + "integrity": "sha512-hE+HE8rnG1Z6Wzo+MhaKE5lM5eMx71T4EHJgku2E3xIfaULhDcxiiRxUYgwX8qwP1BBSlag+TdGOt6JAidIZTA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + } + }, + "@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true + }, + "@msgpack/msgpack": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@msgpack/msgpack/-/msgpack-2.7.0.tgz", + "integrity": "sha512-mlRYq9FSsOd4m+3wZWatemn3hGFZPWNJ4JQOdrir4rrMK2PyIk26idKBoUWrqF3HJJHl+5GpRU+M0wEruJwecg==" + }, + "@socket.io/postgres-adapter": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/postgres-adapter/-/postgres-adapter-0.1.0.tgz", + "integrity": "sha512-WzoH87gs2Yo5DB/KwPEuQmlikFO1/s2NT9kRuB7XilcvFzQdj32X9FUaCwq2XnyRC9Uj0wdBGppB4sT6B6aO2A==", + "dev": true, + "requires": { + "@msgpack/msgpack": "~2.7.0", + "debug": "~4.3.1", + "socket.io-adapter": "~2.3.0" + } + }, + "@types/component-emitter": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.10.tgz", + "integrity": "sha512-bsjleuRKWmGqajMerkzox19aGbscQX5rmmvvXl3wlIp5gMG1HgkiwPxsN5p070fBDKTNSPgojVbuY1+HWMbFhg==", + "dev": true + }, + "@types/cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-y7mImlc/rNkvCRmg8gC3/lj87S7pTUIJ6QGjwHR9WQJcFs+ZMTOaoPrkdFA/YdbuqVEmEbb5RdhVxMkAcgOnpg==", + "dev": true + }, + "@types/cors": { + "version": "2.8.10", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.10.tgz", + "integrity": "sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ==", + "dev": true + }, + "@types/debug": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.5.tgz", + "integrity": "sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ==", + "dev": true + }, + "@types/expect.js": { + "version": "0.3.29", + "resolved": "https://registry.npmjs.org/@types/expect.js/-/expect.js-0.3.29.tgz", + "integrity": "sha1-KN01kVW4S47LCUr8P0t0wyItyjs=", + "dev": true + }, + "@types/mocha": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.1.tgz", + "integrity": "sha512-NysN+bNqj6E0Hv4CTGWSlPzMW6vTKjDpOteycDkV4IWBsO+PU48JonrPzV9ODjiI2XrjmA05KInLgF5ivZ/YGQ==", + "dev": true + }, + "@types/node": { + "version": "14.14.35", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.35.tgz", + "integrity": "sha512-Lt+wj8NVPx0zUmUwumiVXapmaLUcAk3yPuHCFVXras9k5VT9TdhJqKqGVUQCD60OTMCl0qxJ57OiTL0Mic3Iag==", + "dev": true + }, + "@types/pg": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.6.0.tgz", + "integrity": "sha512-3JXFrsl8COoqVB1+2Pqelx6soaiFVXzkT3fkuSNe7GB40ysfT0FHphZFPiqIXpMyTHSFRdLTyZzrFBrJRPAArA==", + "dev": true, + "requires": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^2.2.0" + } + }, + "@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "dev": true, + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "append-transform": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", + "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", + "dev": true, + "requires": { + "default-require-extensions": "^3.0.0" + } + }, + "archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", + "dev": true + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "backo2": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", + "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base64-arraybuffer": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz", + "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI=", + "dev": true + }, + "base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "dev": true + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "browserslist": { + "version": "4.16.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", + "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001219", + "colorette": "^1.2.2", + "electron-to-chromium": "^1.3.723", + "escalade": "^3.1.1", + "node-releases": "^1.1.71" + }, + "dependencies": { + "caniuse-lite": { + "version": "1.0.30001232", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001232.tgz", + "integrity": "sha512-e4Gyp7P8vqC2qV2iHA+cJNf/yqUKOShXQOJHQt81OHxlIZl/j/j3soEA0adAQi8CPUQgvOdDENyQ5kd6a6mNSg==", + "dev": true + }, + "electron-to-chromium": { + "version": "1.3.743", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.743.tgz", + "integrity": "sha512-K2wXfo9iZQzNJNx67+Pld0DRF+9bYinj62gXCdgPhcu1vidwVuLPHQPPFnCdO55njWigXXpfBiT90jGUPbw8Zg==", + "dev": true + } + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "buffer-writer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", + "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==", + "dev": true + }, + "caching-transform": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", + "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", + "dev": true, + "requires": { + "hasha": "^5.0.0", + "make-dir": "^3.0.0", + "package-hash": "^4.0.0", + "write-file-atomic": "^3.0.0" + } + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "chokidar": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", + "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "dev": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.3.1", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" + } + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "colorette": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", + "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "dev": true + }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "default-require-extensions": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", + "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", + "dev": true, + "requires": { + "strip-bom": "^4.0.0" + } + }, + "diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "engine.io": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-5.1.0.tgz", + "integrity": "sha512-A2i4kVvOA3qezQLlMz+FayGFdqOo0LP3fYrb0VqXMDXKoXcbgM0KxcEYnsdVzOMJQErIAb1GIStRj7UWFoiqlQ==", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~4.0.0", + "ws": "~7.4.2" + } + }, + "engine.io-client": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-5.1.1.tgz", + "integrity": "sha512-jPFpw2HLL0lhZ2KY0BpZhIJdleQcUO9W1xkIpo0h3d6s+5D6+EV/xgQw9qWOmymszv2WXef/6KUUehyxEKomlQ==", + "dev": true, + "requires": { + "base64-arraybuffer": "0.1.4", + "component-emitter": "~1.3.0", + "debug": "~4.3.1", + "engine.io-parser": "~4.0.1", + "has-cors": "1.1.0", + "parseqs": "0.0.6", + "parseuri": "0.0.6", + "ws": "~7.4.2", + "yeast": "0.1.2" + } + }, + "engine.io-parser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-4.0.2.tgz", + "integrity": "sha512-sHfEQv6nmtJrq6TKuIz5kyEKH/qSdK56H/A+7DnAuUPWosnIZAS2NHNcPLmyjtY3cGS/MqJdZbUjW97JU72iYg==", + "dev": true, + "requires": { + "base64-arraybuffer": "0.1.4" + } + }, + "es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "expect.js": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/expect.js/-/expect.js-0.3.1.tgz", + "integrity": "sha1-sKWaDS7/VDdUTr8M6qYBWEHQm1s=", + "dev": true + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-cache-dir": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", + "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true + }, + "foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" + } + }, + "fromentries": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", + "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "graceful-fs": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", + "dev": true + }, + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true + }, + "has-cors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", + "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "hasha": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", + "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", + "dev": true, + "requires": { + "is-stream": "^2.0.0", + "type-fest": "^0.8.0" + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "dev": true + }, + "istanbul-lib-hook": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", + "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", + "dev": true, + "requires": { + "append-transform": "^2.0.0" + } + }, + "istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "dev": true, + "requires": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + } + }, + "istanbul-lib-processinfo": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz", + "integrity": "sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw==", + "dev": true, + "requires": { + "archy": "^1.0.0", + "cross-spawn": "^7.0.0", + "istanbul-lib-coverage": "^3.0.0-alpha.1", + "make-dir": "^3.0.0", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "uuid": "^3.3.3" + } + }, + "istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", + "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "istanbul-reports": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", + "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json5": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + }, + "dependencies": { + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + } + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", + "dev": true + }, + "log-symbols": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", + "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", + "dev": true, + "requires": { + "chalk": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "mime-db": { + "version": "1.47.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.47.0.tgz", + "integrity": "sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==", + "dev": true + }, + "mime-types": { + "version": "2.1.30", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.30.tgz", + "integrity": "sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg==", + "dev": true, + "requires": { + "mime-db": "1.47.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "mocha": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.3.2.tgz", + "integrity": "sha512-UdmISwr/5w+uXLPKspgoV7/RXZwKRTiTjJ2/AC5ZiEztIoOYdfKb19+9jNmEInzx5pBsCyJQzarAxqIGBNYJhg==", + "dev": true, + "requires": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.1", + "debug": "4.3.1", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.1.6", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "4.0.0", + "log-symbols": "4.0.0", + "minimatch": "3.0.4", + "ms": "2.1.3", + "nanoid": "3.1.20", + "serialize-javascript": "5.0.1", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "wide-align": "1.1.3", + "workerpool": "6.1.0", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "js-yaml": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", + "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "y18n": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.5.tgz", + "integrity": "sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg==", + "dev": true + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true + } + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "nanoid": { + "version": "3.1.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", + "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", + "dev": true + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "dev": true + }, + "node-preload": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", + "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", + "dev": true, + "requires": { + "process-on-spawn": "^1.0.0" + } + }, + "node-releases": { + "version": "1.1.71", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.71.tgz", + "integrity": "sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg==", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "nyc": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", + "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", + "dev": true, + "requires": { + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "caching-transform": "^4.0.0", + "convert-source-map": "^1.7.0", + "decamelize": "^1.2.0", + "find-cache-dir": "^3.2.0", + "find-up": "^4.1.0", + "foreground-child": "^2.0.0", + "get-package-type": "^0.1.0", + "glob": "^7.1.6", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-hook": "^3.0.0", + "istanbul-lib-instrument": "^4.0.0", + "istanbul-lib-processinfo": "^2.0.2", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "make-dir": "^3.0.0", + "node-preload": "^0.2.1", + "p-map": "^3.0.0", + "process-on-spawn": "^1.0.0", + "resolve-from": "^5.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "spawn-wrap": "^2.0.0", + "test-exclude": "^6.0.0", + "yargs": "^15.0.2" + }, + "dependencies": { + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "package-hash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", + "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.15", + "hasha": "^5.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" + } + }, + "packet-reader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", + "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==", + "dev": true + }, + "parseqs": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz", + "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==", + "dev": true + }, + "parseuri": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz", + "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "pg": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.6.0.tgz", + "integrity": "sha512-qNS9u61lqljTDFvmk/N66EeGq3n6Ujzj0FFyNMGQr6XuEv4tgNTXvJQTfJdcvGit5p5/DWPu+wj920hAJFI+QQ==", + "dev": true, + "requires": { + "buffer-writer": "2.0.0", + "packet-reader": "1.0.0", + "pg-connection-string": "^2.5.0", + "pg-pool": "^3.3.0", + "pg-protocol": "^1.5.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" + } + }, + "pg-connection-string": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", + "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==", + "dev": true + }, + "pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "dev": true + }, + "pg-pool": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.3.0.tgz", + "integrity": "sha512-0O5huCql8/D6PIRFAlmccjphLYWC+JIzvUhSzXSpGaf+tjTZc4nn+Lr7mLXBbFJfvwbP0ywDv73EiaBsxn7zdg==", + "dev": true + }, + "pg-protocol": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.5.0.tgz", + "integrity": "sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ==", + "dev": true + }, + "pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "dev": true, + "requires": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + } + }, + "pgpass": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.4.tgz", + "integrity": "sha512-YmuA56alyBq7M59vxVBfPJrGSozru8QAdoNlWuW3cz8l+UX3cWge0vTvjKhsSHSJpo3Bom8/Mm6hf0TR5GY0+w==", + "dev": true, + "requires": { + "split2": "^3.1.1" + } + }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "dev": true + }, + "postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU=", + "dev": true + }, + "postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "dev": true + }, + "postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "dev": true, + "requires": { + "xtend": "^4.0.0" + } + }, + "prettier": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", + "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", + "dev": true + }, + "process-on-spawn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", + "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", + "dev": true, + "requires": { + "fromentries": "^1.2.0" + } + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "release-zalgo": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", + "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", + "dev": true, + "requires": { + "es6-error": "^4.0.1" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + }, + "dependencies": { + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "serialize-javascript": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", + "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "dev": true + }, + "socket.io": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.1.1.tgz", + "integrity": "sha512-YKPdhaYRGEXjP+VqoKlfOEPgDjm0aMq1YWonjHg4rBU1xJCmgceNh2XL1vO4czWupH+WX1+d9Wqb768h7BC5kw==", + "dev": true, + "requires": { + "@types/cookie": "^0.4.0", + "@types/cors": "^2.8.8", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "debug": "~4.3.1", + "engine.io": "~5.1.0", + "socket.io-adapter": "~2.3.0", + "socket.io-parser": "~4.0.3" + }, + "dependencies": { + "socket.io-adapter": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.3.0.tgz", + "integrity": "sha512-jdIbSFRWOkaZpo5mXy8T7rXEN6qo3bOFuq4nVeX1ZS7AtFlkbk39y153xTXEIW7W94vZfhVOux1wTU88YxcM1w==", + "dev": true + } + } + }, + "socket.io-adapter": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.3.1.tgz", + "integrity": "sha512-8cVkRxI8Nt2wadkY6u60Y4rpW3ejA1rxgcK2JuyIhmF+RMNpTy1QRtkHIDUOf3B4HlQwakMsWbKftMv/71VMmw==", + "dev": true + }, + "socket.io-client": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.1.1.tgz", + "integrity": "sha512-avzRzFZIkmyNxqvhmm5ns0Itq5dgEkesDPB6Tl0Yben47U08MvdFnVXAuFDULQhDXjuYdCb6QUEILYLUKQEuGg==", + "dev": true, + "requires": { + "@types/component-emitter": "^1.2.10", + "backo2": "~1.0.2", + "component-emitter": "~1.3.0", + "debug": "~4.3.1", + "engine.io-client": "~5.1.1", + "parseuri": "0.0.6", + "socket.io-parser": "~4.0.4" + } + }, + "socket.io-parser": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz", + "integrity": "sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==", + "dev": true, + "requires": { + "@types/component-emitter": "^1.2.10", + "component-emitter": "~1.3.0", + "debug": "~4.3.1" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + } + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "spawn-wrap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", + "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", + "dev": true, + "requires": { + "foreground-child": "^2.0.0", + "is-windows": "^1.0.2", + "make-dir": "^3.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "which": "^2.0.1" + } + }, + "split2": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "dev": true, + "requires": { + "readable-stream": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "dependencies": { + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "ts-node": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", + "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", + "dev": true, + "requires": { + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" + }, + "dependencies": { + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + } + } + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "typescript": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.3.tgz", + "integrity": "sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "requires": { + "string-width": "^1.0.2 || 2" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "workerpool": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz", + "integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==", + "dev": true + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "ws": { + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.5.tgz", + "integrity": "sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g==", + "dev": true + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, + "y18n": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", + "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", + "dev": true + }, + "yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + } + }, + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "requires": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "dependencies": { + "camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "dev": true + }, + "decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true + } + } + }, + "yeast": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", + "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=", + "dev": true + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..26502e69 --- /dev/null +++ b/package.json @@ -0,0 +1,48 @@ +{ + "name": "@socket.io/postgres-emitter", + "version": "0.0.1", + "description": "The Socket.IO Postgres emitter, allowing to communicate with a group of Socket.IO servers from another Node.js process", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/socketio/socket.io-postgres-emitter.git" + }, + "files": [ + "dist/" + ], + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "scripts": { + "test": "npm run format:check && tsc && nyc mocha --require ts-node/register --timeout 5000 test/index.ts", + "format:check": "prettier --parser typescript --check 'lib/**/*.ts' 'test/**/*.ts'", + "format:fix": "prettier --parser typescript --write 'lib/**/*.ts' 'test/**/*.ts'", + "prepack": "tsc" + }, + "dependencies": { + "@msgpack/msgpack": "^2.7.0", + "debug": "~4.3.1" + }, + "devDependencies": { + "@socket.io/postgres-adapter": "^0.1.0", + "@types/debug": "^4.1.5", + "@types/expect.js": "^0.3.29", + "@types/mocha": "^8.2.1", + "@types/node": "^14.14.35", + "@types/pg": "^8.6.0", + "expect.js": "~0.3.1", + "mocha": "^8.3.2", + "nyc": "^15.1.0", + "pg": "^8.6.0", + "prettier": "^2.2.1", + "socket.io": "^4.1.1", + "socket.io-client": "^4.1.1", + "ts-node": "^9.1.1", + "typescript": "^4.2.3" + }, + "keywords": [ + "socket.io", + "postgres", + "postgresql", + "emitter" + ] +} diff --git a/test/index.ts b/test/index.ts new file mode 100644 index 00000000..0fed699f --- /dev/null +++ b/test/index.ts @@ -0,0 +1,278 @@ +import { createServer } from "http"; +import { Server, Socket as ServerSocket } from "socket.io"; +import { io as ioc, Socket as ClientSocket } from "socket.io-client"; +import expect = require("expect.js"); +import { createAdapter } from "@socket.io/postgres-adapter"; +import type { AddressInfo } from "net"; +import { Pool } from "pg"; +import { times, sleep } from "./util"; +import { Emitter } from ".."; + +const NODES_COUNT = 3; + +describe("@socket.io/postgres-emitter", () => { + let servers: Server[], + serverSockets: ServerSocket[], + clientSockets: ClientSocket[], + pool: Pool, + emitter: Emitter; + + beforeEach((done) => { + servers = []; + serverSockets = []; + clientSockets = []; + pool = new Pool({ + user: "postgres", + host: "localhost", + database: "postgres", + password: "changeit", + port: 5432, + }); + + pool.query( + ` + CREATE TABLE IF NOT EXISTS socket_io_attachments ( + id bigserial UNIQUE, + created_at timestamptz DEFAULT NOW(), + payload bytea + ); + `, + () => {} + ); + + emitter = new Emitter(pool); + + for (let i = 1; i <= NODES_COUNT; i++) { + const httpServer = createServer(); + const io = new Server(httpServer); + // @ts-ignore + io.adapter(createAdapter(pool)); + httpServer.listen(() => { + const port = (httpServer.address() as AddressInfo).port; + const clientSocket = ioc(`http://localhost:${port}`); + + io.on("connection", async (socket) => { + clientSockets.push(clientSocket); + serverSockets.push(socket); + servers.push(io); + if (servers.length === NODES_COUNT) { + // ensure all nodes know each other + servers[0].emit("ping"); + servers[1].emit("ping"); + servers[2].emit("ping"); + + await sleep(200); + + done(); + } + }); + }); + } + }); + + afterEach((done) => { + servers.forEach((server) => { + // @ts-ignore + server.httpServer.close(); + server.of("/").adapter.close(); + }); + clientSockets.forEach((socket) => { + socket.disconnect(); + }); + pool.end(done); + }); + + describe("broadcast", function () { + it("broadcasts to all clients", (done) => { + const partialDone = times(3, done); + + clientSockets.forEach((clientSocket) => { + clientSocket.on("test", (arg1, arg2, arg3) => { + expect(arg1).to.eql(1); + expect(arg2).to.eql("2"); + expect(Buffer.isBuffer(arg3)).to.be(true); + partialDone(); + }); + }); + + emitter.emit("test", 1, "2", Buffer.from([3, 4])); + }); + + it("broadcasts to all clients in a namespace", (done) => { + const partialDone = times(3, () => { + servers.forEach((server) => server.of("/custom").adapter.close()); + done(); + }); + + servers.forEach((server) => server.of("/custom")); + + const onConnect = times(3, async () => { + await sleep(200); + + emitter.of("/custom").emit("test"); + }); + + clientSockets.forEach((clientSocket) => { + const socket = clientSocket.io.socket("/custom"); + socket.on("connect", onConnect); + socket.on("test", () => { + socket.disconnect(); + partialDone(); + }); + }); + }); + + it("broadcasts to all clients in a room", (done) => { + serverSockets[1].join("room1"); + + clientSockets[0].on("test", () => { + done(new Error("should not happen")); + }); + + clientSockets[1].on("test", () => { + done(); + }); + + clientSockets[2].on("test", () => { + done(new Error("should not happen")); + }); + + emitter.to("room1").emit("test"); + }); + + it("broadcasts to all clients except in room", (done) => { + const partialDone = times(2, done); + serverSockets[1].join("room1"); + + clientSockets[0].on("test", () => { + partialDone(); + }); + + clientSockets[1].on("test", () => { + done(new Error("should not happen")); + }); + + clientSockets[2].on("test", () => { + partialDone(); + }); + + emitter.of("/").except("room1").emit("test"); + }); + }); + + describe("socketsJoin", () => { + it("makes all socket instances join the specified room", async () => { + emitter.socketsJoin("room1"); + + await sleep(200); + + expect(serverSockets[0].rooms.has("room1")).to.be(true); + expect(serverSockets[1].rooms.has("room1")).to.be(true); + expect(serverSockets[2].rooms.has("room1")).to.be(true); + }); + + it("makes the matching socket instances join the specified room", async () => { + serverSockets[0].join("room1"); + serverSockets[2].join("room1"); + + emitter.in("room1").socketsJoin("room2"); + + await sleep(200); + + expect(serverSockets[0].rooms.has("room2")).to.be(true); + expect(serverSockets[1].rooms.has("room2")).to.be(false); + expect(serverSockets[2].rooms.has("room2")).to.be(true); + }); + + it("makes the given socket instance join the specified room", async () => { + emitter.in(serverSockets[1].id).socketsJoin("room3"); + + await sleep(200); + + expect(serverSockets[0].rooms.has("room3")).to.be(false); + expect(serverSockets[1].rooms.has("room3")).to.be(true); + expect(serverSockets[2].rooms.has("room3")).to.be(false); + }); + }); + + describe("socketsLeave", () => { + it("makes all socket instances leave the specified room", async () => { + serverSockets[0].join("room1"); + serverSockets[2].join("room1"); + + emitter.socketsLeave("room1"); + + await sleep(200); + + expect(serverSockets[0].rooms.has("room1")).to.be(false); + expect(serverSockets[1].rooms.has("room1")).to.be(false); + expect(serverSockets[2].rooms.has("room1")).to.be(false); + }); + + it("makes the matching socket instances leave the specified room", async () => { + serverSockets[0].join(["room1", "room2"]); + serverSockets[1].join(["room1", "room2"]); + serverSockets[2].join(["room2"]); + + emitter.in("room1").socketsLeave("room2"); + + await sleep(200); + + expect(serverSockets[0].rooms.has("room2")).to.be(false); + expect(serverSockets[1].rooms.has("room2")).to.be(false); + expect(serverSockets[2].rooms.has("room2")).to.be(true); + }); + + it("makes the given socket instance leave the specified room", async () => { + serverSockets[0].join("room3"); + serverSockets[1].join("room3"); + serverSockets[2].join("room3"); + + emitter.in(serverSockets[1].id).socketsLeave("room3"); + + await sleep(200); + + expect(serverSockets[0].rooms.has("room3")).to.be(true); + expect(serverSockets[1].rooms.has("room3")).to.be(false); + expect(serverSockets[2].rooms.has("room3")).to.be(true); + }); + }); + + describe("disconnectSockets", () => { + it("makes all socket instances disconnect", (done) => { + const partialDone = times(3, done); + + clientSockets.forEach((clientSocket) => { + clientSocket.on("disconnect", (reason) => { + expect(reason).to.eql("io server disconnect"); + partialDone(); + }); + }); + + emitter.disconnectSockets(); + }); + }); + + describe("serverSideEmit", () => { + it("sends an event to other server instances", (done) => { + const partialDone = times(3, done); + + emitter.serverSideEmit("hello", "world", 1, "2"); + + servers[0].on("hello", (arg1, arg2, arg3) => { + expect(arg1).to.eql("world"); + expect(arg2).to.eql(1); + expect(arg3).to.eql("2"); + partialDone(); + }); + + servers[1].on("hello", (arg1, arg2, arg3) => { + partialDone(); + }); + + servers[2].of("/").on("hello", () => { + partialDone(); + }); + }); + }); +}); diff --git a/test/util.ts b/test/util.ts new file mode 100644 index 00000000..c815f22c --- /dev/null +++ b/test/util.ts @@ -0,0 +1,13 @@ +export function times(count: number, fn: () => void) { + let i = 0; + return () => { + i++; + if (i === count) { + fn(); + } + }; +} + +export function sleep(duration: number) { + return new Promise((resolve) => setTimeout(resolve, duration)); +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..87d51bf0 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "outDir": "./dist", + "allowJs": false, + "target": "es2017", + "module": "commonjs", + "declaration": true + }, + "include": [ + "./lib/**/*" + ] +}