├── .dockerignore ├── .eslintignore ├── .eslintrc.js ├── .github └── workflows │ └── npm-publish.yml ├── .gitignore ├── .npmignore ├── API.md ├── CHANGELOG.md ├── Dockerfile.tests ├── README.md ├── bin └── run-tests.sh ├── docker-compose.tests.yml ├── mongo-replicator ├── Dockerfile ├── keyfile └── setup.sh ├── package-lock.json ├── package.json ├── src ├── MongoServiceError.ts ├── config │ ├── .gitignore │ └── index.ts ├── database.ts ├── dbChangePublisher.ts ├── idGenerator.ts ├── inMemoryEventBus.ts ├── index.ts ├── logger.ts ├── main.d.ts ├── outboxService.ts ├── service.spec.ts ├── service.ts └── types │ ├── IDatabase.ts │ ├── ServiceOptions.ts │ └── index.ts ├── tsconfig.json └── tslint.json /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: "@typescript-eslint/parser", 4 | plugins: [ 5 | "@typescript-eslint" 6 | ], 7 | extends: [ 8 | "airbnb-typescript/base", 9 | "plugin:@typescript-eslint/recommended" 10 | ], 11 | parserOptions: { 12 | ecmaVersion: 2018, 13 | sourceType: "module", 14 | project: "./tsconfig.json", 15 | // use tsconfig relative to eslintrc file for IDE 16 | tsconfigRootDir: __dirname 17 | }, 18 | rules: { 19 | // mongodb has _id 20 | "no-underscore-dangle": "off", 21 | "no-param-reassign": "off", 22 | "import/prefer-default-export": "warn" 23 | }, 24 | settings: { 25 | "import/resolver": { 26 | "node": { 27 | "moduleDirectory": [ 28 | "src", 29 | "node_modules" 30 | ] 31 | } 32 | } 33 | }, 34 | ignorePatterns: [ 35 | // ignore this file 36 | ".eslintrc.js", 37 | // never lint node modules 38 | "node_modules", 39 | // ignore prod_node_modules copied in Docker 40 | "prod_node_modules", 41 | // ignore output build files 42 | "dist" 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages 3 | 4 | name: Node.js Package 5 | 6 | on: 7 | release: 8 | types: [created] 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - uses: actions/setup-node@v1 16 | with: 17 | node-version: 14 18 | - run: npm ci 19 | 20 | publish-npm: 21 | needs: build 22 | runs-on: ubuntu-latest 23 | steps: 24 | - uses: actions/checkout@v2 25 | - uses: actions/setup-node@v1 26 | with: 27 | node-version: 14 28 | registry-url: https://registry.npmjs.org/ 29 | - run: npm ci 30 | - run: npm run build 31 | - run: npm publish 32 | env: 33 | NODE_AUTH_TOKEN: ${{secrets.npm_token}} 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .idea/ 3 | coverage 4 | .DS_Store 5 | dist/ -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .eslintignore 2 | .eslintrc.js 3 | src/config 4 | src/*.test.js 5 | -------------------------------------------------------------------------------- /API.md: -------------------------------------------------------------------------------- 1 | # 2.0.0 API Reference 2 | 3 | - [Node Mongo](#node-mongo) 4 | - [connect](#connect) 5 | - [Manager](#manager) 6 | - [createService](#createservice) 7 | - [setServiceMethod](#setservicemethod) 8 | - [createQueryService](#createqueryservice) 9 | - [setQueryServiceMethod](#setqueryservicemethod) 10 | - [Query Service](#query-service) 11 | - [name](#name) 12 | - [exists](#exists) 13 | - [find](#find) 14 | - [findOne](#findone) 15 | - [aggregate](#aggregate) 16 | - [count](#count) 17 | - [distinct](#distinct) 18 | - [geoHaystackSearch](#geohaystacksearch) 19 | - [indexes](#indexes) 20 | - [mapReduce](#mapreduce) 21 | - [stats](#stats) 22 | - [Service](#service) 23 | - [on](#on) 24 | - [once](#once) 25 | - [onPropertiesUpdated](#onpropertiesupdated) 26 | - [generateId](#generateid) 27 | - [create](#create) 28 | - [updateOne](#updateone) 29 | - [updateMany](#updatemany) 30 | - [remove](#remove) 31 | - [performTransaction](#performtransaction) 32 | - [atomic.bulkWrite](#atomicbulkwrite) 33 | - [atomic.createIndex](#atomiccreateindex) 34 | - [atomic.drop](#atomicdrop) 35 | - [atomic.dropIndex](#atomicdropindex) 36 | - [atomic.dropIndexes](#atomicdropindexes) 37 | - [atomic.findOneAndDelete](#atomicfindoneanddelete) 38 | - [atomic.findOneAndUpdate](#atomicfindoneandupdate) 39 | - [atomic.insert](#atomicinsert) 40 | - [atomic.remove](#atomicremove) 41 | - [atomic.update](#atomicupdate) 42 | 43 | ## Node Mongo 44 | 45 | ### connect 46 | 47 | Connect to MongoDB. 48 | 49 | #### Arguments: 50 | - `connectionString: String` - [connection string](https://docs.mongodb.com/manual/reference/connection-string/). 51 | - `connectionSettings: Object` - optional [connection settings](http://mongodb.github.io/node-mongodb-native/2.1/reference/connecting/connection-settings/). 52 | 53 | #### Returns: 54 | A [Manager](#manager) instance. 55 | 56 | #### Example: 57 | ```js 58 | const db = require('node-mongo').connect( 59 | 'mongodb://localhost:27017/home', 60 | { poolSize: 10 }, 61 | ); 62 | ``` 63 | 64 | ## Manager 65 | 66 | ### Methods: 67 | - [createQueryService](#createqueryservice) 68 | - [setQueryServiceMethod](#setqueryservicemethod) 69 | - [createService](#createservice) 70 | - [setServiceMethod](#setservicemethod) 71 | 72 | ### createQueryService 73 | 74 | Create and return [Query Service](#query-service) instance. 75 | 76 | #### Arguments: 77 | - `collectionName: String` - name of the MongoDB collection. 78 | 79 | #### Returns: 80 | A [Query Service](#query-service) instance. 81 | 82 | #### Example: 83 | 84 | ```js 85 | const usersQueryService = db.createQueryService('users'); 86 | ``` 87 | 88 | ### setQueryServiceMethod 89 | 90 | Add custom method for [Query Service](#query-service). 91 | 92 | #### Arguments: 93 | - `name: String` - name of the method, that will be used to call method. 94 | - `method: (QueryService, ...args) => any` - custom function in which we can manipulate the collection. The custom function takes the service itself as the first parameter, and the remaining parameters are the parameters that are passed when this custom function is called. 95 | 96 | #### Example: 97 | ```js 98 | const db = require('node-mongo').connect(connectionString); 99 | 100 | db.setQueryServiceMethod('findByName', (service, name, options) => { 101 | return service.findOne({ name }, { collation: 'en', ...options }); 102 | }); 103 | 104 | const userService = db.createQueryService('users'); 105 | 106 | const user = userService.findByName('Bob', { projection: { name: 1, age: 1 } }); 107 | ``` 108 | 109 | ### createService 110 | 111 | Create and return [Service](#service) instance. 112 | 113 | #### Arguments: 114 | - `collectionName: String` - the name of the collection with which the service will work. 115 | - `options: Object` - optional object with options of the service. 116 | - `addCreatedOnField: Boolean = true` - if `true`, we add the `createdOn` field for each document to be created using the [create](#create) method. 117 | - `addUpdatedOnField: Boolean = true` - if `true`, we add and update the `updatedOn` field for each document to be updated using [updateOne](#updateone) or [updateMany](#updatemany) methods. 118 | - `useStringId: Boolean = true` - if `true`, we replace `_id` ([ObjectId](https://docs.mongodb.com/manual/reference/method/ObjectId/) by default) with a string that is generated using the [generateId](#generateid) method. 119 | - `validate: (doc) => Promise<{ error, value }>` - optional function that accepts a collection document and returns the result of the validation of this document. Result should be an object with `value` and `error` fields. The error will be thrown if `error` is a truthy value. 120 | - `emitter: Emitter = new EventEmitter()` - optional instance of Emitter, which partially implements the [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter) interface ([emit](https://nodejs.org/api/events.html#events_emitter_emit_eventname_args), [on](https://nodejs.org/api/events.html#events_emitter_on_eventname_listener), [once](https://nodejs.org/api/events.html#events_emitter_once_eventname_listener) methods are enough). 121 | 122 | #### Returns: 123 | A [Service](#service) instance. 124 | 125 | #### Example: 126 | 127 | `user.schema.js` 128 | ```js 129 | const Joi = require('Joi'); 130 | 131 | const userSchema = Joi.object({ 132 | _id: Joi.string(), 133 | createdOn: Joi.date(), 134 | updatedOn: Joi.date(), 135 | name: Joi.string(), 136 | status: Joi.string().valid('active', 'inactive'), 137 | }); 138 | 139 | // you can use validate method from Joi 140 | module.validate = (obj) => userSchema.validate(obj); 141 | 142 | // or it could be your custom function 143 | module.validate = (obj) => { 144 | if (!obj.name) { 145 | return { 146 | value: obj, 147 | error: { 148 | details: [{ message: 'Name is required' }], 149 | }, 150 | }; 151 | } 152 | return { value: obj }; 153 | }; 154 | ``` 155 | 156 | `user.service.js` 157 | ```js 158 | const { validate } = require('./user.schema'); 159 | 160 | const userService = db.createService('users', { 161 | useStringId: false, 162 | validate, 163 | }); 164 | ``` 165 | 166 | ### setServiceMethod 167 | 168 | Add custom method for [Service](#service). 169 | 170 | #### Arguments: 171 | - `name: String` - name of the method, that will be used to call method. 172 | - `method: (Service, ...args) => any` - custom function in which we can manipulate the collection. The custom function takes the service itself as the first parameter, and the remaining parameters are the parameters that are passed when this custom function is called. 173 | 174 | #### Example: 175 | ```js 176 | const db = require('node-mongo').connect(connectionString); 177 | 178 | db.setServiceMethod('createByName', (service, name) => { 179 | return service.create({ name }); 180 | }); 181 | 182 | const userService = db.createService('users'); 183 | 184 | const user = userService.createByName('Bob'); 185 | ``` 186 | 187 | ## Query Service 188 | 189 | Query Service allows you to make requests to the database to get needed data, but this service not allow to modify data in the database. 190 | 191 | ### Properties 192 | - [name](#name) 193 | 194 | ### Methods 195 | - [exists](#exists) 196 | - [find](#find) 197 | - [findOne](#findone) 198 | - [aggregate](#aggregate) 199 | - [count](#count) 200 | - [distinct](#distinct) 201 | - [geoHaystackSearch](#geohaystacksearch) 202 | - [indexes](#indexes) 203 | - [mapReduce](#mapreduce) 204 | - [stats](#stats) 205 | 206 | ### name 207 | 208 | Name of the collection for which service was created. 209 | 210 | #### Example: 211 | ```js 212 | const db = require('node-mongo').connect(connectionString); 213 | 214 | const userQueryService = db.createQueryService('users'); 215 | 216 | console.log(userQueryService.name); // users 217 | ``` 218 | 219 | ### exists 220 | 221 | Gets existence of the documents matching the filter. Under the hood, [count](#count) method is used. 222 | 223 | #### Arguments: 224 | - `query: Object` - query for [count](#count) operation. 225 | - `options: Object` - optional settings for [count](#count) operation. 226 | 227 | #### Returns: 228 | Boolean value. 229 | 230 | #### Example: 231 | ```js 232 | const userService = db.createService('users'); 233 | const userExists = await userService.exists({ name: 'Bob' }); 234 | ``` 235 | 236 | ### find 237 | 238 | Gets documents matching the filter. Under the hood, monk's [find](https://automattic.github.io/monk/docs/collection/find.html) method is used. 239 | 240 | #### Arguments: 241 | - `query: Object` - object, according to which we receive documents. 242 | - `options: Object` - optional object with options for query. 243 | - `perPage: Number = 100` - optional number of returned documents. 244 | - `page: Number = 0` - optional page number with results. 245 | - `rawCursor: Boolean` - optional parameter to get the raw [mongo cursor](http://mongodb.github.io/node-mongodb-native/3.2/api/Cursor.html). You can find more usage examples in [monk docs](https://automattic.github.io/monk/docs/collection/find.html). 246 | - [...default mongo options](https://mongodb.github.io/node-mongodb-native/3.2/api/Collection.html#find) 247 | 248 | #### Returns: 249 | An object with following fields: 250 | - `results: Object[]` - array of documents. 251 | 252 | Additional fields will be added, if the `page` option exists and is greater than zero: 253 | - `pagesCount: Number` - total number of pages. 254 | - `count: Number` - total number of documents that satisfy the condition. 255 | 256 | #### Example: 257 | ```js 258 | const db = require('node-mongo').connect(connectionString); 259 | 260 | const userQueryService = db.createQueryService('users'); 261 | 262 | const { results, pagesCount, count } = await userQueryService.find( 263 | { name: 'Bob' }, 264 | { page: 1, perPage: 30 }, 265 | ); 266 | ``` 267 | 268 | ### findOne 269 | 270 | Get one document that satisfies the specified condition. Under the hood, [find](#find) method is used. 271 | 272 | #### Arguments: 273 | - `query: Object` - query for [find](#find) operation. 274 | - `options: Object` - optional settings for [find](#find) operation. 275 | 276 | #### Returns: 277 | A document or `null`. If several documents satisfy the condition, then we throw an error. 278 | 279 | #### Example: 280 | ```js 281 | const userService = db.createService('users'); 282 | try { 283 | const user = await userService.findOne({ name: 'Bob' }); 284 | } catch (error) { 285 | console.error('Several users were found'); 286 | } 287 | ``` 288 | 289 | ### aggregate 290 | 291 | Calculates aggregate values for the data in a collection. 292 | 293 | [Monk's method](https://automattic.github.io/monk/docs/collection/aggregate.html). Under the hood, native [aggregate](http://mongodb.github.io/node-mongodb-native/3.2/api/Collection.html#aggregate) method is used. 294 | 295 | ### count 296 | 297 | Gets the number of documents matching the filter. 298 | 299 | [Monk's method](https://automattic.github.io/monk/docs/collection/count.html). Under the hood, native [countDocuments](http://mongodb.github.io/node-mongodb-native/3.2/api/Collection.html#countDocuments) method is used. 300 | 301 | ### distinct 302 | 303 | The distinct command returns a list of distinct values for the given key across a collection. 304 | 305 | [Monk's method](https://automattic.github.io/monk/docs/collection/distinct.html). Under the hood, native [distinct](http://mongodb.github.io/node-mongodb-native/3.2/api/Collection.html#distinct) method is used. 306 | 307 | ### geoHaystackSearch 308 | 309 | Execute a geo search using a geo haystack index on a collection. 310 | 311 | [Monk's method](https://automattic.github.io/monk/docs/collection/geoHaystackSearch.html). Under the hood, native [geoHaystackSearch](http://mongodb.github.io/node-mongodb-native/3.2/api/Collection.html#geoHaystackSearch) method is used. 312 | 313 | ### indexes 314 | 315 | Returns an array that holds a list of documents that identify and describe the existing indexes on the collection. 316 | 317 | [Monk's method](https://automattic.github.io/monk/docs/collection/indexes.html). Under the hood, native [indexes](http://mongodb.github.io/node-mongodb-native/3.2/api/Collection.html#indexes) method is used. 318 | 319 | ### mapReduce 320 | 321 | Run Map Reduce across a collection. Be aware that the inline option for out will return an array of results not a collection. 322 | 323 | [Monk's method](https://automattic.github.io/monk/docs/collection/mapReduce.html). Under the hood, native [mapReduce](http://mongodb.github.io/node-mongodb-native/3.2/api/Collection.html#mapReduce) method is used. 324 | 325 | ### stats 326 | 327 | Get all the collection statistics. 328 | 329 | [Monk's method](https://automattic.github.io/monk/docs/collection/stats.html). Under the hood, native [stats](http://mongodb.github.io/node-mongodb-native/3.2/api/Collection.html#stats) method is used. 330 | 331 | ## Service 332 | 333 | Service extends [Query Service](#query-service), therefore instance of this service has all methods of the [Query Service](#query-service). 334 | 335 | Service emits events that you can subscribe to. **Please note that only the methods mentioned below can emit events**. 336 | 337 | #### Events: 338 | - `created` — emits when you create a document with [create](#create) method. 339 | - `updated` — emits when you create a document with [updateOne](#updateone) or [updateMany](#updatemany) methods. 340 | - `removed` — emits when you remove a document with [remove](#remove) method. 341 | 342 | Methods in the `atomic` namespace are ordinary monk's methods. They don't emit any events and don't validate data. 343 | 344 | #### Methods: 345 | - [on](#on) 346 | - [once](#once) 347 | - [onPropertiesUpdated](#onpropertiesupdated) 348 | - [generateId](#generateid) 349 | - [create](#create) 350 | - [updateOne](#updateone) 351 | - [updateMany](#updatemany) 352 | - [remove](#remove) 353 | - [performTransaction](#performtransaction) 354 | - [atomic.bulkWrite](#atomicbulkwrite) 355 | - [atomic.createIndex](#atomiccreateindex) 356 | - [atomic.drop](#atomicdrop) 357 | - [atomic.dropIndex](#atomicdropindex) 358 | - [atomic.dropIndexes](#atomicdropindexes) 359 | - [atomic.findOneAndDelete](#atomicfindoneanddelete) 360 | - [atomic.findOneAndUpdate](#atomicfindoneandupdate) 361 | - [atomic.insert](#atomicinsert) 362 | - [atomic.remove](#atomicremove) 363 | - [atomic.update](#atomicupdate) 364 | 365 | ### on 366 | 367 | Subscribes to database change events. 368 | 369 | #### Arguments: 370 | - `eventName: String` - name of the database [event](#events). 371 | - `handler: ({ doc, prevDoc }) => any` - event handler. 372 | 373 | #### Returns: 374 | A reference to the `EventEmitter`. 375 | 376 | #### Example: 377 | ```js 378 | const userService = db.createService('users'); 379 | userService.on('updated', ({ doc, prevDoc }) => { 380 | }); 381 | ``` 382 | 383 | ### once 384 | 385 | Subscribe to database change events only once. The first time evenName is triggered listener handler is removed and then invoked. 386 | 387 | #### Arguments: 388 | - `eventName: String` - name of the database [event](#events). 389 | - `handler: ({ doc, prevDoc }) => any` - event handler. 390 | 391 | #### Returns: 392 | Returns a reference to the `EventEmitter`. 393 | 394 | #### Example: 395 | ```js 396 | const userService = db.createService('users'); 397 | userService.once('updated', ({ doc, prevDoc }) => { 398 | }); 399 | ``` 400 | 401 | ### onPropertiesUpdated 402 | 403 | Deep compare doc and prevDoc from `updated` event. When something changed - executes callback. 404 | 405 | #### Arguments: 406 | - `properties: String[] | Object` - properties to compare 407 | - `handler: ({ doc, prevDoc }) => any` - event handler. 408 | 409 | #### Returns: 410 | A reference to the `EventEmitter`. 411 | 412 | #### Example: 413 | ```js 414 | const userService = db.createService('users'); 415 | 416 | // Callback executed only if user lastName or firstName are different in current or updated document 417 | userService.onPropertiesUpdated( 418 | ['user.firstName', 'user.lastName'], 419 | ({ doc, prevDoc }) => {}, 420 | ); 421 | 422 | // Callback executed only if user first name changes to `Bob` 423 | userService.onPropertiesUpdated( 424 | { 'user.firstName': 'Bob' }, 425 | ({ doc, prevDoc }) => {}, 426 | ); 427 | ``` 428 | 429 | ### generateId 430 | 431 | Get ID for mongoDB documents. 432 | 433 | #### Returns: 434 | ID string. 435 | 436 | #### Example: 437 | ```js 438 | const userService = db.createService('users'); 439 | const id = userService.generateId(); 440 | ``` 441 | 442 | ### create 443 | 444 | Inserts one object or array of the objects to the database. Validates the documents before creation if service was created with `validate` option. Adds `createdOn` field to the document. Publishes the `created` event. 445 | 446 | #### Arguments: 447 | - `documents: Object | Object[]` - object or array of objects to create. 448 | 449 | #### Returns: 450 | Object or array of created objects. 451 | 452 | #### Example: 453 | ```js 454 | const userService = db.createService('users'); 455 | const users = await userService.create([ 456 | { name: 'Bob' }, 457 | { name: 'Alice' }, 458 | ]); 459 | ``` 460 | 461 | ### updateOne 462 | 463 | Updates entity found by query in the database. Validates the document before save if service was created with `validate` option. Updates `updatedOn` field in the document. Publishes the `updated` event. **Throws out an error if more than one document is found or if no document is found**. 464 | 465 | #### Arguments: 466 | - `query: Object` - query for [findOne](#findone) operation. 467 | - `updateFn: (doc) => doc` - update function that recieves old document and should return updated one. 468 | - `options: Object` - optional options for [findOne](#findone) operation. 469 | 470 | #### Returns: 471 | Updated document. 472 | 473 | #### Example: 474 | ```js 475 | const userService = db.createService('users'); 476 | try { 477 | const updatedUser = await userService.updateOne( 478 | { _id: '1'}, 479 | (doc) => ({ ...name, name: 'Alex' }), 480 | ); 481 | } catch (error) { 482 | console.error(error.message); 483 | } 484 | ``` 485 | 486 | ### updateMany 487 | 488 | Updates entity found by query in the database. Validates the documents before save if service was created with `validate` option. Updates `updatedOn` field in the every the document. Publishes the `updated` event for every document. 489 | 490 | #### Arguments: 491 | - `query: Object` - query for [find](#find) operation. 492 | - `updateFn: (doc) => doc` - update function that recieves old document and should return updated one. 493 | - `options: Object` - optional options for [find](#find) operation. 494 | 495 | #### Returns: 496 | Array of updated documents. 497 | 498 | #### Example: 499 | ```js 500 | const userService = db.createService('users'); 501 | const updatedUsers = await userService.updateMany( 502 | { age: '27' }, 503 | (doc) => ({ ...name, alive: false }), 504 | ); 505 | ``` 506 | 507 | ### remove 508 | 509 | Removes documents found by query. Publishes the `removed` event for every document. 510 | 511 | #### Arguments: 512 | - `query: Object` - query for [find](#find) operation. 513 | - `options: Object` - optional options for [find](#find) operation. 514 | 515 | #### Returns: 516 | Array of removed documents. 517 | 518 | #### Example: 519 | ```js 520 | const userService = db.createService('users'); 521 | const removedUsers = await userService.remove({ name: 'Alex' }); 522 | ``` 523 | 524 | ### performTransaction 525 | 526 | Starts a new session, performs transaction and ends this session. 527 | 528 | #### Arguments: 529 | - `transactionFn: (Session) => Promise` - function to be performed within a transaction. **It must return Promise**. 530 | - `options: Object` - optional settings for [startSession](http://mongodb.github.io/node-mongodb-native/3.2/api/MongoClient.html#startSession) operation. 531 | 532 | #### Returns: 533 | Resulting Promise of operations run within transaction. 534 | 535 | #### Example: 536 | ```js 537 | const userService = db.createService('users'); 538 | const teamService = db.createService('teams'); 539 | 540 | await userService.performTransaction(async (session) => { 541 | await userService.create({}, { session }); 542 | await teamService.create({}, { session }); 543 | }); 544 | ``` 545 | 546 | ### atomic.bulkWrite 547 | 548 | Perform a bulkWrite operation without a fluent API. 549 | 550 | [Monk's method](https://automattic.github.io/monk/docs/collection/bulkWrite.html). Under the hood, native [bulkWrite](http://mongodb.github.io/node-mongodb-native/3.2/api/Collection.html#bulkWrite) method is used. 551 | 552 | ### atomic.createIndex 553 | 554 | Creates an index on the db and collection (will not create if already exists). 555 | 556 | [Monk's method](https://automattic.github.io/monk/docs/collection/createIndex.html). Under the hood, native [createIndex](http://mongodb.github.io/node-mongodb-native/3.2/api/Collection.html#createIndex) method is used. 557 | 558 | ### atomic.drop 559 | 560 | Drop the collection from the database, removing it permanently. New accesses will create a new collection. 561 | 562 | [Monk's method](https://automattic.github.io/monk/docs/collection/drop.html). Under the hood, native [drop](http://mongodb.github.io/node-mongodb-native/3.2/api/Collection.html#drop) method is used. 563 | 564 | ### atomic.dropIndex 565 | 566 | Drops indexes from this collection. 567 | 568 | [Monk's method](https://automattic.github.io/monk/docs/collection/dropIndex.html). Under the hood, native [dropIndex](http://mongodb.github.io/node-mongodb-native/3.2/api/Collection.html#dropIndex) method is used. 569 | 570 | ### atomic.dropIndexes 571 | 572 | Drops all indexes from this collection. 573 | 574 | [Monk's method](https://automattic.github.io/monk/docs/collection/dropIndexes.html). Under the hood, native [dropIndexes](http://mongodb.github.io/node-mongodb-native/3.2/api/Collection.html#dropIndexes) method is used. 575 | 576 | ### atomic.findOneAndDelete 577 | 578 | Find a document and delete it in one atomic operation. 579 | 580 | [Monk's method](https://automattic.github.io/monk/docs/collection/findOneAndDelete.html). Under the hood, native [findOneAndDelete](http://mongodb.github.io/node-mongodb-native/3.2/api/Collection.html#findOneAndDelete) method is used. 581 | 582 | ### atomic.findOneAndUpdate 583 | 584 | Find a document and update it in one atomic operation. 585 | 586 | [Monk's method](https://automattic.github.io/monk/docs/collection/findOneAndUpdate.html). Under the hood, native [findOneAndUpdate](http://mongodb.github.io/node-mongodb-native/3.2/api/Collection.html#findOneAndUpdate) method is used. 587 | 588 | ### atomic.insert 589 | 590 | Inserts a single document or a an array of documents into MongoDB. 591 | 592 | [Monk's method](https://automattic.github.io/monk/docs/collection/insert.html). Under the hood, native [insertOne](http://mongodb.github.io/node-mongodb-native/3.2/api/Collection.html#insertOne) and [insertMany](http://mongodb.github.io/node-mongodb-native/3.2/api/Collection.html#insertMany) methods are used. 593 | 594 | ### atomic.remove 595 | 596 | Remove documents. 597 | 598 | [Monk's method](https://automattic.github.io/monk/docs/collection/remove.html). Under the hood, native [deleteOne](http://mongodb.github.io/node-mongodb-native/3.2/api/Collection.html#deleteOne) and [deleteMany](http://mongodb.github.io/node-mongodb-native/3.2/api/Collection.html#deleteMany) methods are used. 599 | 600 | ### atomic.update 601 | 602 | Modifies an existing document or documents in a collection. 603 | 604 | [Monk's method](https://automattic.github.io/monk/docs/collection/update.html). Under the hood, native [updateOne](http://mongodb.github.io/node-mongodb-native/3.2/api/Collection.html#updateOne) and [updateMany](http://mongodb.github.io/node-mongodb-native/3.2/api/Collection.html#updateMany) methods are used. 605 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## v3.0.0 (2022-03-28) 2 | 3 | The release includes a lot of changes to make sure that the package is compatible with the latest MongoDB version. 4 | Most notable changes: 5 | - Rewritten in typescript 6 | - Removed [monk](https://github.com/Automattic/monk) dependency. 7 | - Added [mongodb native Node.JS sdk](https://www.mongodb.com/docs/drivers/node/current/) as dependency. 8 | - Added support for transactional events using [transactional outbox pattern](https://microservices.io/patterns/data/transactional-outbox.html) 9 | - Introduced shared in-memory events bus. It should be used to listen for CUD updates. 10 | 11 | ## v2.1.0 (2020-10-15) 12 | 13 | ### Features 14 | 15 | #### Manager 16 | 17 | [createService](API.md#createservice) 18 | - Add [emitter](API.md#createservice) option. 19 | 20 | ## v2.0.0 (2020-09-29) 21 | 22 | * Update dependencies. 23 | 24 | ### Breaking Changes 25 | 26 | #### [Manager](API.md#manager) 27 | 28 | [createQueryService](API.md#createqueryservice) 29 | - Rename `validateSchema` option to `validate`. 30 | - Change `addCreatedOnField` default to `true`. 31 | - Change `addUpdatedOnField` default to `true`. 32 | 33 | [createService](API.md#createservice) 34 | - Rename `validateSchema` option to `validate`. 35 | - Change `addCreatedOnField` default to `true`. 36 | - Change `addUpdatedOnField` default to `true`. 37 | 38 | #### [Query Service](API.md#query-service) 39 | - Remove `generateId` method. 40 | - Remove `expectDocument` method. 41 | 42 | #### [Service](API.md#service) 43 | - Remove `update` method. Use [updateOne](API.md#updateone) or [updateMany](API.md#updatemany). 44 | - Remove `ensureIndex`. Use [atomic.createIndex](API.md#atomiccreateindex). 45 | - Remove `createOrUpdate`. Use [create](API.md#create) or [updateOne](API.md#updateone) or [updateMany](API.md#updatemany). 46 | - Remove `findOneAndUpdate`. Use [findOne](API.md#findone) and [updateOne](API.md#updateone). 47 | 48 | ### Features 49 | 50 | #### Manager 51 | 52 | [createQueryService](API.md#createqueryservice) 53 | - Add `useStringId` option. 54 | 55 | [createService](API.md#createservice) 56 | - Add `useStringId` option. 57 | 58 | #### [Query Service](API.md#query-service) 59 | - Add more monk's methods. [See full list](API.md#query-service) 60 | 61 | #### [Service](API.md#service) 62 | - Add [generateId](API.md#generateid) method. 63 | - Add [updateOne](API.md#updateone) method. 64 | - Add [updateMany](API.md#updatemany) method. 65 | - Add [performTransaction](API.md#performtransaction) method. 66 | - Add more monk's methods in `atomic` namespace. [See full list](API.md#service) 67 | 68 | 69 | ## v1.1.0 (2019-06-25) 70 | 71 | * Update dependencies. 72 | * Fix required version of the Node.js. 73 | 74 | ### Breaking Changes 75 | 76 | * Now `update` function will work via [set](https://docs.mongodb.com/manual/reference/operator/update/set/) operator. It means the new doc will be the result of merge of the old doc and the provided one. 77 | 78 | ## v1.0.0 (2018-05-23) 79 | 80 | * Update dependencies. 81 | * Add tests. 82 | * Fix required version of the Node.js. 83 | 84 | ### Breaking Changes 85 | 86 | * Now, by default, we do not add the fields `createdOn` and` updatedOn` automatically to the model. If you want to save the current behavior, add the appropriate `addCreatedOnField` and` addUpdatedOnField` options to the service definitions. 87 | 88 | ## v0.3.1 (2017-12-16) 89 | 90 | * Stop using deprecated method `ensureIndex` of the `monk`. 91 | 92 | ## v0.3.0 (2017-10-24) 93 | 94 | * Add ability to create custom methods for service and query service. 95 | * Add tests. 96 | 97 | ## v0.2.0 (2017-10-12) 98 | 99 | * Add support of the [joi](https://github.com/hapijs/joi) for validating data schema. 100 | * Add tests for validating of the schema. 101 | -------------------------------------------------------------------------------- /Dockerfile.tests: -------------------------------------------------------------------------------- 1 | FROM node:16.13.1-alpine3.13 2 | 3 | WORKDIR /app 4 | COPY ["package*.json", "/app/"] 5 | RUN npm ci --quiet 6 | 7 | COPY . . 8 | 9 | RUN npm run build 10 | 11 | CMD npm run all 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This repository is no longer maintained. Check [Ship](https://github.com/paralect/ship). 2 | 3 | # Node Mongo 4 | 5 | [![npm version](https://badge.fury.io/js/%40paralect%2Fnode-mongo.svg)](https://badge.fury.io/js/%40paralect%2Fnode-mongo) 6 | 7 | Node Mongo is reactive extension to MongoDB API. 8 | 9 | ## Features 10 | 11 | * ️️**Reactive**. Fires events as document stored, updated or deleted from database 12 | * **Paging**. Implements high level paging API 13 | * **Schema validation**. Validates your data before save 14 | 15 | ## Installation 16 | 17 | ``` 18 | npm i @paralect/node-mongo 19 | ``` 20 | 21 | ## Documentation 22 | 23 | ### Migrate from v2 to v3 24 | 25 | 1. Methods `updateOne` and `updateMany` were removed. You should use `update` to perform update of single document, matched by a query. There is no replacement for `updateMany`, normally you should just perform multiple individual updates. 26 | 2. `service.count()` renamed into `service.countDocuments` to match MongoDB driver. 27 | 3. Use `service.atomic.updateMany` instead `service.atomic.update` to match MongoDB. 28 | 4. `service.aggregate()` now returns cursor instead of list of documents. You can add `toArray()` 29 | 5. Service accepts `schema` object instead of `validateSchema` method. 30 | 31 | ### Connect 32 | 33 | Usually, you need to define a file called `database` is does two things: 34 | 1. Creates database instance and connects to the database 35 | 2. Exposed factory method `createService` to create different services to work with MongoDB. 36 | 37 | ```typescript 38 | import config from 'config'; 39 | import { Database, Service, ServiceOptions } from '@paralect/node-mongo'; 40 | 41 | const connectionString = 'mongodb://localhost:27017'; 42 | const dbName = 'home-db'; 43 | const database = new Database(connectionString, dbName); 44 | database.connect(); 45 | 46 | // Extended service can be used here. 47 | function createService(collectionName: string, options: ServiceOptions = {}) { 48 | return new Service(collectionName, database, options); 49 | } 50 | 51 | export default { 52 | database, 53 | createService, 54 | }; 55 | ``` 56 | 57 | See [how to add additional functionality to base serivce](#extend) 58 | 59 | 60 | ### Schema validation 61 | ```javascript 62 | const Joi = require('Joi'); 63 | 64 | const userSchema = Joi.object({ 65 | _id: Joi.string(), 66 | createdOn: Joi.date(), 67 | updatedOn: Joi.date(), 68 | deletedOn: Joi.date(), 69 | name: Joi.string(), 70 | status: Joi.string().valid('active', 'inactive'), 71 | }); 72 | 73 | // Pass schema object to enable schema validation 74 | const userService = db.createService('users', { schema: userSchema }); 75 | ``` 76 | 77 | ### Extend 78 | 79 | The whole idea is to import service and extend it with custom methods: 80 | 81 | ```typescript 82 | import { Service } from '@paralect/node-mongo'; 83 | 84 | class CustomService extends Service { 85 | createOrUpdate = async (query: any, updateCallback: (item?: T) => Partial) => { 86 | const docExists = await this.exists(query); 87 | if (!docExists) { 88 | const newDoc = updateCallback(); 89 | return this.create(newDoc); 90 | } 91 | 92 | return this.update(query, doc => { 93 | return updateCallback(doc); 94 | }); 95 | }; 96 | } 97 | 98 | export default CustomService; 99 | ``` 100 | 101 | ### Query data 102 | 103 | ```typescript 104 | // find one document 105 | const user = await userService.findOne({ name: 'Bob' }); 106 | 107 | // find many documents with pagination 108 | const {results, pagesCount, count } = await userService.find( 109 | { name: 'Bob' }, 110 | { page: 1, perPage: 30 }, 111 | ); 112 | ``` 113 | 114 | ### Create or update data (and publish CUD events) 115 | 116 | The key difference of the `@paralect/node-mongo` sdk is that every create, update or remove operation peforms 117 | an udpate and also publeshes CUD event. Events are used to easily update denormalized data and also to implement 118 | complex business logic without tight coupling of different entities. 119 | 120 | - Reactive updates (every update publishes event) 121 | - [create](#create) — create one or many documents, publishes `document.created` event 122 | - [update](#update) — update one document, publishes `document.updated` event 123 | - [remove](#remove) — remove document, publishes `document.removed` 124 | - [removeSoft](#removeSoft) — set `deleteOn` field and publish `document.removed` event 125 | 126 | Atomic udpates **do not publish events** and usually used to update denormalized data. Most the time you should be using reactive updates. 127 | 128 | - Atomic updates (events are not published) 129 | - `atomic.deleteMany` 130 | - `atomic.insertMany` 131 | - `atomic.updateMany` 132 | - `findOneAndUpdate` 133 | 134 | [API Reference V2](API.md). 135 | 136 | #### create 137 | 138 | ```typescript 139 | const users = await userService.create([ 140 | { name: 'Alex' }, 141 | { name: 'Bob' }, 142 | ]); 143 | ``` 144 | 145 | #### update 146 | 147 | Update using callback function: 148 | ```typescript 149 | const updatedUser = await userService.update({ _id: '1' }, (doc) => { 150 | doc.name = 'Alex'; 151 | }); 152 | ``` 153 | 154 | Update by returning fields you need to update: 155 | ```typescript 156 | const updatedUser = await userService.update({ _id: '1' }, () => ({ name: 'Alex' })); 157 | ``` 158 | 159 | ### remove 160 | ```typescript 161 | const removedUser = await userService.remove({ _id: '1' }); 162 | ``` 163 | 164 | ### removeSoft 165 | ```typescript 166 | const removedUser = await userService.removeSoft({ _id: '1' }); 167 | ``` 168 | 169 | ### Event handlers 170 | 171 | SDK support two kind of events: 172 | - `in memory events` (published by default), can be lost on service failure, work out of the box. 173 | - `transactional events` guarantee that every database write will also produce an event. Transactional events can be enabled by setting `{ outbox: true }` when creating service. Transactional events require additonal infrastructure components. 174 | 175 | To subscribe to the in memory events you can just do following: 176 | 177 | ```typescript 178 | import { inMemoryEventBus, InMemoryEvent } from '@paralect/node-mongo'; 179 | 180 | type UserCreatedType = InMemoryEvent; 181 | type UserUpdatedType = InMemoryEvent; 182 | type UserRemovedType = InMemoryEvent; 183 | 184 | inMemoryEventBus.on('user.created', (doc: UserCreatedType) => {}); 185 | 186 | inMemoryEventBus.on('user.updated', (doc: UserUpdatedType) => {}); 187 | 188 | inMemoryEventBus.on('user.removed', (doc: UserRemovedType) => {}); 189 | ``` 190 | 191 | ## Change Log 192 | 193 | This project adheres to [Semantic Versioning](http://semver.org/). 194 | 195 | Every release is documented on the Github [Releases](https://github.com/paralect/node-mongo/releases) page. 196 | -------------------------------------------------------------------------------- /bin/run-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | docker-compose -f docker-compose.tests.yml up --build 3 | -------------------------------------------------------------------------------- /docker-compose.tests.yml: -------------------------------------------------------------------------------- 1 | version: '3.6' 2 | services: 3 | mongo: 4 | container_name: mongo 5 | image: mongo:4.4 6 | command: --replSet rs --bind_ip_all --keyFile /mongo-replicator/keyfile 7 | environment: 8 | - MONGO_INITDB_ROOT_USERNAME=root 9 | - MONGO_INITDB_ROOT_PASSWORD=root 10 | networks: 11 | - node-mongo-tests 12 | ports: 13 | - 27017:27017 14 | volumes: 15 | - /var/run/docker.sock:/var/run/docker.sock 16 | - ./mongo-replicator:/mongo-replicator 17 | - type: volume 18 | source: mongodb 19 | target: /data/db 20 | - type: volume 21 | source: mongodb-cfg 22 | target: /data/configdb 23 | mongo-replicator: 24 | container_name: mongo-replicator 25 | build: ./mongo-replicator 26 | environment: 27 | - HOST=mongo 28 | - PORT=27017 29 | - USERNAME=root 30 | - PASSWORD=root 31 | - REPLICA_SET_NAME=rs 32 | networks: 33 | - node-mongo-tests 34 | depends_on: 35 | - mongo 36 | tests: 37 | container_name: tests 38 | build: 39 | context: . 40 | dockerfile: ./Dockerfile.tests 41 | networks: 42 | - node-mongo-tests 43 | depends_on: 44 | - mongo-replicator 45 | 46 | networks: 47 | node-mongo-tests: 48 | name: node-mongo-tests-network 49 | 50 | volumes: 51 | mongodb: 52 | mongodb-cfg: 53 | -------------------------------------------------------------------------------- /mongo-replicator/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mongo:4.4 2 | ADD ./setup.sh /setup.sh 3 | CMD ["/setup.sh"] 4 | -------------------------------------------------------------------------------- /mongo-replicator/keyfile: -------------------------------------------------------------------------------- 1 | eATfKkkUG4tSqfr3dJiRruI6QBYBzxhBBMVu4f8IoMQ8Hmf9GC9/6/TRvdk7mt4p 2 | z8MccxDRBqGgpAoMOyosUGZxprnJjzNSRKgofRgT9YE4mopMGB4nsRMRS+PR5o6v 3 | YlUSKrK/rrsKF+ogBKn350S1328aBaucjEuajsL5lMlt+wuqRvRVo4SzPizLhXuv 4 | W2Y31+7Yex4V8AmEfs5h0wPDEzsdAdifgW4Io6A6pX17tJkVRW+DkLYNEu9laquF 5 | L6+S5vFYO7Rv85GhBeSFGSgj0Sqchj3UhkPoy7R17nKfAmeF0m6H1WEPw0iFk/bk 6 | E2y+9r/dpVuRX/NcyosXGa6RMs622hVU+Bi9Kme52mRm1LQJAPFhgZYMAYNUt3S8 7 | ygT2iLJSyWx68MFtYGoKHcH0ISWo/2ouzZa7tt2+BzQXylfWhQrE1xJl+XcFfeei 8 | qEZcC9bZ/za3eoWODPjHRCfp+uL4OEtztZ++S9n2eLmP2VyviUotvL0vnBR2GNEW 9 | 5DIqCa/UZnuGaFbHqbmfoRqrl0E6cH3AvrEXWWkC42nshCv+rHnFu15v2bG5z8nF 10 | 0UMUyXn7lcLqLYYwwrv3RQrfdNLImBu+JNBH/BRS7ZI6IsrU90rn+t+9xq1N/yvs 11 | dS2ujZRskroTX6hDh9PeCBPoOcEjDldGEt0aWkzQWFSgk+BpOv6UO5CzVUacIVa+ 12 | xmjm16ksC0WsoPzxiOiM/4ChbcNCOOdErqQsnptAS5/kzb2r/CZTyDgTd1REiQR3 13 | PbiLWWx5C+p+tzdeI4ueyDjuriaiKZqsS+Hv7qmy1UAra8ZzOEzdYYv+pueLNZrf 14 | Q7+DU4uNEBbT3EOR6+T72+NVnid+Zq1z3Apo83zQEcMHAUhUQIKNYv8mUV89cxSE 15 | 3DUEVaovguI42qpmeYeBbURxyhrBsI2bnpw42rfGIClZRc7dK67SP9XYDtrz/990 16 | LG6V0OVD7Mt6h35pkdvSLNJhFJmHYFVLqluHJdlb9ui3mHv5 17 | -------------------------------------------------------------------------------- /mongo-replicator/setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | command="rs.initiate({ _id: '$REPLICA_SET_NAME', members: [{ _id: 0, host: '$HOST:$PORT' }]})" 5 | 6 | for i in $(seq 1 20); do 7 | if [[ $i -eq 20 ]]; then 8 | echo "Replication failed" 9 | exit 1 10 | fi 11 | 12 | echo "Replication attempt" 13 | 14 | if [[ $USERNAME ]]; then 15 | mongo $HOST:$PORT --authenticationDatabase "admin" -u $USERNAME -p $PASSWORD --quiet --eval "$command" && break 16 | else 17 | mongo $HOST:$PORT --quiet --eval "$command" && break 18 | fi 19 | 20 | sleep 2 21 | done 22 | 23 | echo "Replication done" 24 | 25 | [[ $IMMORTAL ]] && while true; do sleep 1; done 26 | 27 | exit 0 28 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@paralect/node-mongo", 3 | "version": "3.0.0-beta1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@babel/code-frame": { 8 | "version": "7.12.11", 9 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", 10 | "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", 11 | "dev": true, 12 | "requires": { 13 | "@babel/highlight": "^7.10.4" 14 | } 15 | }, 16 | "@babel/helper-validator-identifier": { 17 | "version": "7.16.7", 18 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", 19 | "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", 20 | "dev": true 21 | }, 22 | "@babel/highlight": { 23 | "version": "7.16.10", 24 | "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", 25 | "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", 26 | "dev": true, 27 | "requires": { 28 | "@babel/helper-validator-identifier": "^7.16.7", 29 | "chalk": "^2.0.0", 30 | "js-tokens": "^4.0.0" 31 | }, 32 | "dependencies": { 33 | "ansi-styles": { 34 | "version": "3.2.1", 35 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 36 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 37 | "dev": true, 38 | "requires": { 39 | "color-convert": "^1.9.0" 40 | } 41 | }, 42 | "chalk": { 43 | "version": "2.4.2", 44 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 45 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 46 | "dev": true, 47 | "requires": { 48 | "ansi-styles": "^3.2.1", 49 | "escape-string-regexp": "^1.0.5", 50 | "supports-color": "^5.3.0" 51 | } 52 | }, 53 | "color-convert": { 54 | "version": "1.9.3", 55 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 56 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 57 | "dev": true, 58 | "requires": { 59 | "color-name": "1.1.3" 60 | } 61 | }, 62 | "color-name": { 63 | "version": "1.1.3", 64 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 65 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 66 | "dev": true 67 | }, 68 | "escape-string-regexp": { 69 | "version": "1.0.5", 70 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 71 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 72 | "dev": true 73 | }, 74 | "has-flag": { 75 | "version": "3.0.0", 76 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 77 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 78 | "dev": true 79 | }, 80 | "supports-color": { 81 | "version": "5.5.0", 82 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 83 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 84 | "dev": true, 85 | "requires": { 86 | "has-flag": "^3.0.0" 87 | } 88 | } 89 | } 90 | }, 91 | "@eslint/eslintrc": { 92 | "version": "0.4.3", 93 | "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", 94 | "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", 95 | "dev": true, 96 | "requires": { 97 | "ajv": "^6.12.4", 98 | "debug": "^4.1.1", 99 | "espree": "^7.3.0", 100 | "globals": "^13.9.0", 101 | "ignore": "^4.0.6", 102 | "import-fresh": "^3.2.1", 103 | "js-yaml": "^3.13.1", 104 | "minimatch": "^3.0.4", 105 | "strip-json-comments": "^3.1.1" 106 | } 107 | }, 108 | "@hapi/hoek": { 109 | "version": "9.2.1", 110 | "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.1.tgz", 111 | "integrity": "sha512-gfta+H8aziZsm8pZa0vj04KO6biEiisppNgA1kbJvFrrWu9Vm7eaUEy76DIxsuTaWvti5fkJVhllWc6ZTE+Mdw==" 112 | }, 113 | "@hapi/topo": { 114 | "version": "5.1.0", 115 | "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", 116 | "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", 117 | "requires": { 118 | "@hapi/hoek": "^9.0.0" 119 | } 120 | }, 121 | "@nodelib/fs.scandir": { 122 | "version": "2.1.5", 123 | "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", 124 | "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", 125 | "dev": true, 126 | "requires": { 127 | "@nodelib/fs.stat": "2.0.5", 128 | "run-parallel": "^1.1.9" 129 | } 130 | }, 131 | "@nodelib/fs.stat": { 132 | "version": "2.0.5", 133 | "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", 134 | "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", 135 | "dev": true 136 | }, 137 | "@nodelib/fs.walk": { 138 | "version": "1.2.8", 139 | "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", 140 | "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", 141 | "dev": true, 142 | "requires": { 143 | "@nodelib/fs.scandir": "2.1.5", 144 | "fastq": "^1.6.0" 145 | } 146 | }, 147 | "@sideway/address": { 148 | "version": "4.1.3", 149 | "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.3.tgz", 150 | "integrity": "sha512-8ncEUtmnTsMmL7z1YPB47kPUq7LpKWJNFPsRzHiIajGC5uXlWGn+AmkYPcHNl8S4tcEGx+cnORnNYaw2wvL+LQ==", 151 | "requires": { 152 | "@hapi/hoek": "^9.0.0" 153 | } 154 | }, 155 | "@sideway/formula": { 156 | "version": "3.0.0", 157 | "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz", 158 | "integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==" 159 | }, 160 | "@sideway/pinpoint": { 161 | "version": "2.0.0", 162 | "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", 163 | "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" 164 | }, 165 | "@types/chai": { 166 | "version": "4.2.16", 167 | "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.16.tgz", 168 | "integrity": "sha512-vI5iOAsez9+roLS3M3+Xx7w+WRuDtSmF8bQkrbcIJ2sC1PcDgVoA0WGpa+bIrJ+y8zqY2oi//fUctkxtIcXJCw==", 169 | "dev": true 170 | }, 171 | "@types/chai-spies": { 172 | "version": "1.0.3", 173 | "resolved": "https://registry.npmjs.org/@types/chai-spies/-/chai-spies-1.0.3.tgz", 174 | "integrity": "sha512-RBZjhVuK7vrg4rWMt04UF5zHYwfHnpk5mIWu3nQvU3AKGDixXzSjZ6v0zke6pBcaJqMv3IBZ5ibLWPMRDL0sLw==", 175 | "dev": true, 176 | "requires": { 177 | "@types/chai": "*" 178 | } 179 | }, 180 | "@types/deep-diff": { 181 | "version": "1.0.0", 182 | "resolved": "https://registry.npmjs.org/@types/deep-diff/-/deep-diff-1.0.0.tgz", 183 | "integrity": "sha512-ENsJcujGbCU/oXhDfQ12mSo/mCBWodT2tpARZKmatoSrf8+cGRCPi0KVj3I0FORhYZfLXkewXu7AoIWqiBLkNw==", 184 | "dev": true 185 | }, 186 | "@types/hapi__joi": { 187 | "version": "17.1.6", 188 | "resolved": "https://registry.npmjs.org/@types/hapi__joi/-/hapi__joi-17.1.6.tgz", 189 | "integrity": "sha512-y3A1MzNC0FmzD5+ys59RziE1WqKrL13nxtJgrSzjoO7boue5B7zZD2nZLPwrSuUviFjpKFQtgHYSvhDGfIE4jA==", 190 | "dev": true 191 | }, 192 | "@types/json-schema": { 193 | "version": "7.0.9", 194 | "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", 195 | "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", 196 | "dev": true 197 | }, 198 | "@types/json5": { 199 | "version": "0.0.29", 200 | "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", 201 | "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", 202 | "dev": true 203 | }, 204 | "@types/lodash": { 205 | "version": "4.14.168", 206 | "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.168.tgz", 207 | "integrity": "sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q==", 208 | "dev": true 209 | }, 210 | "@types/mocha": { 211 | "version": "8.2.2", 212 | "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.2.tgz", 213 | "integrity": "sha512-Lwh0lzzqT5Pqh6z61P3c3P5nm6fzQK/MMHl9UKeneAeInVflBSz1O2EkX6gM6xfJd7FBXBY5purtLx7fUiZ7Hw==", 214 | "dev": true 215 | }, 216 | "@types/node": { 217 | "version": "14.14.35", 218 | "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.35.tgz", 219 | "integrity": "sha512-Lt+wj8NVPx0zUmUwumiVXapmaLUcAk3yPuHCFVXras9k5VT9TdhJqKqGVUQCD60OTMCl0qxJ57OiTL0Mic3Iag==" 220 | }, 221 | "@types/webidl-conversions": { 222 | "version": "6.1.1", 223 | "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-6.1.1.tgz", 224 | "integrity": "sha512-XAahCdThVuCFDQLT7R7Pk/vqeObFNL3YqRyFZg+AqAP/W1/w3xHaIxuW7WszQqTbIBOPRcItYJIou3i/mppu3Q==" 225 | }, 226 | "@types/whatwg-url": { 227 | "version": "8.2.1", 228 | "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.1.tgz", 229 | "integrity": "sha512-2YubE1sjj5ifxievI5Ge1sckb9k/Er66HyR2c+3+I6VDUUg1TLPdYYTEbQ+DjRkS4nTxMJhgWfSfMRD2sl2EYQ==", 230 | "requires": { 231 | "@types/node": "*", 232 | "@types/webidl-conversions": "*" 233 | } 234 | }, 235 | "@typescript-eslint/eslint-plugin": { 236 | "version": "4.22.1", 237 | "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.22.1.tgz", 238 | "integrity": "sha512-kVTAghWDDhsvQ602tHBc6WmQkdaYbkcTwZu+7l24jtJiYvm9l+/y/b2BZANEezxPDiX5MK2ZecE+9BFi/YJryw==", 239 | "dev": true, 240 | "requires": { 241 | "@typescript-eslint/experimental-utils": "4.22.1", 242 | "@typescript-eslint/scope-manager": "4.22.1", 243 | "debug": "^4.1.1", 244 | "functional-red-black-tree": "^1.0.1", 245 | "lodash": "^4.17.15", 246 | "regexpp": "^3.0.0", 247 | "semver": "^7.3.2", 248 | "tsutils": "^3.17.1" 249 | }, 250 | "dependencies": { 251 | "@typescript-eslint/experimental-utils": { 252 | "version": "4.22.1", 253 | "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.22.1.tgz", 254 | "integrity": "sha512-svYlHecSMCQGDO2qN1v477ax/IDQwWhc7PRBiwAdAMJE7GXk5stF4Z9R/8wbRkuX/5e9dHqbIWxjeOjckK3wLQ==", 255 | "dev": true, 256 | "requires": { 257 | "@types/json-schema": "^7.0.3", 258 | "@typescript-eslint/scope-manager": "4.22.1", 259 | "@typescript-eslint/types": "4.22.1", 260 | "@typescript-eslint/typescript-estree": "4.22.1", 261 | "eslint-scope": "^5.0.0", 262 | "eslint-utils": "^2.0.0" 263 | } 264 | }, 265 | "@typescript-eslint/types": { 266 | "version": "4.22.1", 267 | "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.22.1.tgz", 268 | "integrity": "sha512-2HTkbkdAeI3OOcWbqA8hWf/7z9c6gkmnWNGz0dKSLYLWywUlkOAQ2XcjhlKLj5xBFDf8FgAOF5aQbnLRvgNbCw==", 269 | "dev": true 270 | }, 271 | "@typescript-eslint/typescript-estree": { 272 | "version": "4.22.1", 273 | "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.22.1.tgz", 274 | "integrity": "sha512-p3We0pAPacT+onSGM+sPR+M9CblVqdA9F1JEdIqRVlxK5Qth4ochXQgIyb9daBomyQKAXbygxp1aXQRV0GC79A==", 275 | "dev": true, 276 | "requires": { 277 | "@typescript-eslint/types": "4.22.1", 278 | "@typescript-eslint/visitor-keys": "4.22.1", 279 | "debug": "^4.1.1", 280 | "globby": "^11.0.1", 281 | "is-glob": "^4.0.1", 282 | "semver": "^7.3.2", 283 | "tsutils": "^3.17.1" 284 | } 285 | }, 286 | "tsutils": { 287 | "version": "3.21.0", 288 | "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", 289 | "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", 290 | "dev": true, 291 | "requires": { 292 | "tslib": "^1.8.1" 293 | } 294 | } 295 | } 296 | }, 297 | "@typescript-eslint/parser": { 298 | "version": "4.18.0", 299 | "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.18.0.tgz", 300 | "integrity": "sha512-W3z5S0ZbecwX3PhJEAnq4mnjK5JJXvXUDBYIYGoweCyWyuvAKfGHvzmpUzgB5L4cRBb+cTu9U/ro66dx7dIimA==", 301 | "dev": true, 302 | "requires": { 303 | "@typescript-eslint/scope-manager": "4.18.0", 304 | "@typescript-eslint/types": "4.18.0", 305 | "@typescript-eslint/typescript-estree": "4.18.0", 306 | "debug": "^4.1.1" 307 | }, 308 | "dependencies": { 309 | "@typescript-eslint/scope-manager": { 310 | "version": "4.18.0", 311 | "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.18.0.tgz", 312 | "integrity": "sha512-olX4yN6rvHR2eyFOcb6E4vmhDPsfdMyfQ3qR+oQNkAv8emKKlfxTWUXU5Mqxs2Fwe3Pf1BoPvrwZtwngxDzYzQ==", 313 | "dev": true, 314 | "requires": { 315 | "@typescript-eslint/types": "4.18.0", 316 | "@typescript-eslint/visitor-keys": "4.18.0" 317 | } 318 | }, 319 | "@typescript-eslint/visitor-keys": { 320 | "version": "4.18.0", 321 | "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.18.0.tgz", 322 | "integrity": "sha512-Q9t90JCvfYaN0OfFUgaLqByOfz8yPeTAdotn/XYNm5q9eHax90gzdb+RJ6E9T5s97Kv/UHWKERTmqA0jTKAEHw==", 323 | "dev": true, 324 | "requires": { 325 | "@typescript-eslint/types": "4.18.0", 326 | "eslint-visitor-keys": "^2.0.0" 327 | } 328 | } 329 | } 330 | }, 331 | "@typescript-eslint/scope-manager": { 332 | "version": "4.22.1", 333 | "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.22.1.tgz", 334 | "integrity": "sha512-d5bAiPBiessSmNi8Amq/RuLslvcumxLmyhf1/Xa9IuaoFJ0YtshlJKxhlbY7l2JdEk3wS0EnmnfeJWSvADOe0g==", 335 | "dev": true, 336 | "requires": { 337 | "@typescript-eslint/types": "4.22.1", 338 | "@typescript-eslint/visitor-keys": "4.22.1" 339 | }, 340 | "dependencies": { 341 | "@typescript-eslint/types": { 342 | "version": "4.22.1", 343 | "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.22.1.tgz", 344 | "integrity": "sha512-2HTkbkdAeI3OOcWbqA8hWf/7z9c6gkmnWNGz0dKSLYLWywUlkOAQ2XcjhlKLj5xBFDf8FgAOF5aQbnLRvgNbCw==", 345 | "dev": true 346 | } 347 | } 348 | }, 349 | "@typescript-eslint/types": { 350 | "version": "4.18.0", 351 | "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.18.0.tgz", 352 | "integrity": "sha512-/BRociARpj5E+9yQ7cwCF/SNOWwXJ3qhjurMuK2hIFUbr9vTuDeu476Zpu+ptxY2kSxUHDGLLKy+qGq2sOg37A==", 353 | "dev": true 354 | }, 355 | "@typescript-eslint/typescript-estree": { 356 | "version": "4.18.0", 357 | "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.18.0.tgz", 358 | "integrity": "sha512-wt4xvF6vvJI7epz+rEqxmoNQ4ZADArGQO9gDU+cM0U5fdVv7N+IAuVoVAoZSOZxzGHBfvE3XQMLdy+scsqFfeg==", 359 | "dev": true, 360 | "requires": { 361 | "@typescript-eslint/types": "4.18.0", 362 | "@typescript-eslint/visitor-keys": "4.18.0", 363 | "debug": "^4.1.1", 364 | "globby": "^11.0.1", 365 | "is-glob": "^4.0.1", 366 | "semver": "^7.3.2", 367 | "tsutils": "^3.17.1" 368 | }, 369 | "dependencies": { 370 | "@typescript-eslint/visitor-keys": { 371 | "version": "4.18.0", 372 | "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.18.0.tgz", 373 | "integrity": "sha512-Q9t90JCvfYaN0OfFUgaLqByOfz8yPeTAdotn/XYNm5q9eHax90gzdb+RJ6E9T5s97Kv/UHWKERTmqA0jTKAEHw==", 374 | "dev": true, 375 | "requires": { 376 | "@typescript-eslint/types": "4.18.0", 377 | "eslint-visitor-keys": "^2.0.0" 378 | } 379 | }, 380 | "tsutils": { 381 | "version": "3.21.0", 382 | "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", 383 | "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", 384 | "dev": true, 385 | "requires": { 386 | "tslib": "^1.8.1" 387 | } 388 | } 389 | } 390 | }, 391 | "@typescript-eslint/visitor-keys": { 392 | "version": "4.22.1", 393 | "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.22.1.tgz", 394 | "integrity": "sha512-WPkOrIRm+WCLZxXQHCi+WG8T2MMTUFR70rWjdWYddLT7cEfb2P4a3O/J2U1FBVsSFTocXLCoXWY6MZGejeStvQ==", 395 | "dev": true, 396 | "requires": { 397 | "@typescript-eslint/types": "4.22.1", 398 | "eslint-visitor-keys": "^2.0.0" 399 | }, 400 | "dependencies": { 401 | "@typescript-eslint/types": { 402 | "version": "4.22.1", 403 | "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.22.1.tgz", 404 | "integrity": "sha512-2HTkbkdAeI3OOcWbqA8hWf/7z9c6gkmnWNGz0dKSLYLWywUlkOAQ2XcjhlKLj5xBFDf8FgAOF5aQbnLRvgNbCw==", 405 | "dev": true 406 | } 407 | } 408 | }, 409 | "@ungap/promise-all-settled": { 410 | "version": "1.1.2", 411 | "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", 412 | "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", 413 | "dev": true 414 | }, 415 | "acorn": { 416 | "version": "7.4.1", 417 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", 418 | "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", 419 | "dev": true 420 | }, 421 | "acorn-jsx": { 422 | "version": "5.3.2", 423 | "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", 424 | "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", 425 | "dev": true 426 | }, 427 | "ajv": { 428 | "version": "6.12.6", 429 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", 430 | "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", 431 | "dev": true, 432 | "requires": { 433 | "fast-deep-equal": "^3.1.1", 434 | "fast-json-stable-stringify": "^2.0.0", 435 | "json-schema-traverse": "^0.4.1", 436 | "uri-js": "^4.2.2" 437 | } 438 | }, 439 | "ansi-colors": { 440 | "version": "4.1.1", 441 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", 442 | "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", 443 | "dev": true 444 | }, 445 | "ansi-regex": { 446 | "version": "5.0.1", 447 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 448 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 449 | "dev": true 450 | }, 451 | "ansi-styles": { 452 | "version": "4.3.0", 453 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 454 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 455 | "dev": true, 456 | "requires": { 457 | "color-convert": "^2.0.1" 458 | } 459 | }, 460 | "anymatch": { 461 | "version": "3.1.2", 462 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", 463 | "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", 464 | "dev": true, 465 | "requires": { 466 | "normalize-path": "^3.0.0", 467 | "picomatch": "^2.0.4" 468 | } 469 | }, 470 | "arg": { 471 | "version": "4.1.3", 472 | "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", 473 | "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", 474 | "dev": true 475 | }, 476 | "argparse": { 477 | "version": "1.0.10", 478 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 479 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 480 | "dev": true, 481 | "requires": { 482 | "sprintf-js": "~1.0.2" 483 | } 484 | }, 485 | "array-includes": { 486 | "version": "3.1.4", 487 | "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz", 488 | "integrity": "sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==", 489 | "requires": { 490 | "call-bind": "^1.0.2", 491 | "define-properties": "^1.1.3", 492 | "es-abstract": "^1.19.1", 493 | "get-intrinsic": "^1.1.1", 494 | "is-string": "^1.0.7" 495 | } 496 | }, 497 | "array-union": { 498 | "version": "2.1.0", 499 | "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", 500 | "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", 501 | "dev": true 502 | }, 503 | "array.prototype.flat": { 504 | "version": "1.2.5", 505 | "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz", 506 | "integrity": "sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg==", 507 | "dev": true, 508 | "requires": { 509 | "call-bind": "^1.0.2", 510 | "define-properties": "^1.1.3", 511 | "es-abstract": "^1.19.0" 512 | } 513 | }, 514 | "assertion-error": { 515 | "version": "1.1.0", 516 | "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", 517 | "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", 518 | "dev": true 519 | }, 520 | "astral-regex": { 521 | "version": "2.0.0", 522 | "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", 523 | "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", 524 | "dev": true 525 | }, 526 | "balanced-match": { 527 | "version": "1.0.2", 528 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 529 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" 530 | }, 531 | "base64-js": { 532 | "version": "1.5.1", 533 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 534 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" 535 | }, 536 | "binary-extensions": { 537 | "version": "2.2.0", 538 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 539 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", 540 | "dev": true 541 | }, 542 | "brace-expansion": { 543 | "version": "1.1.11", 544 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 545 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 546 | "requires": { 547 | "balanced-match": "^1.0.0", 548 | "concat-map": "0.0.1" 549 | } 550 | }, 551 | "braces": { 552 | "version": "3.0.2", 553 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 554 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 555 | "dev": true, 556 | "requires": { 557 | "fill-range": "^7.0.1" 558 | } 559 | }, 560 | "browser-stdout": { 561 | "version": "1.3.1", 562 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", 563 | "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", 564 | "dev": true 565 | }, 566 | "bson": { 567 | "version": "4.6.1", 568 | "resolved": "https://registry.npmjs.org/bson/-/bson-4.6.1.tgz", 569 | "integrity": "sha512-I1LQ7Hz5zgwR4QquilLNZwbhPw0Apx7i7X9kGMBTsqPdml/03Q9NBtD9nt/19ahjlphktQImrnderxqpzeVDjw==", 570 | "requires": { 571 | "buffer": "^5.6.0" 572 | } 573 | }, 574 | "buffer": { 575 | "version": "5.7.1", 576 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", 577 | "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", 578 | "requires": { 579 | "base64-js": "^1.3.1", 580 | "ieee754": "^1.1.13" 581 | } 582 | }, 583 | "buffer-from": { 584 | "version": "1.1.2", 585 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", 586 | "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", 587 | "dev": true 588 | }, 589 | "call-bind": { 590 | "version": "1.0.2", 591 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", 592 | "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", 593 | "requires": { 594 | "function-bind": "^1.1.1", 595 | "get-intrinsic": "^1.0.2" 596 | } 597 | }, 598 | "callsites": { 599 | "version": "3.1.0", 600 | "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", 601 | "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", 602 | "dev": true 603 | }, 604 | "camelcase": { 605 | "version": "6.3.0", 606 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", 607 | "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", 608 | "dev": true 609 | }, 610 | "chai": { 611 | "version": "4.3.4", 612 | "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.4.tgz", 613 | "integrity": "sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA==", 614 | "dev": true, 615 | "requires": { 616 | "assertion-error": "^1.1.0", 617 | "check-error": "^1.0.2", 618 | "deep-eql": "^3.0.1", 619 | "get-func-name": "^2.0.0", 620 | "pathval": "^1.1.1", 621 | "type-detect": "^4.0.5" 622 | } 623 | }, 624 | "chai-spies": { 625 | "version": "1.0.0", 626 | "resolved": "https://registry.npmjs.org/chai-spies/-/chai-spies-1.0.0.tgz", 627 | "integrity": "sha512-elF2ZUczBsFoP07qCfMO/zeggs8pqCf3fZGyK5+2X4AndS8jycZYID91ztD9oQ7d/0tnS963dPkd0frQEThDsg==", 628 | "dev": true 629 | }, 630 | "chalk": { 631 | "version": "4.1.2", 632 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 633 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 634 | "dev": true, 635 | "requires": { 636 | "ansi-styles": "^4.1.0", 637 | "supports-color": "^7.1.0" 638 | } 639 | }, 640 | "check-error": { 641 | "version": "1.0.2", 642 | "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", 643 | "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", 644 | "dev": true 645 | }, 646 | "chokidar": { 647 | "version": "3.5.1", 648 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", 649 | "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", 650 | "dev": true, 651 | "requires": { 652 | "anymatch": "~3.1.1", 653 | "braces": "~3.0.2", 654 | "fsevents": "~2.3.1", 655 | "glob-parent": "~5.1.0", 656 | "is-binary-path": "~2.1.0", 657 | "is-glob": "~4.0.1", 658 | "normalize-path": "~3.0.0", 659 | "readdirp": "~3.5.0" 660 | } 661 | }, 662 | "cliui": { 663 | "version": "7.0.4", 664 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", 665 | "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", 666 | "dev": true, 667 | "requires": { 668 | "string-width": "^4.2.0", 669 | "strip-ansi": "^6.0.0", 670 | "wrap-ansi": "^7.0.0" 671 | } 672 | }, 673 | "color-convert": { 674 | "version": "2.0.1", 675 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 676 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 677 | "dev": true, 678 | "requires": { 679 | "color-name": "~1.1.4" 680 | } 681 | }, 682 | "color-name": { 683 | "version": "1.1.4", 684 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 685 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 686 | "dev": true 687 | }, 688 | "concat-map": { 689 | "version": "0.0.1", 690 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 691 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 692 | }, 693 | "confusing-browser-globals": { 694 | "version": "1.0.11", 695 | "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", 696 | "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", 697 | "dev": true 698 | }, 699 | "contains-path": { 700 | "version": "0.1.0", 701 | "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", 702 | "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", 703 | "dev": true 704 | }, 705 | "create-require": { 706 | "version": "1.1.1", 707 | "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", 708 | "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", 709 | "dev": true 710 | }, 711 | "cross-spawn": { 712 | "version": "7.0.3", 713 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", 714 | "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", 715 | "dev": true, 716 | "requires": { 717 | "path-key": "^3.1.0", 718 | "shebang-command": "^2.0.0", 719 | "which": "^2.0.1" 720 | } 721 | }, 722 | "debug": { 723 | "version": "4.3.3", 724 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", 725 | "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", 726 | "dev": true, 727 | "requires": { 728 | "ms": "2.1.2" 729 | } 730 | }, 731 | "decamelize": { 732 | "version": "4.0.0", 733 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", 734 | "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", 735 | "dev": true 736 | }, 737 | "deep-diff": { 738 | "version": "1.0.2", 739 | "resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-1.0.2.tgz", 740 | "integrity": "sha512-aWS3UIVH+NPGCD1kki+DCU9Dua032iSsO43LqQpcs4R3+dVv7tX0qBGjiVHJHjplsoUM2XRO/KB92glqc68awg==" 741 | }, 742 | "deep-eql": { 743 | "version": "3.0.1", 744 | "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", 745 | "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", 746 | "dev": true, 747 | "requires": { 748 | "type-detect": "^4.0.0" 749 | } 750 | }, 751 | "deep-is": { 752 | "version": "0.1.4", 753 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", 754 | "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", 755 | "dev": true 756 | }, 757 | "define-properties": { 758 | "version": "1.1.3", 759 | "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", 760 | "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", 761 | "requires": { 762 | "object-keys": "^1.0.12" 763 | } 764 | }, 765 | "denque": { 766 | "version": "2.0.1", 767 | "resolved": "https://registry.npmjs.org/denque/-/denque-2.0.1.tgz", 768 | "integrity": "sha512-tfiWc6BQLXNLpNiR5iGd0Ocu3P3VpxfzFiqubLgMfhfOw9WyvgJBd46CClNn9k3qfbjvT//0cf7AlYRX/OslMQ==" 769 | }, 770 | "diff": { 771 | "version": "5.0.0", 772 | "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", 773 | "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", 774 | "dev": true 775 | }, 776 | "dir-glob": { 777 | "version": "3.0.1", 778 | "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", 779 | "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", 780 | "dev": true, 781 | "requires": { 782 | "path-type": "^4.0.0" 783 | } 784 | }, 785 | "doctrine": { 786 | "version": "3.0.0", 787 | "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", 788 | "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", 789 | "dev": true, 790 | "requires": { 791 | "esutils": "^2.0.2" 792 | } 793 | }, 794 | "emoji-regex": { 795 | "version": "8.0.0", 796 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 797 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 798 | "dev": true 799 | }, 800 | "enquirer": { 801 | "version": "2.3.6", 802 | "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", 803 | "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", 804 | "dev": true, 805 | "requires": { 806 | "ansi-colors": "^4.1.1" 807 | } 808 | }, 809 | "error-ex": { 810 | "version": "1.3.2", 811 | "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", 812 | "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", 813 | "dev": true, 814 | "requires": { 815 | "is-arrayish": "^0.2.1" 816 | } 817 | }, 818 | "es-abstract": { 819 | "version": "1.19.1", 820 | "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", 821 | "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", 822 | "requires": { 823 | "call-bind": "^1.0.2", 824 | "es-to-primitive": "^1.2.1", 825 | "function-bind": "^1.1.1", 826 | "get-intrinsic": "^1.1.1", 827 | "get-symbol-description": "^1.0.0", 828 | "has": "^1.0.3", 829 | "has-symbols": "^1.0.2", 830 | "internal-slot": "^1.0.3", 831 | "is-callable": "^1.2.4", 832 | "is-negative-zero": "^2.0.1", 833 | "is-regex": "^1.1.4", 834 | "is-shared-array-buffer": "^1.0.1", 835 | "is-string": "^1.0.7", 836 | "is-weakref": "^1.0.1", 837 | "object-inspect": "^1.11.0", 838 | "object-keys": "^1.1.1", 839 | "object.assign": "^4.1.2", 840 | "string.prototype.trimend": "^1.0.4", 841 | "string.prototype.trimstart": "^1.0.4", 842 | "unbox-primitive": "^1.0.1" 843 | } 844 | }, 845 | "es-to-primitive": { 846 | "version": "1.2.1", 847 | "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", 848 | "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", 849 | "requires": { 850 | "is-callable": "^1.1.4", 851 | "is-date-object": "^1.0.1", 852 | "is-symbol": "^1.0.2" 853 | } 854 | }, 855 | "escalade": { 856 | "version": "3.1.1", 857 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", 858 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", 859 | "dev": true 860 | }, 861 | "escape-string-regexp": { 862 | "version": "4.0.0", 863 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 864 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 865 | "dev": true 866 | }, 867 | "eslint": { 868 | "version": "7.23.0", 869 | "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.23.0.tgz", 870 | "integrity": "sha512-kqvNVbdkjzpFy0XOszNwjkKzZ+6TcwCQ/h+ozlcIWwaimBBuhlQ4nN6kbiM2L+OjDcznkTJxzYfRFH92sx4a0Q==", 871 | "dev": true, 872 | "requires": { 873 | "@babel/code-frame": "7.12.11", 874 | "@eslint/eslintrc": "^0.4.0", 875 | "ajv": "^6.10.0", 876 | "chalk": "^4.0.0", 877 | "cross-spawn": "^7.0.2", 878 | "debug": "^4.0.1", 879 | "doctrine": "^3.0.0", 880 | "enquirer": "^2.3.5", 881 | "eslint-scope": "^5.1.1", 882 | "eslint-utils": "^2.1.0", 883 | "eslint-visitor-keys": "^2.0.0", 884 | "espree": "^7.3.1", 885 | "esquery": "^1.4.0", 886 | "esutils": "^2.0.2", 887 | "file-entry-cache": "^6.0.1", 888 | "functional-red-black-tree": "^1.0.1", 889 | "glob-parent": "^5.0.0", 890 | "globals": "^13.6.0", 891 | "ignore": "^4.0.6", 892 | "import-fresh": "^3.0.0", 893 | "imurmurhash": "^0.1.4", 894 | "is-glob": "^4.0.0", 895 | "js-yaml": "^3.13.1", 896 | "json-stable-stringify-without-jsonify": "^1.0.1", 897 | "levn": "^0.4.1", 898 | "lodash": "^4.17.21", 899 | "minimatch": "^3.0.4", 900 | "natural-compare": "^1.4.0", 901 | "optionator": "^0.9.1", 902 | "progress": "^2.0.0", 903 | "regexpp": "^3.1.0", 904 | "semver": "^7.2.1", 905 | "strip-ansi": "^6.0.0", 906 | "strip-json-comments": "^3.1.0", 907 | "table": "^6.0.4", 908 | "text-table": "^0.2.0", 909 | "v8-compile-cache": "^2.0.3" 910 | } 911 | }, 912 | "eslint-config-airbnb-typescript": { 913 | "version": "12.3.1", 914 | "resolved": "https://registry.npmjs.org/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-12.3.1.tgz", 915 | "integrity": "sha512-ql/Pe6/hppYuRp4m3iPaHJqkBB7dgeEmGPQ6X0UNmrQOfTF+dXw29/ZjU2kQ6RDoLxaxOA+Xqv07Vbef6oVTWw==", 916 | "dev": true, 917 | "requires": { 918 | "@typescript-eslint/parser": "^4.4.1", 919 | "eslint-config-airbnb": "^18.2.0", 920 | "eslint-config-airbnb-base": "^14.2.0" 921 | }, 922 | "dependencies": { 923 | "doctrine": { 924 | "version": "2.1.0", 925 | "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", 926 | "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", 927 | "requires": { 928 | "esutils": "^2.0.2" 929 | } 930 | }, 931 | "emoji-regex": { 932 | "version": "9.2.2", 933 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", 934 | "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" 935 | }, 936 | "eslint-config-airbnb": { 937 | "version": "18.2.1", 938 | "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-18.2.1.tgz", 939 | "integrity": "sha512-glZNDEZ36VdlZWoxn/bUR1r/sdFKPd1mHPbqUtkctgNG4yT2DLLtJ3D+yCV+jzZCc2V1nBVkmdknOJBZ5Hc0fg==", 940 | "dev": true, 941 | "requires": { 942 | "eslint-config-airbnb-base": "^14.2.1", 943 | "object.assign": "^4.1.2", 944 | "object.entries": "^1.1.2" 945 | } 946 | }, 947 | "eslint-config-airbnb-base": { 948 | "version": "14.2.1", 949 | "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.1.tgz", 950 | "integrity": "sha512-GOrQyDtVEc1Xy20U7vsB2yAoB4nBlfH5HZJeatRXHleO+OS5Ot+MWij4Dpltw4/DyIkqUfqz1epfhVR5XWWQPA==", 951 | "dev": true, 952 | "requires": { 953 | "confusing-browser-globals": "^1.0.10", 954 | "object.assign": "^4.1.2", 955 | "object.entries": "^1.1.2" 956 | } 957 | }, 958 | "eslint-plugin-jsx-a11y": { 959 | "version": "6.5.1", 960 | "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.5.1.tgz", 961 | "integrity": "sha512-sVCFKX9fllURnXT2JwLN5Qgo24Ug5NF6dxhkmxsMEUZhXRcGg+X3e1JbJ84YePQKBl5E0ZjAH5Q4rkdcGY99+g==", 962 | "requires": { 963 | "array-includes": "^3.1.4", 964 | "emoji-regex": "^9.2.2", 965 | "has": "^1.0.3", 966 | "minimatch": "^3.0.4" 967 | } 968 | }, 969 | "eslint-plugin-react": { 970 | "version": "7.29.4", 971 | "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.29.4.tgz", 972 | "integrity": "sha512-CVCXajliVh509PcZYRFyu/BoUEz452+jtQJq2b3Bae4v3xBUWPLCmtmBM+ZinG4MzwmxJgJ2M5rMqhqLVn7MtQ==", 973 | "requires": { 974 | "array-includes": "^3.1.4", 975 | "doctrine": "^2.1.0", 976 | "estraverse": "^5.3.0", 977 | "minimatch": "^3.1.2", 978 | "object.entries": "^1.1.5", 979 | "object.values": "^1.1.5", 980 | "resolve": "^2.0.0-next.3", 981 | "semver": "^6.3.0" 982 | } 983 | }, 984 | "eslint-plugin-react-hooks": { 985 | "version": "4.3.0", 986 | "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.3.0.tgz", 987 | "integrity": "sha512-XslZy0LnMn+84NEG9jSGR6eGqaZB3133L8xewQo3fQagbQuGt7a63gf+P1NGKZavEYEC3UXaWEAA/AqDkuN6xA==" 988 | }, 989 | "estraverse": { 990 | "version": "5.3.0", 991 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", 992 | "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" 993 | }, 994 | "resolve": { 995 | "version": "2.0.0-next.3", 996 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz", 997 | "integrity": "sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==", 998 | "requires": { 999 | "is-core-module": "^2.2.0", 1000 | "path-parse": "^1.0.6" 1001 | } 1002 | }, 1003 | "semver": { 1004 | "version": "6.3.0", 1005 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", 1006 | "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" 1007 | } 1008 | } 1009 | }, 1010 | "eslint-import-resolver-node": { 1011 | "version": "0.3.6", 1012 | "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", 1013 | "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", 1014 | "dev": true, 1015 | "requires": { 1016 | "debug": "^3.2.7", 1017 | "resolve": "^1.20.0" 1018 | }, 1019 | "dependencies": { 1020 | "debug": { 1021 | "version": "3.2.7", 1022 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", 1023 | "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", 1024 | "dev": true, 1025 | "requires": { 1026 | "ms": "^2.1.1" 1027 | } 1028 | } 1029 | } 1030 | }, 1031 | "eslint-module-utils": { 1032 | "version": "2.7.3", 1033 | "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz", 1034 | "integrity": "sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==", 1035 | "dev": true, 1036 | "requires": { 1037 | "debug": "^3.2.7", 1038 | "find-up": "^2.1.0" 1039 | }, 1040 | "dependencies": { 1041 | "debug": { 1042 | "version": "3.2.7", 1043 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", 1044 | "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", 1045 | "dev": true, 1046 | "requires": { 1047 | "ms": "^2.1.1" 1048 | } 1049 | } 1050 | } 1051 | }, 1052 | "eslint-plugin-import": { 1053 | "version": "2.22.1", 1054 | "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz", 1055 | "integrity": "sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw==", 1056 | "dev": true, 1057 | "requires": { 1058 | "array-includes": "^3.1.1", 1059 | "array.prototype.flat": "^1.2.3", 1060 | "contains-path": "^0.1.0", 1061 | "debug": "^2.6.9", 1062 | "doctrine": "1.5.0", 1063 | "eslint-import-resolver-node": "^0.3.4", 1064 | "eslint-module-utils": "^2.6.0", 1065 | "has": "^1.0.3", 1066 | "minimatch": "^3.0.4", 1067 | "object.values": "^1.1.1", 1068 | "read-pkg-up": "^2.0.0", 1069 | "resolve": "^1.17.0", 1070 | "tsconfig-paths": "^3.9.0" 1071 | }, 1072 | "dependencies": { 1073 | "debug": { 1074 | "version": "2.6.9", 1075 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 1076 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 1077 | "dev": true, 1078 | "requires": { 1079 | "ms": "2.0.0" 1080 | } 1081 | }, 1082 | "doctrine": { 1083 | "version": "1.5.0", 1084 | "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", 1085 | "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", 1086 | "dev": true, 1087 | "requires": { 1088 | "esutils": "^2.0.2", 1089 | "isarray": "^1.0.0" 1090 | } 1091 | }, 1092 | "ms": { 1093 | "version": "2.0.0", 1094 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 1095 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", 1096 | "dev": true 1097 | } 1098 | } 1099 | }, 1100 | "eslint-scope": { 1101 | "version": "5.1.1", 1102 | "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", 1103 | "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", 1104 | "dev": true, 1105 | "requires": { 1106 | "esrecurse": "^4.3.0", 1107 | "estraverse": "^4.1.1" 1108 | } 1109 | }, 1110 | "eslint-utils": { 1111 | "version": "2.1.0", 1112 | "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", 1113 | "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", 1114 | "dev": true, 1115 | "requires": { 1116 | "eslint-visitor-keys": "^1.1.0" 1117 | }, 1118 | "dependencies": { 1119 | "eslint-visitor-keys": { 1120 | "version": "1.3.0", 1121 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", 1122 | "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", 1123 | "dev": true 1124 | } 1125 | } 1126 | }, 1127 | "eslint-visitor-keys": { 1128 | "version": "2.1.0", 1129 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", 1130 | "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", 1131 | "dev": true 1132 | }, 1133 | "espree": { 1134 | "version": "7.3.1", 1135 | "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", 1136 | "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", 1137 | "dev": true, 1138 | "requires": { 1139 | "acorn": "^7.4.0", 1140 | "acorn-jsx": "^5.3.1", 1141 | "eslint-visitor-keys": "^1.3.0" 1142 | }, 1143 | "dependencies": { 1144 | "eslint-visitor-keys": { 1145 | "version": "1.3.0", 1146 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", 1147 | "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", 1148 | "dev": true 1149 | } 1150 | } 1151 | }, 1152 | "esprima": { 1153 | "version": "4.0.1", 1154 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 1155 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", 1156 | "dev": true 1157 | }, 1158 | "esquery": { 1159 | "version": "1.4.0", 1160 | "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", 1161 | "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", 1162 | "dev": true, 1163 | "requires": { 1164 | "estraverse": "^5.1.0" 1165 | }, 1166 | "dependencies": { 1167 | "estraverse": { 1168 | "version": "5.3.0", 1169 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", 1170 | "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", 1171 | "dev": true 1172 | } 1173 | } 1174 | }, 1175 | "esrecurse": { 1176 | "version": "4.3.0", 1177 | "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", 1178 | "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", 1179 | "dev": true, 1180 | "requires": { 1181 | "estraverse": "^5.2.0" 1182 | }, 1183 | "dependencies": { 1184 | "estraverse": { 1185 | "version": "5.3.0", 1186 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", 1187 | "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", 1188 | "dev": true 1189 | } 1190 | } 1191 | }, 1192 | "estraverse": { 1193 | "version": "4.3.0", 1194 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", 1195 | "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", 1196 | "dev": true 1197 | }, 1198 | "esutils": { 1199 | "version": "2.0.3", 1200 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", 1201 | "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" 1202 | }, 1203 | "fast-deep-equal": { 1204 | "version": "3.1.3", 1205 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 1206 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", 1207 | "dev": true 1208 | }, 1209 | "fast-glob": { 1210 | "version": "3.2.11", 1211 | "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", 1212 | "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", 1213 | "dev": true, 1214 | "requires": { 1215 | "@nodelib/fs.stat": "^2.0.2", 1216 | "@nodelib/fs.walk": "^1.2.3", 1217 | "glob-parent": "^5.1.2", 1218 | "merge2": "^1.3.0", 1219 | "micromatch": "^4.0.4" 1220 | } 1221 | }, 1222 | "fast-json-stable-stringify": { 1223 | "version": "2.1.0", 1224 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 1225 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", 1226 | "dev": true 1227 | }, 1228 | "fast-levenshtein": { 1229 | "version": "2.0.6", 1230 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 1231 | "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", 1232 | "dev": true 1233 | }, 1234 | "fastq": { 1235 | "version": "1.13.0", 1236 | "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", 1237 | "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", 1238 | "dev": true, 1239 | "requires": { 1240 | "reusify": "^1.0.4" 1241 | } 1242 | }, 1243 | "file-entry-cache": { 1244 | "version": "6.0.1", 1245 | "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", 1246 | "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", 1247 | "dev": true, 1248 | "requires": { 1249 | "flat-cache": "^3.0.4" 1250 | } 1251 | }, 1252 | "fill-range": { 1253 | "version": "7.0.1", 1254 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 1255 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 1256 | "dev": true, 1257 | "requires": { 1258 | "to-regex-range": "^5.0.1" 1259 | } 1260 | }, 1261 | "find-up": { 1262 | "version": "2.1.0", 1263 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", 1264 | "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", 1265 | "dev": true, 1266 | "requires": { 1267 | "locate-path": "^2.0.0" 1268 | } 1269 | }, 1270 | "flat": { 1271 | "version": "5.0.2", 1272 | "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", 1273 | "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", 1274 | "dev": true 1275 | }, 1276 | "flat-cache": { 1277 | "version": "3.0.4", 1278 | "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", 1279 | "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", 1280 | "dev": true, 1281 | "requires": { 1282 | "flatted": "^3.1.0", 1283 | "rimraf": "^3.0.2" 1284 | } 1285 | }, 1286 | "flatted": { 1287 | "version": "3.2.5", 1288 | "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", 1289 | "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", 1290 | "dev": true 1291 | }, 1292 | "fs.realpath": { 1293 | "version": "1.0.0", 1294 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 1295 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 1296 | "dev": true 1297 | }, 1298 | "fsevents": { 1299 | "version": "2.3.2", 1300 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 1301 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 1302 | "dev": true, 1303 | "optional": true 1304 | }, 1305 | "function-bind": { 1306 | "version": "1.1.1", 1307 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 1308 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" 1309 | }, 1310 | "functional-red-black-tree": { 1311 | "version": "1.0.1", 1312 | "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", 1313 | "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", 1314 | "dev": true 1315 | }, 1316 | "get-caller-file": { 1317 | "version": "2.0.5", 1318 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 1319 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 1320 | "dev": true 1321 | }, 1322 | "get-func-name": { 1323 | "version": "2.0.0", 1324 | "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", 1325 | "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", 1326 | "dev": true 1327 | }, 1328 | "get-intrinsic": { 1329 | "version": "1.1.1", 1330 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", 1331 | "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", 1332 | "requires": { 1333 | "function-bind": "^1.1.1", 1334 | "has": "^1.0.3", 1335 | "has-symbols": "^1.0.1" 1336 | } 1337 | }, 1338 | "get-symbol-description": { 1339 | "version": "1.0.0", 1340 | "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", 1341 | "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", 1342 | "requires": { 1343 | "call-bind": "^1.0.2", 1344 | "get-intrinsic": "^1.1.1" 1345 | } 1346 | }, 1347 | "glob": { 1348 | "version": "7.1.6", 1349 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", 1350 | "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", 1351 | "dev": true, 1352 | "requires": { 1353 | "fs.realpath": "^1.0.0", 1354 | "inflight": "^1.0.4", 1355 | "inherits": "2", 1356 | "minimatch": "^3.0.4", 1357 | "once": "^1.3.0", 1358 | "path-is-absolute": "^1.0.0" 1359 | } 1360 | }, 1361 | "glob-parent": { 1362 | "version": "5.1.2", 1363 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 1364 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 1365 | "dev": true, 1366 | "requires": { 1367 | "is-glob": "^4.0.1" 1368 | } 1369 | }, 1370 | "globals": { 1371 | "version": "13.13.0", 1372 | "resolved": "https://registry.npmjs.org/globals/-/globals-13.13.0.tgz", 1373 | "integrity": "sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A==", 1374 | "dev": true, 1375 | "requires": { 1376 | "type-fest": "^0.20.2" 1377 | } 1378 | }, 1379 | "globby": { 1380 | "version": "11.1.0", 1381 | "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", 1382 | "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", 1383 | "dev": true, 1384 | "requires": { 1385 | "array-union": "^2.1.0", 1386 | "dir-glob": "^3.0.1", 1387 | "fast-glob": "^3.2.9", 1388 | "ignore": "^5.2.0", 1389 | "merge2": "^1.4.1", 1390 | "slash": "^3.0.0" 1391 | }, 1392 | "dependencies": { 1393 | "ignore": { 1394 | "version": "5.2.0", 1395 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", 1396 | "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", 1397 | "dev": true 1398 | } 1399 | } 1400 | }, 1401 | "graceful-fs": { 1402 | "version": "4.2.9", 1403 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", 1404 | "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", 1405 | "dev": true 1406 | }, 1407 | "growl": { 1408 | "version": "1.10.5", 1409 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", 1410 | "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", 1411 | "dev": true 1412 | }, 1413 | "has": { 1414 | "version": "1.0.3", 1415 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 1416 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 1417 | "requires": { 1418 | "function-bind": "^1.1.1" 1419 | } 1420 | }, 1421 | "has-bigints": { 1422 | "version": "1.0.1", 1423 | "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", 1424 | "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==" 1425 | }, 1426 | "has-flag": { 1427 | "version": "4.0.0", 1428 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 1429 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 1430 | "dev": true 1431 | }, 1432 | "has-symbols": { 1433 | "version": "1.0.3", 1434 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", 1435 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" 1436 | }, 1437 | "has-tostringtag": { 1438 | "version": "1.0.0", 1439 | "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", 1440 | "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", 1441 | "requires": { 1442 | "has-symbols": "^1.0.2" 1443 | } 1444 | }, 1445 | "he": { 1446 | "version": "1.2.0", 1447 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", 1448 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", 1449 | "dev": true 1450 | }, 1451 | "hosted-git-info": { 1452 | "version": "2.8.9", 1453 | "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", 1454 | "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", 1455 | "dev": true 1456 | }, 1457 | "ieee754": { 1458 | "version": "1.2.1", 1459 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 1460 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" 1461 | }, 1462 | "ignore": { 1463 | "version": "4.0.6", 1464 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", 1465 | "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", 1466 | "dev": true 1467 | }, 1468 | "import-fresh": { 1469 | "version": "3.3.0", 1470 | "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", 1471 | "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", 1472 | "dev": true, 1473 | "requires": { 1474 | "parent-module": "^1.0.0", 1475 | "resolve-from": "^4.0.0" 1476 | } 1477 | }, 1478 | "imurmurhash": { 1479 | "version": "0.1.4", 1480 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 1481 | "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", 1482 | "dev": true 1483 | }, 1484 | "inflight": { 1485 | "version": "1.0.6", 1486 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 1487 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 1488 | "dev": true, 1489 | "requires": { 1490 | "once": "^1.3.0", 1491 | "wrappy": "1" 1492 | } 1493 | }, 1494 | "inherits": { 1495 | "version": "2.0.4", 1496 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 1497 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 1498 | "dev": true 1499 | }, 1500 | "internal-slot": { 1501 | "version": "1.0.3", 1502 | "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", 1503 | "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", 1504 | "requires": { 1505 | "get-intrinsic": "^1.1.0", 1506 | "has": "^1.0.3", 1507 | "side-channel": "^1.0.4" 1508 | } 1509 | }, 1510 | "ip": { 1511 | "version": "1.1.5", 1512 | "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", 1513 | "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" 1514 | }, 1515 | "is-arrayish": { 1516 | "version": "0.2.1", 1517 | "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", 1518 | "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", 1519 | "dev": true 1520 | }, 1521 | "is-bigint": { 1522 | "version": "1.0.4", 1523 | "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", 1524 | "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", 1525 | "requires": { 1526 | "has-bigints": "^1.0.1" 1527 | } 1528 | }, 1529 | "is-binary-path": { 1530 | "version": "2.1.0", 1531 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 1532 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 1533 | "dev": true, 1534 | "requires": { 1535 | "binary-extensions": "^2.0.0" 1536 | } 1537 | }, 1538 | "is-boolean-object": { 1539 | "version": "1.1.2", 1540 | "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", 1541 | "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", 1542 | "requires": { 1543 | "call-bind": "^1.0.2", 1544 | "has-tostringtag": "^1.0.0" 1545 | } 1546 | }, 1547 | "is-callable": { 1548 | "version": "1.2.4", 1549 | "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", 1550 | "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==" 1551 | }, 1552 | "is-core-module": { 1553 | "version": "2.8.1", 1554 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", 1555 | "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", 1556 | "requires": { 1557 | "has": "^1.0.3" 1558 | } 1559 | }, 1560 | "is-date-object": { 1561 | "version": "1.0.5", 1562 | "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", 1563 | "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", 1564 | "requires": { 1565 | "has-tostringtag": "^1.0.0" 1566 | } 1567 | }, 1568 | "is-extglob": { 1569 | "version": "2.1.1", 1570 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 1571 | "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", 1572 | "dev": true 1573 | }, 1574 | "is-fullwidth-code-point": { 1575 | "version": "3.0.0", 1576 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 1577 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 1578 | "dev": true 1579 | }, 1580 | "is-glob": { 1581 | "version": "4.0.3", 1582 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 1583 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 1584 | "dev": true, 1585 | "requires": { 1586 | "is-extglob": "^2.1.1" 1587 | } 1588 | }, 1589 | "is-negative-zero": { 1590 | "version": "2.0.2", 1591 | "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", 1592 | "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==" 1593 | }, 1594 | "is-number": { 1595 | "version": "7.0.0", 1596 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 1597 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 1598 | "dev": true 1599 | }, 1600 | "is-number-object": { 1601 | "version": "1.0.6", 1602 | "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", 1603 | "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", 1604 | "requires": { 1605 | "has-tostringtag": "^1.0.0" 1606 | } 1607 | }, 1608 | "is-plain-obj": { 1609 | "version": "2.1.0", 1610 | "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", 1611 | "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", 1612 | "dev": true 1613 | }, 1614 | "is-regex": { 1615 | "version": "1.1.4", 1616 | "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", 1617 | "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", 1618 | "requires": { 1619 | "call-bind": "^1.0.2", 1620 | "has-tostringtag": "^1.0.0" 1621 | } 1622 | }, 1623 | "is-shared-array-buffer": { 1624 | "version": "1.0.1", 1625 | "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", 1626 | "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==" 1627 | }, 1628 | "is-string": { 1629 | "version": "1.0.7", 1630 | "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", 1631 | "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", 1632 | "requires": { 1633 | "has-tostringtag": "^1.0.0" 1634 | } 1635 | }, 1636 | "is-symbol": { 1637 | "version": "1.0.4", 1638 | "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", 1639 | "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", 1640 | "requires": { 1641 | "has-symbols": "^1.0.2" 1642 | } 1643 | }, 1644 | "is-weakref": { 1645 | "version": "1.0.2", 1646 | "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", 1647 | "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", 1648 | "requires": { 1649 | "call-bind": "^1.0.2" 1650 | } 1651 | }, 1652 | "isarray": { 1653 | "version": "1.0.0", 1654 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 1655 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", 1656 | "dev": true 1657 | }, 1658 | "isexe": { 1659 | "version": "2.0.0", 1660 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 1661 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 1662 | "dev": true 1663 | }, 1664 | "joi": { 1665 | "version": "17.4.2", 1666 | "resolved": "https://registry.npmjs.org/joi/-/joi-17.4.2.tgz", 1667 | "integrity": "sha512-Lm56PP+n0+Z2A2rfRvsfWVDXGEWjXxatPopkQ8qQ5mxCEhwHG+Ettgg5o98FFaxilOxozoa14cFhrE/hOzh/Nw==", 1668 | "requires": { 1669 | "@hapi/hoek": "^9.0.0", 1670 | "@hapi/topo": "^5.0.0", 1671 | "@sideway/address": "^4.1.0", 1672 | "@sideway/formula": "^3.0.0", 1673 | "@sideway/pinpoint": "^2.0.0" 1674 | } 1675 | }, 1676 | "js-tokens": { 1677 | "version": "4.0.0", 1678 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 1679 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 1680 | "dev": true 1681 | }, 1682 | "js-yaml": { 1683 | "version": "3.14.1", 1684 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", 1685 | "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", 1686 | "dev": true, 1687 | "requires": { 1688 | "argparse": "^1.0.7", 1689 | "esprima": "^4.0.0" 1690 | } 1691 | }, 1692 | "json-parse-better-errors": { 1693 | "version": "1.0.2", 1694 | "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", 1695 | "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", 1696 | "dev": true 1697 | }, 1698 | "json-schema-traverse": { 1699 | "version": "0.4.1", 1700 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 1701 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 1702 | "dev": true 1703 | }, 1704 | "json-stable-stringify-without-jsonify": { 1705 | "version": "1.0.1", 1706 | "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", 1707 | "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", 1708 | "dev": true 1709 | }, 1710 | "json5": { 1711 | "version": "1.0.1", 1712 | "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", 1713 | "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", 1714 | "dev": true, 1715 | "requires": { 1716 | "minimist": "^1.2.0" 1717 | } 1718 | }, 1719 | "levn": { 1720 | "version": "0.4.1", 1721 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", 1722 | "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", 1723 | "dev": true, 1724 | "requires": { 1725 | "prelude-ls": "^1.2.1", 1726 | "type-check": "~0.4.0" 1727 | } 1728 | }, 1729 | "load-json-file": { 1730 | "version": "4.0.0", 1731 | "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", 1732 | "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", 1733 | "dev": true, 1734 | "requires": { 1735 | "graceful-fs": "^4.1.2", 1736 | "parse-json": "^4.0.0", 1737 | "pify": "^3.0.0", 1738 | "strip-bom": "^3.0.0" 1739 | } 1740 | }, 1741 | "locate-path": { 1742 | "version": "2.0.0", 1743 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", 1744 | "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", 1745 | "dev": true, 1746 | "requires": { 1747 | "p-locate": "^2.0.0", 1748 | "path-exists": "^3.0.0" 1749 | } 1750 | }, 1751 | "lodash": { 1752 | "version": "4.17.21", 1753 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 1754 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" 1755 | }, 1756 | "lodash.truncate": { 1757 | "version": "4.4.2", 1758 | "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", 1759 | "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", 1760 | "dev": true 1761 | }, 1762 | "log-symbols": { 1763 | "version": "4.0.0", 1764 | "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", 1765 | "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", 1766 | "dev": true, 1767 | "requires": { 1768 | "chalk": "^4.0.0" 1769 | } 1770 | }, 1771 | "lru-cache": { 1772 | "version": "6.0.0", 1773 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", 1774 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", 1775 | "dev": true, 1776 | "requires": { 1777 | "yallist": "^4.0.0" 1778 | } 1779 | }, 1780 | "make-error": { 1781 | "version": "1.3.6", 1782 | "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", 1783 | "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", 1784 | "dev": true 1785 | }, 1786 | "memory-pager": { 1787 | "version": "1.5.0", 1788 | "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", 1789 | "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", 1790 | "optional": true 1791 | }, 1792 | "memorystream": { 1793 | "version": "0.3.1", 1794 | "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", 1795 | "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=", 1796 | "dev": true 1797 | }, 1798 | "merge2": { 1799 | "version": "1.4.1", 1800 | "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", 1801 | "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", 1802 | "dev": true 1803 | }, 1804 | "micromatch": { 1805 | "version": "4.0.4", 1806 | "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", 1807 | "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", 1808 | "dev": true, 1809 | "requires": { 1810 | "braces": "^3.0.1", 1811 | "picomatch": "^2.2.3" 1812 | } 1813 | }, 1814 | "minimatch": { 1815 | "version": "3.1.2", 1816 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 1817 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 1818 | "requires": { 1819 | "brace-expansion": "^1.1.7" 1820 | } 1821 | }, 1822 | "minimist": { 1823 | "version": "1.2.5", 1824 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", 1825 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", 1826 | "dev": true 1827 | }, 1828 | "mocha": { 1829 | "version": "8.3.2", 1830 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.3.2.tgz", 1831 | "integrity": "sha512-UdmISwr/5w+uXLPKspgoV7/RXZwKRTiTjJ2/AC5ZiEztIoOYdfKb19+9jNmEInzx5pBsCyJQzarAxqIGBNYJhg==", 1832 | "dev": true, 1833 | "requires": { 1834 | "@ungap/promise-all-settled": "1.1.2", 1835 | "ansi-colors": "4.1.1", 1836 | "browser-stdout": "1.3.1", 1837 | "chokidar": "3.5.1", 1838 | "debug": "4.3.1", 1839 | "diff": "5.0.0", 1840 | "escape-string-regexp": "4.0.0", 1841 | "find-up": "5.0.0", 1842 | "glob": "7.1.6", 1843 | "growl": "1.10.5", 1844 | "he": "1.2.0", 1845 | "js-yaml": "4.0.0", 1846 | "log-symbols": "4.0.0", 1847 | "minimatch": "3.0.4", 1848 | "ms": "2.1.3", 1849 | "nanoid": "3.1.20", 1850 | "serialize-javascript": "5.0.1", 1851 | "strip-json-comments": "3.1.1", 1852 | "supports-color": "8.1.1", 1853 | "which": "2.0.2", 1854 | "wide-align": "1.1.3", 1855 | "workerpool": "6.1.0", 1856 | "yargs": "16.2.0", 1857 | "yargs-parser": "20.2.4", 1858 | "yargs-unparser": "2.0.0" 1859 | }, 1860 | "dependencies": { 1861 | "argparse": { 1862 | "version": "2.0.1", 1863 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 1864 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", 1865 | "dev": true 1866 | }, 1867 | "debug": { 1868 | "version": "4.3.1", 1869 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", 1870 | "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", 1871 | "dev": true, 1872 | "requires": { 1873 | "ms": "2.1.2" 1874 | }, 1875 | "dependencies": { 1876 | "ms": { 1877 | "version": "2.1.2", 1878 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1879 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 1880 | "dev": true 1881 | } 1882 | } 1883 | }, 1884 | "find-up": { 1885 | "version": "5.0.0", 1886 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", 1887 | "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", 1888 | "dev": true, 1889 | "requires": { 1890 | "locate-path": "^6.0.0", 1891 | "path-exists": "^4.0.0" 1892 | } 1893 | }, 1894 | "js-yaml": { 1895 | "version": "4.0.0", 1896 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", 1897 | "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", 1898 | "dev": true, 1899 | "requires": { 1900 | "argparse": "^2.0.1" 1901 | } 1902 | }, 1903 | "locate-path": { 1904 | "version": "6.0.0", 1905 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", 1906 | "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", 1907 | "dev": true, 1908 | "requires": { 1909 | "p-locate": "^5.0.0" 1910 | } 1911 | }, 1912 | "minimatch": { 1913 | "version": "3.0.4", 1914 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 1915 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 1916 | "dev": true, 1917 | "requires": { 1918 | "brace-expansion": "^1.1.7" 1919 | } 1920 | }, 1921 | "ms": { 1922 | "version": "2.1.3", 1923 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 1924 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 1925 | "dev": true 1926 | }, 1927 | "p-limit": { 1928 | "version": "3.1.0", 1929 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", 1930 | "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", 1931 | "dev": true, 1932 | "requires": { 1933 | "yocto-queue": "^0.1.0" 1934 | } 1935 | }, 1936 | "p-locate": { 1937 | "version": "5.0.0", 1938 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", 1939 | "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", 1940 | "dev": true, 1941 | "requires": { 1942 | "p-limit": "^3.0.2" 1943 | } 1944 | }, 1945 | "path-exists": { 1946 | "version": "4.0.0", 1947 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 1948 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 1949 | "dev": true 1950 | }, 1951 | "supports-color": { 1952 | "version": "8.1.1", 1953 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", 1954 | "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", 1955 | "dev": true, 1956 | "requires": { 1957 | "has-flag": "^4.0.0" 1958 | } 1959 | } 1960 | } 1961 | }, 1962 | "mongodb": { 1963 | "version": "4.4.1", 1964 | "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.4.1.tgz", 1965 | "integrity": "sha512-IAD3nFtCR4s22vi5qjqkCBnuyDDrOW8WVSSmgHquOvGaP1iTD+XpC5tr8wAUbZ2EeZkaswwBKQFHDvl4qYcKqQ==", 1966 | "requires": { 1967 | "bson": "^4.6.1", 1968 | "denque": "^2.0.1", 1969 | "mongodb-connection-string-url": "^2.5.2", 1970 | "saslprep": "^1.0.3", 1971 | "socks": "^2.6.2" 1972 | } 1973 | }, 1974 | "mongodb-connection-string-url": { 1975 | "version": "2.5.2", 1976 | "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.5.2.tgz", 1977 | "integrity": "sha512-tWDyIG8cQlI5k3skB6ywaEA5F9f5OntrKKsT/Lteub2zgwSUlhqEN2inGgBTm8bpYJf8QYBdA/5naz65XDpczA==", 1978 | "requires": { 1979 | "@types/whatwg-url": "^8.2.1", 1980 | "whatwg-url": "^11.0.0" 1981 | } 1982 | }, 1983 | "ms": { 1984 | "version": "2.1.2", 1985 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1986 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 1987 | "dev": true 1988 | }, 1989 | "nanoid": { 1990 | "version": "3.1.20", 1991 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", 1992 | "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", 1993 | "dev": true 1994 | }, 1995 | "natural-compare": { 1996 | "version": "1.4.0", 1997 | "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", 1998 | "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", 1999 | "dev": true 2000 | }, 2001 | "nice-try": { 2002 | "version": "1.0.5", 2003 | "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", 2004 | "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", 2005 | "dev": true 2006 | }, 2007 | "normalize-package-data": { 2008 | "version": "2.5.0", 2009 | "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", 2010 | "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", 2011 | "dev": true, 2012 | "requires": { 2013 | "hosted-git-info": "^2.1.4", 2014 | "resolve": "^1.10.0", 2015 | "semver": "2 || 3 || 4 || 5", 2016 | "validate-npm-package-license": "^3.0.1" 2017 | }, 2018 | "dependencies": { 2019 | "semver": { 2020 | "version": "5.7.1", 2021 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 2022 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", 2023 | "dev": true 2024 | } 2025 | } 2026 | }, 2027 | "normalize-path": { 2028 | "version": "3.0.0", 2029 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 2030 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 2031 | "dev": true 2032 | }, 2033 | "npm-run-all": { 2034 | "version": "4.1.5", 2035 | "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", 2036 | "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", 2037 | "dev": true, 2038 | "requires": { 2039 | "ansi-styles": "^3.2.1", 2040 | "chalk": "^2.4.1", 2041 | "cross-spawn": "^6.0.5", 2042 | "memorystream": "^0.3.1", 2043 | "minimatch": "^3.0.4", 2044 | "pidtree": "^0.3.0", 2045 | "read-pkg": "^3.0.0", 2046 | "shell-quote": "^1.6.1", 2047 | "string.prototype.padend": "^3.0.0" 2048 | }, 2049 | "dependencies": { 2050 | "ansi-styles": { 2051 | "version": "3.2.1", 2052 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 2053 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 2054 | "dev": true, 2055 | "requires": { 2056 | "color-convert": "^1.9.0" 2057 | } 2058 | }, 2059 | "chalk": { 2060 | "version": "2.4.2", 2061 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 2062 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 2063 | "dev": true, 2064 | "requires": { 2065 | "ansi-styles": "^3.2.1", 2066 | "escape-string-regexp": "^1.0.5", 2067 | "supports-color": "^5.3.0" 2068 | } 2069 | }, 2070 | "color-convert": { 2071 | "version": "1.9.3", 2072 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 2073 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 2074 | "dev": true, 2075 | "requires": { 2076 | "color-name": "1.1.3" 2077 | } 2078 | }, 2079 | "color-name": { 2080 | "version": "1.1.3", 2081 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 2082 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 2083 | "dev": true 2084 | }, 2085 | "cross-spawn": { 2086 | "version": "6.0.5", 2087 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", 2088 | "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", 2089 | "dev": true, 2090 | "requires": { 2091 | "nice-try": "^1.0.4", 2092 | "path-key": "^2.0.1", 2093 | "semver": "^5.5.0", 2094 | "shebang-command": "^1.2.0", 2095 | "which": "^1.2.9" 2096 | } 2097 | }, 2098 | "escape-string-regexp": { 2099 | "version": "1.0.5", 2100 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 2101 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 2102 | "dev": true 2103 | }, 2104 | "has-flag": { 2105 | "version": "3.0.0", 2106 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 2107 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 2108 | "dev": true 2109 | }, 2110 | "path-key": { 2111 | "version": "2.0.1", 2112 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", 2113 | "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", 2114 | "dev": true 2115 | }, 2116 | "semver": { 2117 | "version": "5.7.1", 2118 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 2119 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", 2120 | "dev": true 2121 | }, 2122 | "shebang-command": { 2123 | "version": "1.2.0", 2124 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", 2125 | "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", 2126 | "dev": true, 2127 | "requires": { 2128 | "shebang-regex": "^1.0.0" 2129 | } 2130 | }, 2131 | "shebang-regex": { 2132 | "version": "1.0.0", 2133 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", 2134 | "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", 2135 | "dev": true 2136 | }, 2137 | "supports-color": { 2138 | "version": "5.5.0", 2139 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 2140 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 2141 | "dev": true, 2142 | "requires": { 2143 | "has-flag": "^3.0.0" 2144 | } 2145 | }, 2146 | "which": { 2147 | "version": "1.3.1", 2148 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", 2149 | "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", 2150 | "dev": true, 2151 | "requires": { 2152 | "isexe": "^2.0.0" 2153 | } 2154 | } 2155 | } 2156 | }, 2157 | "object-inspect": { 2158 | "version": "1.12.0", 2159 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", 2160 | "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==" 2161 | }, 2162 | "object-keys": { 2163 | "version": "1.1.1", 2164 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", 2165 | "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" 2166 | }, 2167 | "object.assign": { 2168 | "version": "4.1.2", 2169 | "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", 2170 | "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", 2171 | "requires": { 2172 | "call-bind": "^1.0.0", 2173 | "define-properties": "^1.1.3", 2174 | "has-symbols": "^1.0.1", 2175 | "object-keys": "^1.1.1" 2176 | } 2177 | }, 2178 | "object.entries": { 2179 | "version": "1.1.5", 2180 | "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz", 2181 | "integrity": "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==", 2182 | "requires": { 2183 | "call-bind": "^1.0.2", 2184 | "define-properties": "^1.1.3", 2185 | "es-abstract": "^1.19.1" 2186 | } 2187 | }, 2188 | "object.values": { 2189 | "version": "1.1.5", 2190 | "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", 2191 | "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", 2192 | "requires": { 2193 | "call-bind": "^1.0.2", 2194 | "define-properties": "^1.1.3", 2195 | "es-abstract": "^1.19.1" 2196 | } 2197 | }, 2198 | "once": { 2199 | "version": "1.4.0", 2200 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 2201 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 2202 | "dev": true, 2203 | "requires": { 2204 | "wrappy": "1" 2205 | } 2206 | }, 2207 | "optionator": { 2208 | "version": "0.9.1", 2209 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", 2210 | "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", 2211 | "dev": true, 2212 | "requires": { 2213 | "deep-is": "^0.1.3", 2214 | "fast-levenshtein": "^2.0.6", 2215 | "levn": "^0.4.1", 2216 | "prelude-ls": "^1.2.1", 2217 | "type-check": "^0.4.0", 2218 | "word-wrap": "^1.2.3" 2219 | } 2220 | }, 2221 | "p-limit": { 2222 | "version": "1.3.0", 2223 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", 2224 | "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", 2225 | "dev": true, 2226 | "requires": { 2227 | "p-try": "^1.0.0" 2228 | } 2229 | }, 2230 | "p-locate": { 2231 | "version": "2.0.0", 2232 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", 2233 | "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", 2234 | "dev": true, 2235 | "requires": { 2236 | "p-limit": "^1.1.0" 2237 | } 2238 | }, 2239 | "p-try": { 2240 | "version": "1.0.0", 2241 | "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", 2242 | "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", 2243 | "dev": true 2244 | }, 2245 | "parent-module": { 2246 | "version": "1.0.1", 2247 | "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", 2248 | "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", 2249 | "dev": true, 2250 | "requires": { 2251 | "callsites": "^3.0.0" 2252 | } 2253 | }, 2254 | "parse-json": { 2255 | "version": "4.0.0", 2256 | "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", 2257 | "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", 2258 | "dev": true, 2259 | "requires": { 2260 | "error-ex": "^1.3.1", 2261 | "json-parse-better-errors": "^1.0.1" 2262 | } 2263 | }, 2264 | "path-exists": { 2265 | "version": "3.0.0", 2266 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", 2267 | "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", 2268 | "dev": true 2269 | }, 2270 | "path-is-absolute": { 2271 | "version": "1.0.1", 2272 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 2273 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 2274 | "dev": true 2275 | }, 2276 | "path-key": { 2277 | "version": "3.1.1", 2278 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 2279 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 2280 | "dev": true 2281 | }, 2282 | "path-parse": { 2283 | "version": "1.0.7", 2284 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 2285 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" 2286 | }, 2287 | "path-type": { 2288 | "version": "4.0.0", 2289 | "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", 2290 | "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", 2291 | "dev": true 2292 | }, 2293 | "pathval": { 2294 | "version": "1.1.1", 2295 | "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", 2296 | "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", 2297 | "dev": true 2298 | }, 2299 | "picomatch": { 2300 | "version": "2.3.1", 2301 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 2302 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 2303 | "dev": true 2304 | }, 2305 | "pidtree": { 2306 | "version": "0.3.1", 2307 | "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", 2308 | "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", 2309 | "dev": true 2310 | }, 2311 | "pify": { 2312 | "version": "3.0.0", 2313 | "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", 2314 | "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", 2315 | "dev": true 2316 | }, 2317 | "prelude-ls": { 2318 | "version": "1.2.1", 2319 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", 2320 | "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", 2321 | "dev": true 2322 | }, 2323 | "prettier": { 2324 | "version": "2.2.1", 2325 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", 2326 | "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", 2327 | "dev": true 2328 | }, 2329 | "progress": { 2330 | "version": "2.0.3", 2331 | "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", 2332 | "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", 2333 | "dev": true 2334 | }, 2335 | "punycode": { 2336 | "version": "2.1.1", 2337 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 2338 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" 2339 | }, 2340 | "queue-microtask": { 2341 | "version": "1.2.3", 2342 | "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", 2343 | "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", 2344 | "dev": true 2345 | }, 2346 | "randombytes": { 2347 | "version": "2.1.0", 2348 | "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", 2349 | "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", 2350 | "dev": true, 2351 | "requires": { 2352 | "safe-buffer": "^5.1.0" 2353 | } 2354 | }, 2355 | "read-pkg": { 2356 | "version": "3.0.0", 2357 | "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", 2358 | "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", 2359 | "dev": true, 2360 | "requires": { 2361 | "load-json-file": "^4.0.0", 2362 | "normalize-package-data": "^2.3.2", 2363 | "path-type": "^3.0.0" 2364 | }, 2365 | "dependencies": { 2366 | "path-type": { 2367 | "version": "3.0.0", 2368 | "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", 2369 | "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", 2370 | "dev": true, 2371 | "requires": { 2372 | "pify": "^3.0.0" 2373 | } 2374 | } 2375 | } 2376 | }, 2377 | "read-pkg-up": { 2378 | "version": "2.0.0", 2379 | "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", 2380 | "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", 2381 | "dev": true, 2382 | "requires": { 2383 | "find-up": "^2.0.0", 2384 | "read-pkg": "^2.0.0" 2385 | }, 2386 | "dependencies": { 2387 | "load-json-file": { 2388 | "version": "2.0.0", 2389 | "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", 2390 | "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", 2391 | "dev": true, 2392 | "requires": { 2393 | "graceful-fs": "^4.1.2", 2394 | "parse-json": "^2.2.0", 2395 | "pify": "^2.0.0", 2396 | "strip-bom": "^3.0.0" 2397 | } 2398 | }, 2399 | "parse-json": { 2400 | "version": "2.2.0", 2401 | "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", 2402 | "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", 2403 | "dev": true, 2404 | "requires": { 2405 | "error-ex": "^1.2.0" 2406 | } 2407 | }, 2408 | "path-type": { 2409 | "version": "2.0.0", 2410 | "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", 2411 | "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", 2412 | "dev": true, 2413 | "requires": { 2414 | "pify": "^2.0.0" 2415 | } 2416 | }, 2417 | "pify": { 2418 | "version": "2.3.0", 2419 | "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", 2420 | "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", 2421 | "dev": true 2422 | }, 2423 | "read-pkg": { 2424 | "version": "2.0.0", 2425 | "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", 2426 | "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", 2427 | "dev": true, 2428 | "requires": { 2429 | "load-json-file": "^2.0.0", 2430 | "normalize-package-data": "^2.3.2", 2431 | "path-type": "^2.0.0" 2432 | } 2433 | } 2434 | } 2435 | }, 2436 | "readdirp": { 2437 | "version": "3.5.0", 2438 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", 2439 | "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", 2440 | "dev": true, 2441 | "requires": { 2442 | "picomatch": "^2.2.1" 2443 | } 2444 | }, 2445 | "regexpp": { 2446 | "version": "3.2.0", 2447 | "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", 2448 | "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", 2449 | "dev": true 2450 | }, 2451 | "require-directory": { 2452 | "version": "2.1.1", 2453 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 2454 | "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", 2455 | "dev": true 2456 | }, 2457 | "require-from-string": { 2458 | "version": "2.0.2", 2459 | "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", 2460 | "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", 2461 | "dev": true 2462 | }, 2463 | "resolve": { 2464 | "version": "1.22.0", 2465 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", 2466 | "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", 2467 | "dev": true, 2468 | "requires": { 2469 | "is-core-module": "^2.8.1", 2470 | "path-parse": "^1.0.7", 2471 | "supports-preserve-symlinks-flag": "^1.0.0" 2472 | } 2473 | }, 2474 | "resolve-from": { 2475 | "version": "4.0.0", 2476 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", 2477 | "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", 2478 | "dev": true 2479 | }, 2480 | "reusify": { 2481 | "version": "1.0.4", 2482 | "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", 2483 | "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", 2484 | "dev": true 2485 | }, 2486 | "rimraf": { 2487 | "version": "3.0.2", 2488 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", 2489 | "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", 2490 | "dev": true, 2491 | "requires": { 2492 | "glob": "^7.1.3" 2493 | } 2494 | }, 2495 | "run-parallel": { 2496 | "version": "1.2.0", 2497 | "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", 2498 | "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", 2499 | "dev": true, 2500 | "requires": { 2501 | "queue-microtask": "^1.2.2" 2502 | } 2503 | }, 2504 | "safe-buffer": { 2505 | "version": "5.2.1", 2506 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 2507 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 2508 | "dev": true 2509 | }, 2510 | "saslprep": { 2511 | "version": "1.0.3", 2512 | "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", 2513 | "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", 2514 | "optional": true, 2515 | "requires": { 2516 | "sparse-bitfield": "^3.0.3" 2517 | } 2518 | }, 2519 | "semver": { 2520 | "version": "7.3.5", 2521 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", 2522 | "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", 2523 | "dev": true, 2524 | "requires": { 2525 | "lru-cache": "^6.0.0" 2526 | } 2527 | }, 2528 | "serialize-javascript": { 2529 | "version": "5.0.1", 2530 | "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", 2531 | "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", 2532 | "dev": true, 2533 | "requires": { 2534 | "randombytes": "^2.1.0" 2535 | } 2536 | }, 2537 | "shebang-command": { 2538 | "version": "2.0.0", 2539 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 2540 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 2541 | "dev": true, 2542 | "requires": { 2543 | "shebang-regex": "^3.0.0" 2544 | } 2545 | }, 2546 | "shebang-regex": { 2547 | "version": "3.0.0", 2548 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 2549 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 2550 | "dev": true 2551 | }, 2552 | "shell-quote": { 2553 | "version": "1.7.3", 2554 | "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz", 2555 | "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==", 2556 | "dev": true 2557 | }, 2558 | "side-channel": { 2559 | "version": "1.0.4", 2560 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", 2561 | "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", 2562 | "requires": { 2563 | "call-bind": "^1.0.0", 2564 | "get-intrinsic": "^1.0.2", 2565 | "object-inspect": "^1.9.0" 2566 | } 2567 | }, 2568 | "slash": { 2569 | "version": "3.0.0", 2570 | "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", 2571 | "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", 2572 | "dev": true 2573 | }, 2574 | "slice-ansi": { 2575 | "version": "4.0.0", 2576 | "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", 2577 | "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", 2578 | "dev": true, 2579 | "requires": { 2580 | "ansi-styles": "^4.0.0", 2581 | "astral-regex": "^2.0.0", 2582 | "is-fullwidth-code-point": "^3.0.0" 2583 | } 2584 | }, 2585 | "smart-buffer": { 2586 | "version": "4.2.0", 2587 | "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", 2588 | "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==" 2589 | }, 2590 | "socks": { 2591 | "version": "2.6.2", 2592 | "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.2.tgz", 2593 | "integrity": "sha512-zDZhHhZRY9PxRruRMR7kMhnf3I8hDs4S3f9RecfnGxvcBHQcKcIH/oUcEWffsfl1XxdYlA7nnlGbbTvPz9D8gA==", 2594 | "requires": { 2595 | "ip": "^1.1.5", 2596 | "smart-buffer": "^4.2.0" 2597 | } 2598 | }, 2599 | "source-map": { 2600 | "version": "0.6.1", 2601 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 2602 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 2603 | "dev": true 2604 | }, 2605 | "source-map-support": { 2606 | "version": "0.5.21", 2607 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", 2608 | "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", 2609 | "dev": true, 2610 | "requires": { 2611 | "buffer-from": "^1.0.0", 2612 | "source-map": "^0.6.0" 2613 | } 2614 | }, 2615 | "sparse-bitfield": { 2616 | "version": "3.0.3", 2617 | "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", 2618 | "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=", 2619 | "optional": true, 2620 | "requires": { 2621 | "memory-pager": "^1.0.2" 2622 | } 2623 | }, 2624 | "spdx-correct": { 2625 | "version": "3.1.1", 2626 | "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", 2627 | "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", 2628 | "dev": true, 2629 | "requires": { 2630 | "spdx-expression-parse": "^3.0.0", 2631 | "spdx-license-ids": "^3.0.0" 2632 | } 2633 | }, 2634 | "spdx-exceptions": { 2635 | "version": "2.3.0", 2636 | "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", 2637 | "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", 2638 | "dev": true 2639 | }, 2640 | "spdx-expression-parse": { 2641 | "version": "3.0.1", 2642 | "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", 2643 | "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", 2644 | "dev": true, 2645 | "requires": { 2646 | "spdx-exceptions": "^2.1.0", 2647 | "spdx-license-ids": "^3.0.0" 2648 | } 2649 | }, 2650 | "spdx-license-ids": { 2651 | "version": "3.0.11", 2652 | "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", 2653 | "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", 2654 | "dev": true 2655 | }, 2656 | "sprintf-js": { 2657 | "version": "1.0.3", 2658 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 2659 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", 2660 | "dev": true 2661 | }, 2662 | "string-width": { 2663 | "version": "4.2.3", 2664 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 2665 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 2666 | "dev": true, 2667 | "requires": { 2668 | "emoji-regex": "^8.0.0", 2669 | "is-fullwidth-code-point": "^3.0.0", 2670 | "strip-ansi": "^6.0.1" 2671 | } 2672 | }, 2673 | "string.prototype.padend": { 2674 | "version": "3.1.3", 2675 | "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.3.tgz", 2676 | "integrity": "sha512-jNIIeokznm8SD/TZISQsZKYu7RJyheFNt84DUPrh482GC8RVp2MKqm2O5oBRdGxbDQoXrhhWtPIWQOiy20svUg==", 2677 | "dev": true, 2678 | "requires": { 2679 | "call-bind": "^1.0.2", 2680 | "define-properties": "^1.1.3", 2681 | "es-abstract": "^1.19.1" 2682 | } 2683 | }, 2684 | "string.prototype.trimend": { 2685 | "version": "1.0.4", 2686 | "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", 2687 | "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", 2688 | "requires": { 2689 | "call-bind": "^1.0.2", 2690 | "define-properties": "^1.1.3" 2691 | } 2692 | }, 2693 | "string.prototype.trimstart": { 2694 | "version": "1.0.4", 2695 | "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", 2696 | "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", 2697 | "requires": { 2698 | "call-bind": "^1.0.2", 2699 | "define-properties": "^1.1.3" 2700 | } 2701 | }, 2702 | "strip-ansi": { 2703 | "version": "6.0.1", 2704 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 2705 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 2706 | "dev": true, 2707 | "requires": { 2708 | "ansi-regex": "^5.0.1" 2709 | } 2710 | }, 2711 | "strip-bom": { 2712 | "version": "3.0.0", 2713 | "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", 2714 | "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", 2715 | "dev": true 2716 | }, 2717 | "strip-json-comments": { 2718 | "version": "3.1.1", 2719 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 2720 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 2721 | "dev": true 2722 | }, 2723 | "supports-color": { 2724 | "version": "7.2.0", 2725 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 2726 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 2727 | "dev": true, 2728 | "requires": { 2729 | "has-flag": "^4.0.0" 2730 | } 2731 | }, 2732 | "supports-preserve-symlinks-flag": { 2733 | "version": "1.0.0", 2734 | "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", 2735 | "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", 2736 | "dev": true 2737 | }, 2738 | "table": { 2739 | "version": "6.8.0", 2740 | "resolved": "https://registry.npmjs.org/table/-/table-6.8.0.tgz", 2741 | "integrity": "sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==", 2742 | "dev": true, 2743 | "requires": { 2744 | "ajv": "^8.0.1", 2745 | "lodash.truncate": "^4.4.2", 2746 | "slice-ansi": "^4.0.0", 2747 | "string-width": "^4.2.3", 2748 | "strip-ansi": "^6.0.1" 2749 | }, 2750 | "dependencies": { 2751 | "ajv": { 2752 | "version": "8.10.0", 2753 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.10.0.tgz", 2754 | "integrity": "sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw==", 2755 | "dev": true, 2756 | "requires": { 2757 | "fast-deep-equal": "^3.1.1", 2758 | "json-schema-traverse": "^1.0.0", 2759 | "require-from-string": "^2.0.2", 2760 | "uri-js": "^4.2.2" 2761 | } 2762 | }, 2763 | "json-schema-traverse": { 2764 | "version": "1.0.0", 2765 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", 2766 | "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", 2767 | "dev": true 2768 | } 2769 | } 2770 | }, 2771 | "text-table": { 2772 | "version": "0.2.0", 2773 | "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", 2774 | "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", 2775 | "dev": true 2776 | }, 2777 | "to-regex-range": { 2778 | "version": "5.0.1", 2779 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 2780 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 2781 | "dev": true, 2782 | "requires": { 2783 | "is-number": "^7.0.0" 2784 | } 2785 | }, 2786 | "tr46": { 2787 | "version": "3.0.0", 2788 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", 2789 | "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", 2790 | "requires": { 2791 | "punycode": "^2.1.1" 2792 | } 2793 | }, 2794 | "ts-node": { 2795 | "version": "9.1.1", 2796 | "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", 2797 | "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", 2798 | "dev": true, 2799 | "requires": { 2800 | "arg": "^4.1.0", 2801 | "create-require": "^1.1.0", 2802 | "diff": "^4.0.1", 2803 | "make-error": "^1.1.1", 2804 | "source-map-support": "^0.5.17", 2805 | "yn": "3.1.1" 2806 | }, 2807 | "dependencies": { 2808 | "diff": { 2809 | "version": "4.0.2", 2810 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", 2811 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", 2812 | "dev": true 2813 | } 2814 | } 2815 | }, 2816 | "tsconfig-paths": { 2817 | "version": "3.14.0", 2818 | "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.0.tgz", 2819 | "integrity": "sha512-cg/1jAZoL57R39+wiw4u/SCC6Ic9Q5NqjBOb+9xISedOYurfog9ZNmKJSxAnb2m/5Bq4lE9lhUcau33Ml8DM0g==", 2820 | "dev": true, 2821 | "requires": { 2822 | "@types/json5": "^0.0.29", 2823 | "json5": "^1.0.1", 2824 | "minimist": "^1.2.0", 2825 | "strip-bom": "^3.0.0" 2826 | } 2827 | }, 2828 | "tslib": { 2829 | "version": "1.14.1", 2830 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", 2831 | "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", 2832 | "dev": true 2833 | }, 2834 | "type-check": { 2835 | "version": "0.4.0", 2836 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", 2837 | "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", 2838 | "dev": true, 2839 | "requires": { 2840 | "prelude-ls": "^1.2.1" 2841 | } 2842 | }, 2843 | "type-detect": { 2844 | "version": "4.0.8", 2845 | "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", 2846 | "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", 2847 | "dev": true 2848 | }, 2849 | "type-fest": { 2850 | "version": "0.20.2", 2851 | "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", 2852 | "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", 2853 | "dev": true 2854 | }, 2855 | "typescript": { 2856 | "version": "4.2.3", 2857 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.3.tgz", 2858 | "integrity": "sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==", 2859 | "dev": true 2860 | }, 2861 | "unbox-primitive": { 2862 | "version": "1.0.1", 2863 | "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", 2864 | "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", 2865 | "requires": { 2866 | "function-bind": "^1.1.1", 2867 | "has-bigints": "^1.0.1", 2868 | "has-symbols": "^1.0.2", 2869 | "which-boxed-primitive": "^1.0.2" 2870 | } 2871 | }, 2872 | "uri-js": { 2873 | "version": "4.4.1", 2874 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", 2875 | "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", 2876 | "dev": true, 2877 | "requires": { 2878 | "punycode": "^2.1.0" 2879 | } 2880 | }, 2881 | "v8-compile-cache": { 2882 | "version": "2.3.0", 2883 | "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", 2884 | "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", 2885 | "dev": true 2886 | }, 2887 | "validate-npm-package-license": { 2888 | "version": "3.0.4", 2889 | "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", 2890 | "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", 2891 | "dev": true, 2892 | "requires": { 2893 | "spdx-correct": "^3.0.0", 2894 | "spdx-expression-parse": "^3.0.0" 2895 | } 2896 | }, 2897 | "webidl-conversions": { 2898 | "version": "7.0.0", 2899 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", 2900 | "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==" 2901 | }, 2902 | "whatwg-url": { 2903 | "version": "11.0.0", 2904 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", 2905 | "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", 2906 | "requires": { 2907 | "tr46": "^3.0.0", 2908 | "webidl-conversions": "^7.0.0" 2909 | } 2910 | }, 2911 | "which": { 2912 | "version": "2.0.2", 2913 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 2914 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 2915 | "dev": true, 2916 | "requires": { 2917 | "isexe": "^2.0.0" 2918 | } 2919 | }, 2920 | "which-boxed-primitive": { 2921 | "version": "1.0.2", 2922 | "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", 2923 | "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", 2924 | "requires": { 2925 | "is-bigint": "^1.0.1", 2926 | "is-boolean-object": "^1.1.0", 2927 | "is-number-object": "^1.0.4", 2928 | "is-string": "^1.0.5", 2929 | "is-symbol": "^1.0.3" 2930 | } 2931 | }, 2932 | "wide-align": { 2933 | "version": "1.1.3", 2934 | "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", 2935 | "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", 2936 | "dev": true, 2937 | "requires": { 2938 | "string-width": "^1.0.2 || 2" 2939 | }, 2940 | "dependencies": { 2941 | "ansi-regex": { 2942 | "version": "3.0.0", 2943 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", 2944 | "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", 2945 | "dev": true 2946 | }, 2947 | "is-fullwidth-code-point": { 2948 | "version": "2.0.0", 2949 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 2950 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", 2951 | "dev": true 2952 | }, 2953 | "string-width": { 2954 | "version": "2.1.1", 2955 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", 2956 | "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", 2957 | "dev": true, 2958 | "requires": { 2959 | "is-fullwidth-code-point": "^2.0.0", 2960 | "strip-ansi": "^4.0.0" 2961 | } 2962 | }, 2963 | "strip-ansi": { 2964 | "version": "4.0.0", 2965 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", 2966 | "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", 2967 | "dev": true, 2968 | "requires": { 2969 | "ansi-regex": "^3.0.0" 2970 | } 2971 | } 2972 | } 2973 | }, 2974 | "word-wrap": { 2975 | "version": "1.2.3", 2976 | "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", 2977 | "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", 2978 | "dev": true 2979 | }, 2980 | "workerpool": { 2981 | "version": "6.1.0", 2982 | "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz", 2983 | "integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==", 2984 | "dev": true 2985 | }, 2986 | "wrap-ansi": { 2987 | "version": "7.0.0", 2988 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 2989 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 2990 | "dev": true, 2991 | "requires": { 2992 | "ansi-styles": "^4.0.0", 2993 | "string-width": "^4.1.0", 2994 | "strip-ansi": "^6.0.0" 2995 | } 2996 | }, 2997 | "wrappy": { 2998 | "version": "1.0.2", 2999 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 3000 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 3001 | "dev": true 3002 | }, 3003 | "y18n": { 3004 | "version": "5.0.8", 3005 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", 3006 | "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", 3007 | "dev": true 3008 | }, 3009 | "yallist": { 3010 | "version": "4.0.0", 3011 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", 3012 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", 3013 | "dev": true 3014 | }, 3015 | "yargs": { 3016 | "version": "16.2.0", 3017 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", 3018 | "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", 3019 | "dev": true, 3020 | "requires": { 3021 | "cliui": "^7.0.2", 3022 | "escalade": "^3.1.1", 3023 | "get-caller-file": "^2.0.5", 3024 | "require-directory": "^2.1.1", 3025 | "string-width": "^4.2.0", 3026 | "y18n": "^5.0.5", 3027 | "yargs-parser": "^20.2.2" 3028 | } 3029 | }, 3030 | "yargs-parser": { 3031 | "version": "20.2.4", 3032 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", 3033 | "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", 3034 | "dev": true 3035 | }, 3036 | "yargs-unparser": { 3037 | "version": "2.0.0", 3038 | "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", 3039 | "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", 3040 | "dev": true, 3041 | "requires": { 3042 | "camelcase": "^6.0.0", 3043 | "decamelize": "^4.0.0", 3044 | "flat": "^5.0.2", 3045 | "is-plain-obj": "^2.1.0" 3046 | } 3047 | }, 3048 | "yn": { 3049 | "version": "3.1.1", 3050 | "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", 3051 | "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", 3052 | "dev": true 3053 | }, 3054 | "yocto-queue": { 3055 | "version": "0.1.0", 3056 | "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", 3057 | "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", 3058 | "dev": true 3059 | } 3060 | } 3061 | } 3062 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@paralect/node-mongo", 3 | "version": "3.0.0-beta3", 4 | "author": "Paralect", 5 | "description": "Reactive MongoDB wrapper for Node.JS", 6 | "private": false, 7 | "main": "./dist/index.js", 8 | "types": "./dist/index.d.ts", 9 | "scripts": { 10 | "tests": "./bin/run-tests.sh", 11 | "all": "run-s test:*", 12 | "test:eslint": "tsc --noEmit && eslint \"**/*.{js,ts}\" --quiet --fix", 13 | "test:mocha": "NODE_ENV=test mocha --exit --timeout 20000 -r ts-node/register src/**/*.spec.ts src/*.spec.ts", 14 | "build": "tsc" 15 | }, 16 | "keywords": [ 17 | "mongo", 18 | "paralect" 19 | ], 20 | "license": "MIT", 21 | "dependencies": { 22 | "deep-diff": "1.0.2", 23 | "joi": "17.4.2", 24 | "lodash": "4.17.21", 25 | "mongodb": "4.4.1" 26 | }, 27 | "devDependencies": { 28 | "@types/chai": "4.2.16", 29 | "@types/chai-spies": "1.0.3", 30 | "@types/deep-diff": "1.0.0", 31 | "@types/hapi__joi": "17.1.6", 32 | "@types/lodash": "4.14.168", 33 | "@types/mocha": "8.2.2", 34 | "@types/node": "14.14.35", 35 | "@typescript-eslint/eslint-plugin": "4.22.1", 36 | "@typescript-eslint/parser": "4.18.0", 37 | "chai": "4.3.4", 38 | "chai-spies": "1.0.0", 39 | "eslint": "7.23.0", 40 | "eslint-config-airbnb-typescript": "12.3.1", 41 | "eslint-plugin-import": "2.22.1", 42 | "mocha": "8.3.2", 43 | "npm-run-all": "4.1.5", 44 | "prettier": "2.2.1", 45 | "ts-node": "9.1.1", 46 | "typescript": "4.2.3" 47 | }, 48 | "engines": { 49 | "node": ">=12.0.0" 50 | }, 51 | "repository": { 52 | "type": "git", 53 | "url": "git+https://github.com/paralect/node-mongo.git" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/MongoServiceError.ts: -------------------------------------------------------------------------------- 1 | class MongoServiceError extends Error { 2 | static NOT_FOUND = 'NOT_FOUND'; 3 | 4 | static INVALID_SCHEMA = 'INVALID_SCHEMA'; 5 | 6 | static MORE_THAN_ONE = 'MORE_THAN_ONE'; 7 | 8 | name: string; 9 | 10 | code: string; 11 | 12 | error: any; 13 | 14 | constructor(code: string, message: string, error?: any) { 15 | super(message); 16 | 17 | Error.captureStackTrace(this, this.constructor); 18 | 19 | this.name = this.constructor.name; 20 | this.code = code; 21 | this.error = error; 22 | } 23 | } 24 | 25 | export default MongoServiceError; 26 | -------------------------------------------------------------------------------- /src/config/.gitignore: -------------------------------------------------------------------------------- 1 | local.ts -------------------------------------------------------------------------------- /src/config/index.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable global-require */ 2 | import * as _ from 'lodash'; 3 | 4 | export interface Config { 5 | env: string, 6 | mongo: { 7 | connection: string; 8 | dbName?: string; 9 | }; 10 | } 11 | 12 | const env = 'test'; 13 | // eslint-disable-next-line 14 | let base: Config = { 15 | env, 16 | mongo: { 17 | connection: process.env.TEST_MONGO_URL || 'mongodb://root:root@mongo/node-mongo-tests?authSource=admin&replicaSet=rs', 18 | dbName: 'node-mongo-tests', 19 | }, 20 | }; 21 | 22 | export const load = (): Config => { 23 | let resultConfig = base; 24 | 25 | let localConfig = { default: {} }; 26 | try { 27 | // eslint-disable-next-line @typescript-eslint/no-var-requires 28 | localConfig = require('./local'); 29 | resultConfig = _.merge(resultConfig, localConfig.default); 30 | // eslint-disable-next-line no-empty 31 | } catch {} 32 | 33 | return resultConfig; 34 | }; 35 | 36 | base = load(); 37 | 38 | export default base; 39 | -------------------------------------------------------------------------------- /src/database.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from 'events'; 2 | import { 3 | MongoClient, 4 | MongoClientOptions, Db, 5 | CollectionOptions, 6 | CreateCollectionOptions, 7 | Collection, 8 | MongoError, 9 | } from 'mongodb'; 10 | import ServiceOptions from './types/ServiceOptions'; 11 | import Service from './service'; 12 | import logger from './logger'; 13 | import OutboxService from './outboxService'; 14 | 15 | const defaultOptions = { 16 | useNewUrlParser: true, 17 | useUnifiedTopology: true, 18 | }; 19 | 20 | class Database extends EventEmitter { 21 | url: string; 22 | 23 | dbName?: string; 24 | 25 | options: MongoClientOptions; 26 | 27 | private db?: Db; 28 | 29 | connectPromise: Promise; 30 | 31 | connectPromiseResolve?: (value: void) => void; 32 | 33 | outboxService: OutboxService; 34 | 35 | private client?: MongoClient; 36 | 37 | constructor(url: string, dbName?: string, options?: MongoClientOptions) { 38 | super(); 39 | 40 | this.url = url; 41 | this.dbName = dbName; 42 | this.options = { 43 | ...defaultOptions, 44 | ...options, 45 | }; 46 | 47 | this.connectPromise = new Promise((res) => { this.connectPromiseResolve = res; }); 48 | this.outboxService = new OutboxService(this.getOrCreateCollection, this.waitForConnection); 49 | 50 | this.db = undefined; 51 | } 52 | 53 | public waitForConnection = async (): Promise => { 54 | await this.connectPromise; 55 | }; 56 | 57 | public getOutboxService = (): OutboxService => this.outboxService; 58 | 59 | connect = async (): Promise => { 60 | try { 61 | this.client = await MongoClient.connect(this.url, this.options); 62 | this.db = this.client.db(this.dbName); 63 | 64 | this.emit('connected'); 65 | logger.info('Connected to mongodb.'); 66 | this.client.on('close', this.onClose); 67 | 68 | if (this.connectPromiseResolve) { 69 | this.connectPromiseResolve(); 70 | } 71 | } catch (e) { 72 | this.emit('error', e); 73 | } 74 | }; 75 | 76 | close = async (): Promise => { 77 | if (!this.client) { 78 | return; 79 | } 80 | logger.info('Disconnecting from mongodb.'); 81 | await this.client.close(); 82 | }; 83 | 84 | createService(collectionName: string, options?: ServiceOptions | undefined): Service { 85 | return new Service( 86 | collectionName, 87 | this, 88 | options, 89 | ); 90 | } 91 | 92 | async ping(): Promise { 93 | await this.waitForConnection(); 94 | 95 | if (!this.db) { 96 | return null; 97 | } 98 | 99 | return this.db.command({ ping: 1 }); 100 | } 101 | 102 | private onClose(error: any) { 103 | this.emit('disconnected', error); 104 | } 105 | 106 | public getOrCreateCollection = async ( 107 | name: string, 108 | opt: { 109 | collectionCreateOptions?: CreateCollectionOptions; 110 | collectionOptions?: CollectionOptions; 111 | } = {}, 112 | ): Promise> => { 113 | await this.waitForConnection(); 114 | 115 | if (!this.db) { 116 | throw new Error('The db object is not defined'); 117 | } 118 | 119 | try { 120 | await this.db.createCollection(name, opt.collectionCreateOptions || {}); 121 | } catch (error) { 122 | if (error instanceof MongoError && error.code === 48) { 123 | return this.db.collection(name, opt.collectionOptions || {}); 124 | } 125 | throw error; 126 | } 127 | 128 | return this.db.collection(name, opt.collectionOptions || {}); 129 | }; 130 | 131 | public getClient = async (): Promise => { 132 | await this.connectPromise; 133 | return this.client; 134 | }; 135 | } 136 | 137 | export default Database; 138 | -------------------------------------------------------------------------------- /src/dbChangePublisher.ts: -------------------------------------------------------------------------------- 1 | import { 2 | DbChangeData, DbChangeType, IChangePublisher, InMemoryEvent, 3 | } from './types'; 4 | 5 | import inMemoryBus from './inMemoryEventBus'; 6 | 7 | class InMemoryPublisher implements IChangePublisher { 8 | private _bus = inMemoryBus; 9 | 10 | private static getEventName(collectionName: string, eventType: DbChangeType) { 11 | return `${collectionName}.${eventType}d`; 12 | } 13 | 14 | async publishDbChange( 15 | collectionName: string, 16 | changeType: DbChangeType, 17 | eventData: DbChangeData, 18 | ): Promise { 19 | const name = InMemoryPublisher.getEventName(collectionName, changeType); 20 | const evt: Partial = { 21 | name, 22 | data: { 23 | object: eventData.data, 24 | diff: eventData.diff, 25 | change: null, 26 | }, 27 | }; 28 | 29 | this._bus.publish(name, evt); 30 | } 31 | 32 | async publishDbChanges( 33 | collectionName: string, 34 | changeType: DbChangeType, 35 | eventsData: DbChangeData[], 36 | ): Promise { 37 | eventsData.forEach((evt: DbChangeData) => { 38 | this.publishDbChange(collectionName, changeType, evt); 39 | }); 40 | } 41 | } 42 | 43 | const inMemoryPublisher = new InMemoryPublisher(); 44 | 45 | export default inMemoryPublisher; 46 | -------------------------------------------------------------------------------- /src/idGenerator.ts: -------------------------------------------------------------------------------- 1 | import { ObjectId } from 'mongodb'; 2 | 3 | const generateId = () => { 4 | const objectId = new ObjectId(); 5 | return objectId.toHexString(); 6 | }; 7 | 8 | export { generateId }; 9 | -------------------------------------------------------------------------------- /src/inMemoryEventBus.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from 'events'; 2 | 3 | import { 4 | InMemoryEvent, InMemoryEventHandler, 5 | } from './types'; 6 | 7 | class EventBus { 8 | private _bus: EventEmitter; 9 | 10 | constructor() { 11 | this._bus = new EventEmitter(); 12 | } 13 | 14 | async publish( 15 | name: string, event: Partial, 16 | ): Promise { 17 | const evtCopy = { 18 | ...event, 19 | }; 20 | if (!evtCopy.createdOn) { 21 | evtCopy.createdOn = new Date().toISOString(); 22 | } 23 | 24 | this._bus.emit(name, evtCopy); 25 | } 26 | 27 | on = (eventName: string, handler: InMemoryEventHandler): void => { 28 | this._bus.on(eventName, handler); 29 | }; 30 | 31 | once = (eventName: string, handler: InMemoryEventHandler): void => { 32 | this._bus.once(eventName, handler); 33 | }; 34 | } 35 | 36 | const eventBus = new EventBus(); 37 | 38 | export default eventBus; 39 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { ClientSession } from 'mongodb'; 2 | import { InMemoryEvent } from './types'; 3 | 4 | import Database from './database'; 5 | import Service from './service'; 6 | import { generateId } from './idGenerator'; 7 | import inMemoryEventBus from './inMemoryEventBus'; 8 | import ServiceOptions from './types/ServiceOptions'; 9 | 10 | export { 11 | Database, 12 | Service, 13 | generateId, 14 | }; 15 | 16 | export { 17 | ClientSession, 18 | ServiceOptions, 19 | }; 20 | 21 | export { 22 | inMemoryEventBus, 23 | InMemoryEvent, 24 | }; 25 | 26 | export default Database; 27 | -------------------------------------------------------------------------------- /src/logger.ts: -------------------------------------------------------------------------------- 1 | const globalAny: any = global; 2 | 3 | const logger: Console = globalAny.logger || console; 4 | 5 | export default logger; 6 | -------------------------------------------------------------------------------- /src/main.d.ts: -------------------------------------------------------------------------------- 1 | import IDatabase from './types/IDatabase'; 2 | import ServiceOptions from './types/ServiceOptions'; 3 | 4 | export { 5 | IDatabase, 6 | ServiceOptions, 7 | }; 8 | -------------------------------------------------------------------------------- /src/outboxService.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Collection, 3 | CreateCollectionOptions, 4 | InsertOneOptions, 5 | BulkWriteOptions, 6 | CollectionOptions, 7 | } from 'mongodb'; 8 | import { 9 | DbChangeData, IChangePublisher, PublishEventOptions, OutboxEvent, DbChangeType, 10 | } from './types'; 11 | import { generateId } from './idGenerator'; 12 | 13 | class OutboxService implements IChangePublisher { 14 | private getOrCreateCollection: ( 15 | name: string, 16 | opt: { 17 | collectionCreateOptions: CreateCollectionOptions; collectionOptions: CollectionOptions, 18 | }, 19 | ) => Promise | null>; 20 | 21 | private connectionPromise: Promise; 22 | 23 | private connectionPromiseResolve?: (value: void) => void; 24 | 25 | private collectionsMap: { [key: string]: Collection | null } = {}; 26 | 27 | constructor( 28 | getOrCreateCollection: ( 29 | name: string, 30 | opt: { 31 | collectionCreateOptions: CreateCollectionOptions; 32 | collectionOptions: CollectionOptions, 33 | }, 34 | ) => Promise | null>, 35 | waitForConnection: () => Promise, 36 | ) { 37 | this.connectionPromise = new Promise((res) => { this.connectionPromiseResolve = res; }); 38 | 39 | waitForConnection().then(() => { 40 | if (this.connectionPromiseResolve) { 41 | this.connectionPromiseResolve(); 42 | } 43 | }); 44 | 45 | this.getOrCreateCollection = getOrCreateCollection; 46 | } 47 | 48 | private async waitForConnection() { 49 | await this.connectionPromise; 50 | } 51 | 52 | private getCollection = async (collectionName: string) => { 53 | if (this.collectionsMap[collectionName]) { 54 | return this.collectionsMap[collectionName]; 55 | } 56 | 57 | const name = `${collectionName}_outbox`; 58 | 59 | const collection = await this.getOrCreateCollection( 60 | name, { collectionCreateOptions: {}, collectionOptions: {} }, 61 | ); 62 | 63 | this.collectionsMap[collectionName] = collection; 64 | 65 | return collection; 66 | }; 67 | 68 | private async createEvent( 69 | collectionName: string, 70 | changeType: DbChangeType, 71 | data: DbChangeData, 72 | option: InsertOneOptions = {}, 73 | ): Promise { 74 | await this.waitForConnection(); 75 | const collection = await this.getCollection(collectionName); 76 | if (!collection) { 77 | return null; 78 | } 79 | 80 | const event: OutboxEvent = { 81 | _id: generateId(), 82 | createdOn: new Date().toISOString(), 83 | type: changeType, 84 | ...data, 85 | }; 86 | 87 | await collection.insertOne(event, option); 88 | 89 | return event; 90 | } 91 | 92 | private async createManyEvents( 93 | collectionName: string, 94 | changeType: DbChangeType, 95 | data: DbChangeData[], 96 | option: BulkWriteOptions = {}, 97 | ): Promise { 98 | await this.waitForConnection(); 99 | const collection = await this.getCollection(collectionName); 100 | if (!collection) { 101 | return null; 102 | } 103 | 104 | const events: OutboxEvent[] = data.map((e) => ({ 105 | _id: generateId(), 106 | createdOn: new Date().toISOString(), 107 | type: changeType, 108 | ...e, 109 | })); 110 | 111 | await collection.insertMany(events, option); 112 | 113 | return events; 114 | } 115 | 116 | async publishDbChange( 117 | collectionName: string, 118 | changeType: DbChangeType, 119 | eventData: DbChangeData, 120 | options?: PublishEventOptions, 121 | ): Promise { 122 | await this.createEvent(collectionName, changeType, eventData, { session: options?.session }); 123 | } 124 | 125 | async publishDbChanges( 126 | collectionName: string, 127 | changeType: DbChangeType, 128 | eventsData: DbChangeData[], 129 | options?: PublishEventOptions, 130 | ): Promise { 131 | await this.createManyEvents( 132 | collectionName, changeType, eventsData, { session: options?.session }, 133 | ); 134 | } 135 | } 136 | 137 | export default OutboxService; 138 | -------------------------------------------------------------------------------- /src/service.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unused-expressions */ 2 | import chai from 'chai'; 3 | import spies from 'chai-spies'; 4 | 5 | import { Database, inMemoryEventBus } from './index'; 6 | 7 | import 'mocha'; 8 | 9 | import config from './config'; 10 | 11 | chai.use(spies); 12 | chai.should(); 13 | const { assert } = chai; 14 | 15 | const database = new Database(config.mongo.connection, config.mongo.dbName); 16 | 17 | type UserType = { 18 | _id: string; 19 | createdOn: string; 20 | fullName: string; 21 | deletedOn: string; 22 | }; 23 | 24 | const usersService = database.createService('users', { 25 | outbox: false, 26 | }); 27 | 28 | describe('mongo/service.ts', () => { 29 | before(async () => { 30 | await database.connect(); 31 | }); 32 | after(async () => { 33 | await usersService.remove({}); 34 | await database.close(); 35 | }); 36 | it('should create document', async () => { 37 | const u = await usersService.create({ 38 | fullName: 'John', 39 | }); 40 | 41 | u?.fullName.should.be.equal('John'); 42 | }); 43 | 44 | it('should update document', async () => { 45 | const u = await usersService.create({ 46 | fullName: 'User to update', 47 | }); 48 | 49 | await usersService.update( 50 | { _id: u?._id }, (doc) => ({ 51 | ...doc, 52 | fullName: 'Updated fullname', 53 | }), 54 | ); 55 | 56 | const updatedUser = await usersService.findOne({ 57 | _id: u?._id, 58 | }, { doNotAddDeletedOn: true }); 59 | 60 | updatedUser?.fullName.should.be.equal('Updated fullname'); 61 | }); 62 | 63 | it('should remove document', async () => { 64 | const u = await usersService.create({ 65 | fullName: 'User to remove', 66 | }); 67 | 68 | await usersService.remove({ _id: u?._id }); 69 | 70 | const removedUser = await usersService.findOne({ 71 | _id: u?._id, 72 | }); 73 | 74 | (removedUser === null).should.be.equal(true); 75 | }); 76 | 77 | it('should set deletedOn date to current JS date on remove', async () => { 78 | const u = await usersService.create({ 79 | fullName: 'User to remove', 80 | }); 81 | 82 | await usersService.removeSoft({ 83 | _id: u?._id, 84 | }); 85 | const updatedUser = await usersService.findOne({ 86 | _id: u?._id, 87 | }, { doNotAddDeletedOn: true }); 88 | 89 | assert.exists(updatedUser?.deletedOn); 90 | }); 91 | 92 | describe('dbChangesPublisher', () => { 93 | const collectionName = 'dbChanges'; 94 | const service = database.createService(collectionName, { 95 | outbox: false, 96 | }); 97 | 98 | after(async () => { 99 | await service.remove({}); 100 | }); 101 | 102 | it('should publish entity.created event on create', async () => { 103 | const spy = chai.spy(); 104 | inMemoryEventBus.on(`${collectionName}.created`, spy); 105 | await service.create({}); 106 | spy.should.have.been.called.at.least(1); 107 | }); 108 | 109 | it('should publish entity.updated event on update', async () => { 110 | const spy = chai.spy(); 111 | inMemoryEventBus.on(`${collectionName}.updated`, spy); 112 | const doc = await service.create({}); 113 | await service.update({ _id: doc._id }, () => ({ newField: true })); 114 | spy.should.have.been.called.at.least(1); 115 | }); 116 | 117 | it('should publish entity.removed event when document removed', async () => { 118 | const spy = chai.spy(); 119 | inMemoryEventBus.on(`${collectionName}.removed`, spy); 120 | const doc = await service.create({}); 121 | await service.remove({ _id: doc._id }); 122 | spy.should.have.been.called.at.least(1); 123 | }); 124 | }); 125 | }); 126 | -------------------------------------------------------------------------------- /src/service.ts: -------------------------------------------------------------------------------- 1 | import { cloneDeep, isArray } from 'lodash'; 2 | import { diff } from 'deep-diff'; 3 | import { 4 | ChangeStreamOptions, 5 | ClientSession, 6 | Collection, 7 | AggregateOptions, 8 | BulkWriteOptions, 9 | Filter, 10 | FindOptions, 11 | CreateIndexesOptions, 12 | MongoClient, 13 | IndexSpecification, 14 | OptionalUnlessRequiredId, 15 | TransactionOptions, 16 | UpdateFilter, 17 | } from 'mongodb'; 18 | 19 | import logger from './logger'; 20 | import ServiceOptions from './types/ServiceOptions'; 21 | import { generateId } from './idGenerator'; 22 | import inMemoryPublisher from './dbChangePublisher'; 23 | import IDatabase from './types/IDatabase'; 24 | import { DbChangeData, IChangePublisher } from './types'; 25 | 26 | const defaultOptions: ServiceOptions = { 27 | addCreatedOnField: true, 28 | addUpdatedOnField: true, 29 | outbox: false, 30 | requireDeletedOn: true, 31 | }; 32 | 33 | const transactionOptions: TransactionOptions = { 34 | readConcern: { level: 'local' }, 35 | writeConcern: { w: 1 }, 36 | }; 37 | 38 | export type Document = { 39 | _id?: string; 40 | updatedOn?: string; 41 | deletedOn?: string | null; 42 | createdOn?: string; 43 | }; 44 | 45 | export type FindResult = { 46 | /** 47 | * Array of documents returned by query 48 | */ 49 | results: T[]; 50 | pagesCount: number; 51 | count: number; 52 | }; 53 | 54 | type GeneralRequestOptions = { 55 | session?: ClientSession; 56 | doNotAddDeletedOn?: boolean; 57 | }; 58 | 59 | type ExtendedFindOptions = FindOptions & { 60 | doNotAddDeletedOn?: boolean; 61 | page: number; 62 | perPage: number; 63 | }; 64 | 65 | class Service { 66 | private client?: MongoClient; 67 | 68 | private collection: Collection | null; 69 | 70 | private _collectionName: string; 71 | 72 | private options: ServiceOptions; 73 | 74 | private db; 75 | 76 | private waitForConnection: () => Promise; 77 | 78 | private changePublisher: IChangePublisher; 79 | 80 | constructor( 81 | collectionName: string, 82 | db: IDatabase, 83 | options: ServiceOptions = { }, 84 | ) { 85 | this._collectionName = collectionName; 86 | this.db = db; 87 | this.options = { 88 | ...defaultOptions, 89 | ...options, 90 | }; 91 | this.waitForConnection = db.waitForConnection; 92 | if (this.options.outbox) { 93 | this.changePublisher = db.getOutboxService(); 94 | } else { 95 | this.changePublisher = inMemoryPublisher; 96 | } 97 | this.collection = null; 98 | 99 | this.db.getClient() 100 | .then((client) => { 101 | this.client = client; 102 | }); 103 | } 104 | 105 | public get collectionName() : string { 106 | return this._collectionName; 107 | } 108 | 109 | public validateSchema = async (entity: T | Partial): Promise => { 110 | if (this.options.schema) { 111 | const { schema } = this.options; 112 | try { 113 | return await schema.validateAsync(entity); 114 | } catch (err: any) { 115 | logger.error(`Schema is not valid for ${this._collectionName} collection: ${err.stack || err}`, entity); 116 | throw err; 117 | } 118 | } 119 | 120 | return entity as T; 121 | }; 122 | 123 | protected addQueryDefaults = ( 124 | query: Filter, options: H, 125 | ): Filter => { 126 | if (!query) { 127 | throw new Error(`MongoDB service query for [${this.collectionName}] collection must be defined`); 128 | } 129 | if (query.deletedOn) { 130 | return query; 131 | } 132 | if (!this.options.requireDeletedOn) { 133 | return query; 134 | } 135 | if (options.doNotAddDeletedOn) { 136 | return query; 137 | } 138 | 139 | return { ...query, deletedOn: { $exists: false } }; 140 | }; 141 | 142 | protected validateQuery = (query: any, options: GeneralRequestOptions) => { 143 | // A hook to add query validation 144 | // Often used to check if some required keys are always in the query 145 | // e.x. companyId or workspaceId 146 | }; 147 | 148 | protected getCollection = async (): Promise> => { 149 | await this.waitForConnection(); 150 | if (!this.collection) { 151 | this.collection = await this.db.getOrCreateCollection(this.collectionName, { 152 | collectionCreateOptions: this.options.collectionCreateOptions || {}, 153 | collectionOptions: this.options.collectionOptions || {}, 154 | }); 155 | if (!this.collection) { 156 | throw new Error(`Mongo collection ${this._collectionName} is not initialized.`); 157 | } 158 | } 159 | 160 | return this.collection; 161 | }; 162 | 163 | findOne = async ( 164 | query: Filter = { }, 165 | options: GeneralRequestOptions = {}, 166 | ): Promise => { 167 | const collection = await this.getCollection(); 168 | 169 | query = this.addQueryDefaults(query, options); 170 | this.validateQuery(query, options); 171 | 172 | return collection.findOne(query); 173 | }; 174 | 175 | findAll = async ( 176 | query: Filter, 177 | options: FindOptions, 178 | ): Promise => { 179 | const collection = await this.getCollection(); 180 | return collection.find(query, options).toArray(); 181 | }; 182 | 183 | find = async ( 184 | query: Filter = {}, 185 | options: ExtendedFindOptions = { perPage: 100, page: 0 }, 186 | ): Promise> => { 187 | const collection = await this.getCollection(); 188 | 189 | query = this.addQueryDefaults(query, options); 190 | this.validateQuery(query, options); 191 | 192 | const { 193 | page, 194 | perPage, 195 | ...opts 196 | } = options; 197 | 198 | const findOptions: Record = { 199 | page, 200 | perPage, 201 | ...opts, 202 | }; 203 | const hasPaging = page > 0; 204 | if (hasPaging) { 205 | findOptions.skip = (page - 1) * perPage; 206 | findOptions.limit = perPage; 207 | } 208 | 209 | const results = await collection 210 | .find(query, findOptions) 211 | .toArray(); 212 | const count = await collection.countDocuments(query); 213 | const pagesCount = Math.ceil(count / perPage) || 1; 214 | 215 | return { 216 | pagesCount, 217 | results, 218 | count, 219 | }; 220 | }; 221 | 222 | cursor = async ( 223 | query: Filter, opt: FindOptions = {}, 224 | ): Promise => { 225 | const collection = await this.getCollection(); 226 | 227 | return collection.find(query, opt); 228 | }; 229 | 230 | exists = async (query: Filter, options: GeneralRequestOptions = {}): Promise => { 231 | const doc = await this.findOne(query, options); 232 | query = this.addQueryDefaults(query, options); 233 | this.validateQuery(query, options); 234 | 235 | return !!doc; 236 | }; 237 | 238 | countDocuments = async ( 239 | query: Filter, options: GeneralRequestOptions = {}, 240 | ): Promise => { 241 | const collection = await this.getCollection(); 242 | query = this.addQueryDefaults(query, options); 243 | this.validateQuery(query, options); 244 | 245 | return collection.countDocuments(query); 246 | }; 247 | 248 | async create(object: Partial, options?: GeneralRequestOptions): Promise; 249 | async create(objects: Partial[], options?: GeneralRequestOptions): Promise; 250 | async create( 251 | objects: Partial[] | Partial, 252 | options: GeneralRequestOptions = {}, 253 | ): Promise { 254 | const collection = await this.getCollection(); 255 | 256 | if (!this.client) { 257 | throw new Error('MongoDB client is not connected'); 258 | } 259 | 260 | const isCreateMany = isArray(objects); 261 | let entities: Partial[] = []; 262 | if (isCreateMany) { 263 | entities = objects as Partial[]; 264 | } else { 265 | entities = [objects as Partial]; 266 | } 267 | if (entities.length === 0) { 268 | return []; 269 | } 270 | 271 | const validEntities = await Promise.all(entities.map(async (item: Partial) => { 272 | const entity = item; 273 | if (!entity._id) { 274 | entity._id = generateId(); 275 | } 276 | 277 | if (!entity.createdOn && this.options.addCreatedOnField) { 278 | entity.createdOn = new Date().toISOString(); 279 | } 280 | if (!entity.updatedOn && this.options.addUpdatedOnField) { 281 | entity.updatedOn = new Date().toISOString(); 282 | } 283 | 284 | return this.validateSchema(entity); 285 | })); 286 | 287 | const transactionCreate = async (session: ClientSession): Promise => { 288 | if (!isArray(validEntities)) { // ts bug 289 | return; 290 | } 291 | 292 | await this.changePublisher.publishDbChanges(this._collectionName, 'create', validEntities.map((e) => ({ 293 | data: e, 294 | })), { session }); 295 | 296 | await collection.insertMany( 297 | validEntities as OptionalUnlessRequiredId[], 298 | { session }, 299 | ); 300 | }; 301 | 302 | if (options?.session) { 303 | await transactionCreate(options.session); 304 | } else { 305 | await this.withTransaction(async (session) => { 306 | await transactionCreate(session); 307 | }); 308 | } 309 | 310 | return isCreateMany ? entities as T[] : entities[0] as T; 311 | } 312 | 313 | update = async ( 314 | query: Filter, 315 | updateFn: (doc: T) => void | Partial, 316 | options?: GeneralRequestOptions, 317 | ): Promise => { 318 | const collection = await this.getCollection(); 319 | if (!this.client) { 320 | return null; 321 | } 322 | 323 | const doc = await this.findOne(query, options); 324 | if (!doc) { 325 | logger.warn(`Document not found when updating ${this._collectionName} collection. Request query — ${JSON.stringify(query)}`); 326 | return null; 327 | } 328 | 329 | const prevDoc = cloneDeep(doc); 330 | if (this.options.addUpdatedOnField) { 331 | doc.updatedOn = new Date().toISOString(); 332 | } 333 | 334 | // Update function can return full document for update or partial 335 | const mbUpdatedDoc = await updateFn(doc); 336 | let updatedDoc: T = doc as T; 337 | if (mbUpdatedDoc) { 338 | updatedDoc = { 339 | ...updatedDoc, 340 | ...mbUpdatedDoc, 341 | }; 342 | } 343 | const validatedDoc = await this.validateSchema(updatedDoc); 344 | 345 | let isUpdated = false; 346 | 347 | const transactionUpdate = async (session: ClientSession) => { 348 | const updateResult = await collection.updateOne({ 349 | _id: validatedDoc._id, 350 | } as Filter, { $set: validatedDoc }, { session }); 351 | isUpdated = updateResult.modifiedCount === 1; 352 | 353 | if (isUpdated) { 354 | const change: DbChangeData = { 355 | data: { 356 | ...validatedDoc, 357 | }, 358 | diff: diff(prevDoc, validatedDoc), 359 | }; 360 | await this.changePublisher.publishDbChange(this._collectionName, 'update', change, { session }); 361 | } 362 | }; 363 | 364 | if (options?.session) { 365 | await transactionUpdate(options.session); 366 | } else { 367 | await this.withTransaction(async (session) => { 368 | await transactionUpdate(session); 369 | }); 370 | } 371 | 372 | return isUpdated ? validatedDoc : null; 373 | }; 374 | 375 | /** 376 | * Set deletedOn field and send removed event 377 | */ 378 | removeSoft = async (query: Filter, options: GeneralRequestOptions = {}): Promise => { 379 | const collection = await this.getCollection(); 380 | query = this.addQueryDefaults(query, options); 381 | this.validateQuery(query, options); 382 | if (!this.client) { 383 | return []; 384 | } 385 | 386 | const docs = await collection.find(query).toArray(); 387 | 388 | if (docs.length === 0) { 389 | return []; 390 | } 391 | 392 | const transactionRemoveSoft = async (session: ClientSession) => { 393 | const dbChanges = docs.map((doc: any) => ({ 394 | entity: this._collectionName, 395 | data: doc, 396 | })); 397 | 398 | await this.changePublisher.publishDbChanges(this._collectionName, 'remove', dbChanges, { session }); 399 | 400 | const uq: any = { 401 | $set: { 402 | deletedOn: new Date().toISOString(), 403 | }, 404 | }; 405 | 406 | await collection.updateMany( 407 | query, 408 | uq, 409 | { session }, 410 | ); 411 | }; 412 | 413 | if (options?.session) { 414 | await transactionRemoveSoft(options.session); 415 | } else { 416 | await this.withTransaction(async (session) => { 417 | await transactionRemoveSoft(session); 418 | }); 419 | } 420 | 421 | return docs; 422 | }; 423 | 424 | remove = async ( 425 | query: Filter, 426 | options: GeneralRequestOptions = {}, 427 | ): Promise => { 428 | const collection = await this.getCollection(); 429 | this.validateQuery(query, options); 430 | if (!this.client) { 431 | return []; 432 | } 433 | 434 | const docs = await collection.find(query).toArray(); 435 | 436 | if (docs.length === 0) { 437 | return []; 438 | } 439 | 440 | const transactionRemove = async (session: ClientSession) => { 441 | const changes = docs.map((doc: any) => ({ 442 | type: 'remove', 443 | entity: this._collectionName, 444 | data: doc, 445 | })); 446 | await this.changePublisher.publishDbChanges(this._collectionName, 'remove', changes, { session }); 447 | await collection.deleteMany(query, { session }); 448 | }; 449 | 450 | if (options?.session) { 451 | await transactionRemove(options.session); 452 | } else { 453 | await this.withTransaction(async (session) => { 454 | await transactionRemove(session); 455 | }); 456 | } 457 | 458 | return docs; 459 | }; 460 | 461 | ensureIndex = async ( 462 | index: IndexSpecification, 463 | options: CreateIndexesOptions = {}, 464 | ): Promise => { 465 | const collection = await this.getCollection(); 466 | 467 | return collection.createIndex(index, options) 468 | .catch((err: any) => { 469 | logger.info(err, { collection: this.collectionName }); 470 | }); 471 | }; 472 | 473 | createIndex = async ( 474 | index: IndexSpecification, 475 | options: CreateIndexesOptions = {}, 476 | ): Promise => this.ensureIndex(index, options); 477 | 478 | dropIndexes = async ( 479 | options: TransactionOptions = {}, 480 | ): Promise => { 481 | const collection = await this.getCollection(); 482 | 483 | return collection.dropIndexes(options) 484 | .catch((err: any) => { 485 | logger.info(err); 486 | }); 487 | }; 488 | 489 | dropIndex = async ( 490 | indexName: string, 491 | options: TransactionOptions = {}, 492 | ): Promise => { 493 | const collection = await this.getCollection(); 494 | 495 | return collection.dropIndex(indexName, options) 496 | .catch((err: any) => { 497 | logger.info(err); 498 | }); 499 | }; 500 | 501 | watch = async ( 502 | pipeline: any[] | undefined, 503 | options?: ChangeStreamOptions & { session: ClientSession }, 504 | ): Promise => { 505 | const collection = await this.getCollection(); 506 | 507 | return collection.watch(pipeline, options); 508 | }; 509 | 510 | distinct = async ( 511 | key: string, query: Filter, 512 | options: GeneralRequestOptions = {}, 513 | ): Promise => { 514 | const collection = await this.getCollection(); 515 | query = this.addQueryDefaults(query, options); 516 | this.validateQuery(query, options); 517 | 518 | return collection.distinct(key, query); 519 | }; 520 | 521 | aggregate = async (query: any[], options?: AggregateOptions): Promise => { 522 | const collection = await this.getCollection(); 523 | return collection.aggregate(query, options); 524 | }; 525 | 526 | dropCollection = async (recreate = false): Promise => { 527 | const collection = await this.getCollection(); 528 | await collection.drop(); 529 | 530 | if (recreate) { 531 | this.collection = await this.db.getOrCreateCollection(this.collectionName, { 532 | collectionCreateOptions: this.options.collectionCreateOptions || {}, 533 | collectionOptions: this.options.collectionOptions || {}, 534 | }); 535 | } else { 536 | this.collection = null; 537 | } 538 | }; 539 | 540 | atomic = { 541 | deleteMany: async ( 542 | query: Filter, 543 | options: GeneralRequestOptions = {}, 544 | ): Promise => { 545 | const collection = await this.getCollection(); 546 | this.validateQuery(query, options); 547 | 548 | return collection.deleteMany(query); 549 | }, 550 | insertMany: async ( 551 | doc: OptionalUnlessRequiredId[], options: BulkWriteOptions = {}, 552 | ): Promise => { 553 | const collection = await this.getCollection(); 554 | 555 | return collection.insertMany(doc, options); 556 | }, 557 | updateMany: async ( 558 | query: Filter, 559 | update: Partial | UpdateFilter, 560 | options: GeneralRequestOptions = {}, 561 | ): Promise => { 562 | options.doNotAddDeletedOn = true; 563 | const collection = await this.getCollection(); 564 | query = this.addQueryDefaults(query, options); 565 | this.validateQuery(query, options); 566 | 567 | return collection.updateMany(query, update); 568 | }, 569 | findOneAndUpdate: async ( 570 | query: Filter, 571 | update: T | UpdateFilter, 572 | options: GeneralRequestOptions = {}, 573 | ): Promise => { 574 | options.doNotAddDeletedOn = true; 575 | const collection = await this.getCollection(); 576 | query = this.addQueryDefaults(query, options); 577 | this.validateQuery(query, options); 578 | 579 | let result = null; 580 | try { 581 | result = await collection.findOneAndUpdate(query, update); 582 | } catch (err: any) { 583 | logger.error(`${this.collectionName}, findOneAndUpdate() error: ${err.stack || err}`, { query, update, options }); 584 | throw err; 585 | } 586 | 587 | return result; 588 | }, 589 | }; 590 | 591 | generateId = (): string => generateId(); 592 | 593 | async withTransaction( 594 | transactionFn: (session: ClientSession) => Promise, 595 | ): Promise { 596 | if (!this.client) { 597 | throw new Error('MongoDB client is not connected'); 598 | } 599 | 600 | const session = this.client.startSession(); 601 | 602 | let res: any; 603 | try { 604 | await session.withTransaction(async () => { 605 | res = await transactionFn(session); 606 | }, transactionOptions); 607 | } catch (error: any) { 608 | logger.error(error.stack || error); 609 | throw error; 610 | } finally { 611 | await session.endSession(); 612 | } 613 | 614 | return res as TRes; 615 | } 616 | } 617 | 618 | export default Service; 619 | -------------------------------------------------------------------------------- /src/types/IDatabase.ts: -------------------------------------------------------------------------------- 1 | import { 2 | MongoClient, 3 | Collection, 4 | CreateCollectionOptions, 5 | CollectionOptions, 6 | } from 'mongodb'; 7 | 8 | import OutboxService from '../outboxService'; 9 | 10 | export default interface IDatabase { 11 | getOutboxService: () => OutboxService; 12 | waitForConnection: () => Promise; 13 | getOrCreateCollection: ( 14 | name: string, 15 | opt: { 16 | collectionCreateOptions: CreateCollectionOptions; 17 | collectionOptions: CollectionOptions; 18 | }, 19 | ) => Promise | null>; 20 | 21 | getClient: () => Promise; 22 | } 23 | -------------------------------------------------------------------------------- /src/types/ServiceOptions.ts: -------------------------------------------------------------------------------- 1 | import { ObjectSchema } from 'joi'; 2 | import { 3 | CollectionOptions, 4 | CreateCollectionOptions, 5 | } from 'mongodb'; 6 | 7 | export default interface ServiceOptions { 8 | addCreatedOnField?: boolean; 9 | addUpdatedOnField?: boolean; 10 | outbox?: boolean; 11 | schema?: ObjectSchema; 12 | collectionOptions?: CollectionOptions; 13 | collectionCreateOptions?: CreateCollectionOptions; 14 | requireDeletedOn?: boolean; 15 | } 16 | -------------------------------------------------------------------------------- /src/types/index.ts: -------------------------------------------------------------------------------- 1 | import { ClientSession } from 'mongodb'; 2 | 3 | export type DbChangeType = 'create' | 'update' | 'remove'; 4 | 5 | export interface DbChangeData { 6 | data: any; 7 | diff?: any[]; 8 | 9 | entity?: string; 10 | } 11 | 12 | export type InMemoryEvent = { 13 | name: string; 14 | createdOn: string; 15 | userId?: string; 16 | data: { 17 | object: T, 18 | diff?: any[], 19 | change?: TChange, 20 | }, 21 | }; 22 | 23 | export type InMemoryEventHandler = (evt: InMemoryEvent) => Promise | void; 24 | 25 | export type PublishEventOptions = { 26 | session: ClientSession | undefined 27 | }; 28 | 29 | export type IChangePublisher = { 30 | publishDbChange: ( 31 | collectionName: string, 32 | changeType: DbChangeType, 33 | eventData: DbChangeData, 34 | options?: PublishEventOptions, 35 | ) => Promise, 36 | publishDbChanges: ( 37 | collectionName: string, 38 | changeType: DbChangeType, 39 | eventsData: DbChangeData[], 40 | options?: PublishEventOptions, 41 | ) => Promise, 42 | }; 43 | 44 | export type OutboxEvent = { 45 | _id: string; 46 | type: 'create' | 'update' | 'remove'; 47 | data: any; 48 | diff?: any[]; 49 | createdOn: string; 50 | }; 51 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "target": "ES2017" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, 5 | "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, 6 | "lib": ["es6"] /* Specify library files to be included in the compilation. */, 7 | "declaration": true, 8 | "outDir": "./dist", 9 | "strict": true, 10 | "esModuleInterop": true, 11 | "moduleResolution": "node", 12 | "skipLibCheck": true, 13 | "noImplicitThis": false, 14 | "declarationMap": true 15 | }, 16 | "include": ["src"], 17 | "exclude": ["node_modules"] 18 | } 19 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["tslint:recommended", "tslint-config-prettier"], 3 | "rules": { 4 | "object-literal-sort-keys": false, 5 | "ordered-imports": false 6 | } 7 | } --------------------------------------------------------------------------------