├── assets ├── adapter.png ├── adapter_dark.png └── adapter.excalidraw ├── .gitignore ├── tsconfig.json ├── test ├── util.ts ├── connection-state-recovery.ts └── index.ts ├── examples ├── ttl-example │ ├── package.json │ ├── index.js │ └── package-lock.json └── capped-example │ ├── package.json │ └── index.js ├── compose.yaml ├── .github └── workflows │ ├── publish.yml │ └── ci.yml ├── LICENSE ├── package.json ├── README.md ├── CHANGELOG.md └── lib └── index.ts /assets/adapter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/socketio/socket.io-mongo-adapter/HEAD/assets/adapter.png -------------------------------------------------------------------------------- /assets/adapter_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/socketio/socket.io-mongo-adapter/HEAD/assets/adapter_dark.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | npm-debug.log 15 | node_modules 16 | .idea 17 | .nyc_output/ 18 | dist/ 19 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist", 4 | "allowJs": false, 5 | "target": "es2017", 6 | "module": "commonjs", 7 | "declaration": true, 8 | "strict": true 9 | }, 10 | "include": [ 11 | "./lib/**/*" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /test/util.ts: -------------------------------------------------------------------------------- 1 | export function times(count: number, fn: () => void) { 2 | let i = 0; 3 | return () => { 4 | i++; 5 | if (i === count) { 6 | fn(); 7 | } 8 | }; 9 | } 10 | 11 | export function sleep(duration: number) { 12 | return new Promise((resolve) => setTimeout(resolve, duration)); 13 | } 14 | -------------------------------------------------------------------------------- /examples/ttl-example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "ttl-example", 4 | "version": "0.0.1", 5 | "description": "", 6 | "main": "index.js", 7 | "type": "module", 8 | "dependencies": { 9 | "@socket.io/mongo-adapter": "^0.3.2", 10 | "mongodb": "^6.3.0", 11 | "socket.io": "^4.7.4", 12 | "socket.io-client": "^4.7.4" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/capped-example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "capped-example", 4 | "version": "0.0.1", 5 | "description": "", 6 | "main": "index.js", 7 | "type": "module", 8 | "dependencies": { 9 | "@socket.io/mongo-adapter": "^0.3.2", 10 | "mongodb": "^6.3.0", 11 | "socket.io": "^4.7.4", 12 | "socket.io-client": "^4.7.4" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | mongo: 3 | image: mongo:4.4 4 | entrypoint: [ "/usr/bin/mongod", "--bind_ip_all", "--replSet", "rs0" ] 5 | # you'll need to run "rs.initiate()" on the node (https://docs.mongodb.com/manual/tutorial/convert-standalone-to-replica-set/) 6 | # commands: 7 | # $ docker compose exec mongo /bin/bash 8 | # $ mongosh 9 | # $ rs.initiate() 10 | # $ rs.status() 11 | ports: 12 | - "27017:27017" 13 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | # reference: https://docs.npmjs.com/generating-provenance-statements 2 | 3 | name: Publish 4 | 5 | on: 6 | push: 7 | tags: 8 | - '*' 9 | 10 | jobs: 11 | publish: 12 | runs-on: ubuntu-latest 13 | permissions: 14 | contents: read 15 | id-token: write 16 | 17 | steps: 18 | - name: Checkout repository 19 | uses: actions/checkout@v4 20 | 21 | - name: Use Node.js 20 22 | uses: actions/setup-node@v4 23 | with: 24 | node-version: 20 25 | registry-url: 'https://registry.npmjs.org' 26 | 27 | - name: Install dependencies 28 | run: npm ci 29 | 30 | - name: Publish package 31 | run: npm publish --provenance --access public 32 | env: 33 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 34 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | pull_request: 6 | schedule: 7 | - cron: '0 0 * * 0' 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | test-node: 14 | runs-on: ubuntu-latest 15 | timeout-minutes: 10 16 | 17 | strategy: 18 | matrix: 19 | node-version: 20 | - 16 21 | - 20 22 | 23 | steps: 24 | - name: Checkout repository 25 | uses: actions/checkout@v3 26 | 27 | - name: Use Node.js ${{ matrix.node-version }} 28 | uses: actions/setup-node@v3 29 | with: 30 | node-version: ${{ matrix.node-version }} 31 | 32 | - name: Start MongoDB 33 | uses: supercharge/mongodb-github-action@v1 34 | with: 35 | mongodb-version: 5.0 36 | mongodb-replica-set: rs0 37 | 38 | - name: Install dependencies 39 | run: npm ci 40 | 41 | - name: Run tests 42 | run: npm test 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021 Damien Arrachequesne (@darrachequesne) 2 | 3 | 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: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | 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. 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@socket.io/mongo-adapter", 3 | "version": "0.4.0", 4 | "description": "The Socket.IO MongoDB adapter, allowing to broadcast events between several Socket.IO servers", 5 | "license": "MIT", 6 | "repository": { 7 | "type": "git", 8 | "url": "git@github.com:socketio/socket.io-mongo-adapter.git" 9 | }, 10 | "files": [ 11 | "dist/" 12 | ], 13 | "main": "./dist/index.js", 14 | "types": "./dist/index.d.ts", 15 | "scripts": { 16 | "compile": "rimraf ./dist && tsc", 17 | "test": "npm run format:check && npm run compile && nyc mocha --require ts-node/register test/index.ts", 18 | "format:check": "prettier --parser typescript --check 'lib/**/*.ts' 'test/**/*.ts'", 19 | "format:fix": "prettier --parser typescript --write 'lib/**/*.ts' 'test/**/*.ts'", 20 | "prepack": "npm run compile" 21 | }, 22 | "dependencies": { 23 | "debug": "~4.3.1", 24 | "mongodb": "*" 25 | }, 26 | "peerDependencies": { 27 | "socket.io-adapter": "^2.5.2" 28 | }, 29 | "devDependencies": { 30 | "@types/expect.js": "^0.3.29", 31 | "@types/mocha": "^8.2.1", 32 | "@types/node": "^14.14.7", 33 | "expect.js": "0.3.1", 34 | "mocha": "^10.2.0", 35 | "nyc": "^15.1.0", 36 | "prettier": "^2.1.2", 37 | "rimraf": "^6.0.1", 38 | "socket.io": "^4.6.1", 39 | "socket.io-client": "^4.6.1", 40 | "ts-node": "^10.9.1", 41 | "typescript": "^4.9.4" 42 | }, 43 | "engines": { 44 | "node": ">=10.0.0" 45 | }, 46 | "keywords": [ 47 | "socket.io", 48 | "mongodb", 49 | "mongo", 50 | "adapter" 51 | ] 52 | } 53 | -------------------------------------------------------------------------------- /examples/capped-example/index.js: -------------------------------------------------------------------------------- 1 | import { MongoClient } from "mongodb"; 2 | import { Server } from "socket.io"; 3 | import { createAdapter } from "@socket.io/mongo-adapter"; 4 | import { io } from "socket.io-client"; 5 | 6 | async function initMongoCollection() { 7 | const DB = "mydb"; 8 | const COLLECTION = "socket.io-adapter-events-capped"; 9 | 10 | const mongoClient = new MongoClient( 11 | "mongodb://localhost:27017/?replicaSet=rs0&directConnection=true" 12 | ); 13 | 14 | await mongoClient.connect(); 15 | 16 | try { 17 | await mongoClient.db(DB).createCollection(COLLECTION, { 18 | capped: true, 19 | size: 1e6, 20 | }); 21 | } catch (e) { 22 | // collection already exists 23 | } 24 | 25 | return mongoClient.db(DB).collection(COLLECTION); 26 | } 27 | 28 | function initServer(mongoCollection) { 29 | const io = new Server({ 30 | connectionStateRecovery: {}, 31 | }); 32 | 33 | io.adapter(createAdapter(mongoCollection)); 34 | 35 | return io; 36 | } 37 | 38 | function initClient(port) { 39 | const socket = io(`http://localhost:${port}`); 40 | 41 | socket.on("connect", () => { 42 | console.log(`[${port}] connected (recovered? ${socket.recovered})`); 43 | }); 44 | 45 | socket.on("ping", () => { 46 | console.log(`[${port}] got ping`); 47 | }); 48 | 49 | socket.on("disconnect", (reason) => { 50 | console.log(`[${port}] disconnected due to ${reason}`); 51 | }); 52 | 53 | return socket; 54 | } 55 | 56 | const mongoCollection = await initMongoCollection(); 57 | 58 | const io1 = initServer(mongoCollection); 59 | const io2 = initServer(mongoCollection); 60 | const io3 = initServer(mongoCollection); 61 | 62 | io1.listen(3000); 63 | io2.listen(3001); 64 | io3.listen(3002); 65 | 66 | initClient(3000); 67 | initClient(3001); 68 | initClient(3002); 69 | 70 | setInterval(() => { 71 | io1.emit("ping"); 72 | }, 2000); 73 | 74 | // uncomment to test connection state recovery 75 | // setTimeout(() => { 76 | // io2.close(() => { 77 | // io2.listen(3001); 78 | // }); 79 | // }, 3000); 80 | -------------------------------------------------------------------------------- /examples/ttl-example/index.js: -------------------------------------------------------------------------------- 1 | import { MongoClient } from "mongodb"; 2 | import { Server } from "socket.io"; 3 | import { createAdapter } from "@socket.io/mongo-adapter"; 4 | import { io } from "socket.io-client"; 5 | 6 | async function initMongoCollection() { 7 | const mongoClient = new MongoClient( 8 | "mongodb://localhost:27017/?replicaSet=rs0&directConnection=true" 9 | ); 10 | 11 | await mongoClient.connect(); 12 | 13 | const mongoCollection = mongoClient 14 | .db("mydb") 15 | .collection("socket.io-adapter-events-ttl"); 16 | 17 | await mongoCollection.createIndex( 18 | { createdAt: 1 }, 19 | { expireAfterSeconds: 3600, background: true } 20 | ); 21 | 22 | return mongoCollection; 23 | } 24 | 25 | function initServer(mongoCollection) { 26 | const io = new Server({ 27 | connectionStateRecovery: {}, 28 | }); 29 | 30 | io.adapter( 31 | createAdapter(mongoCollection, { 32 | addCreatedAtField: true, 33 | }) 34 | ); 35 | 36 | return io; 37 | } 38 | 39 | function initClient(port) { 40 | const socket = io(`http://localhost:${port}`); 41 | 42 | socket.on("connect", () => { 43 | console.log(`[${port}] connected (recovered? ${socket.recovered})`); 44 | }); 45 | 46 | socket.on("ping", () => { 47 | console.log(`[${port}] got ping`); 48 | }); 49 | 50 | socket.on("disconnect", (reason) => { 51 | console.log(`[${port}] disconnected due to ${reason}`); 52 | }); 53 | 54 | return socket; 55 | } 56 | 57 | const mongoCollection = await initMongoCollection(); 58 | 59 | const io1 = initServer(mongoCollection); 60 | const io2 = initServer(mongoCollection); 61 | const io3 = initServer(mongoCollection); 62 | 63 | io1.listen(3000); 64 | io2.listen(3001); 65 | io3.listen(3002); 66 | 67 | initClient(3000); 68 | initClient(3001); 69 | initClient(3002); 70 | 71 | setInterval(() => { 72 | io1.emit("ping"); 73 | }, 2000); 74 | 75 | // uncomment to test connection state recovery 76 | // setTimeout(() => { 77 | // io2.close(() => { 78 | // io2.listen(3001); 79 | // }); 80 | // }, 3000); 81 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Socket.IO MongoDB adapter 2 | 3 | The `@socket.io/mongo-adapter` package allows broadcasting packets between multiple Socket.IO servers. 4 | 5 | 6 | 7 | Diagram of Socket.IO packets forwarded through MongoDB 8 | 9 | 10 | Unlike the existing [`socket.io-adapter-mongo`](https://github.com/lklepner/socket.io-adapter-mongo) package which uses [tailable cursors](https://docs.mongodb.com/manual/core/tailable-cursors/), this package relies on [change streams](https://docs.mongodb.com/manual/changeStreams/) and thus requires a replica set or a sharded cluster. 11 | 12 | **Table of contents** 13 | 14 | - [Supported features](#supported-features) 15 | - [Installation](#installation) 16 | - [Usage](#usage) 17 | - [Usage with a capped collection](#usage-with-a-capped-collection) 18 | - [Usage with a TTL index](#usage-with-a-ttl-index) 19 | - [Known errors](#known-errors) 20 | - [License](#license) 21 | 22 | ## Supported features 23 | 24 | | Feature | `socket.io` version | Support | 25 | |---------------------------------|---------------------|------------------------------------------------| 26 | | Socket management | `4.0.0` | :white_check_mark: YES (since version `0.1.0`) | 27 | | Inter-server communication | `4.1.0` | :white_check_mark: YES (since version `0.1.0`) | 28 | | Broadcast with acknowledgements | `4.5.0` | :white_check_mark: YES (since version `0.2.0`) | 29 | | Connection state recovery | `4.6.0` | :white_check_mark: YES (since version `0.3.0`) | 30 | 31 | ## Installation 32 | 33 | ``` 34 | npm install @socket.io/mongo-adapter mongodb 35 | ``` 36 | 37 | ## Usage 38 | 39 | Broadcasting packets within a Socket.IO cluster is achieved by creating MongoDB documents and using a [change stream](https://docs.mongodb.com/manual/changeStreams/) on each Socket.IO server. 40 | 41 | There are two ways to clean up the documents in MongoDB: 42 | 43 | - a [capped collection](https://www.mongodb.com/docs/manual/core/capped-collections/) 44 | - a [TTL index](https://www.mongodb.com/docs/manual/core/index-ttl/) 45 | 46 | ### Usage with a capped collection 47 | 48 | ```js 49 | import { Server } from "socket.io"; 50 | import { createAdapter } from "@socket.io/mongo-adapter"; 51 | import { MongoClient } from "mongodb"; 52 | 53 | const DB = "mydb"; 54 | const COLLECTION = "socket.io-adapter-events"; 55 | 56 | const io = new Server(); 57 | 58 | const mongoClient = new MongoClient("mongodb://localhost:27017/?replicaSet=rs0"); 59 | 60 | await mongoClient.connect(); 61 | 62 | try { 63 | await mongoClient.db(DB).createCollection(COLLECTION, { 64 | capped: true, 65 | size: 1e6 66 | }); 67 | } catch (e) { 68 | // collection already exists 69 | } 70 | const mongoCollection = mongoClient.db(DB).collection(COLLECTION); 71 | 72 | io.adapter(createAdapter(mongoCollection)); 73 | io.listen(3000); 74 | ``` 75 | 76 | ### Usage with a TTL index 77 | 78 | ```js 79 | import { Server } from "socket.io"; 80 | import { createAdapter } from "@socket.io/mongo-adapter"; 81 | import { MongoClient } from "mongodb"; 82 | 83 | const DB = "mydb"; 84 | const COLLECTION = "socket.io-adapter-events"; 85 | 86 | const io = new Server(); 87 | 88 | const mongoClient = new MongoClient("mongodb://localhost:27017/?replicaSet=rs0"); 89 | 90 | await mongoClient.connect(); 91 | 92 | const mongoCollection = mongoClient.db(DB).collection(COLLECTION); 93 | 94 | await mongoCollection.createIndex( 95 | { createdAt: 1 }, 96 | { expireAfterSeconds: 3600, background: true } 97 | ); 98 | 99 | io.adapter(createAdapter(mongoCollection, { 100 | addCreatedAtField: true 101 | })); 102 | 103 | io.listen(3000); 104 | ``` 105 | 106 | ## Known errors 107 | 108 | - `MongoError: The $changeStream stage is only supported on replica sets` 109 | 110 | Change streams are only available for replica sets and sharded clusters. 111 | 112 | More information [here](https://docs.mongodb.com/manual/changeStreams/). 113 | 114 | Please note that, for development purposes, you can have a single MongoDB process acting as a replica set by running `rs.initiate()` on the node. 115 | 116 | - `TypeError: this.mongoCollection.insertOne is not a function` 117 | 118 | You probably passed a MongoDB client instead of a MongoDB collection to the `createAdapter` method. 119 | 120 | ## License 121 | 122 | [MIT](LICENSE) 123 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # History 2 | 3 | | Version | Release date | 4 | |--------------------------|---------------| 5 | | [0.4.0](#040-2025-08-10) | August 2025 | 6 | | [0.3.2](#032-2024-01-23) | January 2024 | 7 | | [0.3.1](#031-2024-01-10) | January 2024 | 8 | | [0.3.0](#030-2023-02-23) | February 2023 | 9 | | [0.2.1](#021-2022-05-03) | May 2022 | 10 | | [0.2.0](#020-2022-04-27) | April 2022 | 11 | | [0.1.0](#010-2021-06-01) | June 2021 | 12 | 13 | # Release notes 14 | 15 | ## [0.4.0](https://github.com/socketio/socket.io-mongo-adapter/compare/0.3.2...0.4.0) (2025-08-10) 16 | 17 | 18 | ### Features 19 | 20 | * allow to specify additional change stream options ([#26](https://github.com/socketio/socket.io-mongo-adapter/issues/26)) ([eae849b](https://github.com/socketio/socket.io-mongo-adapter/commit/eae849b02202afa7ea7cfcf1e48ba2775aaaa982)) 21 | 22 | 23 | 24 | 25 | ## [0.3.2](https://github.com/socketio/socket.io-mongo-adapter/compare/0.3.1...0.3.2) (2024-01-23) 26 | 27 | 28 | ### Bug Fixes 29 | 30 | * add support for AWS DocumentDB ([#21](https://github.com/socketio/socket.io-mongo-adapter/issues/21)) ([0c80f7f](https://github.com/socketio/socket.io-mongo-adapter/commit/0c80f7fd1da772cc54971fd93a1fa93f0c5e47d0)) 31 | * ensure CSR works with a capped collection ([d3fa038](https://github.com/socketio/socket.io-mongo-adapter/commit/d3fa03874038ed9ec011d8795ac7dc6d840f4abe)) 32 | * exclude offline nodes when calling serverCount() ([e2fb8c2](https://github.com/socketio/socket.io-mongo-adapter/commit/e2fb8c2f9d126e763e4f0c0ffba158f2d0c5c17a)) 33 | 34 | 35 | 36 | ## [0.3.1](https://github.com/socketio/socket.io-mongo-adapter/compare/0.3.0...0.3.1) (2024-01-10) 37 | 38 | 39 | ### Bug Fixes 40 | 41 | * add support for mongodb@6 ([1a04885](https://github.com/socketio/socket.io-mongo-adapter/commit/1a0488562f1e8b4171af20378aacfa43072980dd)) 42 | * properly handle promise rejections ([075216f](https://github.com/socketio/socket.io-mongo-adapter/commit/075216f7decac3e8660c39dc1009a27d786ca1ad)) 43 | 44 | 45 | 46 | ## [0.3.0](https://github.com/socketio/socket.io-mongo-adapter/compare/0.2.1...0.3.0) (2023-02-23) 47 | 48 | 49 | ### Features 50 | 51 | #### Connection state recovery 52 | 53 | This adapter is now compatible with the connection state recovery feature, which was added in `socket.io@4.6.0`. 54 | 55 | Reference: https://socket.io/docs/v4/connection-state-recovery 56 | 57 | Added in [02e4d57](https://github.com/socketio/socket.io-mongo-adapter/commit/02e4d57721937ce832fc9a83abddaecd4f8d38aa). 58 | 59 | #### Resume token 60 | 61 | Upon reconnection to the MongoDB server, the client will now try to resume the stream at the last offset it has processed. 62 | 63 | If the MongoDB client is disconnected for too long and its token is no longer valid, then the Socket.IO clients connected to this server may miss some packets (which was the previous behavior). 64 | 65 | Added in [e77063b](https://github.com/socketio/socket.io-mongo-adapter/commit/e77063b8fd88b68df58e7bfdc7f3ef4edb51dca0). 66 | 67 | 68 | 69 | ## [0.2.1](https://github.com/socketio/socket.io-mongo-adapter/compare/0.2.0...0.2.1) (2022-05-03) 70 | 71 | 72 | ### Bug Fixes 73 | 74 | * properly handle invalidate events ([938674d](https://github.com/socketio/socket.io-mongo-adapter/commit/938674d101fc01add3b6e01d59d20c2aa84b48eb)) 75 | 76 | 77 | 78 | ## [0.2.0](https://github.com/socketio/socket.io-mongo-adapter/compare/0.1.0...0.2.0) (2022-04-27) 79 | 80 | 81 | ### Features 82 | 83 | * add an option to use a TTL index ([#4](https://github.com/socketio/socket.io-mongo-adapter/issues/4)) ([7fdbb25](https://github.com/socketio/socket.io-mongo-adapter/commit/7fdbb25831255e5f6a37a5df25b4fc41c770ab6a)) 84 | 85 | The `addCreatedAtField` option allows to use a TTL index instead of a capped collection, which is slightly less efficient but more predictable. 86 | 87 | * broadcast and expect multiple acks ([e87a0ce](https://github.com/socketio/socket.io-mongo-adapter/commit/e87a0cec4c6920b5e4ef38c4de3e45c1eba5e4cf)) 88 | 89 | This feature was added in `socket.io@4.5.0`: 90 | 91 | ```js 92 | io.timeout(1000).emit("some-event", (err, responses) => { 93 | // ... 94 | }); 95 | ``` 96 | 97 | Thanks to this change, it will now work with multiple Socket.IO servers. 98 | 99 | * use a single stream for all namespaces ([9b5f4c8](https://github.com/socketio/socket.io-mongo-adapter/commit/9b5f4c83038cc212b898b7fb7ff0ccec3124447c)) 100 | 101 | The adapter will now create one single MongoDB stream for all namespaces, instead of one per namespace, which could lead to performance issues. 102 | 103 | ## 0.1.0 (2021-06-01) 104 | 105 | Initial commit 106 | 107 | -------------------------------------------------------------------------------- /test/connection-state-recovery.ts: -------------------------------------------------------------------------------- 1 | import { createServer } from "http"; 2 | import { Server } from "socket.io"; 3 | import expect = require("expect.js"); 4 | import { io as ioc } from "socket.io-client"; 5 | import { MongoClient } from "mongodb"; 6 | import { createAdapter } from "../lib"; 7 | import { AddressInfo } from "net"; 8 | 9 | const NODES_COUNT = 3; 10 | 11 | describe("connection state recovery", () => { 12 | describe("with a capped collection", () => { 13 | let servers: Server[], ports: number[], mongoClient: MongoClient; 14 | 15 | beforeEach(async () => { 16 | servers = []; 17 | ports = []; 18 | 19 | mongoClient = new MongoClient( 20 | "mongodb://localhost:27017/?replicaSet=rs0&directConnection=true" 21 | ); 22 | await mongoClient.connect(); 23 | 24 | try { 25 | await mongoClient.db("test").createCollection("events-capped", { 26 | capped: true, 27 | size: 1e6, 28 | }); 29 | } catch (e) { 30 | // collection already exists 31 | } 32 | 33 | const collection = mongoClient.db("test").collection("events-capped"); 34 | 35 | return new Promise((resolve) => { 36 | for (let i = 1; i <= NODES_COUNT; i++) { 37 | const httpServer = createServer(); 38 | const io = new Server(httpServer, { 39 | pingInterval: 1500, 40 | pingTimeout: 1600, 41 | connectionStateRecovery: { 42 | maxDisconnectionDuration: 5000, 43 | }, 44 | adapter: createAdapter(collection), 45 | }); 46 | httpServer.listen(async () => { 47 | const port = (httpServer.address() as AddressInfo).port; 48 | 49 | servers.push(io); 50 | ports.push(port); 51 | 52 | if (servers.length === NODES_COUNT) { 53 | resolve(); 54 | } 55 | }); 56 | } 57 | }); 58 | }); 59 | 60 | afterEach(async () => { 61 | servers.forEach((server) => server.close()); 62 | await mongoClient.close(); 63 | }); 64 | 65 | it("should restore the session", (done) => { 66 | const socket = ioc(`http://localhost:${ports[0]}`, { 67 | reconnectionDelay: 20, 68 | }); 69 | 70 | let initialId: string; 71 | 72 | socket.once("connect", () => { 73 | expect(socket.recovered).to.eql(false); 74 | initialId = socket.id; 75 | 76 | servers[0].emit("init"); 77 | }); 78 | 79 | socket.on("init", () => { 80 | // under the hood, the client saves the offset of this packet, so now we force the reconnection 81 | socket.io.engine.close(); 82 | 83 | socket.on("connect", () => { 84 | expect(socket.recovered).to.eql(true); 85 | expect(socket.id).to.eql(initialId); 86 | 87 | socket.disconnect(); 88 | done(); 89 | }); 90 | }); 91 | }); 92 | 93 | it("should restore any missed packets", (done) => { 94 | const socket = ioc(`http://localhost:${ports[0]}`, { 95 | reconnectionDelay: 20, 96 | }); 97 | 98 | servers[0].once("connection", (socket) => { 99 | socket.join("room1"); 100 | 101 | socket.on("disconnect", () => { 102 | // let's send some packets while the client is disconnected 103 | socket.emit("myEvent", 1); 104 | servers[0].emit("myEvent", 2); 105 | servers[0].to("room1").emit("myEvent", 3); 106 | 107 | // those packets should not be received by the client upon reconnection (room mismatch) 108 | servers[0].to("room2").emit("myEvent", 4); 109 | servers[0].except("room1").emit("myEvent", 5); 110 | servers[0].of("/foo").emit("myEvent", 6); 111 | }); 112 | }); 113 | 114 | socket.once("connect", () => { 115 | servers[1].emit("init"); 116 | }); 117 | 118 | socket.on("init", () => { 119 | // under the hood, the client saves the offset of this packet, so now we force the reconnection 120 | socket.io.engine.close(); 121 | 122 | socket.on("connect", () => { 123 | expect(socket.recovered).to.eql(true); 124 | 125 | setTimeout(() => { 126 | expect(events).to.eql([1, 2, 3]); 127 | 128 | socket.disconnect(); 129 | done(); 130 | }, 50); 131 | }); 132 | }); 133 | 134 | const events: number[] = []; 135 | 136 | socket.on("myEvent", (val) => { 137 | events.push(val); 138 | }); 139 | }); 140 | 141 | it("should restore the session only once", (done) => { 142 | const socket = ioc(`http://localhost:${ports[0]}`, { 143 | reconnectionDelay: 20, 144 | }); 145 | 146 | let initialId: string; 147 | 148 | socket.once("connect", () => { 149 | expect(socket.recovered).to.eql(false); 150 | initialId = socket.id; 151 | 152 | servers[0].emit("init"); 153 | }); 154 | 155 | socket.once("init", () => { 156 | // under the hood, the client saves the offset of this packet, so now we force the reconnection 157 | socket.io.engine.close(); 158 | 159 | socket.once("connect", () => { 160 | expect(socket.recovered).to.eql(true); 161 | expect(socket.id).to.eql(initialId); 162 | 163 | // unlike above, manual disconnection is not recoverable 164 | socket.disconnect().connect(); 165 | socket.once("connect", () => { 166 | expect(socket.recovered).to.eql(false); 167 | 168 | socket.disconnect(); 169 | done(); 170 | }); 171 | }); 172 | }); 173 | }); 174 | 175 | it("should fail to restore an unknown session (invalid session ID)", (done) => { 176 | const socket = ioc(`http://localhost:${ports[0]}`, { 177 | reconnectionDelay: 20, 178 | }); 179 | 180 | socket.once("connect", () => { 181 | // @ts-ignore 182 | socket._pid = "abc"; 183 | // @ts-ignore 184 | socket._lastOffset = "507f191e810c19729de860ea"; 185 | // force reconnection 186 | socket.io.engine.close(); 187 | 188 | socket.on("connect", () => { 189 | expect(socket.recovered).to.eql(false); 190 | 191 | socket.disconnect(); 192 | done(); 193 | }); 194 | }); 195 | }); 196 | 197 | it("should fail to restore an unknown session (invalid offset)", (done) => { 198 | const socket = ioc(`http://localhost:${ports[0]}`, { 199 | reconnectionDelay: 20, 200 | upgrade: false, 201 | }); 202 | 203 | socket.once("connect", () => { 204 | // @ts-ignore 205 | socket._lastOffset = "abc"; 206 | socket.io.engine.close(); 207 | 208 | socket.on("connect", () => { 209 | expect(socket.recovered).to.eql(false); 210 | 211 | socket.disconnect(); 212 | done(); 213 | }); 214 | }); 215 | }); 216 | }); 217 | 218 | describe("with a TTL index", () => { 219 | let servers: Server[], ports: number[], mongoClient: MongoClient; 220 | 221 | beforeEach(async () => { 222 | servers = []; 223 | ports = []; 224 | 225 | mongoClient = new MongoClient( 226 | "mongodb://localhost:27017/?replicaSet=rs0&directConnection=true" 227 | ); 228 | await mongoClient.connect(); 229 | 230 | const collection = mongoClient.db("test").collection("events-ttl"); 231 | 232 | await collection.createIndex( 233 | { createdAt: 1 }, 234 | { expireAfterSeconds: 3600, background: true } 235 | ); 236 | 237 | return new Promise((resolve) => { 238 | for (let i = 1; i <= NODES_COUNT; i++) { 239 | const httpServer = createServer(); 240 | const io = new Server(httpServer, { 241 | pingInterval: 1500, 242 | pingTimeout: 1600, 243 | connectionStateRecovery: { 244 | maxDisconnectionDuration: 5000, 245 | }, 246 | adapter: createAdapter(collection, { 247 | addCreatedAtField: true, 248 | }), 249 | }); 250 | httpServer.listen(async () => { 251 | const port = (httpServer.address() as AddressInfo).port; 252 | 253 | servers.push(io); 254 | ports.push(port); 255 | 256 | if (servers.length === NODES_COUNT) { 257 | resolve(); 258 | } 259 | }); 260 | } 261 | }); 262 | }); 263 | 264 | afterEach(async () => { 265 | servers.forEach((server) => server.close()); 266 | await mongoClient.close(); 267 | }); 268 | 269 | it("should restore the session", (done) => { 270 | const socket = ioc(`http://localhost:${ports[0]}`, { 271 | reconnectionDelay: 20, 272 | }); 273 | 274 | let initialId: string; 275 | 276 | socket.once("connect", () => { 277 | expect(socket.recovered).to.eql(false); 278 | initialId = socket.id; 279 | 280 | servers[0].emit("init"); 281 | }); 282 | 283 | socket.on("init", () => { 284 | // under the hood, the client saves the offset of this packet, so now we force the reconnection 285 | socket.io.engine.close(); 286 | 287 | socket.on("connect", () => { 288 | expect(socket.recovered).to.eql(true); 289 | expect(socket.id).to.eql(initialId); 290 | 291 | socket.disconnect(); 292 | done(); 293 | }); 294 | }); 295 | }); 296 | 297 | it("should restore any missed packets", (done) => { 298 | const socket = ioc(`http://localhost:${ports[0]}`, { 299 | reconnectionDelay: 20, 300 | }); 301 | 302 | servers[0].once("connection", (socket) => { 303 | socket.join("room1"); 304 | 305 | socket.on("disconnect", () => { 306 | // let's send some packets while the client is disconnected 307 | socket.emit("myEvent", 1); 308 | servers[0].emit("myEvent", 2); 309 | servers[0].to("room1").emit("myEvent", 3); 310 | 311 | // those packets should not be received by the client upon reconnection (room mismatch) 312 | servers[0].to("room2").emit("myEvent", 4); 313 | servers[0].except("room1").emit("myEvent", 5); 314 | servers[0].of("/foo").emit("myEvent", 6); 315 | }); 316 | }); 317 | 318 | socket.once("connect", () => { 319 | servers[1].emit("init"); 320 | }); 321 | 322 | socket.on("init", () => { 323 | // under the hood, the client saves the offset of this packet, so now we force the reconnection 324 | socket.io.engine.close(); 325 | 326 | socket.on("connect", () => { 327 | expect(socket.recovered).to.eql(true); 328 | 329 | setTimeout(() => { 330 | expect(events).to.eql([1, 2, 3]); 331 | 332 | socket.disconnect(); 333 | done(); 334 | }, 50); 335 | }); 336 | }); 337 | 338 | const events: number[] = []; 339 | 340 | socket.on("myEvent", (val) => { 341 | events.push(val); 342 | }); 343 | }); 344 | 345 | it("should restore the session only once", (done) => { 346 | const socket = ioc(`http://localhost:${ports[0]}`, { 347 | reconnectionDelay: 20, 348 | }); 349 | 350 | let initialId: string; 351 | 352 | socket.once("connect", () => { 353 | expect(socket.recovered).to.eql(false); 354 | initialId = socket.id; 355 | 356 | servers[0].emit("init"); 357 | }); 358 | 359 | socket.once("init", () => { 360 | // under the hood, the client saves the offset of this packet, so now we force the reconnection 361 | socket.io.engine.close(); 362 | 363 | socket.once("connect", () => { 364 | expect(socket.recovered).to.eql(true); 365 | expect(socket.id).to.eql(initialId); 366 | 367 | // unlike above, manual disconnection is not recoverable 368 | socket.disconnect().connect(); 369 | socket.once("connect", () => { 370 | expect(socket.recovered).to.eql(false); 371 | 372 | socket.disconnect(); 373 | done(); 374 | }); 375 | }); 376 | }); 377 | }); 378 | 379 | it("should fail to restore an unknown session (invalid session ID)", (done) => { 380 | const socket = ioc(`http://localhost:${ports[0]}`, { 381 | reconnectionDelay: 20, 382 | }); 383 | 384 | socket.once("connect", () => { 385 | // @ts-ignore 386 | socket._pid = "abc"; 387 | // @ts-ignore 388 | socket._lastOffset = "507f191e810c19729de860ea"; 389 | // force reconnection 390 | socket.io.engine.close(); 391 | 392 | socket.on("connect", () => { 393 | expect(socket.recovered).to.eql(false); 394 | 395 | socket.disconnect(); 396 | done(); 397 | }); 398 | }); 399 | }); 400 | 401 | it("should fail to restore an unknown session (invalid offset)", (done) => { 402 | const socket = ioc(`http://localhost:${ports[0]}`, { 403 | reconnectionDelay: 20, 404 | upgrade: false, 405 | }); 406 | 407 | socket.once("connect", () => { 408 | // @ts-ignore 409 | socket._lastOffset = "abc"; 410 | socket.io.engine.close(); 411 | 412 | socket.on("connect", () => { 413 | expect(socket.recovered).to.eql(false); 414 | 415 | socket.disconnect(); 416 | done(); 417 | }); 418 | }); 419 | }); 420 | }); 421 | }); 422 | -------------------------------------------------------------------------------- /test/index.ts: -------------------------------------------------------------------------------- 1 | import { createServer } from "http"; 2 | import { Server, Socket as ServerSocket } from "socket.io"; 3 | import { io as ioc, Socket as ClientSocket } from "socket.io-client"; 4 | import expect = require("expect.js"); 5 | import { createAdapter, MongoAdapter } from "../lib"; 6 | import type { AddressInfo } from "net"; 7 | import { MongoClient } from "mongodb"; 8 | import { times, sleep } from "./util"; 9 | 10 | const NODES_COUNT = 3; 11 | 12 | describe("@socket.io/mongodb-adapter", () => { 13 | let servers: Server[], 14 | serverSockets: ServerSocket[], 15 | clientSockets: ClientSocket[], 16 | mongoClient: MongoClient; 17 | 18 | beforeEach(async () => { 19 | servers = []; 20 | serverSockets = []; 21 | clientSockets = []; 22 | 23 | mongoClient = new MongoClient( 24 | "mongodb://localhost:27017/?replicaSet=rs0&directConnection=true" 25 | ); 26 | await mongoClient.connect(); 27 | 28 | const collection = mongoClient.db("test").collection("events"); 29 | 30 | return new Promise((resolve) => { 31 | for (let i = 1; i <= NODES_COUNT; i++) { 32 | const httpServer = createServer(); 33 | const io = new Server(httpServer); 34 | io.adapter(createAdapter(collection)); 35 | httpServer.listen(() => { 36 | const port = (httpServer.address() as AddressInfo).port; 37 | const clientSocket = ioc(`http://localhost:${port}`); 38 | 39 | io.on("connection", async (socket) => { 40 | clientSockets.push(clientSocket); 41 | serverSockets.push(socket); 42 | servers.push(io); 43 | if (servers.length === NODES_COUNT) { 44 | // ensure all nodes know each other 45 | servers[0].emit("ping"); 46 | servers[1].emit("ping"); 47 | servers[2].emit("ping"); 48 | 49 | await sleep(200); 50 | 51 | resolve(); 52 | } 53 | }); 54 | }); 55 | } 56 | }); 57 | }); 58 | 59 | afterEach(async () => { 60 | servers.forEach((server) => server.close()); 61 | clientSockets.forEach((socket) => socket.disconnect()); 62 | await mongoClient.close(); 63 | }); 64 | 65 | describe("broadcast", function () { 66 | it("broadcasts to all clients", (done) => { 67 | const partialDone = times(3, done); 68 | 69 | clientSockets.forEach((clientSocket) => { 70 | clientSocket.on("test", (arg1, arg2, arg3) => { 71 | expect(arg1).to.eql(1); 72 | expect(arg2).to.eql("2"); 73 | expect(Buffer.isBuffer(arg3)).to.be(true); 74 | partialDone(); 75 | }); 76 | }); 77 | 78 | servers[0].emit("test", 1, "2", Buffer.from([3, 4])); 79 | }); 80 | 81 | it("broadcasts to all clients in a namespace", (done) => { 82 | const partialDone = times(3, done); 83 | 84 | servers.forEach((server) => server.of("/custom")); 85 | 86 | const onConnect = times(3, () => { 87 | servers[0].of("/custom").emit("test"); 88 | }); 89 | 90 | clientSockets.forEach((clientSocket) => { 91 | const socket = clientSocket.io.socket("/custom"); 92 | socket.on("connect", onConnect); 93 | socket.on("test", () => { 94 | socket.disconnect(); 95 | partialDone(); 96 | }); 97 | }); 98 | }); 99 | 100 | it("broadcasts to all clients in a room", (done) => { 101 | serverSockets[1].join("room1"); 102 | 103 | clientSockets[0].on("test", () => { 104 | done(new Error("should not happen")); 105 | }); 106 | 107 | clientSockets[1].on("test", () => { 108 | done(); 109 | }); 110 | 111 | clientSockets[2].on("test", () => { 112 | done(new Error("should not happen")); 113 | }); 114 | 115 | servers[0].to("room1").emit("test"); 116 | }); 117 | 118 | it("broadcasts to all clients except in room", (done) => { 119 | const partialDone = times(2, done); 120 | serverSockets[1].join("room1"); 121 | 122 | clientSockets[0].on("test", () => { 123 | partialDone(); 124 | }); 125 | 126 | clientSockets[1].on("test", () => { 127 | done(new Error("should not happen")); 128 | }); 129 | 130 | clientSockets[2].on("test", () => { 131 | partialDone(); 132 | }); 133 | 134 | servers[0].of("/").except("room1").emit("test"); 135 | }); 136 | 137 | it("broadcasts to local clients only", (done) => { 138 | clientSockets[0].on("test", () => { 139 | done(); 140 | }); 141 | 142 | clientSockets[1].on("test", () => { 143 | done(new Error("should not happen")); 144 | }); 145 | 146 | clientSockets[2].on("test", () => { 147 | done(new Error("should not happen")); 148 | }); 149 | 150 | servers[0].local.emit("test"); 151 | }); 152 | 153 | it("broadcasts with multiple acknowledgements", (done) => { 154 | clientSockets[0].on("test", (cb) => { 155 | cb(1); 156 | }); 157 | 158 | clientSockets[1].on("test", (cb) => { 159 | cb(2); 160 | }); 161 | 162 | clientSockets[2].on("test", (cb) => { 163 | cb(3); 164 | }); 165 | 166 | servers[0].timeout(500).emit("test", (err: Error, responses: any[]) => { 167 | expect(err).to.be(null); 168 | expect(responses).to.contain(1); 169 | expect(responses).to.contain(2); 170 | expect(responses).to.contain(3); 171 | 172 | setTimeout(() => { 173 | // @ts-ignore 174 | expect(servers[0].of("/").adapter.ackRequests.size).to.eql(0); 175 | 176 | done(); 177 | }, 500); 178 | }); 179 | }); 180 | 181 | it("broadcasts with multiple acknowledgements (binary content)", (done) => { 182 | clientSockets[0].on("test", (cb) => { 183 | cb(Buffer.from([1])); 184 | }); 185 | 186 | clientSockets[1].on("test", (cb) => { 187 | cb(Buffer.from([2])); 188 | }); 189 | 190 | clientSockets[2].on("test", (cb) => { 191 | cb(Buffer.from([3])); 192 | }); 193 | 194 | servers[0].timeout(500).emit("test", (err: Error, responses: any[]) => { 195 | expect(err).to.be(null); 196 | responses.forEach((response) => { 197 | expect(Buffer.isBuffer(response)).to.be(true); 198 | }); 199 | 200 | done(); 201 | }); 202 | }); 203 | 204 | it("broadcasts with multiple acknowledgements (no client)", (done) => { 205 | servers[0] 206 | .to("abc") 207 | .timeout(500) 208 | .emit("test", (err: Error, responses: any[]) => { 209 | expect(err).to.be(null); 210 | expect(responses).to.eql([]); 211 | 212 | done(); 213 | }); 214 | }); 215 | 216 | it("broadcasts with multiple acknowledgements (timeout)", (done) => { 217 | clientSockets[0].on("test", (cb) => { 218 | cb(1); 219 | }); 220 | 221 | clientSockets[1].on("test", (cb) => { 222 | cb(2); 223 | }); 224 | 225 | clientSockets[2].on("test", (cb) => { 226 | // do nothing 227 | }); 228 | 229 | servers[0].timeout(500).emit("test", (err: Error, responses: any[]) => { 230 | expect(err).to.be.an(Error); 231 | expect(responses).to.contain(1); 232 | expect(responses).to.contain(2); 233 | 234 | done(); 235 | }); 236 | }); 237 | 238 | it("broadcasts with a single acknowledgement (local)", async () => { 239 | clientSockets[0].on("test", () => expect().fail()); 240 | clientSockets[1].on("test", (cb) => cb(2)); 241 | clientSockets[2].on("test", () => expect().fail()); 242 | 243 | const response = await serverSockets[1].emitWithAck("test"); 244 | expect(response).to.eql(2); 245 | }); 246 | 247 | it("broadcasts with a single acknowledgement (remote)", async () => { 248 | clientSockets[0].on("test", () => expect().fail()); 249 | clientSockets[1].on("test", (cb) => cb(2)); 250 | clientSockets[2].on("test", () => expect().fail()); 251 | 252 | const sockets = await servers[0].in(serverSockets[1].id).fetchSockets(); 253 | expect(sockets.length).to.eql(1); 254 | 255 | const response = await sockets[0].timeout(500).emitWithAck("test"); 256 | expect(response).to.eql(2); 257 | }); 258 | }); 259 | 260 | describe("socketsJoin", () => { 261 | it("makes all socket instances join the specified room", async () => { 262 | servers[0].socketsJoin("room1"); 263 | 264 | await sleep(200); 265 | 266 | expect(serverSockets[0].rooms.has("room1")).to.be(true); 267 | expect(serverSockets[1].rooms.has("room1")).to.be(true); 268 | expect(serverSockets[2].rooms.has("room1")).to.be(true); 269 | }); 270 | 271 | it("makes the matching socket instances join the specified room", async () => { 272 | serverSockets[0].join("room1"); 273 | serverSockets[2].join("room1"); 274 | 275 | servers[0].in("room1").socketsJoin("room2"); 276 | 277 | await sleep(200); 278 | 279 | expect(serverSockets[0].rooms.has("room2")).to.be(true); 280 | expect(serverSockets[1].rooms.has("room2")).to.be(false); 281 | expect(serverSockets[2].rooms.has("room2")).to.be(true); 282 | }); 283 | 284 | it("makes the given socket instance join the specified room", async () => { 285 | servers[0].in(serverSockets[1].id).socketsJoin("room3"); 286 | 287 | await sleep(200); 288 | 289 | expect(serverSockets[0].rooms.has("room3")).to.be(false); 290 | expect(serverSockets[1].rooms.has("room3")).to.be(true); 291 | expect(serverSockets[2].rooms.has("room3")).to.be(false); 292 | }); 293 | }); 294 | 295 | describe("socketsLeave", () => { 296 | it("makes all socket instances leave the specified room", async () => { 297 | serverSockets[0].join("room1"); 298 | serverSockets[2].join("room1"); 299 | 300 | servers[0].socketsLeave("room1"); 301 | 302 | await sleep(200); 303 | 304 | expect(serverSockets[0].rooms.has("room1")).to.be(false); 305 | expect(serverSockets[1].rooms.has("room1")).to.be(false); 306 | expect(serverSockets[2].rooms.has("room1")).to.be(false); 307 | }); 308 | 309 | it("makes the matching socket instances leave the specified room", async () => { 310 | serverSockets[0].join(["room1", "room2"]); 311 | serverSockets[1].join(["room1", "room2"]); 312 | serverSockets[2].join(["room2"]); 313 | 314 | servers[0].in("room1").socketsLeave("room2"); 315 | 316 | await sleep(200); 317 | 318 | expect(serverSockets[0].rooms.has("room2")).to.be(false); 319 | expect(serverSockets[1].rooms.has("room2")).to.be(false); 320 | expect(serverSockets[2].rooms.has("room2")).to.be(true); 321 | }); 322 | 323 | it("makes the given socket instance leave the specified room", async () => { 324 | serverSockets[0].join("room3"); 325 | serverSockets[1].join("room3"); 326 | serverSockets[2].join("room3"); 327 | 328 | servers[0].in(serverSockets[1].id).socketsLeave("room3"); 329 | 330 | await sleep(200); 331 | 332 | expect(serverSockets[0].rooms.has("room3")).to.be(true); 333 | expect(serverSockets[1].rooms.has("room3")).to.be(false); 334 | expect(serverSockets[2].rooms.has("room3")).to.be(true); 335 | }); 336 | }); 337 | 338 | describe("disconnectSockets", () => { 339 | it("makes all socket instances disconnect", (done) => { 340 | const partialDone = times(3, done); 341 | 342 | clientSockets.forEach((clientSocket) => { 343 | clientSocket.on("disconnect", (reason) => { 344 | expect(reason).to.eql("io server disconnect"); 345 | partialDone(); 346 | }); 347 | }); 348 | 349 | servers[0].disconnectSockets(); 350 | }); 351 | }); 352 | 353 | describe("fetchSockets", () => { 354 | it("returns all socket instances", async () => { 355 | const sockets = await servers[0].fetchSockets(); 356 | 357 | expect(sockets).to.be.an(Array); 358 | expect(sockets).to.have.length(3); 359 | // @ts-ignore 360 | expect(servers[0].of("/").adapter.requests.size).to.eql(0); // clean up 361 | }); 362 | 363 | it("returns a single socket instance", async () => { 364 | serverSockets[1].data = "test" as any; 365 | 366 | const [remoteSocket] = await servers[0] 367 | .in(serverSockets[1].id) 368 | .fetchSockets(); 369 | 370 | expect(remoteSocket.handshake).to.eql(serverSockets[1].handshake); 371 | expect(remoteSocket.data).to.eql("test"); 372 | expect(remoteSocket.rooms.size).to.eql(1); 373 | }); 374 | 375 | it("returns only local socket instances", async () => { 376 | const sockets = await servers[0].local.fetchSockets(); 377 | 378 | expect(sockets).to.have.length(1); 379 | }); 380 | }); 381 | 382 | describe("serverSideEmit", () => { 383 | it("sends an event to other server instances", (done) => { 384 | const partialDone = times(2, done); 385 | 386 | servers[0].serverSideEmit("hello", "world", 1, "2"); 387 | 388 | servers[0].on("hello", () => { 389 | done(new Error("should not happen")); 390 | }); 391 | 392 | servers[1].on("hello", (arg1, arg2, arg3) => { 393 | expect(arg1).to.eql("world"); 394 | expect(arg2).to.eql(1); 395 | expect(arg3).to.eql("2"); 396 | partialDone(); 397 | }); 398 | 399 | servers[2].of("/").on("hello", () => { 400 | partialDone(); 401 | }); 402 | }); 403 | 404 | it("sends an event and receives a response from the other server instances", (done) => { 405 | servers[0].serverSideEmit("hello", (err: Error, response: any) => { 406 | expect(err).to.be(null); 407 | expect(response).to.be.an(Array); 408 | expect(response).to.contain(2); 409 | expect(response).to.contain("3"); 410 | done(); 411 | }); 412 | 413 | servers[0].on("hello", () => { 414 | done(new Error("should not happen")); 415 | }); 416 | 417 | servers[1].on("hello", (cb) => { 418 | cb(2); 419 | }); 420 | 421 | servers[2].on("hello", (cb) => { 422 | cb("3"); 423 | }); 424 | }); 425 | 426 | it("sends an event but timeout if one server does not respond", (done) => { 427 | (servers[0].of("/").adapter as MongoAdapter).requestsTimeout = 200; 428 | 429 | servers[0].serverSideEmit("hello", (err: Error, response: any) => { 430 | expect(err.message).to.be( 431 | "timeout reached: only 1 responses received out of 2" 432 | ); 433 | expect(response).to.be.an(Array); 434 | expect(response).to.contain(2); 435 | done(); 436 | }); 437 | 438 | servers[0].on("hello", () => { 439 | done(new Error("should not happen")); 440 | }); 441 | 442 | servers[1].on("hello", (cb) => { 443 | cb(2); 444 | }); 445 | 446 | servers[2].on("hello", () => { 447 | // do nothing 448 | }); 449 | }); 450 | }); 451 | 452 | it("should not throw when receiving a drop event", async () => { 453 | await mongoClient.db("test").dropCollection("events"); 454 | 455 | await sleep(100); 456 | }); 457 | 458 | it("should resume the change stream upon reconnection", async () => { 459 | await mongoClient.close(true); 460 | 461 | return new Promise(async (resolve) => { 462 | const partialDone = times(3, resolve); 463 | clientSockets[0].on("test1", partialDone); 464 | clientSockets[1].on("test2", partialDone); 465 | clientSockets[2].on("test3", partialDone); 466 | 467 | await mongoClient.connect(); 468 | servers[0].to(clientSockets[1].id).emit("test2"); 469 | 470 | await sleep(500); 471 | 472 | servers[1].to(clientSockets[2].id).emit("test3"); 473 | servers[2].to(clientSockets[0].id).emit("test1"); 474 | }); 475 | }); 476 | 477 | import("./connection-state-recovery"); 478 | }); 479 | -------------------------------------------------------------------------------- /lib/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Adapter, 3 | BroadcastOptions, 4 | PrivateSessionId, 5 | Room, 6 | Session, 7 | } from "socket.io-adapter"; 8 | import { randomBytes } from "crypto"; 9 | import { ObjectId, MongoServerError, WithId, Document } from "mongodb"; 10 | import type { Collection, ChangeStream, ChangeStreamOptions } from "mongodb"; 11 | 12 | const randomId = () => randomBytes(8).toString("hex"); 13 | const debug = require("debug")("socket.io-mongo-adapter"); 14 | 15 | /** 16 | * Event types, for messages between nodes 17 | */ 18 | 19 | enum EventType { 20 | INITIAL_HEARTBEAT = 1, 21 | HEARTBEAT, 22 | BROADCAST, 23 | SOCKETS_JOIN, 24 | SOCKETS_LEAVE, 25 | DISCONNECT_SOCKETS, 26 | FETCH_SOCKETS, 27 | FETCH_SOCKETS_RESPONSE, 28 | SERVER_SIDE_EMIT, 29 | SERVER_SIDE_EMIT_RESPONSE, 30 | BROADCAST_CLIENT_COUNT, 31 | BROADCAST_ACK, 32 | SESSION, 33 | } 34 | 35 | /** 36 | * The format of the documents in the MongoDB collection 37 | */ 38 | interface AdapterEvent { 39 | /** 40 | * The type of the event 41 | */ 42 | type: EventType; 43 | /** 44 | * The UID of the server, to filter event created by itself (see watch() call) 45 | */ 46 | uid?: string; 47 | /** 48 | * The namespace 49 | */ 50 | nsp?: string; 51 | /** 52 | * The date of creation of the event, to be able to manually clean up the collection. 53 | * 54 | * @see MongoAdapterOptions.addCreatedAtField 55 | */ 56 | createdAt?: Date; 57 | /** 58 | * Some additional data, depending on the event type 59 | */ 60 | data?: any; 61 | } 62 | 63 | interface Request { 64 | type: EventType; 65 | resolve: Function; 66 | timeout: NodeJS.Timeout; 67 | expected: number; 68 | current: number; 69 | responses: any[]; 70 | } 71 | 72 | interface AckRequest { 73 | type: EventType.BROADCAST; 74 | clientCountCallback: (clientCount: number) => void; 75 | ack: (...args: any[]) => void; 76 | } 77 | 78 | /** 79 | * UID of an emitter using the `@socket.io/mongo-emitter` package 80 | */ 81 | const EMITTER_UID = "emitter"; 82 | 83 | export interface MongoAdapterOptions { 84 | /** 85 | * the name of this node 86 | * @default a random id 87 | */ 88 | uid: string; 89 | /** 90 | * after this timeout the adapter will stop waiting from responses to request 91 | * @default 5000 92 | */ 93 | requestsTimeout: number; 94 | /** 95 | * Number of ms between two heartbeats 96 | * @default 5000 97 | */ 98 | heartbeatInterval: number; 99 | /** 100 | * Number of ms without heartbeat before we consider a node down 101 | * @default 10000 102 | */ 103 | heartbeatTimeout: number; 104 | 105 | /** 106 | * Add a createdAt field to each MongoDB document 107 | * @default false 108 | */ 109 | addCreatedAtField: boolean; 110 | 111 | /** 112 | * Options to pass to the MongoDB change stream. 113 | */ 114 | changeStreamOptions?: Partial; 115 | } 116 | 117 | /** 118 | * It seems the `promoteBuffers` option is not always honored, so we manually replace Binary objects by the underlying 119 | * Buffer objects. 120 | * 121 | * Update: it seems to be fixed with `mongodb@4`, but we'll keep it for backward compatibility 122 | * 123 | * Reference: 124 | * - http://mongodb.github.io/node-mongodb-native/3.6/api/Binary.html 125 | * - https://jira.mongodb.org/browse/NODE-1421 126 | */ 127 | const replaceBinaryObjectsByBuffers = (obj: any) => { 128 | if (!obj || typeof obj !== "object") { 129 | return obj; 130 | } 131 | if (obj._bsontype === "Binary" && Buffer.isBuffer(obj.buffer)) { 132 | return obj.buffer; 133 | } 134 | if (Array.isArray(obj)) { 135 | for (let i = 0; i < obj.length; i++) { 136 | obj[i] = replaceBinaryObjectsByBuffers(obj[i]); 137 | } 138 | } else { 139 | for (const key in obj) { 140 | if (Object.prototype.hasOwnProperty.call(obj, key)) { 141 | obj[key] = replaceBinaryObjectsByBuffers(obj[key]); 142 | } 143 | } 144 | } 145 | return obj; 146 | }; 147 | 148 | function onPublishError(err: Error) { 149 | debug("something went wrong when inserting the MongoDB document: %s", err); 150 | } 151 | 152 | /** 153 | * Returns a function that will create a MongoAdapter instance. 154 | * 155 | * @param mongoCollection - a MongoDB collection instance 156 | * @param opts - additional options 157 | * 158 | * @public 159 | */ 160 | export function createAdapter( 161 | mongoCollection: Collection, 162 | opts: Partial = {} 163 | ) { 164 | opts.uid = opts.uid || randomId(); 165 | 166 | let isClosed = false; 167 | let adapters = new Map(); 168 | let changeStream: ChangeStream; 169 | let changeStreamOpts: ChangeStreamOptions = opts.changeStreamOptions ?? {}; 170 | 171 | const initChangeStream = () => { 172 | if (isClosed || (changeStream && !changeStream.closed)) { 173 | return; 174 | } 175 | debug("opening change stream"); 176 | changeStream = mongoCollection.watch( 177 | [ 178 | { 179 | $match: { 180 | "fullDocument.uid": { 181 | $ne: opts.uid, // ignore events from self 182 | }, 183 | }, 184 | }, 185 | ], 186 | changeStreamOpts 187 | ); 188 | 189 | changeStream.on("change", (event: any) => { 190 | if (event.operationType === "insert") { 191 | changeStreamOpts.resumeAfter = changeStream.resumeToken; 192 | adapters.get(event.fullDocument?.nsp)?.onEvent(event); 193 | } 194 | }); 195 | 196 | changeStream.on("error", (err: Error) => { 197 | debug("change stream encountered an error: %s", err.message); 198 | if ( 199 | err instanceof MongoServerError && 200 | !err.hasErrorLabel("ResumableChangeStreamError") 201 | ) { 202 | // the resume token was not found in the oplog 203 | changeStreamOpts = {}; 204 | } 205 | }); 206 | 207 | changeStream.on("close", () => { 208 | debug("change stream was closed, scheduling reconnection..."); 209 | setTimeout(() => { 210 | initChangeStream(); 211 | }, 1000); 212 | }); 213 | }; 214 | 215 | return function (nsp: any) { 216 | if (!changeStream) { 217 | isClosed = false; 218 | initChangeStream(); 219 | } 220 | 221 | let adapter = new MongoAdapter(nsp, mongoCollection, opts); 222 | 223 | adapters.set(nsp.name, adapter); 224 | 225 | const defaultClose = adapter.close; 226 | 227 | adapter.close = async () => { 228 | adapters.delete(nsp.name); 229 | 230 | if (adapters.size === 0) { 231 | changeStream.removeAllListeners("close"); 232 | await changeStream.close(); 233 | // @ts-ignore 234 | changeStream = null; 235 | isClosed = true; 236 | } 237 | 238 | defaultClose.call(adapter); 239 | }; 240 | 241 | return adapter; 242 | }; 243 | } 244 | 245 | export class MongoAdapter extends Adapter { 246 | public readonly uid: string; 247 | public requestsTimeout: number; 248 | public heartbeatInterval: number; 249 | public heartbeatTimeout: number; 250 | public addCreatedAtField: boolean; 251 | 252 | private readonly mongoCollection: Collection; 253 | private nodesMap: Map = new Map(); // uid => timestamp of last message 254 | private heartbeatTimer: NodeJS.Timeout | undefined; 255 | private requests: Map = new Map(); 256 | private ackRequests: Map = new Map(); 257 | private isClosed = false; 258 | 259 | /** 260 | * Adapter constructor. 261 | * 262 | * @param nsp - the namespace 263 | * @param mongoCollection - a MongoDB collection instance 264 | * @param opts - additional options 265 | * 266 | * @public 267 | */ 268 | constructor( 269 | nsp: any, 270 | mongoCollection: Collection, 271 | opts: Partial = {} 272 | ) { 273 | super(nsp); 274 | this.mongoCollection = mongoCollection; 275 | this.uid = opts.uid!; 276 | this.requestsTimeout = opts.requestsTimeout || 5000; 277 | this.heartbeatInterval = opts.heartbeatInterval || 5000; 278 | this.heartbeatTimeout = opts.heartbeatTimeout || 10000; 279 | this.addCreatedAtField = !!opts.addCreatedAtField; 280 | 281 | this.publish({ 282 | type: EventType.INITIAL_HEARTBEAT, 283 | }).catch(onPublishError); 284 | } 285 | 286 | close(): Promise | void { 287 | this.isClosed = true; 288 | if (this.heartbeatTimer) { 289 | clearTimeout(this.heartbeatTimer); 290 | } 291 | return; 292 | } 293 | 294 | public async onEvent(event: any) { 295 | const document = event.fullDocument; 296 | debug( 297 | "new event of type %d for %s from %s", 298 | document.type, 299 | document.nsp, 300 | document.uid 301 | ); 302 | 303 | if (document.uid && document.uid !== EMITTER_UID) { 304 | this.nodesMap.set(document.uid, Date.now()); 305 | } 306 | 307 | switch (document.type) { 308 | case EventType.INITIAL_HEARTBEAT: { 309 | this.publish({ 310 | type: EventType.HEARTBEAT, 311 | }).catch(onPublishError); 312 | break; 313 | } 314 | case EventType.BROADCAST: { 315 | debug("broadcast with opts %j", document.data.opts); 316 | 317 | const withAck = document.data.requestId !== undefined; 318 | if (withAck) { 319 | super.broadcastWithAck( 320 | replaceBinaryObjectsByBuffers(document.data.packet), 321 | MongoAdapter.deserializeOptions(document.data.opts), 322 | (clientCount) => { 323 | debug("waiting for %d client acknowledgements", clientCount); 324 | this.publish({ 325 | type: EventType.BROADCAST_CLIENT_COUNT, 326 | data: { 327 | requestId: document.data.requestId, 328 | clientCount, 329 | }, 330 | }); 331 | }, 332 | (arg) => { 333 | debug("received acknowledgement with value %j", arg); 334 | this.publish({ 335 | type: EventType.BROADCAST_ACK, 336 | data: { 337 | requestId: document.data.requestId, 338 | packet: arg, 339 | }, 340 | }); 341 | } 342 | ); 343 | } else { 344 | const packet = replaceBinaryObjectsByBuffers(document.data.packet); 345 | const opts = MongoAdapter.deserializeOptions(document.data.opts); 346 | 347 | this.addOffsetIfNecessary(packet, opts, document._id); 348 | 349 | super.broadcast(packet, opts); 350 | } 351 | break; 352 | } 353 | 354 | case EventType.BROADCAST_CLIENT_COUNT: { 355 | const request = this.ackRequests.get(document.data.requestId); 356 | request?.clientCountCallback(document.data.clientCount); 357 | break; 358 | } 359 | 360 | case EventType.BROADCAST_ACK: { 361 | const request = this.ackRequests.get(document.data.requestId); 362 | const clientResponse = replaceBinaryObjectsByBuffers( 363 | document.data.packet 364 | ); 365 | request?.ack(clientResponse); 366 | break; 367 | } 368 | 369 | case EventType.SOCKETS_JOIN: { 370 | debug("calling addSockets with opts %j", document.data.opts); 371 | super.addSockets( 372 | MongoAdapter.deserializeOptions(document.data.opts), 373 | document.data.rooms 374 | ); 375 | break; 376 | } 377 | case EventType.SOCKETS_LEAVE: { 378 | debug("calling delSockets with opts %j", document.data.opts); 379 | super.delSockets( 380 | MongoAdapter.deserializeOptions(document.data.opts), 381 | document.data.rooms 382 | ); 383 | break; 384 | } 385 | case EventType.DISCONNECT_SOCKETS: { 386 | debug("calling disconnectSockets with opts %j", document.data.opts); 387 | super.disconnectSockets( 388 | MongoAdapter.deserializeOptions(document.data.opts), 389 | document.data.close 390 | ); 391 | break; 392 | } 393 | case EventType.FETCH_SOCKETS: { 394 | debug("calling fetchSockets with opts %j", document.data.opts); 395 | const localSockets = await super.fetchSockets( 396 | MongoAdapter.deserializeOptions(document.data.opts) 397 | ); 398 | 399 | this.publish({ 400 | type: EventType.FETCH_SOCKETS_RESPONSE, 401 | data: { 402 | requestId: document.data.requestId, 403 | sockets: localSockets.map((socket) => ({ 404 | id: socket.id, 405 | handshake: socket.handshake, 406 | rooms: [...socket.rooms], 407 | data: socket.data, 408 | })), 409 | }, 410 | }).catch(onPublishError); 411 | break; 412 | } 413 | case EventType.FETCH_SOCKETS_RESPONSE: { 414 | const request = this.requests.get(document.data.requestId); 415 | 416 | if (!request) { 417 | return; 418 | } 419 | 420 | request.current++; 421 | document.data.sockets.forEach((socket: any) => 422 | request.responses.push(socket) 423 | ); 424 | 425 | if (request.current === request.expected) { 426 | clearTimeout(request.timeout); 427 | request.resolve(request.responses); 428 | this.requests.delete(document.data.requestId); 429 | } 430 | break; 431 | } 432 | case EventType.SERVER_SIDE_EMIT: { 433 | const packet = document.data.packet; 434 | const withAck = document.data.requestId !== undefined; 435 | if (!withAck) { 436 | this.nsp._onServerSideEmit(packet); 437 | return; 438 | } 439 | let called = false; 440 | const callback = (arg: any) => { 441 | // only one argument is expected 442 | if (called) { 443 | return; 444 | } 445 | called = true; 446 | debug("calling acknowledgement with %j", arg); 447 | this.publish({ 448 | type: EventType.SERVER_SIDE_EMIT_RESPONSE, 449 | data: { 450 | requestId: document.data.requestId, 451 | packet: arg, 452 | }, 453 | }); 454 | }; 455 | 456 | packet.push(callback); 457 | this.nsp._onServerSideEmit(packet); 458 | break; 459 | } 460 | case EventType.SERVER_SIDE_EMIT_RESPONSE: { 461 | const request = this.requests.get(document.data.requestId); 462 | 463 | if (!request) { 464 | return; 465 | } 466 | 467 | request.current++; 468 | request.responses.push(document.data.packet); 469 | 470 | if (request.current === request.expected) { 471 | clearTimeout(request.timeout); 472 | request.resolve(null, request.responses); 473 | this.requests.delete(document.data.requestId); 474 | } 475 | } 476 | } 477 | } 478 | 479 | private scheduleHeartbeat() { 480 | if (this.heartbeatTimer) { 481 | clearTimeout(this.heartbeatTimer); 482 | } 483 | this.heartbeatTimer = setTimeout(() => { 484 | debug("sending heartbeat"); 485 | this.publish({ 486 | type: EventType.HEARTBEAT, 487 | }).catch(onPublishError); 488 | this.scheduleHeartbeat(); 489 | }, this.heartbeatInterval); 490 | } 491 | 492 | private publish(document: AdapterEvent): Promise { 493 | if (this.isClosed) { 494 | return Promise.reject("adapter is closed"); 495 | } 496 | debug("publish document %d", document.type); 497 | document.uid = this.uid; 498 | document.nsp = this.nsp.name; 499 | 500 | if (this.addCreatedAtField) { 501 | document.createdAt = new Date(); 502 | } 503 | 504 | this.scheduleHeartbeat(); 505 | 506 | return this.mongoCollection 507 | .insertOne(document) 508 | .then((result) => result.insertedId.toString("hex")); 509 | } 510 | 511 | /** 512 | * Transform ES6 Set into plain arrays 513 | */ 514 | private static serializeOptions(opts: BroadcastOptions) { 515 | return { 516 | rooms: [...opts.rooms], 517 | except: opts.except ? [...opts.except] : [], 518 | flags: opts.flags, 519 | }; 520 | } 521 | 522 | private static deserializeOptions(opts: any): BroadcastOptions { 523 | return { 524 | rooms: new Set(opts.rooms), 525 | except: new Set(opts.except), 526 | flags: opts.flags, 527 | }; 528 | } 529 | 530 | public async broadcast(packet: any, opts: BroadcastOptions) { 531 | const onlyLocal = opts?.flags?.local; 532 | if (!onlyLocal) { 533 | try { 534 | const offset = await this.publish({ 535 | type: EventType.BROADCAST, 536 | data: { 537 | packet, 538 | opts: MongoAdapter.serializeOptions(opts), 539 | }, 540 | }); 541 | this.addOffsetIfNecessary(packet, opts, offset); 542 | } catch (err) { 543 | debug("error while inserting document: %s", err); 544 | return; 545 | } 546 | } 547 | 548 | // packets with binary contents are modified by the broadcast method, hence the nextTick() 549 | // update: this should be fixed now, but we'll keep it for backward compatibility 550 | // see: https://github.com/socketio/socket.io-parser/commit/ae8dd88995dbd7f89c97e5cc15e5b489fa0efece 551 | process.nextTick(() => { 552 | super.broadcast(packet, opts); 553 | }); 554 | } 555 | 556 | /** 557 | * Adds an offset at the end of the data array in order to allow the client to receive any missed packets when it 558 | * reconnects after a temporary disconnection. 559 | * 560 | * @param packet 561 | * @param opts 562 | * @param offset 563 | * @private 564 | */ 565 | private addOffsetIfNecessary( 566 | packet: any, 567 | opts: BroadcastOptions, 568 | offset: string 569 | ) { 570 | if (!this.nsp.server.opts.connectionStateRecovery) { 571 | return; 572 | } 573 | const isEventPacket = packet.type === 2; 574 | // packets with acknowledgement are not stored because the acknowledgement function cannot be serialized and 575 | // restored on another server upon reconnection 576 | const withoutAcknowledgement = packet.id === undefined; 577 | const notVolatile = opts.flags?.volatile === undefined; 578 | 579 | if (isEventPacket && withoutAcknowledgement && notVolatile) { 580 | packet.data.push(offset); 581 | } 582 | } 583 | 584 | public broadcastWithAck( 585 | packet: any, 586 | opts: BroadcastOptions, 587 | clientCountCallback: (clientCount: number) => void, 588 | ack: (...args: any[]) => void 589 | ) { 590 | const onlyLocal = opts?.flags?.local; 591 | if (!onlyLocal) { 592 | const requestId = randomId(); 593 | 594 | this.publish({ 595 | type: EventType.BROADCAST, 596 | data: { 597 | packet, 598 | requestId, 599 | opts: MongoAdapter.serializeOptions(opts), 600 | }, 601 | }).catch(onPublishError); 602 | 603 | this.ackRequests.set(requestId, { 604 | type: EventType.BROADCAST, 605 | clientCountCallback, 606 | ack, 607 | }); 608 | 609 | // we have no way to know at this level whether the server has received an acknowledgement from each client, so we 610 | // will simply clean up the ackRequests map after the given delay 611 | setTimeout(() => { 612 | this.ackRequests.delete(requestId); 613 | }, opts.flags!.timeout); 614 | } 615 | 616 | // packets with binary contents are modified by the broadcast method, hence the nextTick() 617 | process.nextTick(() => { 618 | super.broadcastWithAck(packet, opts, clientCountCallback, ack); 619 | }); 620 | } 621 | 622 | public serverCount(): Promise { 623 | this.nodesMap.forEach((lastSeen, uid) => { 624 | const nodeSeemsDown = Date.now() - lastSeen > this.heartbeatTimeout; 625 | if (nodeSeemsDown) { 626 | debug("node %s seems down", uid); 627 | this.nodesMap.delete(uid); 628 | } 629 | }); 630 | return Promise.resolve(1 + this.nodesMap.size); 631 | } 632 | 633 | addSockets(opts: BroadcastOptions, rooms: Room[]) { 634 | super.addSockets(opts, rooms); 635 | 636 | const onlyLocal = opts.flags?.local; 637 | if (onlyLocal) { 638 | return; 639 | } 640 | 641 | this.publish({ 642 | type: EventType.SOCKETS_JOIN, 643 | data: { 644 | opts: MongoAdapter.serializeOptions(opts), 645 | rooms, 646 | }, 647 | }).catch(onPublishError); 648 | } 649 | 650 | delSockets(opts: BroadcastOptions, rooms: Room[]) { 651 | super.delSockets(opts, rooms); 652 | 653 | const onlyLocal = opts.flags?.local; 654 | if (onlyLocal) { 655 | return; 656 | } 657 | 658 | this.publish({ 659 | type: EventType.SOCKETS_LEAVE, 660 | data: { 661 | opts: MongoAdapter.serializeOptions(opts), 662 | rooms, 663 | }, 664 | }).catch(onPublishError); 665 | } 666 | 667 | disconnectSockets(opts: BroadcastOptions, close: boolean) { 668 | super.disconnectSockets(opts, close); 669 | 670 | const onlyLocal = opts.flags?.local; 671 | if (onlyLocal) { 672 | return; 673 | } 674 | 675 | this.publish({ 676 | type: EventType.DISCONNECT_SOCKETS, 677 | data: { 678 | opts: MongoAdapter.serializeOptions(opts), 679 | close, 680 | }, 681 | }).catch(onPublishError); 682 | } 683 | 684 | async fetchSockets(opts: BroadcastOptions): Promise { 685 | const localSockets = await super.fetchSockets(opts); 686 | const expectedResponseCount = (await this.serverCount()) - 1; 687 | 688 | if (opts.flags?.local || expectedResponseCount === 0) { 689 | return localSockets; 690 | } 691 | 692 | const requestId = randomId(); 693 | 694 | return new Promise((resolve, reject) => { 695 | const timeout = setTimeout(() => { 696 | const storedRequest = this.requests.get(requestId); 697 | if (storedRequest) { 698 | reject( 699 | new Error( 700 | `timeout reached: only ${storedRequest.current} responses received out of ${storedRequest.expected}` 701 | ) 702 | ); 703 | this.requests.delete(requestId); 704 | } 705 | }, this.requestsTimeout); 706 | 707 | const storedRequest = { 708 | type: EventType.FETCH_SOCKETS, 709 | resolve, 710 | timeout, 711 | current: 0, 712 | expected: expectedResponseCount, 713 | responses: localSockets, 714 | }; 715 | this.requests.set(requestId, storedRequest); 716 | 717 | this.publish({ 718 | type: EventType.FETCH_SOCKETS, 719 | data: { 720 | opts: MongoAdapter.serializeOptions(opts), 721 | requestId, 722 | }, 723 | }); 724 | }); 725 | } 726 | 727 | public serverSideEmit(packet: any[]): void { 728 | const withAck = typeof packet[packet.length - 1] === "function"; 729 | 730 | if (withAck) { 731 | this.serverSideEmitWithAck(packet).catch(() => { 732 | // ignore errors 733 | }); 734 | return; 735 | } 736 | 737 | this.publish({ 738 | type: EventType.SERVER_SIDE_EMIT, 739 | data: { 740 | packet, 741 | }, 742 | }).catch(onPublishError); 743 | } 744 | 745 | private async serverSideEmitWithAck(packet: any[]) { 746 | const ack = packet.pop(); 747 | const expectedResponseCount = (await this.serverCount()) - 1; 748 | 749 | debug( 750 | 'waiting for %d responses to "serverSideEmit" request', 751 | expectedResponseCount 752 | ); 753 | 754 | if (expectedResponseCount <= 0) { 755 | return ack(null, []); 756 | } 757 | 758 | const requestId = randomId(); 759 | 760 | const timeout = setTimeout(() => { 761 | const storedRequest = this.requests.get(requestId); 762 | if (storedRequest) { 763 | ack( 764 | new Error( 765 | `timeout reached: only ${storedRequest.current} responses received out of ${storedRequest.expected}` 766 | ), 767 | storedRequest.responses 768 | ); 769 | this.requests.delete(requestId); 770 | } 771 | }, this.requestsTimeout); 772 | 773 | const storedRequest = { 774 | type: EventType.FETCH_SOCKETS, 775 | resolve: ack, 776 | timeout, 777 | current: 0, 778 | expected: expectedResponseCount, 779 | responses: [], 780 | }; 781 | this.requests.set(requestId, storedRequest); 782 | 783 | this.publish({ 784 | type: EventType.SERVER_SIDE_EMIT, 785 | data: { 786 | requestId, // the presence of this attribute defines whether an acknowledgement is needed 787 | packet, 788 | }, 789 | }).catch(onPublishError); 790 | } 791 | 792 | override persistSession(session: any) { 793 | debug("persisting session: %j", session); 794 | this.publish({ 795 | type: EventType.SESSION, 796 | data: session, 797 | }).catch(onPublishError); 798 | } 799 | 800 | override async restoreSession( 801 | pid: PrivateSessionId, 802 | offset: string 803 | ): Promise { 804 | if (!ObjectId.isValid(offset)) { 805 | return Promise.reject("invalid offset"); 806 | } 807 | debug("restoring session: %s", pid); 808 | const eventOffset = new ObjectId(offset); 809 | 810 | let results; 811 | try { 812 | results = await Promise.all([ 813 | // could use a sparse index on [data.pid] (only index the documents whose type is EventType.SESSION) 814 | this.findSession(pid), 815 | this.mongoCollection.findOne({ 816 | type: EventType.BROADCAST, 817 | _id: eventOffset, 818 | }), 819 | ]); 820 | } catch (e) { 821 | debug("error while fetching session: %s", (e as Error).message); 822 | return Promise.reject("error while fetching session"); 823 | } 824 | 825 | if (!results[0] || !results[1]) { 826 | return Promise.reject("session or offset not found"); 827 | } 828 | 829 | const session = results[0].data; 830 | 831 | // could use a sparse index on [_id, nsp, data.opts.rooms, data.opts.except] (only index the documents whose type is EventType.BROADCAST) 832 | const cursor = this.mongoCollection.find({ 833 | $and: [ 834 | { 835 | type: EventType.BROADCAST, 836 | }, 837 | { 838 | _id: { 839 | $gt: eventOffset, 840 | }, 841 | }, 842 | { 843 | nsp: this.nsp.name, 844 | }, 845 | { 846 | $or: [ 847 | { 848 | "data.opts.rooms": { 849 | $size: 0, 850 | }, 851 | }, 852 | { 853 | "data.opts.rooms": { 854 | $in: session.rooms, 855 | }, 856 | }, 857 | ], 858 | }, 859 | { 860 | $or: [ 861 | { 862 | "data.opts.except": { 863 | $size: 0, 864 | }, 865 | }, 866 | { 867 | "data.opts.except": { 868 | $nin: session.rooms, 869 | }, 870 | }, 871 | ], 872 | }, 873 | ], 874 | }); 875 | 876 | session.missedPackets = []; 877 | 878 | try { 879 | for await (const document of cursor) { 880 | const packetData = document?.data?.packet?.data; 881 | if (packetData) { 882 | session.missedPackets.push(packetData); 883 | } 884 | } 885 | } catch (e) { 886 | return Promise.reject("error while fetching missed packets"); 887 | } 888 | 889 | return session; 890 | } 891 | 892 | private findSession( 893 | pid: PrivateSessionId 894 | ): Promise | undefined> { 895 | const isCollectionCapped = !this.addCreatedAtField; 896 | if (isCollectionCapped) { 897 | return this.mongoCollection 898 | .findOne( 899 | { 900 | type: EventType.SESSION, 901 | "data.pid": pid, 902 | }, 903 | { 904 | sort: { 905 | _id: -1, 906 | }, 907 | } 908 | ) 909 | .then((result) => { 910 | if (!result) { 911 | debug("session not found"); 912 | return; 913 | } 914 | 915 | if (result.data.sid) { 916 | debug("session found, adding tombstone"); 917 | 918 | // since the collection is capped, we cannot remove documents from it, so we add a tombstone to prevent recovering the same session twice 919 | // note: we could also have used two distinct collections, one for the events (capped) and the other for the sessions (not capped, with a TTL) 920 | const TOMBSTONE_SESSION = { pid, tombstone: true }; 921 | this.persistSession(TOMBSTONE_SESSION); 922 | 923 | return result; 924 | } else { 925 | debug("tombstone session found"); 926 | } 927 | }); 928 | } else { 929 | return this.mongoCollection 930 | .findOneAndDelete({ 931 | type: EventType.SESSION, 932 | "data.pid": pid, 933 | }) 934 | .then((result) => { 935 | return result?.ok && result.value 936 | ? result.value // mongodb@5 937 | : (result as unknown as WithId); // mongodb@6 938 | }); 939 | } 940 | } 941 | } 942 | -------------------------------------------------------------------------------- /assets/adapter.excalidraw: -------------------------------------------------------------------------------- 1 | { 2 | "type": "excalidraw", 3 | "version": 2, 4 | "source": "https://excalidraw.com", 5 | "elements": [ 6 | { 7 | "type": "text", 8 | "version": 133, 9 | "versionNonce": 840310243, 10 | "isDeleted": false, 11 | "id": "5hUB5ALUlsn26W0PzU4fM", 12 | "fillStyle": "hachure", 13 | "strokeWidth": 1, 14 | "strokeStyle": "solid", 15 | "roughness": 1, 16 | "opacity": 100, 17 | "angle": 0, 18 | "x": 894, 19 | "y": 3.5, 20 | "strokeColor": "#000000", 21 | "backgroundColor": "transparent", 22 | "width": 64, 23 | "height": 25, 24 | "seed": 28708370, 25 | "groupIds": [], 26 | "strokeSharpness": "sharp", 27 | "boundElementIds": [], 28 | "fontSize": 20, 29 | "fontFamily": 1, 30 | "text": "socket", 31 | "baseline": 18, 32 | "textAlign": "center", 33 | "verticalAlign": "middle" 34 | }, 35 | { 36 | "type": "rectangle", 37 | "version": 133, 38 | "versionNonce": 944596429, 39 | "isDeleted": false, 40 | "id": "lmQ4o4New7xuXQLwavuSn", 41 | "fillStyle": "hachure", 42 | "strokeWidth": 1, 43 | "strokeStyle": "solid", 44 | "roughness": 1, 45 | "opacity": 100, 46 | "angle": 0, 47 | "x": 824, 48 | "y": -80, 49 | "strokeColor": "#000000", 50 | "backgroundColor": "transparent", 51 | "width": 277, 52 | "height": 311, 53 | "seed": 1594950354, 54 | "groupIds": [], 55 | "strokeSharpness": "sharp", 56 | "boundElementIds": [ 57 | "_wBO22vaQplcoKyBXbWRC", 58 | "BZVwnsrGk9G-X87ZHkh-6" 59 | ] 60 | }, 61 | { 62 | "type": "text", 63 | "version": 74, 64 | "versionNonce": 1241409923, 65 | "isDeleted": false, 66 | "id": "ZQsZmj4NaTubBHMkVG2dl", 67 | "fillStyle": "hachure", 68 | "strokeWidth": 1, 69 | "strokeStyle": "solid", 70 | "roughness": 1, 71 | "opacity": 100, 72 | "angle": 0, 73 | "x": 843, 74 | "y": -69, 75 | "strokeColor": "#000000", 76 | "backgroundColor": "transparent", 77 | "width": 85, 78 | "height": 26, 79 | "seed": 126533902, 80 | "groupIds": [], 81 | "strokeSharpness": "sharp", 82 | "boundElementIds": [], 83 | "fontSize": 20, 84 | "fontFamily": 1, 85 | "text": "Server A", 86 | "baseline": 18, 87 | "textAlign": "left", 88 | "verticalAlign": "top" 89 | }, 90 | { 91 | "type": "arrow", 92 | "version": 152, 93 | "versionNonce": 1430498829, 94 | "isDeleted": false, 95 | "id": "ABQydsvmkN5ptLyYQaUA3", 96 | "fillStyle": "hachure", 97 | "strokeWidth": 2, 98 | "strokeStyle": "solid", 99 | "roughness": 1, 100 | "opacity": 100, 101 | "angle": 0, 102 | "x": 837.8983868047594, 103 | "y": 21.868707241881623, 104 | "strokeColor": "#000000", 105 | "backgroundColor": "transparent", 106 | "width": 251.33111393617446, 107 | "height": 0.7613046474941143, 108 | "seed": 1466702734, 109 | "groupIds": [], 110 | "strokeSharpness": "round", 111 | "boundElementIds": [], 112 | "lastCommittedPoint": null, 113 | "startArrowhead": null, 114 | "endArrowhead": "arrow", 115 | "points": [ 116 | [ 117 | 0, 118 | 0 119 | ], 120 | [ 121 | -251.33111393617446, 122 | -0.7613046474941143 123 | ] 124 | ] 125 | }, 126 | { 127 | "type": "rectangle", 128 | "version": 159, 129 | "versionNonce": 1109147939, 130 | "isDeleted": false, 131 | "id": "x54ljUV2PW8AfubZ6fiVJ", 132 | "fillStyle": "hachure", 133 | "strokeWidth": 1, 134 | "strokeStyle": "solid", 135 | "roughness": 1, 136 | "opacity": 100, 137 | "angle": 0, 138 | "x": 436, 139 | "y": -8, 140 | "strokeColor": "#000000", 141 | "backgroundColor": "transparent", 142 | "width": 129, 143 | "height": 56, 144 | "seed": 486293390, 145 | "groupIds": [], 146 | "strokeSharpness": "sharp", 147 | "boundElementIds": [] 148 | }, 149 | { 150 | "type": "arrow", 151 | "version": 168, 152 | "versionNonce": 438068333, 153 | "isDeleted": false, 154 | "id": "zdzgdf3hgOYX0SgjEtyIZ", 155 | "fillStyle": "hachure", 156 | "strokeWidth": 2, 157 | "strokeStyle": "solid", 158 | "roughness": 1, 159 | "opacity": 100, 160 | "angle": 0, 161 | "x": 837.2434176281095, 162 | "y": 87.19281457587147, 163 | "strokeColor": "#000000", 164 | "backgroundColor": "transparent", 165 | "width": 247.23231148719788, 166 | "height": 2.2114410393964476, 167 | "seed": 1674715794, 168 | "groupIds": [], 169 | "strokeSharpness": "round", 170 | "boundElementIds": [], 171 | "lastCommittedPoint": null, 172 | "startArrowhead": null, 173 | "endArrowhead": "arrow", 174 | "points": [ 175 | [ 176 | 0, 177 | 0 178 | ], 179 | [ 180 | -247.23231148719788, 181 | 2.2114410393964476 182 | ] 183 | ] 184 | }, 185 | { 186 | "type": "text", 187 | "version": 136, 188 | "versionNonce": 333325507, 189 | "isDeleted": false, 190 | "id": "dXknKeuYe3X3K-0Hw9P95", 191 | "fillStyle": "hachure", 192 | "strokeWidth": 1, 193 | "strokeStyle": "solid", 194 | "roughness": 1, 195 | "opacity": 100, 196 | "angle": 0, 197 | "x": 484, 198 | "y": 9, 199 | "strokeColor": "#000000", 200 | "backgroundColor": "transparent", 201 | "width": 40, 202 | "height": 20, 203 | "seed": 1858283854, 204 | "groupIds": [], 205 | "strokeSharpness": "sharp", 206 | "boundElementIds": [], 207 | "fontSize": 16, 208 | "fontFamily": 1, 209 | "text": "client", 210 | "baseline": 14, 211 | "textAlign": "left", 212 | "verticalAlign": "top" 213 | }, 214 | { 215 | "type": "rectangle", 216 | "version": 179, 217 | "versionNonce": 1008057581, 218 | "isDeleted": false, 219 | "id": "Ce1Lw4MMOtiunstd3FPJv", 220 | "fillStyle": "hachure", 221 | "strokeWidth": 1, 222 | "strokeStyle": "solid", 223 | "roughness": 1, 224 | "opacity": 100, 225 | "angle": 0, 226 | "x": 437.5, 227 | "y": 67, 228 | "strokeColor": "#000000", 229 | "backgroundColor": "transparent", 230 | "width": 129, 231 | "height": 56, 232 | "seed": 568384654, 233 | "groupIds": [], 234 | "strokeSharpness": "sharp", 235 | "boundElementIds": [] 236 | }, 237 | { 238 | "type": "text", 239 | "version": 157, 240 | "versionNonce": 115512419, 241 | "isDeleted": false, 242 | "id": "rcCUGk-XM0jKzcGaeO0iS", 243 | "fillStyle": "hachure", 244 | "strokeWidth": 1, 245 | "strokeStyle": "solid", 246 | "roughness": 1, 247 | "opacity": 100, 248 | "angle": 0, 249 | "x": 484.5, 250 | "y": 84, 251 | "strokeColor": "#000000", 252 | "backgroundColor": "transparent", 253 | "width": 40, 254 | "height": 20, 255 | "seed": 244546386, 256 | "groupIds": [], 257 | "strokeSharpness": "sharp", 258 | "boundElementIds": [], 259 | "fontSize": 16, 260 | "fontFamily": 1, 261 | "text": "client", 262 | "baseline": 14, 263 | "textAlign": "left", 264 | "verticalAlign": "top" 265 | }, 266 | { 267 | "type": "rectangle", 268 | "version": 220, 269 | "versionNonce": 1032427341, 270 | "isDeleted": false, 271 | "id": "4iido5zQ7QhoIfnOzWp3h", 272 | "fillStyle": "hachure", 273 | "strokeWidth": 1, 274 | "strokeStyle": "solid", 275 | "roughness": 1, 276 | "opacity": 100, 277 | "angle": 0, 278 | "x": 437.5, 279 | "y": 142, 280 | "strokeColor": "#000000", 281 | "backgroundColor": "transparent", 282 | "width": 129, 283 | "height": 56, 284 | "seed": 1055485070, 285 | "groupIds": [], 286 | "strokeSharpness": "sharp", 287 | "boundElementIds": [] 288 | }, 289 | { 290 | "type": "text", 291 | "version": 198, 292 | "versionNonce": 1794641923, 293 | "isDeleted": false, 294 | "id": "D1E2DkimaDb8hGxIfXKmq", 295 | "fillStyle": "hachure", 296 | "strokeWidth": 1, 297 | "strokeStyle": "solid", 298 | "roughness": 1, 299 | "opacity": 100, 300 | "angle": 0, 301 | "x": 484.5, 302 | "y": 159, 303 | "strokeColor": "#000000", 304 | "backgroundColor": "transparent", 305 | "width": 40, 306 | "height": 20, 307 | "seed": 270265170, 308 | "groupIds": [], 309 | "strokeSharpness": "sharp", 310 | "boundElementIds": [], 311 | "fontSize": 16, 312 | "fontFamily": 1, 313 | "text": "client", 314 | "baseline": 14, 315 | "textAlign": "left", 316 | "verticalAlign": "top" 317 | }, 318 | { 319 | "type": "rectangle", 320 | "version": 191, 321 | "versionNonce": 1817388461, 322 | "isDeleted": false, 323 | "id": "RRrk3Vsl-pM8Z1r8Fj3Vu", 324 | "fillStyle": "hachure", 325 | "strokeWidth": 1, 326 | "strokeStyle": "solid", 327 | "roughness": 1, 328 | "opacity": 100, 329 | "angle": 0, 330 | "x": 860.5, 331 | "y": -8, 332 | "strokeColor": "#000000", 333 | "backgroundColor": "transparent", 334 | "width": 129, 335 | "height": 56, 336 | "seed": 1013161166, 337 | "groupIds": [], 338 | "strokeSharpness": "sharp", 339 | "boundElementIds": [] 340 | }, 341 | { 342 | "type": "text", 343 | "version": 174, 344 | "versionNonce": 303896483, 345 | "isDeleted": false, 346 | "id": "8pCtm42TpakWdZ7WNS4VN", 347 | "fillStyle": "hachure", 348 | "strokeWidth": 1, 349 | "strokeStyle": "solid", 350 | "roughness": 1, 351 | "opacity": 100, 352 | "angle": 0, 353 | "x": 893, 354 | "y": 73.5, 355 | "strokeColor": "#000000", 356 | "backgroundColor": "transparent", 357 | "width": 64, 358 | "height": 25, 359 | "seed": 684338382, 360 | "groupIds": [], 361 | "strokeSharpness": "sharp", 362 | "boundElementIds": [], 363 | "fontSize": 20, 364 | "fontFamily": 1, 365 | "text": "socket", 366 | "baseline": 18, 367 | "textAlign": "center", 368 | "verticalAlign": "middle" 369 | }, 370 | { 371 | "type": "rectangle", 372 | "version": 232, 373 | "versionNonce": 2073815053, 374 | "isDeleted": false, 375 | "id": "thsI1AfZ_VshmC8wdQoT_", 376 | "fillStyle": "hachure", 377 | "strokeWidth": 1, 378 | "strokeStyle": "solid", 379 | "roughness": 1, 380 | "opacity": 100, 381 | "angle": 0, 382 | "x": 859.5, 383 | "y": 62, 384 | "strokeColor": "#000000", 385 | "backgroundColor": "transparent", 386 | "width": 129, 387 | "height": 56, 388 | "seed": 1104563986, 389 | "groupIds": [], 390 | "strokeSharpness": "sharp", 391 | "boundElementIds": [] 392 | }, 393 | { 394 | "type": "text", 395 | "version": 195, 396 | "versionNonce": 176171843, 397 | "isDeleted": false, 398 | "id": "dfFxeVTIg6OH8ny7WuBsb", 399 | "fillStyle": "hachure", 400 | "strokeWidth": 1, 401 | "strokeStyle": "solid", 402 | "roughness": 1, 403 | "opacity": 100, 404 | "angle": 0, 405 | "x": 890, 406 | "y": 150.5, 407 | "strokeColor": "#000000", 408 | "backgroundColor": "transparent", 409 | "width": 64, 410 | "height": 25, 411 | "seed": 1000469902, 412 | "groupIds": [], 413 | "strokeSharpness": "sharp", 414 | "boundElementIds": [], 415 | "fontSize": 20, 416 | "fontFamily": 1, 417 | "text": "socket", 418 | "baseline": 18, 419 | "textAlign": "center", 420 | "verticalAlign": "middle" 421 | }, 422 | { 423 | "type": "rectangle", 424 | "version": 253, 425 | "versionNonce": 2103271021, 426 | "isDeleted": false, 427 | "id": "Ejm4QTgpRy-0064kg5DDC", 428 | "fillStyle": "hachure", 429 | "strokeWidth": 1, 430 | "strokeStyle": "solid", 431 | "roughness": 1, 432 | "opacity": 100, 433 | "angle": 0, 434 | "x": 856.5, 435 | "y": 139, 436 | "strokeColor": "#000000", 437 | "backgroundColor": "transparent", 438 | "width": 129, 439 | "height": 56, 440 | "seed": 1070363218, 441 | "groupIds": [], 442 | "strokeSharpness": "sharp", 443 | "boundElementIds": [] 444 | }, 445 | { 446 | "type": "arrow", 447 | "version": 203, 448 | "versionNonce": 1869999405, 449 | "isDeleted": false, 450 | "id": "yn0_EJ_FjGmr2PHYTCPsC", 451 | "fillStyle": "hachure", 452 | "strokeWidth": 2, 453 | "strokeStyle": "solid", 454 | "roughness": 1, 455 | "opacity": 100, 456 | "angle": 0, 457 | "x": 837.6161557435989, 458 | "y": 166.89427948030175, 459 | "strokeColor": "#000000", 460 | "backgroundColor": "transparent", 461 | "width": 247.23231148719788, 462 | "height": 2.2114410393964476, 463 | "seed": 1559186084, 464 | "groupIds": [], 465 | "strokeSharpness": "round", 466 | "boundElementIds": [], 467 | "lastCommittedPoint": null, 468 | "startArrowhead": null, 469 | "endArrowhead": "arrow", 470 | "points": [ 471 | [ 472 | 0, 473 | 0 474 | ], 475 | [ 476 | -247.23231148719788, 477 | 2.2114410393964476 478 | ] 479 | ] 480 | }, 481 | { 482 | "type": "text", 483 | "version": 178, 484 | "versionNonce": 140576973, 485 | "isDeleted": false, 486 | "id": "2KQuRzgUL-iSoMHZQ9zbS", 487 | "fillStyle": "hachure", 488 | "strokeWidth": 1, 489 | "strokeStyle": "solid", 490 | "roughness": 1, 491 | "opacity": 100, 492 | "angle": 0, 493 | "x": 892.5, 494 | "y": 406, 495 | "strokeColor": "#000000", 496 | "backgroundColor": "transparent", 497 | "width": 64, 498 | "height": 25, 499 | "seed": 1479277478, 500 | "groupIds": [], 501 | "strokeSharpness": "sharp", 502 | "boundElementIds": [], 503 | "fontSize": 20, 504 | "fontFamily": 1, 505 | "text": "socket", 506 | "baseline": 18, 507 | "textAlign": "center", 508 | "verticalAlign": "middle" 509 | }, 510 | { 511 | "type": "rectangle", 512 | "version": 209, 513 | "versionNonce": 1890219651, 514 | "isDeleted": false, 515 | "id": "dJhDWOnAJOszWt_UNEXdt", 516 | "fillStyle": "hachure", 517 | "strokeWidth": 1, 518 | "strokeStyle": "solid", 519 | "roughness": 1, 520 | "opacity": 100, 521 | "angle": 0, 522 | "x": 822.5, 523 | "y": 322.5, 524 | "strokeColor": "#000000", 525 | "backgroundColor": "transparent", 526 | "width": 277, 527 | "height": 280, 528 | "seed": 224360890, 529 | "groupIds": [], 530 | "strokeSharpness": "sharp", 531 | "boundElementIds": [ 532 | "qmYaJfZ9NO1RK7YHGQGo6", 533 | "GsCkqpyPppqmnpvbaTChx", 534 | "x_nMpLlFEV43XGOAM6Gxj" 535 | ] 536 | }, 537 | { 538 | "type": "text", 539 | "version": 115, 540 | "versionNonce": 507537197, 541 | "isDeleted": false, 542 | "id": "lyh4RgaTTCZNLUjl519k9", 543 | "fillStyle": "hachure", 544 | "strokeWidth": 1, 545 | "strokeStyle": "solid", 546 | "roughness": 1, 547 | "opacity": 100, 548 | "angle": 0, 549 | "x": 841.5, 550 | "y": 333.5, 551 | "strokeColor": "#000000", 552 | "backgroundColor": "transparent", 553 | "width": 87, 554 | "height": 26, 555 | "seed": 364484326, 556 | "groupIds": [], 557 | "strokeSharpness": "sharp", 558 | "boundElementIds": [], 559 | "fontSize": 20, 560 | "fontFamily": 1, 561 | "text": "Server B", 562 | "baseline": 18, 563 | "textAlign": "left", 564 | "verticalAlign": "top" 565 | }, 566 | { 567 | "type": "arrow", 568 | "version": 190, 569 | "versionNonce": 388860867, 570 | "isDeleted": false, 571 | "id": "x7ujWlTTvv0aN7XIFTWjr", 572 | "fillStyle": "hachure", 573 | "strokeWidth": 2, 574 | "strokeStyle": "solid", 575 | "roughness": 1, 576 | "opacity": 100, 577 | "angle": 0, 578 | "x": 842.3983868047594, 579 | "y": 425.3687072418816, 580 | "strokeColor": "#000000", 581 | "backgroundColor": "transparent", 582 | "width": 251.33111393617446, 583 | "height": 0.7613046474941143, 584 | "seed": 1836855930, 585 | "groupIds": [], 586 | "strokeSharpness": "round", 587 | "boundElementIds": [], 588 | "lastCommittedPoint": null, 589 | "startArrowhead": null, 590 | "endArrowhead": "arrow", 591 | "points": [ 592 | [ 593 | 0, 594 | 0 595 | ], 596 | [ 597 | -251.33111393617446, 598 | -0.7613046474941143 599 | ] 600 | ] 601 | }, 602 | { 603 | "type": "rectangle", 604 | "version": 197, 605 | "versionNonce": 1365571981, 606 | "isDeleted": false, 607 | "id": "cqdTPTcZefvtqeNEAMTBe", 608 | "fillStyle": "hachure", 609 | "strokeWidth": 1, 610 | "strokeStyle": "solid", 611 | "roughness": 1, 612 | "opacity": 100, 613 | "angle": 0, 614 | "x": 440.5, 615 | "y": 395.5, 616 | "strokeColor": "#000000", 617 | "backgroundColor": "transparent", 618 | "width": 129, 619 | "height": 56, 620 | "seed": 1567738406, 621 | "groupIds": [], 622 | "strokeSharpness": "sharp", 623 | "boundElementIds": [] 624 | }, 625 | { 626 | "type": "arrow", 627 | "version": 206, 628 | "versionNonce": 1504516963, 629 | "isDeleted": false, 630 | "id": "59kripFevaDD2Mo2bkYk-", 631 | "fillStyle": "hachure", 632 | "strokeWidth": 2, 633 | "strokeStyle": "solid", 634 | "roughness": 1, 635 | "opacity": 100, 636 | "angle": 0, 637 | "x": 841.7434176281095, 638 | "y": 490.69281457587147, 639 | "strokeColor": "#000000", 640 | "backgroundColor": "transparent", 641 | "width": 247.23231148719788, 642 | "height": 2.2114410393964476, 643 | "seed": 1124324154, 644 | "groupIds": [], 645 | "strokeSharpness": "round", 646 | "boundElementIds": [], 647 | "lastCommittedPoint": null, 648 | "startArrowhead": null, 649 | "endArrowhead": "arrow", 650 | "points": [ 651 | [ 652 | 0, 653 | 0 654 | ], 655 | [ 656 | -247.23231148719788, 657 | 2.2114410393964476 658 | ] 659 | ] 660 | }, 661 | { 662 | "type": "text", 663 | "version": 174, 664 | "versionNonce": 721960941, 665 | "isDeleted": false, 666 | "id": "U0x2FIFxg4BZgOIK6sVnW", 667 | "fillStyle": "hachure", 668 | "strokeWidth": 1, 669 | "strokeStyle": "solid", 670 | "roughness": 1, 671 | "opacity": 100, 672 | "angle": 0, 673 | "x": 488.5, 674 | "y": 412.5, 675 | "strokeColor": "#000000", 676 | "backgroundColor": "transparent", 677 | "width": 40, 678 | "height": 20, 679 | "seed": 1044485478, 680 | "groupIds": [], 681 | "strokeSharpness": "sharp", 682 | "boundElementIds": [], 683 | "fontSize": 16, 684 | "fontFamily": 1, 685 | "text": "client", 686 | "baseline": 14, 687 | "textAlign": "left", 688 | "verticalAlign": "top" 689 | }, 690 | { 691 | "type": "rectangle", 692 | "version": 217, 693 | "versionNonce": 411943267, 694 | "isDeleted": false, 695 | "id": "NU9potS0F6f8sxY5IT0Lt", 696 | "fillStyle": "hachure", 697 | "strokeWidth": 1, 698 | "strokeStyle": "solid", 699 | "roughness": 1, 700 | "opacity": 100, 701 | "angle": 0, 702 | "x": 442, 703 | "y": 470.5, 704 | "strokeColor": "#000000", 705 | "backgroundColor": "transparent", 706 | "width": 129, 707 | "height": 56, 708 | "seed": 1884904442, 709 | "groupIds": [], 710 | "strokeSharpness": "sharp", 711 | "boundElementIds": [] 712 | }, 713 | { 714 | "type": "text", 715 | "version": 195, 716 | "versionNonce": 1362344525, 717 | "isDeleted": false, 718 | "id": "IpJJ20xja0yqXQC_netfw", 719 | "fillStyle": "hachure", 720 | "strokeWidth": 1, 721 | "strokeStyle": "solid", 722 | "roughness": 1, 723 | "opacity": 100, 724 | "angle": 0, 725 | "x": 489, 726 | "y": 487.5, 727 | "strokeColor": "#000000", 728 | "backgroundColor": "transparent", 729 | "width": 40, 730 | "height": 20, 731 | "seed": 1635121318, 732 | "groupIds": [], 733 | "strokeSharpness": "sharp", 734 | "boundElementIds": [], 735 | "fontSize": 16, 736 | "fontFamily": 1, 737 | "text": "client", 738 | "baseline": 14, 739 | "textAlign": "left", 740 | "verticalAlign": "top" 741 | }, 742 | { 743 | "type": "rectangle", 744 | "version": 236, 745 | "versionNonce": 1488297219, 746 | "isDeleted": false, 747 | "id": "scSxnujNYgELyMUDbnTNS", 748 | "fillStyle": "hachure", 749 | "strokeWidth": 1, 750 | "strokeStyle": "solid", 751 | "roughness": 1, 752 | "opacity": 100, 753 | "angle": 0, 754 | "x": 859, 755 | "y": 394.5, 756 | "strokeColor": "#000000", 757 | "backgroundColor": "transparent", 758 | "width": 129, 759 | "height": 56, 760 | "seed": 303703418, 761 | "groupIds": [], 762 | "strokeSharpness": "sharp", 763 | "boundElementIds": [] 764 | }, 765 | { 766 | "type": "text", 767 | "version": 219, 768 | "versionNonce": 1824581805, 769 | "isDeleted": false, 770 | "id": "Lyv2NwV0SfYm5kvp9sJEn", 771 | "fillStyle": "hachure", 772 | "strokeWidth": 1, 773 | "strokeStyle": "solid", 774 | "roughness": 1, 775 | "opacity": 100, 776 | "angle": 0, 777 | "x": 891.5, 778 | "y": 476, 779 | "strokeColor": "#000000", 780 | "backgroundColor": "transparent", 781 | "width": 64, 782 | "height": 25, 783 | "seed": 1344309030, 784 | "groupIds": [], 785 | "strokeSharpness": "sharp", 786 | "boundElementIds": [], 787 | "fontSize": 20, 788 | "fontFamily": 1, 789 | "text": "socket", 790 | "baseline": 18, 791 | "textAlign": "center", 792 | "verticalAlign": "middle" 793 | }, 794 | { 795 | "type": "rectangle", 796 | "version": 277, 797 | "versionNonce": 1788937379, 798 | "isDeleted": false, 799 | "id": "e3D2rl_rbVQwQUKshOG8E", 800 | "fillStyle": "hachure", 801 | "strokeWidth": 1, 802 | "strokeStyle": "solid", 803 | "roughness": 1, 804 | "opacity": 100, 805 | "angle": 0, 806 | "x": 858, 807 | "y": 464.5, 808 | "strokeColor": "#000000", 809 | "backgroundColor": "transparent", 810 | "width": 129, 811 | "height": 56, 812 | "seed": 627795514, 813 | "groupIds": [], 814 | "strokeSharpness": "sharp", 815 | "boundElementIds": [] 816 | }, 817 | { 818 | "type": "diamond", 819 | "version": 258, 820 | "versionNonce": 1331251981, 821 | "isDeleted": false, 822 | "id": "k0pJTVL4F3HHsfRPlE-gO", 823 | "fillStyle": "hachure", 824 | "strokeWidth": 2, 825 | "strokeStyle": "solid", 826 | "roughness": 0, 827 | "opacity": 100, 828 | "angle": 0, 829 | "x": 1149, 830 | "y": 66, 831 | "strokeColor": "#000000", 832 | "backgroundColor": "transparent", 833 | "width": 46, 834 | "height": 46, 835 | "seed": 1260350118, 836 | "groupIds": [], 837 | "strokeSharpness": "sharp", 838 | "boundElementIds": [ 839 | "Sp9AvxDh8gwRvSC53VFKe" 840 | ] 841 | }, 842 | { 843 | "type": "text", 844 | "version": 243, 845 | "versionNonce": 1711175747, 846 | "isDeleted": false, 847 | "id": "DiLMkDsU2SrPef3STL9fw", 848 | "fillStyle": "hachure", 849 | "strokeWidth": 1, 850 | "strokeStyle": "dashed", 851 | "roughness": 0, 852 | "opacity": 100, 853 | "angle": 0, 854 | "x": 1111.5, 855 | "y": 27.5, 856 | "strokeColor": "#000000", 857 | "backgroundColor": "transparent", 858 | "width": 144, 859 | "height": 26, 860 | "seed": 1810644198, 861 | "groupIds": [], 862 | "strokeSharpness": "sharp", 863 | "boundElementIds": [], 864 | "fontSize": 20, 865 | "fontFamily": 1, 866 | "text": "Mongo adapter", 867 | "baseline": 18, 868 | "textAlign": "center", 869 | "verticalAlign": "top" 870 | }, 871 | { 872 | "type": "arrow", 873 | "version": 28, 874 | "versionNonce": 1500930413, 875 | "isDeleted": false, 876 | "id": "Sp9AvxDh8gwRvSC53VFKe", 877 | "fillStyle": "hachure", 878 | "strokeWidth": 2, 879 | "strokeStyle": "solid", 880 | "roughness": 0, 881 | "opacity": 100, 882 | "angle": 0, 883 | "x": 1129, 884 | "y": 89, 885 | "strokeColor": "#000000", 886 | "backgroundColor": "transparent", 887 | "width": 109, 888 | "height": 1, 889 | "seed": 714162918, 890 | "groupIds": [], 891 | "strokeSharpness": "round", 892 | "boundElementIds": [], 893 | "startBinding": { 894 | "elementId": "k0pJTVL4F3HHsfRPlE-gO", 895 | "focus": -0.01715197447147986, 896 | "gap": 14.142135623730947 897 | }, 898 | "endBinding": null, 899 | "lastCommittedPoint": null, 900 | "startArrowhead": null, 901 | "endArrowhead": "arrow", 902 | "points": [ 903 | [ 904 | 0, 905 | 0 906 | ], 907 | [ 908 | -109, 909 | -1 910 | ] 911 | ] 912 | }, 913 | { 914 | "type": "arrow", 915 | "version": 44, 916 | "versionNonce": 1756133347, 917 | "isDeleted": false, 918 | "id": "_wBO22vaQplcoKyBXbWRC", 919 | "fillStyle": "hachure", 920 | "strokeWidth": 2, 921 | "strokeStyle": "solid", 922 | "roughness": 0, 923 | "opacity": 100, 924 | "angle": 0, 925 | "x": 1126, 926 | "y": 83, 927 | "strokeColor": "#000000", 928 | "backgroundColor": "transparent", 929 | "width": 105, 930 | "height": 57, 931 | "seed": 1243541542, 932 | "groupIds": [], 933 | "strokeSharpness": "round", 934 | "boundElementIds": [], 935 | "startBinding": { 936 | "elementId": "lmQ4o4New7xuXQLwavuSn", 937 | "focus": 0.35224176368590543, 938 | "gap": 25 939 | }, 940 | "endBinding": null, 941 | "lastCommittedPoint": null, 942 | "startArrowhead": null, 943 | "endArrowhead": "arrow", 944 | "points": [ 945 | [ 946 | 0, 947 | 0 948 | ], 949 | [ 950 | -105, 951 | -57 952 | ] 953 | ] 954 | }, 955 | { 956 | "type": "arrow", 957 | "version": 59, 958 | "versionNonce": 543271885, 959 | "isDeleted": false, 960 | "id": "BZVwnsrGk9G-X87ZHkh-6", 961 | "fillStyle": "hachure", 962 | "strokeWidth": 2, 963 | "strokeStyle": "solid", 964 | "roughness": 0, 965 | "opacity": 100, 966 | "angle": 0, 967 | "x": 1128, 968 | "y": 96, 969 | "strokeColor": "#000000", 970 | "backgroundColor": "transparent", 971 | "width": 95, 972 | "height": 62, 973 | "seed": 1890534970, 974 | "groupIds": [], 975 | "strokeSharpness": "round", 976 | "boundElementIds": [], 977 | "startBinding": { 978 | "elementId": "lmQ4o4New7xuXQLwavuSn", 979 | "focus": -0.522635330379503, 980 | "gap": 27 981 | }, 982 | "endBinding": null, 983 | "lastCommittedPoint": null, 984 | "startArrowhead": null, 985 | "endArrowhead": "arrow", 986 | "points": [ 987 | [ 988 | 0, 989 | 0 990 | ], 991 | [ 992 | -95, 993 | 62 994 | ] 995 | ] 996 | }, 997 | { 998 | "type": "diamond", 999 | "version": 364, 1000 | "versionNonce": 1354335107, 1001 | "isDeleted": false, 1002 | "id": "vJwd2LS9grrvUFlbCugEG", 1003 | "fillStyle": "hachure", 1004 | "strokeWidth": 2, 1005 | "strokeStyle": "solid", 1006 | "roughness": 0, 1007 | "opacity": 100, 1008 | "angle": 0, 1009 | "x": 1149.25, 1010 | "y": 467, 1011 | "strokeColor": "#000000", 1012 | "backgroundColor": "transparent", 1013 | "width": 46, 1014 | "height": 46, 1015 | "seed": 1072510330, 1016 | "groupIds": [], 1017 | "strokeSharpness": "sharp", 1018 | "boundElementIds": [ 1019 | "x_nMpLlFEV43XGOAM6Gxj" 1020 | ] 1021 | }, 1022 | { 1023 | "type": "text", 1024 | "version": 325, 1025 | "versionNonce": 1412737581, 1026 | "isDeleted": false, 1027 | "id": "BC5sEBFfRV7OfrhN1L1Gq", 1028 | "fillStyle": "hachure", 1029 | "strokeWidth": 1, 1030 | "strokeStyle": "dashed", 1031 | "roughness": 0, 1032 | "opacity": 100, 1033 | "angle": 0, 1034 | "x": 1111.75, 1035 | "y": 428.5, 1036 | "strokeColor": "#000000", 1037 | "backgroundColor": "transparent", 1038 | "width": 144, 1039 | "height": 26, 1040 | "seed": 1757837094, 1041 | "groupIds": [], 1042 | "strokeSharpness": "sharp", 1043 | "boundElementIds": [ 1044 | "xDobZ6graJnZZP8g59wJ4" 1045 | ], 1046 | "fontSize": 20, 1047 | "fontFamily": 1, 1048 | "text": "Mongo adapter", 1049 | "baseline": 18, 1050 | "textAlign": "center", 1051 | "verticalAlign": "top" 1052 | }, 1053 | { 1054 | "type": "arrow", 1055 | "version": 248, 1056 | "versionNonce": 912530211, 1057 | "isDeleted": false, 1058 | "id": "x_nMpLlFEV43XGOAM6Gxj", 1059 | "fillStyle": "hachure", 1060 | "strokeWidth": 2, 1061 | "strokeStyle": "solid", 1062 | "roughness": 0, 1063 | "opacity": 100, 1064 | "angle": 0, 1065 | "x": 1123.25, 1066 | "y": 489, 1067 | "strokeColor": "#000000", 1068 | "backgroundColor": "transparent", 1069 | "width": 109, 1070 | "height": 1, 1071 | "seed": 1180464698, 1072 | "groupIds": [], 1073 | "strokeSharpness": "round", 1074 | "boundElementIds": [], 1075 | "startBinding": { 1076 | "elementId": "dJhDWOnAJOszWt_UNEXdt", 1077 | "focus": -0.17704646556482773, 1078 | "gap": 23.75 1079 | }, 1080 | "endBinding": null, 1081 | "lastCommittedPoint": null, 1082 | "startArrowhead": null, 1083 | "endArrowhead": "arrow", 1084 | "points": [ 1085 | [ 1086 | 0, 1087 | 0 1088 | ], 1089 | [ 1090 | -109, 1091 | -1 1092 | ] 1093 | ] 1094 | }, 1095 | { 1096 | "type": "arrow", 1097 | "version": 239, 1098 | "versionNonce": 1708711053, 1099 | "isDeleted": false, 1100 | "id": "qmYaJfZ9NO1RK7YHGQGo6", 1101 | "fillStyle": "hachure", 1102 | "strokeWidth": 2, 1103 | "strokeStyle": "solid", 1104 | "roughness": 0, 1105 | "opacity": 100, 1106 | "angle": 0, 1107 | "x": 1119.9214748277186, 1108 | "y": 479.7229508196721, 1109 | "strokeColor": "#000000", 1110 | "backgroundColor": "transparent", 1111 | "width": 104.67147482771861, 1112 | "height": 53.72295081967212, 1113 | "seed": 880321126, 1114 | "groupIds": [], 1115 | "strokeSharpness": "round", 1116 | "boundElementIds": [], 1117 | "startBinding": { 1118 | "elementId": "dJhDWOnAJOszWt_UNEXdt", 1119 | "focus": 0.304824173970933, 1120 | "gap": 20.421474827718612 1121 | }, 1122 | "endBinding": null, 1123 | "lastCommittedPoint": null, 1124 | "startArrowhead": null, 1125 | "endArrowhead": "arrow", 1126 | "points": [ 1127 | [ 1128 | 0, 1129 | 0 1130 | ], 1131 | [ 1132 | -104.67147482771861, 1133 | -53.72295081967212 1134 | ] 1135 | ] 1136 | }, 1137 | { 1138 | "type": "ellipse", 1139 | "version": 77, 1140 | "versionNonce": 639286979, 1141 | "isDeleted": false, 1142 | "id": "EQmjbilyrf3OcSwGbMZrg", 1143 | "fillStyle": "hachure", 1144 | "strokeWidth": 2, 1145 | "strokeStyle": "solid", 1146 | "roughness": 0, 1147 | "opacity": 100, 1148 | "angle": 0, 1149 | "x": 1224, 1150 | "y": 222, 1151 | "strokeColor": "#000000", 1152 | "backgroundColor": "transparent", 1153 | "width": 104.99999999999999, 1154 | "height": 93.00000000000001, 1155 | "seed": 1885795942, 1156 | "groupIds": [], 1157 | "strokeSharpness": "sharp", 1158 | "boundElementIds": [ 1159 | "xDobZ6graJnZZP8g59wJ4", 1160 | "eU1gfEXnHSjxc-pEgv43A" 1161 | ] 1162 | }, 1163 | { 1164 | "type": "text", 1165 | "version": 26, 1166 | "versionNonce": 1829225197, 1167 | "isDeleted": false, 1168 | "id": "wV6Y3XyIP5TbX50EF6xs6", 1169 | "fillStyle": "hachure", 1170 | "strokeWidth": 2, 1171 | "strokeStyle": "solid", 1172 | "roughness": 0, 1173 | "opacity": 100, 1174 | "angle": 0, 1175 | "x": 1231, 1176 | "y": 256.5, 1177 | "strokeColor": "#000000", 1178 | "backgroundColor": "transparent", 1179 | "width": 90, 1180 | "height": 26, 1181 | "seed": 1433614630, 1182 | "groupIds": [], 1183 | "strokeSharpness": "sharp", 1184 | "boundElementIds": [], 1185 | "fontSize": 20, 1186 | "fontFamily": 1, 1187 | "text": "MongoDB", 1188 | "baseline": 18, 1189 | "textAlign": "center", 1190 | "verticalAlign": "middle" 1191 | }, 1192 | { 1193 | "type": "arrow", 1194 | "version": 59, 1195 | "versionNonce": 444329571, 1196 | "isDeleted": false, 1197 | "id": "eU1gfEXnHSjxc-pEgv43A", 1198 | "fillStyle": "hachure", 1199 | "strokeWidth": 2, 1200 | "strokeStyle": "solid", 1201 | "roughness": 0, 1202 | "opacity": 100, 1203 | "angle": 0, 1204 | "x": 1194, 1205 | "y": 115, 1206 | "strokeColor": "#000000", 1207 | "backgroundColor": "transparent", 1208 | "width": 48.96786219306841, 1209 | "height": 92.0339835266262, 1210 | "seed": 1145880934, 1211 | "groupIds": [], 1212 | "strokeSharpness": "round", 1213 | "boundElementIds": [], 1214 | "startBinding": null, 1215 | "endBinding": { 1216 | "elementId": "EQmjbilyrf3OcSwGbMZrg", 1217 | "focus": -0.01427272665357157, 1218 | "gap": 22.26959346244992 1219 | }, 1220 | "lastCommittedPoint": null, 1221 | "startArrowhead": null, 1222 | "endArrowhead": "arrow", 1223 | "points": [ 1224 | [ 1225 | 0, 1226 | 0 1227 | ], 1228 | [ 1229 | 48.96786219306841, 1230 | 92.0339835266262 1231 | ] 1232 | ] 1233 | }, 1234 | { 1235 | "type": "arrow", 1236 | "version": 97, 1237 | "versionNonce": 1313529165, 1238 | "isDeleted": false, 1239 | "id": "xDobZ6graJnZZP8g59wJ4", 1240 | "fillStyle": "hachure", 1241 | "strokeWidth": 2, 1242 | "strokeStyle": "solid", 1243 | "roughness": 0, 1244 | "opacity": 100, 1245 | "angle": 0, 1246 | "x": 1248.7795172073997, 1247 | "y": 327.50331785153037, 1248 | "strokeColor": "#000000", 1249 | "backgroundColor": "transparent", 1250 | "width": 28.990742099195813, 1251 | "height": 90.08927474106224, 1252 | "seed": 1443544058, 1253 | "groupIds": [], 1254 | "strokeSharpness": "round", 1255 | "boundElementIds": [], 1256 | "startBinding": { 1257 | "elementId": "EQmjbilyrf3OcSwGbMZrg", 1258 | "gap": 17.719220537742967, 1259 | "focus": 0.13877232938640205 1260 | }, 1261 | "endBinding": null, 1262 | "lastCommittedPoint": null, 1263 | "startArrowhead": null, 1264 | "endArrowhead": "arrow", 1265 | "points": [ 1266 | [ 1267 | 0, 1268 | 0 1269 | ], 1270 | [ 1271 | -28.990742099195813, 1272 | 90.08927474106224 1273 | ] 1274 | ] 1275 | } 1276 | ], 1277 | "appState": { 1278 | "gridSize": null, 1279 | "viewBackgroundColor": "#ffffff" 1280 | } 1281 | } -------------------------------------------------------------------------------- /examples/ttl-example/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ttl-example", 3 | "version": "0.0.1", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "ttl-example", 9 | "version": "0.0.1", 10 | "dependencies": { 11 | "@socket.io/mongo-adapter": "^0.3.2", 12 | "mongodb": "^6.3.0", 13 | "socket.io": "^4.7.4", 14 | "socket.io-client": "^4.7.4" 15 | } 16 | }, 17 | "node_modules/@aws-crypto/crc32": { 18 | "version": "3.0.0", 19 | "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-3.0.0.tgz", 20 | "integrity": "sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==", 21 | "optional": true, 22 | "peer": true, 23 | "dependencies": { 24 | "@aws-crypto/util": "^3.0.0", 25 | "@aws-sdk/types": "^3.222.0", 26 | "tslib": "^1.11.1" 27 | } 28 | }, 29 | "node_modules/@aws-crypto/crc32/node_modules/tslib": { 30 | "version": "1.14.1", 31 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", 32 | "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", 33 | "optional": true, 34 | "peer": true 35 | }, 36 | "node_modules/@aws-crypto/ie11-detection": { 37 | "version": "3.0.0", 38 | "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", 39 | "integrity": "sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==", 40 | "optional": true, 41 | "peer": true, 42 | "dependencies": { 43 | "tslib": "^1.11.1" 44 | } 45 | }, 46 | "node_modules/@aws-crypto/ie11-detection/node_modules/tslib": { 47 | "version": "1.14.1", 48 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", 49 | "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", 50 | "optional": true, 51 | "peer": true 52 | }, 53 | "node_modules/@aws-crypto/sha256-browser": { 54 | "version": "3.0.0", 55 | "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz", 56 | "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==", 57 | "optional": true, 58 | "peer": true, 59 | "dependencies": { 60 | "@aws-crypto/ie11-detection": "^3.0.0", 61 | "@aws-crypto/sha256-js": "^3.0.0", 62 | "@aws-crypto/supports-web-crypto": "^3.0.0", 63 | "@aws-crypto/util": "^3.0.0", 64 | "@aws-sdk/types": "^3.222.0", 65 | "@aws-sdk/util-locate-window": "^3.0.0", 66 | "@aws-sdk/util-utf8-browser": "^3.0.0", 67 | "tslib": "^1.11.1" 68 | } 69 | }, 70 | "node_modules/@aws-crypto/sha256-browser/node_modules/tslib": { 71 | "version": "1.14.1", 72 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", 73 | "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", 74 | "optional": true, 75 | "peer": true 76 | }, 77 | "node_modules/@aws-crypto/sha256-js": { 78 | "version": "3.0.0", 79 | "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz", 80 | "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==", 81 | "optional": true, 82 | "peer": true, 83 | "dependencies": { 84 | "@aws-crypto/util": "^3.0.0", 85 | "@aws-sdk/types": "^3.222.0", 86 | "tslib": "^1.11.1" 87 | } 88 | }, 89 | "node_modules/@aws-crypto/sha256-js/node_modules/tslib": { 90 | "version": "1.14.1", 91 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", 92 | "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", 93 | "optional": true, 94 | "peer": true 95 | }, 96 | "node_modules/@aws-crypto/supports-web-crypto": { 97 | "version": "3.0.0", 98 | "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz", 99 | "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==", 100 | "optional": true, 101 | "peer": true, 102 | "dependencies": { 103 | "tslib": "^1.11.1" 104 | } 105 | }, 106 | "node_modules/@aws-crypto/supports-web-crypto/node_modules/tslib": { 107 | "version": "1.14.1", 108 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", 109 | "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", 110 | "optional": true, 111 | "peer": true 112 | }, 113 | "node_modules/@aws-crypto/util": { 114 | "version": "3.0.0", 115 | "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", 116 | "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", 117 | "optional": true, 118 | "peer": true, 119 | "dependencies": { 120 | "@aws-sdk/types": "^3.222.0", 121 | "@aws-sdk/util-utf8-browser": "^3.0.0", 122 | "tslib": "^1.11.1" 123 | } 124 | }, 125 | "node_modules/@aws-crypto/util/node_modules/tslib": { 126 | "version": "1.14.1", 127 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", 128 | "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", 129 | "optional": true, 130 | "peer": true 131 | }, 132 | "node_modules/@aws-sdk/client-cognito-identity": { 133 | "version": "3.496.0", 134 | "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.496.0.tgz", 135 | "integrity": "sha512-rb0Pv8jzJ8XBNmhKl/Up8jnaLWPKuW622s9RR9JyVEu/uUR5tyhdEJvEsS88A9a0+BTRt4G7X1VnUXWAgi8hxQ==", 136 | "optional": true, 137 | "peer": true, 138 | "dependencies": { 139 | "@aws-crypto/sha256-browser": "3.0.0", 140 | "@aws-crypto/sha256-js": "3.0.0", 141 | "@aws-sdk/client-sts": "3.496.0", 142 | "@aws-sdk/core": "3.496.0", 143 | "@aws-sdk/credential-provider-node": "3.496.0", 144 | "@aws-sdk/middleware-host-header": "3.496.0", 145 | "@aws-sdk/middleware-logger": "3.496.0", 146 | "@aws-sdk/middleware-recursion-detection": "3.496.0", 147 | "@aws-sdk/middleware-signing": "3.496.0", 148 | "@aws-sdk/middleware-user-agent": "3.496.0", 149 | "@aws-sdk/region-config-resolver": "3.496.0", 150 | "@aws-sdk/types": "3.496.0", 151 | "@aws-sdk/util-endpoints": "3.496.0", 152 | "@aws-sdk/util-user-agent-browser": "3.496.0", 153 | "@aws-sdk/util-user-agent-node": "3.496.0", 154 | "@smithy/config-resolver": "^2.1.1", 155 | "@smithy/core": "^1.3.1", 156 | "@smithy/fetch-http-handler": "^2.4.1", 157 | "@smithy/hash-node": "^2.1.1", 158 | "@smithy/invalid-dependency": "^2.1.1", 159 | "@smithy/middleware-content-length": "^2.1.1", 160 | "@smithy/middleware-endpoint": "^2.4.1", 161 | "@smithy/middleware-retry": "^2.1.1", 162 | "@smithy/middleware-serde": "^2.1.1", 163 | "@smithy/middleware-stack": "^2.1.1", 164 | "@smithy/node-config-provider": "^2.2.1", 165 | "@smithy/node-http-handler": "^2.3.1", 166 | "@smithy/protocol-http": "^3.1.1", 167 | "@smithy/smithy-client": "^2.3.1", 168 | "@smithy/types": "^2.9.1", 169 | "@smithy/url-parser": "^2.1.1", 170 | "@smithy/util-base64": "^2.1.1", 171 | "@smithy/util-body-length-browser": "^2.1.1", 172 | "@smithy/util-body-length-node": "^2.2.1", 173 | "@smithy/util-defaults-mode-browser": "^2.1.1", 174 | "@smithy/util-defaults-mode-node": "^2.1.1", 175 | "@smithy/util-endpoints": "^1.1.1", 176 | "@smithy/util-retry": "^2.1.1", 177 | "@smithy/util-utf8": "^2.1.1", 178 | "tslib": "^2.5.0" 179 | }, 180 | "engines": { 181 | "node": ">=14.0.0" 182 | } 183 | }, 184 | "node_modules/@aws-sdk/client-sso": { 185 | "version": "3.496.0", 186 | "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.496.0.tgz", 187 | "integrity": "sha512-fuaMuxKg7CMUsP9l3kxYWCOxFsBjdA0xj5nlikaDm1661/gB4KkAiGqRY8LsQkpNXvXU8Nj+f7oCFADFyGYzyw==", 188 | "optional": true, 189 | "peer": true, 190 | "dependencies": { 191 | "@aws-crypto/sha256-browser": "3.0.0", 192 | "@aws-crypto/sha256-js": "3.0.0", 193 | "@aws-sdk/core": "3.496.0", 194 | "@aws-sdk/middleware-host-header": "3.496.0", 195 | "@aws-sdk/middleware-logger": "3.496.0", 196 | "@aws-sdk/middleware-recursion-detection": "3.496.0", 197 | "@aws-sdk/middleware-user-agent": "3.496.0", 198 | "@aws-sdk/region-config-resolver": "3.496.0", 199 | "@aws-sdk/types": "3.496.0", 200 | "@aws-sdk/util-endpoints": "3.496.0", 201 | "@aws-sdk/util-user-agent-browser": "3.496.0", 202 | "@aws-sdk/util-user-agent-node": "3.496.0", 203 | "@smithy/config-resolver": "^2.1.1", 204 | "@smithy/core": "^1.3.1", 205 | "@smithy/fetch-http-handler": "^2.4.1", 206 | "@smithy/hash-node": "^2.1.1", 207 | "@smithy/invalid-dependency": "^2.1.1", 208 | "@smithy/middleware-content-length": "^2.1.1", 209 | "@smithy/middleware-endpoint": "^2.4.1", 210 | "@smithy/middleware-retry": "^2.1.1", 211 | "@smithy/middleware-serde": "^2.1.1", 212 | "@smithy/middleware-stack": "^2.1.1", 213 | "@smithy/node-config-provider": "^2.2.1", 214 | "@smithy/node-http-handler": "^2.3.1", 215 | "@smithy/protocol-http": "^3.1.1", 216 | "@smithy/smithy-client": "^2.3.1", 217 | "@smithy/types": "^2.9.1", 218 | "@smithy/url-parser": "^2.1.1", 219 | "@smithy/util-base64": "^2.1.1", 220 | "@smithy/util-body-length-browser": "^2.1.1", 221 | "@smithy/util-body-length-node": "^2.2.1", 222 | "@smithy/util-defaults-mode-browser": "^2.1.1", 223 | "@smithy/util-defaults-mode-node": "^2.1.1", 224 | "@smithy/util-endpoints": "^1.1.1", 225 | "@smithy/util-retry": "^2.1.1", 226 | "@smithy/util-utf8": "^2.1.1", 227 | "tslib": "^2.5.0" 228 | }, 229 | "engines": { 230 | "node": ">=14.0.0" 231 | } 232 | }, 233 | "node_modules/@aws-sdk/client-sts": { 234 | "version": "3.496.0", 235 | "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.496.0.tgz", 236 | "integrity": "sha512-3pSdqgegdwbK3CT1WvGHhA+Bf91R9cr8G1Ynp+iU2wZvy8ueJfMUk0NYfjo3EEv0YhSbMLKuduzZfvQHFHXYhw==", 237 | "optional": true, 238 | "peer": true, 239 | "dependencies": { 240 | "@aws-crypto/sha256-browser": "3.0.0", 241 | "@aws-crypto/sha256-js": "3.0.0", 242 | "@aws-sdk/core": "3.496.0", 243 | "@aws-sdk/credential-provider-node": "3.496.0", 244 | "@aws-sdk/middleware-host-header": "3.496.0", 245 | "@aws-sdk/middleware-logger": "3.496.0", 246 | "@aws-sdk/middleware-recursion-detection": "3.496.0", 247 | "@aws-sdk/middleware-user-agent": "3.496.0", 248 | "@aws-sdk/region-config-resolver": "3.496.0", 249 | "@aws-sdk/types": "3.496.0", 250 | "@aws-sdk/util-endpoints": "3.496.0", 251 | "@aws-sdk/util-user-agent-browser": "3.496.0", 252 | "@aws-sdk/util-user-agent-node": "3.496.0", 253 | "@smithy/config-resolver": "^2.1.1", 254 | "@smithy/core": "^1.3.1", 255 | "@smithy/fetch-http-handler": "^2.4.1", 256 | "@smithy/hash-node": "^2.1.1", 257 | "@smithy/invalid-dependency": "^2.1.1", 258 | "@smithy/middleware-content-length": "^2.1.1", 259 | "@smithy/middleware-endpoint": "^2.4.1", 260 | "@smithy/middleware-retry": "^2.1.1", 261 | "@smithy/middleware-serde": "^2.1.1", 262 | "@smithy/middleware-stack": "^2.1.1", 263 | "@smithy/node-config-provider": "^2.2.1", 264 | "@smithy/node-http-handler": "^2.3.1", 265 | "@smithy/protocol-http": "^3.1.1", 266 | "@smithy/smithy-client": "^2.3.1", 267 | "@smithy/types": "^2.9.1", 268 | "@smithy/url-parser": "^2.1.1", 269 | "@smithy/util-base64": "^2.1.1", 270 | "@smithy/util-body-length-browser": "^2.1.1", 271 | "@smithy/util-body-length-node": "^2.2.1", 272 | "@smithy/util-defaults-mode-browser": "^2.1.1", 273 | "@smithy/util-defaults-mode-node": "^2.1.1", 274 | "@smithy/util-endpoints": "^1.1.1", 275 | "@smithy/util-middleware": "^2.1.1", 276 | "@smithy/util-retry": "^2.1.1", 277 | "@smithy/util-utf8": "^2.1.1", 278 | "fast-xml-parser": "4.2.5", 279 | "tslib": "^2.5.0" 280 | }, 281 | "engines": { 282 | "node": ">=14.0.0" 283 | } 284 | }, 285 | "node_modules/@aws-sdk/core": { 286 | "version": "3.496.0", 287 | "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.496.0.tgz", 288 | "integrity": "sha512-yT+ug7Cw/3eJi7x2es0+46x12+cIJm5Xv+GPWsrTFD1TKgqO/VPEgfDtHFagDNbFmjNQA65Ygc/kEdIX9ICX/A==", 289 | "optional": true, 290 | "peer": true, 291 | "dependencies": { 292 | "@smithy/core": "^1.3.1", 293 | "@smithy/protocol-http": "^3.1.1", 294 | "@smithy/signature-v4": "^2.1.1", 295 | "@smithy/smithy-client": "^2.3.1", 296 | "@smithy/types": "^2.9.1", 297 | "tslib": "^2.5.0" 298 | }, 299 | "engines": { 300 | "node": ">=14.0.0" 301 | } 302 | }, 303 | "node_modules/@aws-sdk/credential-provider-cognito-identity": { 304 | "version": "3.496.0", 305 | "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.496.0.tgz", 306 | "integrity": "sha512-LIB9hom5mqGxk+hdbpZnxIJ4F1c5ZuY5uu3aWy9luowci03Z5nzYYepzBwpoE5Lk102gqVKeia//mRr25blzWQ==", 307 | "optional": true, 308 | "peer": true, 309 | "dependencies": { 310 | "@aws-sdk/client-cognito-identity": "3.496.0", 311 | "@aws-sdk/types": "3.496.0", 312 | "@smithy/property-provider": "^2.1.1", 313 | "@smithy/types": "^2.9.1", 314 | "tslib": "^2.5.0" 315 | }, 316 | "engines": { 317 | "node": ">=14.0.0" 318 | } 319 | }, 320 | "node_modules/@aws-sdk/credential-provider-env": { 321 | "version": "3.496.0", 322 | "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.496.0.tgz", 323 | "integrity": "sha512-lukQMJ8SWWP5RqkRNOHi/H+WMhRvSWa3Fc5Jf/VP6xHiPLfF1XafcvthtV91e0VwPCiseI+HqChrcGq8pvnxHw==", 324 | "optional": true, 325 | "peer": true, 326 | "dependencies": { 327 | "@aws-sdk/types": "3.496.0", 328 | "@smithy/property-provider": "^2.1.1", 329 | "@smithy/types": "^2.9.1", 330 | "tslib": "^2.5.0" 331 | }, 332 | "engines": { 333 | "node": ">=14.0.0" 334 | } 335 | }, 336 | "node_modules/@aws-sdk/credential-provider-http": { 337 | "version": "3.496.0", 338 | "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.496.0.tgz", 339 | "integrity": "sha512-iphFlFX0qDFsE24XmFlcKmsR4uyNaqQrK+Y18mwSZMs1yWtL4Sck0rcTXU/cU2W3/xisjh7xFXK5L5aowjMZOg==", 340 | "optional": true, 341 | "peer": true, 342 | "dependencies": { 343 | "@aws-sdk/types": "3.496.0", 344 | "@smithy/fetch-http-handler": "^2.4.1", 345 | "@smithy/node-http-handler": "^2.3.1", 346 | "@smithy/property-provider": "^2.1.1", 347 | "@smithy/protocol-http": "^3.1.1", 348 | "@smithy/smithy-client": "^2.3.1", 349 | "@smithy/types": "^2.9.1", 350 | "@smithy/util-stream": "^2.1.1", 351 | "tslib": "^2.5.0" 352 | }, 353 | "engines": { 354 | "node": ">=14.0.0" 355 | } 356 | }, 357 | "node_modules/@aws-sdk/credential-provider-ini": { 358 | "version": "3.496.0", 359 | "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.496.0.tgz", 360 | "integrity": "sha512-2nD1jp1sIwcQaWK1y/9ruQOkW16RUxZpzgjbW/gnK3iiUXwx+/FNQWxshud+GTSx3Q4x6eIhqsbjtP4VVPPuUA==", 361 | "optional": true, 362 | "peer": true, 363 | "dependencies": { 364 | "@aws-sdk/credential-provider-env": "3.496.0", 365 | "@aws-sdk/credential-provider-process": "3.496.0", 366 | "@aws-sdk/credential-provider-sso": "3.496.0", 367 | "@aws-sdk/credential-provider-web-identity": "3.496.0", 368 | "@aws-sdk/types": "3.496.0", 369 | "@smithy/credential-provider-imds": "^2.2.1", 370 | "@smithy/property-provider": "^2.1.1", 371 | "@smithy/shared-ini-file-loader": "^2.3.1", 372 | "@smithy/types": "^2.9.1", 373 | "tslib": "^2.5.0" 374 | }, 375 | "engines": { 376 | "node": ">=14.0.0" 377 | } 378 | }, 379 | "node_modules/@aws-sdk/credential-provider-node": { 380 | "version": "3.496.0", 381 | "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.496.0.tgz", 382 | "integrity": "sha512-IVF9RvLePfRa5S5/eBIRChJCWOzQkGwM8P/L79Gl84u/cH2oSG4NtUI/YTDlrtmnYn7YsGhINSV0WnzfF2twfQ==", 383 | "optional": true, 384 | "peer": true, 385 | "dependencies": { 386 | "@aws-sdk/credential-provider-env": "3.496.0", 387 | "@aws-sdk/credential-provider-ini": "3.496.0", 388 | "@aws-sdk/credential-provider-process": "3.496.0", 389 | "@aws-sdk/credential-provider-sso": "3.496.0", 390 | "@aws-sdk/credential-provider-web-identity": "3.496.0", 391 | "@aws-sdk/types": "3.496.0", 392 | "@smithy/credential-provider-imds": "^2.2.1", 393 | "@smithy/property-provider": "^2.1.1", 394 | "@smithy/shared-ini-file-loader": "^2.3.1", 395 | "@smithy/types": "^2.9.1", 396 | "tslib": "^2.5.0" 397 | }, 398 | "engines": { 399 | "node": ">=14.0.0" 400 | } 401 | }, 402 | "node_modules/@aws-sdk/credential-provider-process": { 403 | "version": "3.496.0", 404 | "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.496.0.tgz", 405 | "integrity": "sha512-/YZscCTGOKVmGr916Th4XF8Sz6JDtZ/n2loHG9exok9iy/qIbACsTRNLP9zexPxhPoue/oZqecY5xbVljfY34A==", 406 | "optional": true, 407 | "peer": true, 408 | "dependencies": { 409 | "@aws-sdk/types": "3.496.0", 410 | "@smithy/property-provider": "^2.1.1", 411 | "@smithy/shared-ini-file-loader": "^2.3.1", 412 | "@smithy/types": "^2.9.1", 413 | "tslib": "^2.5.0" 414 | }, 415 | "engines": { 416 | "node": ">=14.0.0" 417 | } 418 | }, 419 | "node_modules/@aws-sdk/credential-provider-sso": { 420 | "version": "3.496.0", 421 | "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.496.0.tgz", 422 | "integrity": "sha512-eP7GxpT2QYubSDG7uk1GJW4eNymZCq65IxDyEFCXOP/kfqkxriCY+iVEFG6/Mo3LxvgrgHXU4jxrCAXMAWN43g==", 423 | "optional": true, 424 | "peer": true, 425 | "dependencies": { 426 | "@aws-sdk/client-sso": "3.496.0", 427 | "@aws-sdk/token-providers": "3.496.0", 428 | "@aws-sdk/types": "3.496.0", 429 | "@smithy/property-provider": "^2.1.1", 430 | "@smithy/shared-ini-file-loader": "^2.3.1", 431 | "@smithy/types": "^2.9.1", 432 | "tslib": "^2.5.0" 433 | }, 434 | "engines": { 435 | "node": ">=14.0.0" 436 | } 437 | }, 438 | "node_modules/@aws-sdk/credential-provider-web-identity": { 439 | "version": "3.496.0", 440 | "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.496.0.tgz", 441 | "integrity": "sha512-IbP+qLlvJSpNPj+zW6TtFuLRTK5Tf0hW+2pom4vFyi5YSH4pn8UOC136UdewX8vhXGS9BJQ5zBDMasIyl5VeGQ==", 442 | "optional": true, 443 | "peer": true, 444 | "dependencies": { 445 | "@aws-sdk/types": "3.496.0", 446 | "@smithy/property-provider": "^2.1.1", 447 | "@smithy/types": "^2.9.1", 448 | "tslib": "^2.5.0" 449 | }, 450 | "engines": { 451 | "node": ">=14.0.0" 452 | } 453 | }, 454 | "node_modules/@aws-sdk/credential-providers": { 455 | "version": "3.496.0", 456 | "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.496.0.tgz", 457 | "integrity": "sha512-JqdSHFY3t+QMdS7Iok/ymvU3bZjVY7YuX9xroJJjMM/gkw+TxnJtVw/uIGsmoK1FT2KX+Dg4i/gmVyQAWubSJw==", 458 | "optional": true, 459 | "peer": true, 460 | "dependencies": { 461 | "@aws-sdk/client-cognito-identity": "3.496.0", 462 | "@aws-sdk/client-sso": "3.496.0", 463 | "@aws-sdk/client-sts": "3.496.0", 464 | "@aws-sdk/credential-provider-cognito-identity": "3.496.0", 465 | "@aws-sdk/credential-provider-env": "3.496.0", 466 | "@aws-sdk/credential-provider-http": "3.496.0", 467 | "@aws-sdk/credential-provider-ini": "3.496.0", 468 | "@aws-sdk/credential-provider-node": "3.496.0", 469 | "@aws-sdk/credential-provider-process": "3.496.0", 470 | "@aws-sdk/credential-provider-sso": "3.496.0", 471 | "@aws-sdk/credential-provider-web-identity": "3.496.0", 472 | "@aws-sdk/types": "3.496.0", 473 | "@smithy/credential-provider-imds": "^2.2.1", 474 | "@smithy/property-provider": "^2.1.1", 475 | "@smithy/types": "^2.9.1", 476 | "tslib": "^2.5.0" 477 | }, 478 | "engines": { 479 | "node": ">=14.0.0" 480 | } 481 | }, 482 | "node_modules/@aws-sdk/middleware-host-header": { 483 | "version": "3.496.0", 484 | "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.496.0.tgz", 485 | "integrity": "sha512-jUdPpSJeqCYXf6hSjfwsfHway7peIV8Vz51w/BN91bF4vB/bYwAC5o9/iJiK/EoByp5asxA8fg9wFOyGjzdbLg==", 486 | "optional": true, 487 | "peer": true, 488 | "dependencies": { 489 | "@aws-sdk/types": "3.496.0", 490 | "@smithy/protocol-http": "^3.1.1", 491 | "@smithy/types": "^2.9.1", 492 | "tslib": "^2.5.0" 493 | }, 494 | "engines": { 495 | "node": ">=14.0.0" 496 | } 497 | }, 498 | "node_modules/@aws-sdk/middleware-logger": { 499 | "version": "3.496.0", 500 | "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.496.0.tgz", 501 | "integrity": "sha512-EwMVSY6iBMeGbVnvwdaFl/ClMS/YWtxCAo+bcEtgk8ltRuo7qgbJem8Km/fvWC1vdWvIbe4ArdJ8iGzq62ffAw==", 502 | "optional": true, 503 | "peer": true, 504 | "dependencies": { 505 | "@aws-sdk/types": "3.496.0", 506 | "@smithy/types": "^2.9.1", 507 | "tslib": "^2.5.0" 508 | }, 509 | "engines": { 510 | "node": ">=14.0.0" 511 | } 512 | }, 513 | "node_modules/@aws-sdk/middleware-recursion-detection": { 514 | "version": "3.496.0", 515 | "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.496.0.tgz", 516 | "integrity": "sha512-+IuOcFsfqg2WAnaEzH6KhVbicqCxtOq9w3DH2jwTpddRlCx2Kqf6wCzg8luhHRGyjBZdsbIS+OXwyMevoppawA==", 517 | "optional": true, 518 | "peer": true, 519 | "dependencies": { 520 | "@aws-sdk/types": "3.496.0", 521 | "@smithy/protocol-http": "^3.1.1", 522 | "@smithy/types": "^2.9.1", 523 | "tslib": "^2.5.0" 524 | }, 525 | "engines": { 526 | "node": ">=14.0.0" 527 | } 528 | }, 529 | "node_modules/@aws-sdk/middleware-signing": { 530 | "version": "3.496.0", 531 | "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.496.0.tgz", 532 | "integrity": "sha512-Oq73Brs4IConvWnRlh8jM1V7LHoTw9SVQklu/QW2FPlNrB3B8fuTdWHHYIWv7ybw1bykXoCY99v865Mmq/Or/g==", 533 | "optional": true, 534 | "peer": true, 535 | "dependencies": { 536 | "@aws-sdk/types": "3.496.0", 537 | "@smithy/property-provider": "^2.1.1", 538 | "@smithy/protocol-http": "^3.1.1", 539 | "@smithy/signature-v4": "^2.1.1", 540 | "@smithy/types": "^2.9.1", 541 | "@smithy/util-middleware": "^2.1.1", 542 | "tslib": "^2.5.0" 543 | }, 544 | "engines": { 545 | "node": ">=14.0.0" 546 | } 547 | }, 548 | "node_modules/@aws-sdk/middleware-user-agent": { 549 | "version": "3.496.0", 550 | "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.496.0.tgz", 551 | "integrity": "sha512-+iMtRxFk0GmFWNUF4ilxylOQd9PZdR4ZC9jkcPIh1PZlvKtpCyFywKlk5RRZKklSoJ/CttcqwhMvOXTNbWm/0w==", 552 | "optional": true, 553 | "peer": true, 554 | "dependencies": { 555 | "@aws-sdk/types": "3.496.0", 556 | "@aws-sdk/util-endpoints": "3.496.0", 557 | "@smithy/protocol-http": "^3.1.1", 558 | "@smithy/types": "^2.9.1", 559 | "tslib": "^2.5.0" 560 | }, 561 | "engines": { 562 | "node": ">=14.0.0" 563 | } 564 | }, 565 | "node_modules/@aws-sdk/region-config-resolver": { 566 | "version": "3.496.0", 567 | "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.496.0.tgz", 568 | "integrity": "sha512-URrNVOPHPgEDm6QFu6lDC2cUFs+Jx23mA3jEwCvoKlXiEY/ZoWjH8wlX3OMUlLrF1qoUTuD03jjrJzF6zoCgug==", 569 | "optional": true, 570 | "peer": true, 571 | "dependencies": { 572 | "@aws-sdk/types": "3.496.0", 573 | "@smithy/node-config-provider": "^2.2.1", 574 | "@smithy/types": "^2.9.1", 575 | "@smithy/util-config-provider": "^2.2.1", 576 | "@smithy/util-middleware": "^2.1.1", 577 | "tslib": "^2.5.0" 578 | }, 579 | "engines": { 580 | "node": ">=14.0.0" 581 | } 582 | }, 583 | "node_modules/@aws-sdk/token-providers": { 584 | "version": "3.496.0", 585 | "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.496.0.tgz", 586 | "integrity": "sha512-fyi8RcObEa1jNETJdc2H6q9VHrrdKCj/b6+fbLvymb7mUVRd0aWUn+24SNUImnSOnrwYnwaMfyyEC388X4MbFQ==", 587 | "optional": true, 588 | "peer": true, 589 | "dependencies": { 590 | "@aws-crypto/sha256-browser": "3.0.0", 591 | "@aws-crypto/sha256-js": "3.0.0", 592 | "@aws-sdk/middleware-host-header": "3.496.0", 593 | "@aws-sdk/middleware-logger": "3.496.0", 594 | "@aws-sdk/middleware-recursion-detection": "3.496.0", 595 | "@aws-sdk/middleware-user-agent": "3.496.0", 596 | "@aws-sdk/region-config-resolver": "3.496.0", 597 | "@aws-sdk/types": "3.496.0", 598 | "@aws-sdk/util-endpoints": "3.496.0", 599 | "@aws-sdk/util-user-agent-browser": "3.496.0", 600 | "@aws-sdk/util-user-agent-node": "3.496.0", 601 | "@smithy/config-resolver": "^2.1.1", 602 | "@smithy/fetch-http-handler": "^2.4.1", 603 | "@smithy/hash-node": "^2.1.1", 604 | "@smithy/invalid-dependency": "^2.1.1", 605 | "@smithy/middleware-content-length": "^2.1.1", 606 | "@smithy/middleware-endpoint": "^2.4.1", 607 | "@smithy/middleware-retry": "^2.1.1", 608 | "@smithy/middleware-serde": "^2.1.1", 609 | "@smithy/middleware-stack": "^2.1.1", 610 | "@smithy/node-config-provider": "^2.2.1", 611 | "@smithy/node-http-handler": "^2.3.1", 612 | "@smithy/property-provider": "^2.1.1", 613 | "@smithy/protocol-http": "^3.1.1", 614 | "@smithy/shared-ini-file-loader": "^2.3.1", 615 | "@smithy/smithy-client": "^2.3.1", 616 | "@smithy/types": "^2.9.1", 617 | "@smithy/url-parser": "^2.1.1", 618 | "@smithy/util-base64": "^2.1.1", 619 | "@smithy/util-body-length-browser": "^2.1.1", 620 | "@smithy/util-body-length-node": "^2.2.1", 621 | "@smithy/util-defaults-mode-browser": "^2.1.1", 622 | "@smithy/util-defaults-mode-node": "^2.1.1", 623 | "@smithy/util-endpoints": "^1.1.1", 624 | "@smithy/util-retry": "^2.1.1", 625 | "@smithy/util-utf8": "^2.1.1", 626 | "tslib": "^2.5.0" 627 | }, 628 | "engines": { 629 | "node": ">=14.0.0" 630 | } 631 | }, 632 | "node_modules/@aws-sdk/types": { 633 | "version": "3.496.0", 634 | "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.496.0.tgz", 635 | "integrity": "sha512-umkGadK4QuNQaMoDICMm7NKRI/mYSXiyPjcn3d53BhsuArYU/52CebGQKdt4At7SwwsiVJZw9RNBHyN5Mm0HVw==", 636 | "optional": true, 637 | "peer": true, 638 | "dependencies": { 639 | "@smithy/types": "^2.9.1", 640 | "tslib": "^2.5.0" 641 | }, 642 | "engines": { 643 | "node": ">=14.0.0" 644 | } 645 | }, 646 | "node_modules/@aws-sdk/util-endpoints": { 647 | "version": "3.496.0", 648 | "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.496.0.tgz", 649 | "integrity": "sha512-1QzOiWHi383ZwqSi/R2KgKCd7M+6DxkxI5acqLPm8mvDRDP2jRjrnVaC0g9/tlttWousGEemDUWStwrD2mVYSw==", 650 | "optional": true, 651 | "peer": true, 652 | "dependencies": { 653 | "@aws-sdk/types": "3.496.0", 654 | "@smithy/types": "^2.9.1", 655 | "@smithy/util-endpoints": "^1.1.1", 656 | "tslib": "^2.5.0" 657 | }, 658 | "engines": { 659 | "node": ">=14.0.0" 660 | } 661 | }, 662 | "node_modules/@aws-sdk/util-locate-window": { 663 | "version": "3.495.0", 664 | "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.495.0.tgz", 665 | "integrity": "sha512-MfaPXT0kLX2tQaR90saBT9fWQq2DHqSSJRzW+MZWsmF+y5LGCOhO22ac/2o6TKSQm7h0HRc2GaADqYYYor62yg==", 666 | "optional": true, 667 | "peer": true, 668 | "dependencies": { 669 | "tslib": "^2.5.0" 670 | }, 671 | "engines": { 672 | "node": ">=14.0.0" 673 | } 674 | }, 675 | "node_modules/@aws-sdk/util-user-agent-browser": { 676 | "version": "3.496.0", 677 | "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.496.0.tgz", 678 | "integrity": "sha512-4j2spN+h0I0qfSMsGvJXTfQBu1e18rPdekKvzsGJxhaAE1tNgUfUT4nbvc5uVn0sNjZmirskmJ3kfbzVOrqIFg==", 679 | "optional": true, 680 | "peer": true, 681 | "dependencies": { 682 | "@aws-sdk/types": "3.496.0", 683 | "@smithy/types": "^2.9.1", 684 | "bowser": "^2.11.0", 685 | "tslib": "^2.5.0" 686 | } 687 | }, 688 | "node_modules/@aws-sdk/util-user-agent-node": { 689 | "version": "3.496.0", 690 | "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.496.0.tgz", 691 | "integrity": "sha512-h0Ax0jlDc7UIo3KoSI4C4tVLBFoiAdx3+DhTVfgLS7x93d41dMlziPoBX2RgdcFn37qnzw6AQKTVTMwDbRCGpg==", 692 | "optional": true, 693 | "peer": true, 694 | "dependencies": { 695 | "@aws-sdk/types": "3.496.0", 696 | "@smithy/node-config-provider": "^2.2.1", 697 | "@smithy/types": "^2.9.1", 698 | "tslib": "^2.5.0" 699 | }, 700 | "engines": { 701 | "node": ">=14.0.0" 702 | }, 703 | "peerDependencies": { 704 | "aws-crt": ">=1.0.0" 705 | }, 706 | "peerDependenciesMeta": { 707 | "aws-crt": { 708 | "optional": true 709 | } 710 | } 711 | }, 712 | "node_modules/@aws-sdk/util-utf8-browser": { 713 | "version": "3.259.0", 714 | "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", 715 | "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", 716 | "optional": true, 717 | "peer": true, 718 | "dependencies": { 719 | "tslib": "^2.3.1" 720 | } 721 | }, 722 | "node_modules/@mongodb-js/saslprep": { 723 | "version": "1.1.4", 724 | "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.4.tgz", 725 | "integrity": "sha512-8zJ8N1x51xo9hwPh6AWnKdLGEC5N3lDa6kms1YHmFBoRhTpJR6HG8wWk0td1MVCu9cD4YBrvjZEtd5Obw0Fbnw==", 726 | "dependencies": { 727 | "sparse-bitfield": "^3.0.3" 728 | } 729 | }, 730 | "node_modules/@smithy/abort-controller": { 731 | "version": "2.1.1", 732 | "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.1.1.tgz", 733 | "integrity": "sha512-1+qdrUqLhaALYL0iOcN43EP6yAXXQ2wWZ6taf4S2pNGowmOc5gx+iMQv+E42JizNJjB0+gEadOXeV1Bf7JWL1Q==", 734 | "optional": true, 735 | "peer": true, 736 | "dependencies": { 737 | "@smithy/types": "^2.9.1", 738 | "tslib": "^2.5.0" 739 | }, 740 | "engines": { 741 | "node": ">=14.0.0" 742 | } 743 | }, 744 | "node_modules/@smithy/config-resolver": { 745 | "version": "2.1.1", 746 | "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-2.1.1.tgz", 747 | "integrity": "sha512-lxfLDpZm+AWAHPFZps5JfDoO9Ux1764fOgvRUBpHIO8HWHcSN1dkgsago1qLRVgm1BZ8RCm8cgv99QvtaOWIhw==", 748 | "optional": true, 749 | "peer": true, 750 | "dependencies": { 751 | "@smithy/node-config-provider": "^2.2.1", 752 | "@smithy/types": "^2.9.1", 753 | "@smithy/util-config-provider": "^2.2.1", 754 | "@smithy/util-middleware": "^2.1.1", 755 | "tslib": "^2.5.0" 756 | }, 757 | "engines": { 758 | "node": ">=14.0.0" 759 | } 760 | }, 761 | "node_modules/@smithy/core": { 762 | "version": "1.3.1", 763 | "resolved": "https://registry.npmjs.org/@smithy/core/-/core-1.3.1.tgz", 764 | "integrity": "sha512-tf+NIu9FkOh312b6M9G4D68is4Xr7qptzaZGZUREELF8ysE1yLKphqt7nsomjKZVwW7WE5pDDex9idowNGRQ/Q==", 765 | "optional": true, 766 | "peer": true, 767 | "dependencies": { 768 | "@smithy/middleware-endpoint": "^2.4.1", 769 | "@smithy/middleware-retry": "^2.1.1", 770 | "@smithy/middleware-serde": "^2.1.1", 771 | "@smithy/protocol-http": "^3.1.1", 772 | "@smithy/smithy-client": "^2.3.1", 773 | "@smithy/types": "^2.9.1", 774 | "@smithy/util-middleware": "^2.1.1", 775 | "tslib": "^2.5.0" 776 | }, 777 | "engines": { 778 | "node": ">=14.0.0" 779 | } 780 | }, 781 | "node_modules/@smithy/credential-provider-imds": { 782 | "version": "2.2.1", 783 | "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-2.2.1.tgz", 784 | "integrity": "sha512-7XHjZUxmZYnONheVQL7j5zvZXga+EWNgwEAP6OPZTi7l8J4JTeNh9aIOfE5fKHZ/ee2IeNOh54ZrSna+Vc6TFA==", 785 | "optional": true, 786 | "peer": true, 787 | "dependencies": { 788 | "@smithy/node-config-provider": "^2.2.1", 789 | "@smithy/property-provider": "^2.1.1", 790 | "@smithy/types": "^2.9.1", 791 | "@smithy/url-parser": "^2.1.1", 792 | "tslib": "^2.5.0" 793 | }, 794 | "engines": { 795 | "node": ">=14.0.0" 796 | } 797 | }, 798 | "node_modules/@smithy/eventstream-codec": { 799 | "version": "2.1.1", 800 | "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-2.1.1.tgz", 801 | "integrity": "sha512-E8KYBxBIuU4c+zrpR22VsVrOPoEDzk35bQR3E+xm4k6Pa6JqzkDOdMyf9Atac5GPNKHJBdVaQ4JtjdWX2rl/nw==", 802 | "optional": true, 803 | "peer": true, 804 | "dependencies": { 805 | "@aws-crypto/crc32": "3.0.0", 806 | "@smithy/types": "^2.9.1", 807 | "@smithy/util-hex-encoding": "^2.1.1", 808 | "tslib": "^2.5.0" 809 | } 810 | }, 811 | "node_modules/@smithy/fetch-http-handler": { 812 | "version": "2.4.1", 813 | "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-2.4.1.tgz", 814 | "integrity": "sha512-VYGLinPsFqH68lxfRhjQaSkjXM7JysUOJDTNjHBuN/ykyRb2f1gyavN9+VhhPTWCy32L4yZ2fdhpCs/nStEicg==", 815 | "optional": true, 816 | "peer": true, 817 | "dependencies": { 818 | "@smithy/protocol-http": "^3.1.1", 819 | "@smithy/querystring-builder": "^2.1.1", 820 | "@smithy/types": "^2.9.1", 821 | "@smithy/util-base64": "^2.1.1", 822 | "tslib": "^2.5.0" 823 | } 824 | }, 825 | "node_modules/@smithy/hash-node": { 826 | "version": "2.1.1", 827 | "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-2.1.1.tgz", 828 | "integrity": "sha512-Qhoq0N8f2OtCnvUpCf+g1vSyhYQrZjhSwvJ9qvR8BUGOtTXiyv2x1OD2e6jVGmlpC4E4ax1USHoyGfV9JFsACg==", 829 | "optional": true, 830 | "peer": true, 831 | "dependencies": { 832 | "@smithy/types": "^2.9.1", 833 | "@smithy/util-buffer-from": "^2.1.1", 834 | "@smithy/util-utf8": "^2.1.1", 835 | "tslib": "^2.5.0" 836 | }, 837 | "engines": { 838 | "node": ">=14.0.0" 839 | } 840 | }, 841 | "node_modules/@smithy/invalid-dependency": { 842 | "version": "2.1.1", 843 | "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-2.1.1.tgz", 844 | "integrity": "sha512-7WTgnKw+VPg8fxu2v9AlNOQ5yaz6RA54zOVB4f6vQuR0xFKd+RzlCpt0WidYTsye7F+FYDIaS/RnJW4pxjNInw==", 845 | "optional": true, 846 | "peer": true, 847 | "dependencies": { 848 | "@smithy/types": "^2.9.1", 849 | "tslib": "^2.5.0" 850 | } 851 | }, 852 | "node_modules/@smithy/is-array-buffer": { 853 | "version": "2.1.1", 854 | "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.1.1.tgz", 855 | "integrity": "sha512-xozSQrcUinPpNPNPds4S7z/FakDTh1MZWtRP/2vQtYB/u3HYrX2UXuZs+VhaKBd6Vc7g2XPr2ZtwGBNDN6fNKQ==", 856 | "optional": true, 857 | "peer": true, 858 | "dependencies": { 859 | "tslib": "^2.5.0" 860 | }, 861 | "engines": { 862 | "node": ">=14.0.0" 863 | } 864 | }, 865 | "node_modules/@smithy/middleware-content-length": { 866 | "version": "2.1.1", 867 | "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-2.1.1.tgz", 868 | "integrity": "sha512-rSr9ezUl9qMgiJR0UVtVOGEZElMdGFyl8FzWEF5iEKTlcWxGr2wTqGfDwtH3LAB7h+FPkxqv4ZU4cpuCN9Kf/g==", 869 | "optional": true, 870 | "peer": true, 871 | "dependencies": { 872 | "@smithy/protocol-http": "^3.1.1", 873 | "@smithy/types": "^2.9.1", 874 | "tslib": "^2.5.0" 875 | }, 876 | "engines": { 877 | "node": ">=14.0.0" 878 | } 879 | }, 880 | "node_modules/@smithy/middleware-endpoint": { 881 | "version": "2.4.1", 882 | "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-2.4.1.tgz", 883 | "integrity": "sha512-XPZTb1E2Oav60Ven3n2PFx+rX9EDsU/jSTA8VDamt7FXks67ekjPY/XrmmPDQaFJOTUHJNKjd8+kZxVO5Ael4Q==", 884 | "optional": true, 885 | "peer": true, 886 | "dependencies": { 887 | "@smithy/middleware-serde": "^2.1.1", 888 | "@smithy/node-config-provider": "^2.2.1", 889 | "@smithy/shared-ini-file-loader": "^2.3.1", 890 | "@smithy/types": "^2.9.1", 891 | "@smithy/url-parser": "^2.1.1", 892 | "@smithy/util-middleware": "^2.1.1", 893 | "tslib": "^2.5.0" 894 | }, 895 | "engines": { 896 | "node": ">=14.0.0" 897 | } 898 | }, 899 | "node_modules/@smithy/middleware-retry": { 900 | "version": "2.1.1", 901 | "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-2.1.1.tgz", 902 | "integrity": "sha512-eMIHOBTXro6JZ+WWzZWd/8fS8ht5nS5KDQjzhNMHNRcG5FkNTqcKpYhw7TETMYzbLfhO5FYghHy1vqDWM4FLDA==", 903 | "optional": true, 904 | "peer": true, 905 | "dependencies": { 906 | "@smithy/node-config-provider": "^2.2.1", 907 | "@smithy/protocol-http": "^3.1.1", 908 | "@smithy/service-error-classification": "^2.1.1", 909 | "@smithy/smithy-client": "^2.3.1", 910 | "@smithy/types": "^2.9.1", 911 | "@smithy/util-middleware": "^2.1.1", 912 | "@smithy/util-retry": "^2.1.1", 913 | "tslib": "^2.5.0", 914 | "uuid": "^8.3.2" 915 | }, 916 | "engines": { 917 | "node": ">=14.0.0" 918 | } 919 | }, 920 | "node_modules/@smithy/middleware-serde": { 921 | "version": "2.1.1", 922 | "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-2.1.1.tgz", 923 | "integrity": "sha512-D8Gq0aQBeE1pxf3cjWVkRr2W54t+cdM2zx78tNrVhqrDykRA7asq8yVJij1u5NDtKzKqzBSPYh7iW0svUKg76g==", 924 | "optional": true, 925 | "peer": true, 926 | "dependencies": { 927 | "@smithy/types": "^2.9.1", 928 | "tslib": "^2.5.0" 929 | }, 930 | "engines": { 931 | "node": ">=14.0.0" 932 | } 933 | }, 934 | "node_modules/@smithy/middleware-stack": { 935 | "version": "2.1.1", 936 | "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-2.1.1.tgz", 937 | "integrity": "sha512-KPJhRlhsl8CjgGXK/DoDcrFGfAqoqvuwlbxy+uOO4g2Azn1dhH+GVfC3RAp+6PoL5PWPb+vt6Z23FP+Mr6qeCw==", 938 | "optional": true, 939 | "peer": true, 940 | "dependencies": { 941 | "@smithy/types": "^2.9.1", 942 | "tslib": "^2.5.0" 943 | }, 944 | "engines": { 945 | "node": ">=14.0.0" 946 | } 947 | }, 948 | "node_modules/@smithy/node-config-provider": { 949 | "version": "2.2.1", 950 | "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-2.2.1.tgz", 951 | "integrity": "sha512-epzK3x1xNxA9oJgHQ5nz+2j6DsJKdHfieb+YgJ7ATWxzNcB7Hc+Uya2TUck5MicOPhDV8HZImND7ZOecVr+OWg==", 952 | "optional": true, 953 | "peer": true, 954 | "dependencies": { 955 | "@smithy/property-provider": "^2.1.1", 956 | "@smithy/shared-ini-file-loader": "^2.3.1", 957 | "@smithy/types": "^2.9.1", 958 | "tslib": "^2.5.0" 959 | }, 960 | "engines": { 961 | "node": ">=14.0.0" 962 | } 963 | }, 964 | "node_modules/@smithy/node-http-handler": { 965 | "version": "2.3.1", 966 | "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-2.3.1.tgz", 967 | "integrity": "sha512-gLA8qK2nL9J0Rk/WEZSvgin4AppvuCYRYg61dcUo/uKxvMZsMInL5I5ZdJTogOvdfVug3N2dgI5ffcUfS4S9PA==", 968 | "optional": true, 969 | "peer": true, 970 | "dependencies": { 971 | "@smithy/abort-controller": "^2.1.1", 972 | "@smithy/protocol-http": "^3.1.1", 973 | "@smithy/querystring-builder": "^2.1.1", 974 | "@smithy/types": "^2.9.1", 975 | "tslib": "^2.5.0" 976 | }, 977 | "engines": { 978 | "node": ">=14.0.0" 979 | } 980 | }, 981 | "node_modules/@smithy/property-provider": { 982 | "version": "2.1.1", 983 | "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.1.1.tgz", 984 | "integrity": "sha512-FX7JhhD/o5HwSwg6GLK9zxrMUrGnb3PzNBrcthqHKBc3dH0UfgEAU24xnJ8F0uow5mj17UeBEOI6o3CF2k7Mhw==", 985 | "optional": true, 986 | "peer": true, 987 | "dependencies": { 988 | "@smithy/types": "^2.9.1", 989 | "tslib": "^2.5.0" 990 | }, 991 | "engines": { 992 | "node": ">=14.0.0" 993 | } 994 | }, 995 | "node_modules/@smithy/protocol-http": { 996 | "version": "3.1.1", 997 | "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.1.1.tgz", 998 | "integrity": "sha512-6ZRTSsaXuSL9++qEwH851hJjUA0OgXdQFCs+VDw4tGH256jQ3TjYY/i34N4vd24RV3nrjNsgd1yhb57uMoKbzQ==", 999 | "optional": true, 1000 | "peer": true, 1001 | "dependencies": { 1002 | "@smithy/types": "^2.9.1", 1003 | "tslib": "^2.5.0" 1004 | }, 1005 | "engines": { 1006 | "node": ">=14.0.0" 1007 | } 1008 | }, 1009 | "node_modules/@smithy/querystring-builder": { 1010 | "version": "2.1.1", 1011 | "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-2.1.1.tgz", 1012 | "integrity": "sha512-C/ko/CeEa8jdYE4gt6nHO5XDrlSJ3vdCG0ZAc6nD5ZIE7LBp0jCx4qoqp7eoutBu7VrGMXERSRoPqwi1WjCPbg==", 1013 | "optional": true, 1014 | "peer": true, 1015 | "dependencies": { 1016 | "@smithy/types": "^2.9.1", 1017 | "@smithy/util-uri-escape": "^2.1.1", 1018 | "tslib": "^2.5.0" 1019 | }, 1020 | "engines": { 1021 | "node": ">=14.0.0" 1022 | } 1023 | }, 1024 | "node_modules/@smithy/querystring-parser": { 1025 | "version": "2.1.1", 1026 | "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-2.1.1.tgz", 1027 | "integrity": "sha512-H4+6jKGVhG1W4CIxfBaSsbm98lOO88tpDWmZLgkJpt8Zkk/+uG0FmmqMuCAc3HNM2ZDV+JbErxr0l5BcuIf/XQ==", 1028 | "optional": true, 1029 | "peer": true, 1030 | "dependencies": { 1031 | "@smithy/types": "^2.9.1", 1032 | "tslib": "^2.5.0" 1033 | }, 1034 | "engines": { 1035 | "node": ">=14.0.0" 1036 | } 1037 | }, 1038 | "node_modules/@smithy/service-error-classification": { 1039 | "version": "2.1.1", 1040 | "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-2.1.1.tgz", 1041 | "integrity": "sha512-txEdZxPUgM1PwGvDvHzqhXisrc5LlRWYCf2yyHfvITWioAKat7srQvpjMAvgzf0t6t7j8yHrryXU9xt7RZqFpw==", 1042 | "optional": true, 1043 | "peer": true, 1044 | "dependencies": { 1045 | "@smithy/types": "^2.9.1" 1046 | }, 1047 | "engines": { 1048 | "node": ">=14.0.0" 1049 | } 1050 | }, 1051 | "node_modules/@smithy/shared-ini-file-loader": { 1052 | "version": "2.3.1", 1053 | "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.3.1.tgz", 1054 | "integrity": "sha512-2E2kh24igmIznHLB6H05Na4OgIEilRu0oQpYXo3LCNRrawHAcfDKq9004zJs+sAMt2X5AbY87CUCJ7IpqpSgdw==", 1055 | "optional": true, 1056 | "peer": true, 1057 | "dependencies": { 1058 | "@smithy/types": "^2.9.1", 1059 | "tslib": "^2.5.0" 1060 | }, 1061 | "engines": { 1062 | "node": ">=14.0.0" 1063 | } 1064 | }, 1065 | "node_modules/@smithy/signature-v4": { 1066 | "version": "2.1.1", 1067 | "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-2.1.1.tgz", 1068 | "integrity": "sha512-Hb7xub0NHuvvQD3YwDSdanBmYukoEkhqBjqoxo+bSdC0ryV9cTfgmNjuAQhTPYB6yeU7hTR+sPRiFMlxqv6kmg==", 1069 | "optional": true, 1070 | "peer": true, 1071 | "dependencies": { 1072 | "@smithy/eventstream-codec": "^2.1.1", 1073 | "@smithy/is-array-buffer": "^2.1.1", 1074 | "@smithy/types": "^2.9.1", 1075 | "@smithy/util-hex-encoding": "^2.1.1", 1076 | "@smithy/util-middleware": "^2.1.1", 1077 | "@smithy/util-uri-escape": "^2.1.1", 1078 | "@smithy/util-utf8": "^2.1.1", 1079 | "tslib": "^2.5.0" 1080 | }, 1081 | "engines": { 1082 | "node": ">=14.0.0" 1083 | } 1084 | }, 1085 | "node_modules/@smithy/smithy-client": { 1086 | "version": "2.3.1", 1087 | "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-2.3.1.tgz", 1088 | "integrity": "sha512-YsTdU8xVD64r2pLEwmltrNvZV6XIAC50LN6ivDopdt+YiF/jGH6PY9zUOu0CXD/d8GMB8gbhnpPsdrjAXHS9QA==", 1089 | "optional": true, 1090 | "peer": true, 1091 | "dependencies": { 1092 | "@smithy/middleware-endpoint": "^2.4.1", 1093 | "@smithy/middleware-stack": "^2.1.1", 1094 | "@smithy/protocol-http": "^3.1.1", 1095 | "@smithy/types": "^2.9.1", 1096 | "@smithy/util-stream": "^2.1.1", 1097 | "tslib": "^2.5.0" 1098 | }, 1099 | "engines": { 1100 | "node": ">=14.0.0" 1101 | } 1102 | }, 1103 | "node_modules/@smithy/types": { 1104 | "version": "2.9.1", 1105 | "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.9.1.tgz", 1106 | "integrity": "sha512-vjXlKNXyprDYDuJ7UW5iobdmyDm6g8dDG+BFUncAg/3XJaN45Gy5RWWWUVgrzIK7S4R1KWgIX5LeJcfvSI24bw==", 1107 | "optional": true, 1108 | "peer": true, 1109 | "dependencies": { 1110 | "tslib": "^2.5.0" 1111 | }, 1112 | "engines": { 1113 | "node": ">=14.0.0" 1114 | } 1115 | }, 1116 | "node_modules/@smithy/url-parser": { 1117 | "version": "2.1.1", 1118 | "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-2.1.1.tgz", 1119 | "integrity": "sha512-qC9Bv8f/vvFIEkHsiNrUKYNl8uKQnn4BdhXl7VzQRP774AwIjiSMMwkbT+L7Fk8W8rzYVifzJNYxv1HwvfBo3Q==", 1120 | "optional": true, 1121 | "peer": true, 1122 | "dependencies": { 1123 | "@smithy/querystring-parser": "^2.1.1", 1124 | "@smithy/types": "^2.9.1", 1125 | "tslib": "^2.5.0" 1126 | } 1127 | }, 1128 | "node_modules/@smithy/util-base64": { 1129 | "version": "2.1.1", 1130 | "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-2.1.1.tgz", 1131 | "integrity": "sha512-UfHVpY7qfF/MrgndI5PexSKVTxSZIdz9InghTFa49QOvuu9I52zLPLUHXvHpNuMb1iD2vmc6R+zbv/bdMipR/g==", 1132 | "optional": true, 1133 | "peer": true, 1134 | "dependencies": { 1135 | "@smithy/util-buffer-from": "^2.1.1", 1136 | "tslib": "^2.5.0" 1137 | }, 1138 | "engines": { 1139 | "node": ">=14.0.0" 1140 | } 1141 | }, 1142 | "node_modules/@smithy/util-body-length-browser": { 1143 | "version": "2.1.1", 1144 | "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-2.1.1.tgz", 1145 | "integrity": "sha512-ekOGBLvs1VS2d1zM2ER4JEeBWAvIOUKeaFch29UjjJsxmZ/f0L3K3x0dEETgh3Q9bkZNHgT+rkdl/J/VUqSRag==", 1146 | "optional": true, 1147 | "peer": true, 1148 | "dependencies": { 1149 | "tslib": "^2.5.0" 1150 | } 1151 | }, 1152 | "node_modules/@smithy/util-body-length-node": { 1153 | "version": "2.2.1", 1154 | "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-2.2.1.tgz", 1155 | "integrity": "sha512-/ggJG+ta3IDtpNVq4ktmEUtOkH1LW64RHB5B0hcr5ZaWBmo96UX2cIOVbjCqqDickTXqBWZ4ZO0APuaPrD7Abg==", 1156 | "optional": true, 1157 | "peer": true, 1158 | "dependencies": { 1159 | "tslib": "^2.5.0" 1160 | }, 1161 | "engines": { 1162 | "node": ">=14.0.0" 1163 | } 1164 | }, 1165 | "node_modules/@smithy/util-buffer-from": { 1166 | "version": "2.1.1", 1167 | "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.1.1.tgz", 1168 | "integrity": "sha512-clhNjbyfqIv9Md2Mg6FffGVrJxw7bgK7s3Iax36xnfVj6cg0fUG7I4RH0XgXJF8bxi+saY5HR21g2UPKSxVCXg==", 1169 | "optional": true, 1170 | "peer": true, 1171 | "dependencies": { 1172 | "@smithy/is-array-buffer": "^2.1.1", 1173 | "tslib": "^2.5.0" 1174 | }, 1175 | "engines": { 1176 | "node": ">=14.0.0" 1177 | } 1178 | }, 1179 | "node_modules/@smithy/util-config-provider": { 1180 | "version": "2.2.1", 1181 | "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-2.2.1.tgz", 1182 | "integrity": "sha512-50VL/tx9oYYcjJn/qKqNy7sCtpD0+s8XEBamIFo4mFFTclKMNp+rsnymD796uybjiIquB7VCB/DeafduL0y2kw==", 1183 | "optional": true, 1184 | "peer": true, 1185 | "dependencies": { 1186 | "tslib": "^2.5.0" 1187 | }, 1188 | "engines": { 1189 | "node": ">=14.0.0" 1190 | } 1191 | }, 1192 | "node_modules/@smithy/util-defaults-mode-browser": { 1193 | "version": "2.1.1", 1194 | "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-2.1.1.tgz", 1195 | "integrity": "sha512-lqLz/9aWRO6mosnXkArtRuQqqZBhNpgI65YDpww4rVQBuUT7qzKbDLG5AmnQTCiU4rOquaZO/Kt0J7q9Uic7MA==", 1196 | "optional": true, 1197 | "peer": true, 1198 | "dependencies": { 1199 | "@smithy/property-provider": "^2.1.1", 1200 | "@smithy/smithy-client": "^2.3.1", 1201 | "@smithy/types": "^2.9.1", 1202 | "bowser": "^2.11.0", 1203 | "tslib": "^2.5.0" 1204 | }, 1205 | "engines": { 1206 | "node": ">= 10.0.0" 1207 | } 1208 | }, 1209 | "node_modules/@smithy/util-defaults-mode-node": { 1210 | "version": "2.1.1", 1211 | "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-2.1.1.tgz", 1212 | "integrity": "sha512-tYVrc+w+jSBfBd267KDnvSGOh4NMz+wVH7v4CClDbkdPfnjvImBZsOURncT5jsFwR9KCuDyPoSZq4Pa6+eCUrA==", 1213 | "optional": true, 1214 | "peer": true, 1215 | "dependencies": { 1216 | "@smithy/config-resolver": "^2.1.1", 1217 | "@smithy/credential-provider-imds": "^2.2.1", 1218 | "@smithy/node-config-provider": "^2.2.1", 1219 | "@smithy/property-provider": "^2.1.1", 1220 | "@smithy/smithy-client": "^2.3.1", 1221 | "@smithy/types": "^2.9.1", 1222 | "tslib": "^2.5.0" 1223 | }, 1224 | "engines": { 1225 | "node": ">= 10.0.0" 1226 | } 1227 | }, 1228 | "node_modules/@smithy/util-endpoints": { 1229 | "version": "1.1.1", 1230 | "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-1.1.1.tgz", 1231 | "integrity": "sha512-sI4d9rjoaekSGEtq3xSb2nMjHMx8QXcz2cexnVyRWsy4yQ9z3kbDpX+7fN0jnbdOp0b3KSTZJZ2Yb92JWSanLw==", 1232 | "optional": true, 1233 | "peer": true, 1234 | "dependencies": { 1235 | "@smithy/node-config-provider": "^2.2.1", 1236 | "@smithy/types": "^2.9.1", 1237 | "tslib": "^2.5.0" 1238 | }, 1239 | "engines": { 1240 | "node": ">= 14.0.0" 1241 | } 1242 | }, 1243 | "node_modules/@smithy/util-hex-encoding": { 1244 | "version": "2.1.1", 1245 | "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-2.1.1.tgz", 1246 | "integrity": "sha512-3UNdP2pkYUUBGEXzQI9ODTDK+Tcu1BlCyDBaRHwyxhA+8xLP8agEKQq4MGmpjqb4VQAjq9TwlCQX0kP6XDKYLg==", 1247 | "optional": true, 1248 | "peer": true, 1249 | "dependencies": { 1250 | "tslib": "^2.5.0" 1251 | }, 1252 | "engines": { 1253 | "node": ">=14.0.0" 1254 | } 1255 | }, 1256 | "node_modules/@smithy/util-middleware": { 1257 | "version": "2.1.1", 1258 | "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-2.1.1.tgz", 1259 | "integrity": "sha512-mKNrk8oz5zqkNcbcgAAepeJbmfUW6ogrT2Z2gDbIUzVzNAHKJQTYmH9jcy0jbWb+m7ubrvXKb6uMjkSgAqqsFA==", 1260 | "optional": true, 1261 | "peer": true, 1262 | "dependencies": { 1263 | "@smithy/types": "^2.9.1", 1264 | "tslib": "^2.5.0" 1265 | }, 1266 | "engines": { 1267 | "node": ">=14.0.0" 1268 | } 1269 | }, 1270 | "node_modules/@smithy/util-retry": { 1271 | "version": "2.1.1", 1272 | "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-2.1.1.tgz", 1273 | "integrity": "sha512-Mg+xxWPTeSPrthpC5WAamJ6PW4Kbo01Fm7lWM1jmGRvmrRdsd3192Gz2fBXAMURyXpaNxyZf6Hr/nQ4q70oVEA==", 1274 | "optional": true, 1275 | "peer": true, 1276 | "dependencies": { 1277 | "@smithy/service-error-classification": "^2.1.1", 1278 | "@smithy/types": "^2.9.1", 1279 | "tslib": "^2.5.0" 1280 | }, 1281 | "engines": { 1282 | "node": ">= 14.0.0" 1283 | } 1284 | }, 1285 | "node_modules/@smithy/util-stream": { 1286 | "version": "2.1.1", 1287 | "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-2.1.1.tgz", 1288 | "integrity": "sha512-J7SMIpUYvU4DQN55KmBtvaMc7NM3CZ2iWICdcgaovtLzseVhAqFRYqloT3mh0esrFw+3VEK6nQFteFsTqZSECQ==", 1289 | "optional": true, 1290 | "peer": true, 1291 | "dependencies": { 1292 | "@smithy/fetch-http-handler": "^2.4.1", 1293 | "@smithy/node-http-handler": "^2.3.1", 1294 | "@smithy/types": "^2.9.1", 1295 | "@smithy/util-base64": "^2.1.1", 1296 | "@smithy/util-buffer-from": "^2.1.1", 1297 | "@smithy/util-hex-encoding": "^2.1.1", 1298 | "@smithy/util-utf8": "^2.1.1", 1299 | "tslib": "^2.5.0" 1300 | }, 1301 | "engines": { 1302 | "node": ">=14.0.0" 1303 | } 1304 | }, 1305 | "node_modules/@smithy/util-uri-escape": { 1306 | "version": "2.1.1", 1307 | "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-2.1.1.tgz", 1308 | "integrity": "sha512-saVzI1h6iRBUVSqtnlOnc9ssU09ypo7n+shdQ8hBTZno/9rZ3AuRYvoHInV57VF7Qn7B+pFJG7qTzFiHxWlWBw==", 1309 | "optional": true, 1310 | "peer": true, 1311 | "dependencies": { 1312 | "tslib": "^2.5.0" 1313 | }, 1314 | "engines": { 1315 | "node": ">=14.0.0" 1316 | } 1317 | }, 1318 | "node_modules/@smithy/util-utf8": { 1319 | "version": "2.1.1", 1320 | "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.1.1.tgz", 1321 | "integrity": "sha512-BqTpzYEcUMDwAKr7/mVRUtHDhs6ZoXDi9NypMvMfOr/+u1NW7JgqodPDECiiLboEm6bobcPcECxzjtQh865e9A==", 1322 | "optional": true, 1323 | "peer": true, 1324 | "dependencies": { 1325 | "@smithy/util-buffer-from": "^2.1.1", 1326 | "tslib": "^2.5.0" 1327 | }, 1328 | "engines": { 1329 | "node": ">=14.0.0" 1330 | } 1331 | }, 1332 | "node_modules/@socket.io/component-emitter": { 1333 | "version": "3.1.0", 1334 | "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", 1335 | "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" 1336 | }, 1337 | "node_modules/@socket.io/mongo-adapter": { 1338 | "version": "0.3.2", 1339 | "resolved": "https://registry.npmjs.org/@socket.io/mongo-adapter/-/mongo-adapter-0.3.2.tgz", 1340 | "integrity": "sha512-PcVlNuMJgAwDC+iVXmEDvmSik3TqzLqeBJz9uj18+FGApa5WEc35LgEnU0jCZPqZjvPQ2/i6sB/sAMsr58rHfA==", 1341 | "dependencies": { 1342 | "debug": "~4.3.1", 1343 | "mongodb": "*" 1344 | }, 1345 | "engines": { 1346 | "node": ">=10.0.0" 1347 | }, 1348 | "peerDependencies": { 1349 | "socket.io-adapter": "^2.5.2" 1350 | } 1351 | }, 1352 | "node_modules/@types/cookie": { 1353 | "version": "0.4.1", 1354 | "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", 1355 | "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" 1356 | }, 1357 | "node_modules/@types/cors": { 1358 | "version": "2.8.17", 1359 | "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", 1360 | "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", 1361 | "dependencies": { 1362 | "@types/node": "*" 1363 | } 1364 | }, 1365 | "node_modules/@types/node": { 1366 | "version": "20.11.5", 1367 | "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.5.tgz", 1368 | "integrity": "sha512-g557vgQjUUfN76MZAN/dt1z3dzcUsimuysco0KeluHgrPdJXkP/XdAURgyO2W9fZWHRtRBiVKzKn8vyOAwlG+w==", 1369 | "dependencies": { 1370 | "undici-types": "~5.26.4" 1371 | } 1372 | }, 1373 | "node_modules/@types/webidl-conversions": { 1374 | "version": "7.0.3", 1375 | "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", 1376 | "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==" 1377 | }, 1378 | "node_modules/@types/whatwg-url": { 1379 | "version": "11.0.4", 1380 | "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.4.tgz", 1381 | "integrity": "sha512-lXCmTWSHJvf0TRSO58nm978b8HJ/EdsSsEKLd3ODHFjo+3VGAyyTp4v50nWvwtzBxSMQrVOK7tcuN0zGPLICMw==", 1382 | "dependencies": { 1383 | "@types/webidl-conversions": "*" 1384 | } 1385 | }, 1386 | "node_modules/accepts": { 1387 | "version": "1.3.8", 1388 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", 1389 | "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", 1390 | "dependencies": { 1391 | "mime-types": "~2.1.34", 1392 | "negotiator": "0.6.3" 1393 | }, 1394 | "engines": { 1395 | "node": ">= 0.6" 1396 | } 1397 | }, 1398 | "node_modules/base64id": { 1399 | "version": "2.0.0", 1400 | "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", 1401 | "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", 1402 | "engines": { 1403 | "node": "^4.5.0 || >= 5.9" 1404 | } 1405 | }, 1406 | "node_modules/bowser": { 1407 | "version": "2.11.0", 1408 | "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", 1409 | "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", 1410 | "optional": true, 1411 | "peer": true 1412 | }, 1413 | "node_modules/bson": { 1414 | "version": "6.2.0", 1415 | "resolved": "https://registry.npmjs.org/bson/-/bson-6.2.0.tgz", 1416 | "integrity": "sha512-ID1cI+7bazPDyL9wYy9GaQ8gEEohWvcUl/Yf0dIdutJxnmInEEyCsb4awy/OiBfall7zBA179Pahi3vCdFze3Q==", 1417 | "engines": { 1418 | "node": ">=16.20.1" 1419 | } 1420 | }, 1421 | "node_modules/cookie": { 1422 | "version": "0.4.2", 1423 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", 1424 | "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", 1425 | "engines": { 1426 | "node": ">= 0.6" 1427 | } 1428 | }, 1429 | "node_modules/cors": { 1430 | "version": "2.8.5", 1431 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", 1432 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", 1433 | "dependencies": { 1434 | "object-assign": "^4", 1435 | "vary": "^1" 1436 | }, 1437 | "engines": { 1438 | "node": ">= 0.10" 1439 | } 1440 | }, 1441 | "node_modules/debug": { 1442 | "version": "4.3.4", 1443 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 1444 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 1445 | "dependencies": { 1446 | "ms": "2.1.2" 1447 | }, 1448 | "engines": { 1449 | "node": ">=6.0" 1450 | }, 1451 | "peerDependenciesMeta": { 1452 | "supports-color": { 1453 | "optional": true 1454 | } 1455 | } 1456 | }, 1457 | "node_modules/engine.io": { 1458 | "version": "6.5.4", 1459 | "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.4.tgz", 1460 | "integrity": "sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg==", 1461 | "dependencies": { 1462 | "@types/cookie": "^0.4.1", 1463 | "@types/cors": "^2.8.12", 1464 | "@types/node": ">=10.0.0", 1465 | "accepts": "~1.3.4", 1466 | "base64id": "2.0.0", 1467 | "cookie": "~0.4.1", 1468 | "cors": "~2.8.5", 1469 | "debug": "~4.3.1", 1470 | "engine.io-parser": "~5.2.1", 1471 | "ws": "~8.11.0" 1472 | }, 1473 | "engines": { 1474 | "node": ">=10.2.0" 1475 | } 1476 | }, 1477 | "node_modules/engine.io-client": { 1478 | "version": "6.5.3", 1479 | "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.3.tgz", 1480 | "integrity": "sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==", 1481 | "dependencies": { 1482 | "@socket.io/component-emitter": "~3.1.0", 1483 | "debug": "~4.3.1", 1484 | "engine.io-parser": "~5.2.1", 1485 | "ws": "~8.11.0", 1486 | "xmlhttprequest-ssl": "~2.0.0" 1487 | } 1488 | }, 1489 | "node_modules/engine.io-parser": { 1490 | "version": "5.2.1", 1491 | "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.1.tgz", 1492 | "integrity": "sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==", 1493 | "engines": { 1494 | "node": ">=10.0.0" 1495 | } 1496 | }, 1497 | "node_modules/fast-xml-parser": { 1498 | "version": "4.2.5", 1499 | "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz", 1500 | "integrity": "sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==", 1501 | "funding": [ 1502 | { 1503 | "type": "paypal", 1504 | "url": "https://paypal.me/naturalintelligence" 1505 | }, 1506 | { 1507 | "type": "github", 1508 | "url": "https://github.com/sponsors/NaturalIntelligence" 1509 | } 1510 | ], 1511 | "optional": true, 1512 | "peer": true, 1513 | "dependencies": { 1514 | "strnum": "^1.0.5" 1515 | }, 1516 | "bin": { 1517 | "fxparser": "src/cli/cli.js" 1518 | } 1519 | }, 1520 | "node_modules/ip": { 1521 | "version": "2.0.0", 1522 | "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", 1523 | "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==", 1524 | "optional": true, 1525 | "peer": true 1526 | }, 1527 | "node_modules/memory-pager": { 1528 | "version": "1.5.0", 1529 | "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", 1530 | "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==" 1531 | }, 1532 | "node_modules/mime-db": { 1533 | "version": "1.52.0", 1534 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 1535 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 1536 | "engines": { 1537 | "node": ">= 0.6" 1538 | } 1539 | }, 1540 | "node_modules/mime-types": { 1541 | "version": "2.1.35", 1542 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 1543 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 1544 | "dependencies": { 1545 | "mime-db": "1.52.0" 1546 | }, 1547 | "engines": { 1548 | "node": ">= 0.6" 1549 | } 1550 | }, 1551 | "node_modules/mongodb": { 1552 | "version": "6.3.0", 1553 | "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.3.0.tgz", 1554 | "integrity": "sha512-tt0KuGjGtLUhLoU263+xvQmPHEGTw5LbcNC73EoFRYgSHwZt5tsoJC110hDyO1kjQzpgNrpdcSza9PknWN4LrA==", 1555 | "dependencies": { 1556 | "@mongodb-js/saslprep": "^1.1.0", 1557 | "bson": "^6.2.0", 1558 | "mongodb-connection-string-url": "^3.0.0" 1559 | }, 1560 | "engines": { 1561 | "node": ">=16.20.1" 1562 | }, 1563 | "peerDependencies": { 1564 | "@aws-sdk/credential-providers": "^3.188.0", 1565 | "@mongodb-js/zstd": "^1.1.0", 1566 | "gcp-metadata": "^5.2.0", 1567 | "kerberos": "^2.0.1", 1568 | "mongodb-client-encryption": ">=6.0.0 <7", 1569 | "snappy": "^7.2.2", 1570 | "socks": "^2.7.1" 1571 | }, 1572 | "peerDependenciesMeta": { 1573 | "@aws-sdk/credential-providers": { 1574 | "optional": true 1575 | }, 1576 | "@mongodb-js/zstd": { 1577 | "optional": true 1578 | }, 1579 | "gcp-metadata": { 1580 | "optional": true 1581 | }, 1582 | "kerberos": { 1583 | "optional": true 1584 | }, 1585 | "mongodb-client-encryption": { 1586 | "optional": true 1587 | }, 1588 | "snappy": { 1589 | "optional": true 1590 | }, 1591 | "socks": { 1592 | "optional": true 1593 | } 1594 | } 1595 | }, 1596 | "node_modules/mongodb-connection-string-url": { 1597 | "version": "3.0.0", 1598 | "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.0.tgz", 1599 | "integrity": "sha512-t1Vf+m1I5hC2M5RJx/7AtxgABy1cZmIPQRMXw+gEIPn/cZNF3Oiy+l0UIypUwVB5trcWHq3crg2g3uAR9aAwsQ==", 1600 | "dependencies": { 1601 | "@types/whatwg-url": "^11.0.2", 1602 | "whatwg-url": "^13.0.0" 1603 | } 1604 | }, 1605 | "node_modules/ms": { 1606 | "version": "2.1.2", 1607 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1608 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 1609 | }, 1610 | "node_modules/negotiator": { 1611 | "version": "0.6.3", 1612 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", 1613 | "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", 1614 | "engines": { 1615 | "node": ">= 0.6" 1616 | } 1617 | }, 1618 | "node_modules/object-assign": { 1619 | "version": "4.1.1", 1620 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 1621 | "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", 1622 | "engines": { 1623 | "node": ">=0.10.0" 1624 | } 1625 | }, 1626 | "node_modules/punycode": { 1627 | "version": "2.3.1", 1628 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", 1629 | "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", 1630 | "engines": { 1631 | "node": ">=6" 1632 | } 1633 | }, 1634 | "node_modules/smart-buffer": { 1635 | "version": "4.2.0", 1636 | "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", 1637 | "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", 1638 | "optional": true, 1639 | "peer": true, 1640 | "engines": { 1641 | "node": ">= 6.0.0", 1642 | "npm": ">= 3.0.0" 1643 | } 1644 | }, 1645 | "node_modules/socket.io": { 1646 | "version": "4.7.4", 1647 | "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.4.tgz", 1648 | "integrity": "sha512-DcotgfP1Zg9iP/dH9zvAQcWrE0TtbMVwXmlV4T4mqsvY+gw+LqUGPfx2AoVyRk0FLME+GQhufDMyacFmw7ksqw==", 1649 | "dependencies": { 1650 | "accepts": "~1.3.4", 1651 | "base64id": "~2.0.0", 1652 | "cors": "~2.8.5", 1653 | "debug": "~4.3.2", 1654 | "engine.io": "~6.5.2", 1655 | "socket.io-adapter": "~2.5.2", 1656 | "socket.io-parser": "~4.2.4" 1657 | }, 1658 | "engines": { 1659 | "node": ">=10.2.0" 1660 | } 1661 | }, 1662 | "node_modules/socket.io-adapter": { 1663 | "version": "2.5.2", 1664 | "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz", 1665 | "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==", 1666 | "dependencies": { 1667 | "ws": "~8.11.0" 1668 | } 1669 | }, 1670 | "node_modules/socket.io-client": { 1671 | "version": "4.7.4", 1672 | "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.4.tgz", 1673 | "integrity": "sha512-wh+OkeF0rAVCrABWQBaEjLfb7DVPotMbu0cgWgyR0v6eA4EoVnAwcIeIbcdTE3GT/H3kbdLl7OoH2+asoDRIIg==", 1674 | "dependencies": { 1675 | "@socket.io/component-emitter": "~3.1.0", 1676 | "debug": "~4.3.2", 1677 | "engine.io-client": "~6.5.2", 1678 | "socket.io-parser": "~4.2.4" 1679 | }, 1680 | "engines": { 1681 | "node": ">=10.0.0" 1682 | } 1683 | }, 1684 | "node_modules/socket.io-parser": { 1685 | "version": "4.2.4", 1686 | "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", 1687 | "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", 1688 | "dependencies": { 1689 | "@socket.io/component-emitter": "~3.1.0", 1690 | "debug": "~4.3.1" 1691 | }, 1692 | "engines": { 1693 | "node": ">=10.0.0" 1694 | } 1695 | }, 1696 | "node_modules/socks": { 1697 | "version": "2.7.1", 1698 | "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", 1699 | "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", 1700 | "optional": true, 1701 | "peer": true, 1702 | "dependencies": { 1703 | "ip": "^2.0.0", 1704 | "smart-buffer": "^4.2.0" 1705 | }, 1706 | "engines": { 1707 | "node": ">= 10.13.0", 1708 | "npm": ">= 3.0.0" 1709 | } 1710 | }, 1711 | "node_modules/sparse-bitfield": { 1712 | "version": "3.0.3", 1713 | "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", 1714 | "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", 1715 | "dependencies": { 1716 | "memory-pager": "^1.0.2" 1717 | } 1718 | }, 1719 | "node_modules/strnum": { 1720 | "version": "1.0.5", 1721 | "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", 1722 | "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", 1723 | "optional": true, 1724 | "peer": true 1725 | }, 1726 | "node_modules/tr46": { 1727 | "version": "4.1.1", 1728 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", 1729 | "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", 1730 | "dependencies": { 1731 | "punycode": "^2.3.0" 1732 | }, 1733 | "engines": { 1734 | "node": ">=14" 1735 | } 1736 | }, 1737 | "node_modules/tslib": { 1738 | "version": "2.6.2", 1739 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", 1740 | "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", 1741 | "optional": true, 1742 | "peer": true 1743 | }, 1744 | "node_modules/undici-types": { 1745 | "version": "5.26.5", 1746 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", 1747 | "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" 1748 | }, 1749 | "node_modules/uuid": { 1750 | "version": "8.3.2", 1751 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", 1752 | "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", 1753 | "optional": true, 1754 | "peer": true, 1755 | "bin": { 1756 | "uuid": "dist/bin/uuid" 1757 | } 1758 | }, 1759 | "node_modules/vary": { 1760 | "version": "1.1.2", 1761 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 1762 | "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", 1763 | "engines": { 1764 | "node": ">= 0.8" 1765 | } 1766 | }, 1767 | "node_modules/webidl-conversions": { 1768 | "version": "7.0.0", 1769 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", 1770 | "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", 1771 | "engines": { 1772 | "node": ">=12" 1773 | } 1774 | }, 1775 | "node_modules/whatwg-url": { 1776 | "version": "13.0.0", 1777 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz", 1778 | "integrity": "sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==", 1779 | "dependencies": { 1780 | "tr46": "^4.1.1", 1781 | "webidl-conversions": "^7.0.0" 1782 | }, 1783 | "engines": { 1784 | "node": ">=16" 1785 | } 1786 | }, 1787 | "node_modules/ws": { 1788 | "version": "8.11.0", 1789 | "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", 1790 | "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", 1791 | "engines": { 1792 | "node": ">=10.0.0" 1793 | }, 1794 | "peerDependencies": { 1795 | "bufferutil": "^4.0.1", 1796 | "utf-8-validate": "^5.0.2" 1797 | }, 1798 | "peerDependenciesMeta": { 1799 | "bufferutil": { 1800 | "optional": true 1801 | }, 1802 | "utf-8-validate": { 1803 | "optional": true 1804 | } 1805 | } 1806 | }, 1807 | "node_modules/xmlhttprequest-ssl": { 1808 | "version": "2.0.0", 1809 | "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", 1810 | "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", 1811 | "engines": { 1812 | "node": ">=0.4.0" 1813 | } 1814 | } 1815 | } 1816 | } 1817 | --------------------------------------------------------------------------------