├── .dockerignore ├── .github ├── FUNDING.yml └── workflows │ └── test.yml ├── .gitignore ├── Dockerfile ├── LICENSE.txt ├── README.md ├── docker-compose.yml ├── index.d.ts ├── index.js ├── index.test.js ├── libqp ├── LICENSE ├── README.md ├── index.d.ts └── index.js ├── mail ├── 01.eml ├── 02.eml ├── 03.eml └── 04.eml ├── package-lock.json ├── package.json ├── sendmail.sh ├── tsconfig.d.json ├── tsconfig.json └── wait-for-hosts.sh /.dockerignore: -------------------------------------------------------------------------------- 1 | * 2 | !/wait-for-hosts.sh 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [blueimp] 2 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - name: Setup Node.js 11 | uses: actions/setup-node@v1 12 | with: 13 | node-version: 12.x 14 | - name: docker-compose build 15 | run: docker-compose build 16 | - name: npm install 17 | run: npm install 18 | env: 19 | CI: true 20 | - name: npm test 21 | run: npm test 22 | env: 23 | CI: true 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.14 2 | 3 | RUN apk --no-cache add \ 4 | tini \ 5 | nodejs \ 6 | npm \ 7 | && npm install -g \ 8 | npm@latest \ 9 | mocha@9 \ 10 | # Clean up obsolete files: 11 | && rm -rf \ 12 | /tmp/* \ 13 | /root/.npm 14 | 15 | USER nobody 16 | 17 | WORKDIR /opt 18 | 19 | COPY wait-for-hosts.sh /usr/local/bin/wait-for-hosts 20 | 21 | ENTRYPOINT ["tini", "-g", "--", "wait-for-hosts", "--", "mocha"] 22 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright © 2016 Sebastian Tschan, https://blueimp.net 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mailhog 2 | 3 | > A NodeJS library to interact with the 4 | > [MailHog](https://github.com/mailhog/MailHog) API. 5 | 6 | ## Contents 7 | 8 | - [Installation](#installation) 9 | - [Initialization](#initialization) 10 | - [Description](#description) 11 | - [Parameters](#parameters) 12 | - [Returns](#returns) 13 | - [Example](#example) 14 | - [API](#api) 15 | - [messages](#messages) 16 | - [Description](#description-1) 17 | - [Parameters](#parameters-1) 18 | - [Returns](#returns-1) 19 | - [Example](#example-1) 20 | - [search](#search) 21 | - [Description](#description-2) 22 | - [Parameters](#parameters-2) 23 | - [Returns](#returns-2) 24 | - [Example](#example-2) 25 | - [latestFrom](#latestfrom) 26 | - [Description](#description-3) 27 | - [Parameters](#parameters-3) 28 | - [Returns](#returns-3) 29 | - [Example](#example-3) 30 | - [latestTo](#latestto) 31 | - [Description](#description-4) 32 | - [Parameters](#parameters-4) 33 | - [Returns](#returns-4) 34 | - [Example](#example-4) 35 | - [latestContaining](#latestcontaining) 36 | - [Description](#description-5) 37 | - [Parameters](#parameters-5) 38 | - [Returns](#returns-5) 39 | - [Example](#example-5) 40 | - [releaseMessage](#releasemessage) 41 | - [Description](#description-6) 42 | - [Parameters](#parameters-6) 43 | - [Returns](#returns-6) 44 | - [Example](#example-6) 45 | - [deleteMessage](#deletemessage) 46 | - [Description](#description-7) 47 | - [Parameters](#parameters-7) 48 | - [Returns](#returns-7) 49 | - [Example](#example-7) 50 | - [deleteAll](#deleteall) 51 | - [Description](#description-8) 52 | - [Parameters](#parameters-8) 53 | - [Returns](#returns-8) 54 | - [Example](#example-8) 55 | - [encode](#encode) 56 | - [Description](#description-9) 57 | - [Parameters](#parameters-9) 58 | - [Returns](#returns-9) 59 | - [Example](#example-9) 60 | - [decode](#decode) 61 | - [Description](#description-10) 62 | - [Parameters](#parameters-10) 63 | - [Returns](#returns-10) 64 | - [Example](#example-10) 65 | - [Testing](#testing) 66 | - [License](#license) 67 | - [Author](#author) 68 | 69 | ## Installation 70 | 71 | ```sh 72 | npm install mailhog 73 | ``` 74 | 75 | ## Initialization 76 | 77 | ``` 78 | require('mailhog')(options) → Object 79 | ``` 80 | 81 | ### Description 82 | 83 | The `mailhog` module returns an initialization function. 84 | This function accepts an optional `options` object that is used for 85 | [http.request](https://nodejs.org/api/http.html#http_http_request_options_callback) 86 | calls to the MailHog API and returns the `mailhog` API object. 87 | 88 | ### Parameters 89 | 90 | | Name | Type | Required | Default | Description | 91 | | ---------------- | ------ | -------- | --------- | ------------------------ | 92 | | options.protocol | String | no | http: | API protocol | 93 | | options.host | String | no | localhost | API host | 94 | | options.port | Number | no | 8025 | API port | 95 | | options.auth | String | no | | API basic authentication | 96 | | options.basePath | String | no | /api | API base path | 97 | 98 | ### Returns 99 | 100 | Returns the `mailhog` API object with the following properties: 101 | 102 | ```js 103 | { 104 | options: Object, 105 | messages: Function, 106 | search: Function, 107 | latestFrom: Function, 108 | latestTo: Function, 109 | latestContaining: Function, 110 | releaseMessage: Function, 111 | deleteMessage: Function, 112 | deleteAll: Function, 113 | encode: Function, 114 | decode: Function 115 | } 116 | ``` 117 | 118 | ### Example 119 | 120 | ```js 121 | const mailhog = require('mailhog')({ 122 | host: 'mailhog' 123 | }) 124 | 125 | mailhog.messages().then(result => console.log(result)) 126 | ``` 127 | 128 | ## API 129 | 130 | The following API descriptions assume that the `mailhog` API object has been 131 | initialized. 132 | 133 | ### messages 134 | 135 | ``` 136 | mailhog.messages(start, limit) → Promise 137 | ``` 138 | 139 | #### Description 140 | 141 | Retrieves a list of mail objects, sorted from latest to earliest. 142 | 143 | #### Parameters 144 | 145 | | Name | Type | Required | Default | Description | 146 | | ----- | ------ | -------- | ------- | --------------------------------- | 147 | | start | Number | no | 0 | defines the messages query offset | 148 | | limit | Number | no | 50 | defines the max number of results | 149 | 150 | #### Returns 151 | 152 | Returns a `Promise` that resolves with an `Object`. 153 | 154 | The resolved result has the following properties: 155 | 156 | ```js 157 | { 158 | total: Number, // Number of results available 159 | count: Number, // Number of results returned 160 | start: Number, // Offset for the range of results returned 161 | items: Array // List of mail object items 162 | } 163 | ``` 164 | 165 | The individual mail object items have the following properties: 166 | 167 | ```js 168 | { 169 | ID: String, // Mail ID 170 | text: String, // Decoded mail text content 171 | html: String, // Decoded mail HTML content 172 | subject: String, // Decoded mail Subject header 173 | from: String, // Decoded mail From header 174 | to: String, // Decoded mail To header 175 | cc: String, // Decoded mail Cc header 176 | bcc: String, // Decoded mail Bcc header 177 | replyTo: String, // Decoded mail Reply-To header 178 | date: Date, // Mail Date header 179 | deliveryDate: Date, // Mail Delivery-Date header 180 | attachments: Array // List of mail attachments 181 | } 182 | ``` 183 | 184 | The individual attachments have the following properties: 185 | 186 | ```js 187 | { 188 | name: String, // Filename 189 | type: String, // Content-Type 190 | encoding: String, // Content-Transfer-Encoding 191 | Body: String // Encoded content 192 | } 193 | ``` 194 | 195 | #### Example 196 | 197 | ```js 198 | async function example() { 199 | // Retrieve the latest 10 messages: 200 | const result = await mailhog.messages(0, 10) 201 | 202 | // Log the details of each message to the console: 203 | for (let item of result.items) { 204 | console.log('From: ', item.from) 205 | console.log('To: ', item.to) 206 | console.log('Subject: ', item.subject) 207 | console.log('Content: ', item.text) 208 | } 209 | } 210 | ``` 211 | 212 | ### search 213 | 214 | ``` 215 | mailhog.search(query, kind, start, limit) → Promise 216 | ``` 217 | 218 | #### Description 219 | 220 | Retrieves a list of mail objects for the given query, sorted from latest to 221 | earliest. 222 | 223 | #### Parameters 224 | 225 | | Name | Type | Required | Default | Description | 226 | | ----- | ------ | -------- | ---------- | --------------------------------- | 227 | | query | String | yes | | search query | 228 | | kind | String | no | containing | query kind (from/to/containing) | 229 | | start | Number | no | 0 | defines the search query offset | 230 | | limit | Number | no | 50 | defines the max number of results | 231 | 232 | #### Returns 233 | 234 | Returns a `Promise` that resolves with an `Object`. 235 | 236 | The resolved result has the following properties: 237 | 238 | ```js 239 | { 240 | total: Number, // Number of results available 241 | count: Number, // Number of results returned 242 | start: Number, // Offset for the range of results returned 243 | items: Array // List of mail object items 244 | } 245 | ``` 246 | 247 | The individual mail object items have the following properties: 248 | 249 | ```js 250 | { 251 | ID: String, // Mail ID 252 | text: String, // Decoded mail text content 253 | html: String, // Decoded mail HTML content 254 | subject: String, // Decoded mail Subject header 255 | from: String, // Decoded mail From header 256 | to: String, // Decoded mail To header 257 | cc: String, // Decoded mail Cc header 258 | bcc: String, // Decoded mail Bcc header 259 | replyTo: String, // Decoded mail Reply-To header 260 | date: Date, // Mail Date header 261 | deliveryDate: Date, // Mail Delivery-Date header 262 | attachments: Array // List of mail attachments 263 | } 264 | ``` 265 | 266 | The individual attachments have the following properties: 267 | 268 | ```js 269 | { 270 | name: String, // Filename 271 | type: String, // Content-Type 272 | encoding: String, // Content-Transfer-Encoding 273 | Body: String // Encoded content 274 | } 275 | ``` 276 | 277 | #### Example 278 | 279 | ```js 280 | async function example() { 281 | // Search the latest 10 messages containing "banana": 282 | const result = await mailhog.search('banana', 'containing', 0, 10) 283 | 284 | // Log the details of each message to the console: 285 | for (let item of result.items) { 286 | console.log('From: ', item.from) 287 | console.log('To: ', item.to) 288 | console.log('Subject: ', item.subject) 289 | console.log('Content: ', item.text) 290 | } 291 | } 292 | ``` 293 | 294 | ### latestFrom 295 | 296 | ``` 297 | mailhog.latestFrom(query) → Promise 298 | ``` 299 | 300 | #### Description 301 | 302 | Retrieves the latest mail object sent from the given address. 303 | 304 | #### Parameters 305 | 306 | | Name | Type | Required | Description | 307 | | ----- | ------ | -------- | ------------ | 308 | | query | String | yes | from address | 309 | 310 | #### Returns 311 | 312 | Returns a `Promise` that resolves with an `Object`. 313 | 314 | The resolved mail object has the following properties: 315 | 316 | ```js 317 | { 318 | ID: String, // Mail ID 319 | text: String, // Decoded mail text content 320 | html: String, // Decoded mail HTML content 321 | subject: String, // Decoded mail Subject header 322 | from: String, // Decoded mail From header 323 | to: String, // Decoded mail To header 324 | cc: String, // Decoded mail Cc header 325 | bcc: String, // Decoded mail Bcc header 326 | replyTo: String, // Decoded mail Reply-To header 327 | date: Date, // Mail Date header 328 | deliveryDate: Date, // Mail Delivery-Date header 329 | attachments: Array // List of mail attachments 330 | } 331 | ``` 332 | 333 | The individual attachments have the following properties: 334 | 335 | ```js 336 | { 337 | name: String, // Filename 338 | type: String, // Content-Type 339 | encoding: String, // Content-Transfer-Encoding 340 | Body: String // Encoded content 341 | } 342 | ``` 343 | 344 | #### Example 345 | 346 | ```js 347 | async function example() { 348 | // Search the latest message from "test@example.org": 349 | const result = await mailhog.latestFrom('test@example.org') 350 | 351 | // Log the details of this message to the console: 352 | console.log('From: ', result.from) 353 | console.log('To: ', result.to) 354 | console.log('Subject: ', result.subject) 355 | console.log('Content: ', result.text) 356 | } 357 | ``` 358 | 359 | ### latestTo 360 | 361 | ``` 362 | mailhog.latestTo(query) → Promise 363 | ``` 364 | 365 | #### Description 366 | 367 | Retrieves the latest mail object sent to the given address. 368 | 369 | #### Parameters 370 | 371 | | Name | Type | Required | Description | 372 | | ----- | ------ | -------- | ----------- | 373 | | query | String | yes | to address | 374 | 375 | #### Returns 376 | 377 | Returns a `Promise` that resolves with an `Object`. 378 | 379 | The resolved mail object has the following properties: 380 | 381 | ```js 382 | { 383 | ID: String, // Mail ID 384 | text: String, // Decoded mail text content 385 | html: String, // Decoded mail HTML content 386 | subject: String, // Decoded mail Subject header 387 | from: String, // Decoded mail From header 388 | to: String, // Decoded mail To header 389 | cc: String, // Decoded mail Cc header 390 | bcc: String, // Decoded mail Bcc header 391 | replyTo: String, // Decoded mail Reply-To header 392 | date: Date, // Mail Date header 393 | deliveryDate: Date, // Mail Delivery-Date header 394 | attachments: Array // List of mail attachments 395 | } 396 | ``` 397 | 398 | The individual attachments have the following properties: 399 | 400 | ```js 401 | { 402 | name: String, // Filename 403 | type: String, // Content-Type 404 | encoding: String, // Content-Transfer-Encoding 405 | Body: String // Encoded content 406 | } 407 | ``` 408 | 409 | #### Example 410 | 411 | ```js 412 | async function example() { 413 | // Search the latest message to "test@example.org": 414 | const result = await mailhog.latestTo('test@example.org') 415 | 416 | // Log the details of this message to the console: 417 | console.log('From: ', result.from) 418 | console.log('To: ', result.to) 419 | console.log('Subject: ', result.subject) 420 | console.log('Content: ', result.text) 421 | } 422 | ``` 423 | 424 | ### latestContaining 425 | 426 | ``` 427 | mailhog.latestContaining(query) → Promise 428 | ``` 429 | 430 | #### Description 431 | 432 | Retrieves the latest mail object containing the given query. 433 | 434 | #### Parameters 435 | 436 | | Name | Type | Required | Description | 437 | | ----- | ------ | -------- | ------------ | 438 | | query | String | yes | search query | 439 | 440 | #### Returns 441 | 442 | Returns a `Promise` that resolves with an `Object`. 443 | 444 | The resolved mail object has the following properties: 445 | 446 | ```js 447 | { 448 | ID: String, // Mail ID 449 | text: String, // Decoded mail text content 450 | html: String, // Decoded mail HTML content 451 | subject: String, // Decoded mail Subject header 452 | from: String, // Decoded mail From header 453 | to: String, // Decoded mail To header 454 | cc: String, // Decoded mail Cc header 455 | bcc: String, // Decoded mail Bcc header 456 | replyTo: String, // Decoded mail Reply-To header 457 | date: Date, // Mail Date header 458 | deliveryDate: Date, // Mail Delivery-Date header 459 | attachments: Array // List of mail attachments 460 | } 461 | ``` 462 | 463 | The individual attachments have the following properties: 464 | 465 | ```js 466 | { 467 | name: String, // Filename 468 | type: String, // Content-Type 469 | encoding: String, // Content-Transfer-Encoding 470 | Body: String // Encoded content 471 | } 472 | ``` 473 | 474 | #### Example 475 | 476 | ```js 477 | async function example() { 478 | // Search the latest message containing "banana": 479 | const result = await mailhog.latestContaining('banana') 480 | 481 | // Log the details of this message to the console: 482 | console.log('From: ', result.from) 483 | console.log('To: ', result.to) 484 | console.log('Subject: ', result.subject) 485 | console.log('Content: ', result.text) 486 | } 487 | ``` 488 | 489 | ### releaseMessage 490 | 491 | ``` 492 | mailhog.releaseMessage(id, config) → Promise 493 | ``` 494 | 495 | #### Description 496 | 497 | Releases the mail with the given ID using the provided SMTP config. 498 | 499 | #### Parameters 500 | 501 | | Name | Type | Required | Description | 502 | | ---------------- | ------ | -------- | ---------------------------------- | 503 | | id | String | yes | message ID | 504 | | config | Object | yes | SMTP configuration | 505 | | config.host | String | yes | SMTP host | 506 | | config.port | String | yes | SMTP port | 507 | | config.email | String | yes | recipient email | 508 | | config.username | String | no | SMTP username | 509 | | config.password | String | no | SMTP password | 510 | | config.mechanism | String | no | SMTP auth type (PLAIN or CRAM-MD5) | 511 | 512 | #### Returns 513 | 514 | Returns a `Promise` that resolves with an 515 | [http.IncomingMessage](https://nodejs.org/api/http.html#http_class_http_incomingmessage) 516 | object. 517 | 518 | #### Example 519 | 520 | ```js 521 | async function example() { 522 | const result = await mailhog.latestTo('test@example.org') 523 | 524 | const response = await mailhog.releaseMessage(result.ID, { 525 | host: 'localhost', 526 | port: '1025', 527 | email: 'test@example.org' 528 | }) 529 | } 530 | ``` 531 | 532 | ### deleteMessage 533 | 534 | ``` 535 | mailhog.deleteMessage(id) → Promise 536 | ``` 537 | 538 | #### Description 539 | 540 | Deletes the mail with the given ID from MailHog. 541 | 542 | #### Parameters 543 | 544 | | Name | Type | Required | Description | 545 | | ---- | ------ | -------- | ----------- | 546 | | id | String | yes | message ID | 547 | 548 | #### Returns 549 | 550 | Returns a `Promise` that resolves with an 551 | [http.IncomingMessage](https://nodejs.org/api/http.html#http_class_http_incomingmessage) 552 | object. 553 | 554 | #### Example 555 | 556 | ```js 557 | async function example() { 558 | const result = await mailhog.latestTo('test@example.org') 559 | 560 | const response = await mailhog.deleteMessage(result.ID) 561 | 562 | console.log('Status code: ', response.statusCode) 563 | } 564 | ``` 565 | 566 | ### deleteAll 567 | 568 | ``` 569 | mailhog.deleteAll() → Promise 570 | ``` 571 | 572 | #### Description 573 | 574 | Deletes all mails stored in MailHog. 575 | 576 | #### Parameters 577 | 578 | None 579 | 580 | #### Returns 581 | 582 | Returns a `Promise` that resolves with an 583 | [http.IncomingMessage](https://nodejs.org/api/http.html#http_class_http_incomingmessage) 584 | object. 585 | 586 | #### Example 587 | 588 | ```js 589 | async function example() { 590 | const response = await mailhog.deleteAll() 591 | 592 | console.log('Status code: ', response.statusCode) 593 | } 594 | ``` 595 | 596 | ### encode 597 | 598 | ``` 599 | mailhog.encode(str, encoding, charset, lineLength) → String 600 | ``` 601 | 602 | #### Description 603 | 604 | Encodes a String in the given charset to base64 or quoted-printable encoding. 605 | 606 | #### Parameters 607 | 608 | | Name | Type | Required | Default | Description | 609 | | ---------- | ------ | -------- | ------- | --------------------------- | 610 | | str | String | yes | | String to encode | 611 | | encoding | String | yes | utf8 | base64/quoted-printable | 612 | | charset | String | no | utf8 | Charset of the input string | 613 | | lineLength | Number | no | 76 | Soft line break limit | 614 | 615 | #### Returns 616 | 617 | Returns a `String` in the target encoding. 618 | 619 | #### Example 620 | 621 | ```js 622 | const query = mailhog.encode('üäö', 'quoted-printable') 623 | // =C3=BC=C3=A4=C3=B6 624 | 625 | async function example() { 626 | // Search for "üäö" in quoted-printable encoding: 627 | const result = await mailhog.search(query) 628 | } 629 | ``` 630 | 631 | ### decode 632 | 633 | ``` 634 | mailhog.decode(str, encoding, charset) → String 635 | ``` 636 | 637 | #### Description 638 | 639 | Decodes a String from the given encoding and outputs it in the given charset. 640 | 641 | #### Parameters 642 | 643 | | Name | Type | Required | Default | Description | 644 | | -------- | ------ | -------- | ------- | ----------------------------- | 645 | | str | String | yes | | String to decode | 646 | | encoding | String | yes | | base64/quoted-printable | 647 | | charset | String | no | utf8 | Charset to use for the output | 648 | 649 | #### Returns 650 | 651 | Returns a `String` in the target charset. 652 | 653 | #### Example 654 | 655 | ```js 656 | const output = mailhog.decode('5pel5pys', 'base64') 657 | // 日本 658 | ``` 659 | 660 | ## Testing 661 | 662 | 1. Start [Docker](https://docs.docker.com/). 663 | 2. Install development dependencies: 664 | ```sh 665 | npm install 666 | ``` 667 | 3. Run the tests: 668 | ```sh 669 | npm test 670 | ``` 671 | 672 | ## License 673 | 674 | Released under the [MIT license](https://opensource.org/licenses/MIT). 675 | 676 | ## Author 677 | 678 | [Sebastian Tschan](https://blueimp.net/) 679 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | services: 3 | mailhog: 4 | image: blueimp/mailhog 5 | read_only: true 6 | test: 7 | build: . 8 | command: index.test.js 9 | read_only: true 10 | environment: 11 | - WAIT_FOR_HOSTS=mailhog:1025 12 | - MAILHOG_HOST=mailhog 13 | volumes: 14 | - .:/opt:ro 15 | depends_on: 16 | - mailhog 17 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | export = mailhog; 3 | /** 4 | * Returns the mailhog API interface. 5 | * 6 | * @param {Options} [options] API options 7 | * @returns {API} API object 8 | */ 9 | declare function mailhog(options?: Options): API; 10 | declare namespace mailhog { 11 | export { Attachment, MIME, Message, Messages, Options, API, SMTPConfig }; 12 | } 13 | /** 14 | * API options 15 | */ 16 | type Options = { 17 | /** 18 | * API protocol 19 | */ 20 | protocol?: string; 21 | /** 22 | * API host 23 | */ 24 | host?: string; 25 | /** 26 | * API port 27 | */ 28 | port?: number; 29 | /** 30 | * API basic authentication 31 | */ 32 | auth?: string; 33 | /** 34 | * API base path 35 | */ 36 | basePath?: string; 37 | }; 38 | type API = { 39 | /** 40 | * API options 41 | */ 42 | options: Options; 43 | /** 44 | * Gets all messages 45 | */ 46 | messages: typeof messages; 47 | /** 48 | * Gets messages matching a query 49 | */ 50 | search: typeof search; 51 | /** 52 | * Gets latest message from sender 53 | */ 54 | latestFrom: typeof latestFrom; 55 | /** 56 | * Gets latest message to recipient 57 | */ 58 | latestTo: typeof latestTo; 59 | /** 60 | * Gets latest with content 61 | */ 62 | latestContaining: typeof latestContaining; 63 | /** 64 | * Releases given message 65 | */ 66 | releaseMessage: typeof releaseMessage; 67 | /** 68 | * Deletes given message 69 | */ 70 | deleteMessage: typeof deleteMessage; 71 | /** 72 | * Deletes all messages 73 | */ 74 | deleteAll: typeof deleteAll; 75 | /** 76 | * Encodes given content 77 | */ 78 | encode: typeof encode; 79 | /** 80 | * Decodes given content 81 | */ 82 | decode: typeof decode; 83 | }; 84 | type Attachment = { 85 | /** 86 | * Filename 87 | */ 88 | name: string; 89 | /** 90 | * Content-Type 91 | */ 92 | type: string; 93 | /** 94 | * Content-Transfer-Encoding 95 | */ 96 | encoding: string; 97 | /** 98 | * Encoded content 99 | */ 100 | Body: string; 101 | /** 102 | * Encoded headers 103 | */ 104 | Headers: Array; 105 | }; 106 | type MIME = { 107 | /** 108 | * Attachment parts 109 | */ 110 | Parts: Array; 111 | }; 112 | type Message = { 113 | /** 114 | * Message ID 115 | */ 116 | ID: string; 117 | /** 118 | * Decoded mail text content 119 | */ 120 | text: string; 121 | /** 122 | * Decoded mail HTML content 123 | */ 124 | html: string; 125 | /** 126 | * Decoded mail Subject header 127 | */ 128 | subject: string; 129 | /** 130 | * Decoded mail From header 131 | */ 132 | from: string; 133 | /** 134 | * Decoded mail To header 135 | */ 136 | to: string; 137 | /** 138 | * Decoded mail Cc header 139 | */ 140 | cc: string; 141 | /** 142 | * Decoded mail Bcc header 143 | */ 144 | bcc: string; 145 | /** 146 | * Decoded mail Reply-To header 147 | */ 148 | replyTo: string; 149 | /** 150 | * Mail Date header 151 | */ 152 | date: Date; 153 | /** 154 | * Mail Delivery-Date header 155 | */ 156 | deliveryDate: Date; 157 | /** 158 | * List of mail attachments 159 | */ 160 | attachments: Array; 161 | /** 162 | * Mail Created property 163 | */ 164 | Created: string; 165 | /** 166 | * Mail Mime property 167 | */ 168 | MIME: MIME; 169 | }; 170 | type Messages = { 171 | /** 172 | * Number of results available 173 | */ 174 | total: number; 175 | /** 176 | * Number of results returned 177 | */ 178 | count: number; 179 | /** 180 | * Offset for the range of results returned 181 | */ 182 | start: number; 183 | /** 184 | * List of mail object items 185 | */ 186 | items: Array; 187 | }; 188 | type SMTPConfig = { 189 | /** 190 | * SMTP host 191 | */ 192 | host: string; 193 | /** 194 | * SMTP port 195 | */ 196 | port: string; 197 | /** 198 | * recipient email 199 | */ 200 | email: string; 201 | /** 202 | * SMTP username 203 | */ 204 | username?: string; 205 | /** 206 | * SMTP password 207 | */ 208 | password?: string; 209 | /** 210 | * SMTP auth mechanism (PLAIN or CRAM-MD5) 211 | */ 212 | mechanism?: string; 213 | }; 214 | /** 215 | * Requests mail objects from the MailHog API. 216 | * 217 | * @param {number} [start=0] defines the offset for the messages query 218 | * @param {number} [limit=50] defines the max number of results 219 | * @returns {Promise} resolves with object listing the mail items 220 | */ 221 | declare function messages(start?: number, limit?: number): Promise; 222 | /** 223 | * Sends a search request to the MailHog API. 224 | * 225 | * @param {string} query search query 226 | * @param {string} [kind=containing] query kind, can be from|to|containing 227 | * @param {number} [start=0] defines the offset for the search query 228 | * @param {number} [limit=50] defines the max number of results 229 | * @returns {Promise} resolves with object listing the mail items 230 | */ 231 | declare function search(query: string, kind?: string, start?: number, limit?: number): Promise; 232 | /** 233 | * Sends a search request for the latest mail matching the "from" query. 234 | * 235 | * @param {string} query from address 236 | * @returns {Promise} resolves latest mail object for the "from" query 237 | */ 238 | declare function latestFrom(query: string): Promise; 239 | /** 240 | * Sends a search request for the latest mail matching the "to" query. 241 | * 242 | * @param {string} query to address 243 | * @returns {Promise} resolves latest mail object for the "to" query 244 | */ 245 | declare function latestTo(query: string): Promise; 246 | /** 247 | * Sends a search request for the latest mail matching the "containing" query. 248 | * 249 | * @param {string} query search query 250 | * @returns {Promise} resolves latest mail object "containing" query 251 | */ 252 | declare function latestContaining(query: string): Promise; 253 | /** 254 | * Releases the mail with the given ID using the provided SMTP config. 255 | * 256 | * @param {string} id message ID 257 | * @param {SMTPConfig} config SMTP configuration 258 | * @returns {Promise} resolves with http.IncomingMessage 259 | */ 260 | declare function releaseMessage(id: string, config: SMTPConfig): Promise; 261 | /** 262 | * Deletes the mail with the given ID from MailHog. 263 | * 264 | * @param {string} id message ID 265 | * @returns {Promise} resolves with http.IncomingMessage 266 | */ 267 | declare function deleteMessage(id: string): Promise; 268 | /** 269 | * Deletes all mails stored in MailHog. 270 | * 271 | * @returns {Promise} resolves with http.IncomingMessage 272 | */ 273 | declare function deleteAll(): Promise; 274 | /** 275 | * Encodes a String in the given charset to base64 or quoted-printable encoding. 276 | * 277 | * @param {string} str String to encode 278 | * @param {string} [encoding] base64|quoted-printable 279 | * @param {string} [charset=utf8] Charset of the input string 280 | * @param {number} [lineLength=76] Soft line break limit 281 | * @returns {string} Encoded String 282 | */ 283 | declare function encode(str: string, encoding?: string, charset?: string, lineLength?: number): string; 284 | /** 285 | * Decodes a String from the given encoding and outputs it in the given charset. 286 | * 287 | * @param {string} str String to decode 288 | * @param {string} [encoding=utf8] input encoding, e.g. base64|quoted-printable 289 | * @param {string} [charset=utf8] Charset to use for the output 290 | * @returns {string} Decoded String 291 | */ 292 | declare function decode(str: string, encoding?: string, charset?: string): string; 293 | import http = require("http"); 294 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * NodeJS library to interact with the MailHog API. 3 | * https://github.com/blueimp/mailhog-node 4 | * 5 | * Copyright 2016, Sebastian Tschan 6 | * https://blueimp.net 7 | * 8 | * Licensed under the MIT license: 9 | * https://opensource.org/licenses/MIT 10 | */ 11 | 12 | 'use strict' 13 | 14 | /** 15 | * @typedef {object} Attachment 16 | * @property {string} name Filename 17 | * @property {string} type Content-Type 18 | * @property {string} encoding Content-Transfer-Encoding 19 | * @property {string} Body Encoded content 20 | * @property {Array} Headers Encoded headers 21 | */ 22 | 23 | /** 24 | * @typedef {object} MIME 25 | * @property {Array} Parts Attachment parts 26 | */ 27 | 28 | /** 29 | * @typedef {object} Message 30 | * @property {string} ID Message ID 31 | * @property {string} text Decoded mail text content 32 | * @property {string} html Decoded mail HTML content 33 | * @property {string} subject Decoded mail Subject header 34 | * @property {string} from Decoded mail From header 35 | * @property {string} to Decoded mail To header 36 | * @property {string} cc Decoded mail Cc header 37 | * @property {string} bcc Decoded mail Bcc header 38 | * @property {string} replyTo Decoded mail Reply-To header 39 | * @property {Date} date Mail Date header 40 | * @property {Date} deliveryDate Mail Delivery-Date header 41 | * @property {Array} attachments List of mail attachments 42 | * @property {string} Created Mail Created property 43 | * @property {MIME} MIME Mail Mime property 44 | */ 45 | 46 | /** 47 | * @typedef {object} Messages 48 | * @property {number} total Number of results available 49 | * @property {number} count Number of results returned 50 | * @property {number} start Offset for the range of results returned 51 | * @property {Array} items List of mail object items 52 | */ 53 | 54 | /** 55 | * @typedef {object} Options API options 56 | * @property {string} [protocol="http:"] API protocol 57 | * @property {string} [host=localhost] API host 58 | * @property {number} [port=8025] API port 59 | * @property {string} [auth] API basic authentication 60 | * @property {string} [basePath="/api"] API base path 61 | */ 62 | 63 | /* eslint-disable jsdoc/valid-types */ 64 | 65 | /** 66 | * @typedef {object} API 67 | * @property {Options} options API options 68 | * @property {typeof messages} messages Gets all messages 69 | * @property {typeof search} search Gets messages matching a query 70 | * @property {typeof latestFrom} latestFrom Gets latest message from sender 71 | * @property {typeof latestTo} latestTo Gets latest message to recipient 72 | * @property {typeof latestContaining} latestContaining Gets latest with content 73 | * @property {typeof releaseMessage} releaseMessage Releases given message 74 | * @property {typeof deleteMessage} deleteMessage Deletes given message 75 | * @property {typeof deleteAll} deleteAll Deletes all messages 76 | * @property {typeof encode} encode Encodes given content 77 | * @property {typeof decode} decode Decodes given content 78 | */ 79 | 80 | /* eslint-enable jsdoc/valid-types */ 81 | 82 | /** 83 | * @typedef {object} SMTPConfig 84 | * @property {string} host SMTP host 85 | * @property {string} port SMTP port 86 | * @property {string} email recipient email 87 | * @property {string} [username] SMTP username 88 | * @property {string} [password] SMTP password 89 | * @property {string} [mechanism] SMTP auth mechanism (PLAIN or CRAM-MD5) 90 | */ 91 | 92 | /* global BufferEncoding */ 93 | 94 | const http = require('http') 95 | const https = require('https') 96 | const libqp = require('./libqp') 97 | 98 | /** 99 | * Adds soft line breaks to a given String 100 | * 101 | * @param {string} str String to wrap 102 | * @param {number} [lineLength=76] Maximum allowed length for a line 103 | * @returns {string} Soft-wrapped String using `\r\n` as line breaks 104 | */ 105 | function wrap(str, lineLength) { 106 | const maxLength = lineLength || 76 107 | const lines = Math.ceil(str.length / maxLength) 108 | let output = '' 109 | for (let i = 0, offset = 0; i < lines; ++i, offset += maxLength) { 110 | output += str.substr(offset, maxLength) + '\r\n' 111 | } 112 | return output.trim() 113 | } 114 | 115 | /** 116 | * Encodes a String in the given charset to base64 or quoted-printable encoding. 117 | * 118 | * @param {string} str String to encode 119 | * @param {string} [encoding] base64|quoted-printable 120 | * @param {string} [charset=utf8] Charset of the input string 121 | * @param {number} [lineLength=76] Soft line break limit 122 | * @returns {string} Encoded String 123 | */ 124 | function encode(str, encoding, charset, lineLength) { 125 | const maxLength = lineLength === undefined ? 76 : lineLength 126 | const outputEncoding = encoding && encoding.toLowerCase() 127 | let output = str 128 | if (outputEncoding === 'quoted-printable' || outputEncoding === 'base64') { 129 | const isUTF8Input = !charset || /^utf-?8$/.test(charset.toLowerCase()) 130 | let buffer 131 | if (isUTF8Input) { 132 | buffer = Buffer.from(str) 133 | } else { 134 | buffer = require('iconv-lite').encode(str, charset) 135 | } 136 | if (outputEncoding === 'quoted-printable') { 137 | const output = libqp.encode(buffer) 138 | return maxLength ? libqp.wrap(output, maxLength) : output 139 | } 140 | output = buffer.toString('base64') 141 | } 142 | return maxLength ? wrap(output, maxLength) : output 143 | } 144 | 145 | /** 146 | * Decodes a String from the given encoding and outputs it in the given charset. 147 | * 148 | * @param {string} str String to decode 149 | * @param {string} [encoding=utf8] input encoding, e.g. base64|quoted-printable 150 | * @param {string} [charset=utf8] Charset to use for the output 151 | * @returns {string} Decoded String 152 | */ 153 | function decode(str, encoding, charset) { 154 | const inputEncoding = encoding && encoding.toLowerCase() 155 | const utf8Regexp = /^utf-?8$/ 156 | const isUTF8Input = !inputEncoding || utf8Regexp.test(inputEncoding) 157 | const isUTF8Output = !charset || utf8Regexp.test(charset.toLowerCase()) 158 | if (isUTF8Input && isUTF8Output) return str 159 | // 7bit|8bit|binary are not encoded, x-token has an unknown encoding, see: 160 | // https://www.w3.org/Protocols/rfc1341/5_Content-Transfer-Encoding.html 161 | if (/^(7|8)bit|binary|x-.+$/.test(inputEncoding)) return str 162 | let buffer 163 | if (inputEncoding === 'quoted-printable') { 164 | buffer = libqp.decode(str) 165 | } else { 166 | buffer = Buffer.from( 167 | str, 168 | /** @type {BufferEncoding} */ 169 | (inputEncoding) 170 | ) 171 | } 172 | if (isUTF8Output) return buffer.toString() 173 | return require('iconv-lite').decode(buffer, charset) 174 | } 175 | 176 | /** 177 | * Returns the content part matching the given content-type regular expression. 178 | * 179 | * @param {object} mail MailHog mail object 180 | * @param {RegExp} typeRegExp Regular expression matching the content-type 181 | * @returns {string} Decoded content with a type matching the content-type 182 | */ 183 | function getContent(mail, typeRegExp) { 184 | let parts = [mail.Content] 185 | if (mail.MIME) parts = parts.concat(mail.MIME.Parts) 186 | for (const part of parts) { 187 | const type = (part.Headers['Content-Type'] || '').toString() 188 | if (typeRegExp.test(type)) { 189 | const match = /\bcharset=([\w_-]+)(?:;|$)/.exec(type) 190 | const charset = match ? match[1] : undefined 191 | return decode( 192 | part.Body, 193 | (part.Headers['Content-Transfer-Encoding'] || '').toString(), 194 | charset 195 | ) 196 | } 197 | } 198 | } 199 | 200 | /** 201 | * Matches encoded Strings in mail headers and returns decoded content. 202 | * 203 | * @param {string} _ Matched substring (unused) 204 | * @param {string} charset Charset to use for the output 205 | * @param {string} encoding B|Q, which stands for base64 or quoted-printable 206 | * @param {string} data Encoded String data 207 | * @returns {string} Decoded header content 208 | */ 209 | function headerDecoder(_, charset, encoding, data) { 210 | switch (encoding) { 211 | case 'b': 212 | case 'B': 213 | return decode(data, 'base64', charset) 214 | case 'q': 215 | case 'Q': 216 | return decode(data, 'quoted-printable', charset) 217 | } 218 | } 219 | 220 | /** 221 | * Returns header content for the given mail object and header key. 222 | * 223 | * @param {object} mail MailHog mail object 224 | * @param {string} key Header key 225 | * @returns {string} Header content 226 | */ 227 | function getHeader(mail, key) { 228 | const header = (mail.Content || mail).Headers[key] 229 | if (!header || !header.length) return 230 | // Encoded header parts have the following form: 231 | // =?charset?encoding?data?= 232 | return header[0].replace(/=\?([^?]+)\?([BbQq])\?([^?]+)\?=/g, headerDecoder) 233 | } 234 | 235 | /** 236 | * Memoized getter for mail text content. 237 | * 238 | * @this Message 239 | * @returns {string} Decoded mail text content 240 | */ 241 | function getText() { 242 | delete this.text 243 | return (this.text = getContent(this, /^text\/plain($|;)/i)) 244 | } 245 | 246 | /** 247 | * Memoized getter for mail HTML content. 248 | * 249 | * @this Message 250 | * @returns {string} Decoded mail HTML content 251 | */ 252 | function getHTML() { 253 | delete this.html 254 | return (this.html = getContent(this, /^text\/html($|;)/i)) 255 | } 256 | 257 | /** 258 | * Memoized getter for mail Subject header. 259 | * 260 | * @this Message 261 | * @returns {string} Decoded mail Subject header 262 | */ 263 | function getSubject() { 264 | delete this.subject 265 | return (this.subject = getHeader(this, 'Subject')) 266 | } 267 | 268 | /** 269 | * Memoized getter for mail From header. 270 | * 271 | * @this Message 272 | * @returns {string} Decoded mail From header 273 | */ 274 | function getFrom() { 275 | delete this.from 276 | return (this.from = getHeader(this, 'From')) 277 | } 278 | 279 | /** 280 | * Memoized getter for mail To header. 281 | * 282 | * @this Message 283 | * @returns {string} Decoded mail To header 284 | */ 285 | function getTo() { 286 | delete this.to 287 | return (this.to = getHeader(this, 'To')) 288 | } 289 | 290 | /** 291 | * Memoized getter for mail Cc header. 292 | * 293 | * @this Message 294 | * @returns {string} Decoded mail Cc header 295 | */ 296 | function getCc() { 297 | delete this.cc 298 | return (this.cc = getHeader(this, 'Cc')) 299 | } 300 | 301 | /** 302 | * Memoized getter for mail Bcc header. 303 | * 304 | * @this Message 305 | * @returns {string} Decoded mail Bcc header 306 | */ 307 | function getBcc() { 308 | delete this.bcc 309 | return (this.bcc = getHeader(this, 'Bcc')) 310 | } 311 | 312 | /** 313 | * Memoized getter for mail Reply-To header. 314 | * 315 | * @this Message 316 | * @returns {string} Decoded mail Reply-To header 317 | */ 318 | function getReplyTo() { 319 | delete this.replyTo 320 | return (this.replyTo = getHeader(this, 'Reply-To')) 321 | } 322 | 323 | /** 324 | * Memoized getter for mail Date header. 325 | * 326 | * @this Message 327 | * @returns {Date} Mail Date header 328 | */ 329 | function getDate() { 330 | delete this.date 331 | const dateString = getHeader(this, 'Date') 332 | if (dateString) this.date = new Date(Date.parse(dateString)) 333 | return this.date 334 | } 335 | 336 | /** 337 | * Memoized getter for mail Delivery-Date header. 338 | * 339 | * @this Message 340 | * @returns {Date} Mail Delivery-Date header 341 | */ 342 | function getDeliveryDate() { 343 | delete this.deliveryDate 344 | // MailHog does not set the Delivery-Date header, but it sets a Created 345 | // property that serves the same purpose (delivery date to application): 346 | return (this.deliveryDate = new Date(Date.parse(this.Created))) 347 | } 348 | 349 | /** 350 | * Memoized getter for mail Content-Type header. 351 | * 352 | * @this Attachment 353 | * @returns {string} Decoded mail Content-Type header 354 | */ 355 | function getContentType() { 356 | delete this.type 357 | return (this.type = getHeader(this, 'Content-Type')) 358 | } 359 | 360 | /** 361 | * Memoized getter for mail Content-Transfer-Encoding header. 362 | * 363 | * @this Attachment 364 | * @returns {string} Decoded mail Content-Transfer-Encoding header 365 | */ 366 | function getContentTransferEncoding() { 367 | delete this.encoding 368 | return (this.encoding = getHeader(this, 'Content-Transfer-Encoding')) 369 | } 370 | 371 | /** 372 | * Memoized getter for mail attachments. 373 | * 374 | * @this Message 375 | * @returns {Array} List of mail attachments 376 | */ 377 | function getAttachments() { 378 | delete this.attachments 379 | const attachments = [] 380 | if (this.MIME && this.MIME.Parts) { 381 | for (const part of this.MIME.Parts) { 382 | const match = /^attachment;\s*filename="?([^"]+)"?$/.exec( 383 | part.Headers['Content-Disposition'] 384 | ) 385 | if (!match) continue 386 | part.name = match[1] 387 | Object.defineProperty(part, 'type', { 388 | get: getContentType, 389 | configurable: true 390 | }) 391 | Object.defineProperty(part, 'encoding', { 392 | get: getContentTransferEncoding, 393 | configurable: true 394 | }) 395 | attachments.push(part) 396 | } 397 | } 398 | return (this.attachments = attachments) 399 | } 400 | 401 | /** 402 | * Injects convenience properties for each mail item in the given result. 403 | * 404 | * @param {object} result Result object for a MailHog API search/messages query 405 | * @returns {object} Result object with injected properties for each mail item 406 | */ 407 | function injectProperties(result) { 408 | if (!result.count) return result 409 | for (const item of result.items) { 410 | // Define memoized getter for contents and headers: 411 | Object.defineProperty(item, 'text', { get: getText, configurable: true }) 412 | Object.defineProperty(item, 'html', { get: getHTML, configurable: true }) 413 | Object.defineProperty(item, 'subject', { 414 | get: getSubject, 415 | configurable: true 416 | }) 417 | Object.defineProperty(item, 'from', { get: getFrom, configurable: true }) 418 | Object.defineProperty(item, 'to', { get: getTo, configurable: true }) 419 | Object.defineProperty(item, 'cc', { get: getCc, configurable: true }) 420 | Object.defineProperty(item, 'bcc', { get: getBcc, configurable: true }) 421 | Object.defineProperty(item, 'replyTo', { 422 | get: getReplyTo, 423 | configurable: true 424 | }) 425 | Object.defineProperty(item, 'date', { get: getDate, configurable: true }) 426 | Object.defineProperty(item, 'deliveryDate', { 427 | get: getDeliveryDate, 428 | configurable: true 429 | }) 430 | Object.defineProperty(item, 'attachments', { 431 | get: getAttachments, 432 | configurable: true 433 | }) 434 | } 435 | return result 436 | } 437 | 438 | /** 439 | * Sends a http.request and resolves with the parsed JSON response. 440 | * 441 | * @param {object} options http.request options 442 | * @param {string} [data] POST data 443 | * @returns {Promise} resolves with JSON or http.IncomingMessage if no body 444 | */ 445 | function request(options, data) { 446 | const client = options.protocol === 'https:' ? https : http 447 | return new Promise((resolve, reject) => { 448 | const req = client 449 | .request(options, response => { 450 | let body = '' 451 | response 452 | .on('data', chunk => (body += chunk)) 453 | .on('end', () => { 454 | if (!body) return resolve(response) 455 | try { 456 | resolve(JSON.parse(body)) 457 | } catch (error) { 458 | reject(error) 459 | } 460 | }) 461 | }) 462 | .on('error', reject) 463 | if (data) req.write(data) 464 | req.end() 465 | }) 466 | } 467 | 468 | /** 469 | * Requests mail objects from the MailHog API. 470 | * 471 | * @param {number} [start=0] defines the offset for the messages query 472 | * @param {number} [limit=50] defines the max number of results 473 | * @returns {Promise} resolves with object listing the mail items 474 | */ 475 | function messages(start, limit) { 476 | let path = `${this.options.basePath}/v2/messages` 477 | if (start) path += `?start=${start}` 478 | if (limit) path += `${start ? '&' : '?'}limit=${limit}` 479 | const options = Object.assign({}, this.options, { path }) 480 | return request(options).then(result => injectProperties(result)) 481 | } 482 | 483 | /** 484 | * Sends a search request to the MailHog API. 485 | * 486 | * @param {string} query search query 487 | * @param {string} [kind=containing] query kind, can be from|to|containing 488 | * @param {number} [start=0] defines the offset for the search query 489 | * @param {number} [limit=50] defines the max number of results 490 | * @returns {Promise} resolves with object listing the mail items 491 | */ 492 | function search(query, kind, start, limit) { 493 | const basePath = this.options.basePath 494 | const kindParam = kind || 'containing' 495 | const encodedQuery = encodeURIComponent(query) 496 | let path = `${basePath}/v2/search?kind=${kindParam}&query=${encodedQuery}` 497 | if (start) path += `&start=${start}` 498 | if (limit) path += `&limit=${limit}` 499 | const options = Object.assign({}, this.options, { path }) 500 | return request(options).then(result => injectProperties(result)) 501 | } 502 | 503 | /** 504 | * Sends a search request for the latest mail matching the "from" query. 505 | * 506 | * @param {string} query from address 507 | * @returns {Promise} resolves latest mail object for the "from" query 508 | */ 509 | function latestFrom(query) { 510 | return this.search(query, 'from', 0, 1).then( 511 | result => result.count && result.items[0] 512 | ) 513 | } 514 | 515 | /** 516 | * Sends a search request for the latest mail matching the "to" query. 517 | * 518 | * @param {string} query to address 519 | * @returns {Promise} resolves latest mail object for the "to" query 520 | */ 521 | function latestTo(query) { 522 | return this.search(query, 'to', 0, 1).then( 523 | result => result.count && result.items[0] 524 | ) 525 | } 526 | 527 | /** 528 | * Sends a search request for the latest mail matching the "containing" query. 529 | * 530 | * @param {string} query search query 531 | * @returns {Promise} resolves latest mail object "containing" query 532 | */ 533 | function latestContaining(query) { 534 | return this.search(query, 'containing', 0, 1).then( 535 | result => result.count && result.items[0] 536 | ) 537 | } 538 | 539 | /** 540 | * Releases the mail with the given ID using the provided SMTP config. 541 | * 542 | * @param {string} id message ID 543 | * @param {SMTPConfig} config SMTP configuration 544 | * @returns {Promise} resolves with http.IncomingMessage 545 | */ 546 | function releaseMessage(id, config) { 547 | const basePath = this.options.basePath 548 | const options = Object.assign({}, this.options, { 549 | method: 'POST', 550 | path: `${basePath}/v1/messages/${encodeURIComponent(id)}/release` 551 | }) 552 | return request(options, JSON.stringify(config)) 553 | } 554 | 555 | /** 556 | * Deletes the mail with the given ID from MailHog. 557 | * 558 | * @param {string} id message ID 559 | * @returns {Promise} resolves with http.IncomingMessage 560 | */ 561 | function deleteMessage(id) { 562 | const options = Object.assign({}, this.options, { 563 | method: 'DELETE', 564 | path: `${this.options.basePath}/v1/messages/${encodeURIComponent(id)}` 565 | }) 566 | return request(options) 567 | } 568 | 569 | /** 570 | * Deletes all mails stored in MailHog. 571 | * 572 | * @returns {Promise} resolves with http.IncomingMessage 573 | */ 574 | function deleteAll() { 575 | const options = Object.assign({}, this.options, { 576 | method: 'DELETE', 577 | path: `${this.options.basePath}/v1/messages` 578 | }) 579 | return request(options) 580 | } 581 | 582 | /** 583 | * Returns the mailhog API interface. 584 | * 585 | * @param {Options} [options] API options 586 | * @returns {API} API object 587 | */ 588 | function mailhog(options) { 589 | const api = { 590 | options: Object.assign({ port: 8025, basePath: '/api' }, options), 591 | encode, 592 | decode 593 | } 594 | return Object.assign(api, { 595 | messages: messages.bind(api), 596 | search: search.bind(api), 597 | latestFrom: latestFrom.bind(api), 598 | latestTo: latestTo.bind(api), 599 | latestContaining: latestContaining.bind(api), 600 | releaseMessage: releaseMessage.bind(api), 601 | deleteMessage: deleteMessage.bind(api), 602 | deleteAll: deleteAll.bind(api) 603 | }) 604 | } 605 | 606 | module.exports = mailhog 607 | -------------------------------------------------------------------------------- /index.test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /* global before, after, describe, it, BufferEncoding */ 4 | 5 | /** @type {object} */ 6 | const assert = require('assert') 7 | const path = require('path') 8 | const util = require('util') 9 | const exec = util.promisify(require('child_process').exec) 10 | const env = process.env 11 | 12 | const mailhog = require('.')({ 13 | host: env.MAILHOG_HOST 14 | }) 15 | 16 | /** 17 | * Sends all mail configured in the mail directory. 18 | * 19 | * @returns {Promise} Resolves when executing sucessfully, rejects otherwise 20 | */ 21 | async function sendAllMail() { 22 | const sendmailScript = path.join(__dirname, 'sendmail.sh') 23 | await exec(`${sendmailScript} -S ${env.MAILHOG_HOST}:1025`) 24 | } 25 | 26 | /** 27 | * Deletes all mail from MailHog. 28 | * 29 | * @returns {Promise} Resolves when executing sucessfully, rejects otherwise 30 | */ 31 | async function deleteAllMail() { 32 | const result = await mailhog.deleteAll() 33 | assert.strictEqual(result.statusCode, 200, 'Responds with status code 200') 34 | } 35 | 36 | before(sendAllMail) 37 | after(deleteAllMail) 38 | 39 | describe('encode', function () { 40 | it('quoted-printable encoding of utf8 string', function () { 41 | assert.strictEqual( 42 | mailhog.encode('üäö', 'quoted-printable'), 43 | '=C3=BC=C3=A4=C3=B6', 44 | 'Returns quoted-printable encoding of utf8 string' 45 | ) 46 | }) 47 | 48 | it('quoted-printable encoding of iso-8859-1 string', function () { 49 | assert.strictEqual( 50 | mailhog.encode('üäö', 'quoted-printable', 'iso-8859-1'), 51 | '=C3=BC=C3=A4=C3=B6', 52 | 'Returns quoted-printable encoding of iso-8859-1 string' 53 | ) 54 | }) 55 | 56 | it('quoted-printable encoding of long utf8 string', function () { 57 | assert.strictEqual( 58 | mailhog.encode('üäö'.repeat(10), 'quoted-printable', 'utf8'), 59 | [ 60 | '=C3=BC=C3=A4=C3=B6'.repeat(4), 61 | '=C3=BC=C3=A4=C3=B6'.repeat(4), 62 | '=C3=BC=C3=A4=C3=B6'.repeat(2) 63 | ].join('=\r\n'), 64 | 'Returns wrapped quoted-printable encoding of utf8 string' 65 | ) 66 | }) 67 | 68 | it('quoted-printable encoding of utf8 string, linelength:10', function () { 69 | assert.strictEqual( 70 | mailhog.encode('üäöüäö', 'quoted-printable', 'utf8', 10), 71 | '=C3=BC=C3=\r\n=A4=C3=B6=\r\n=C3=BC=C3=\r\n=A4=C3=B6', 72 | 'Returns wrapped quoted-printable encoding of utf8 string' 73 | ) 74 | }) 75 | 76 | it('base64 encoding of utf8 string', function () { 77 | assert.strictEqual( 78 | mailhog.encode('üäö', 'base64'), 79 | 'w7zDpMO2', 80 | 'Returns base64 encoding of utf8 string' 81 | ) 82 | }) 83 | 84 | it('base64 encoding of iso-8859-1 string', function () { 85 | assert.strictEqual( 86 | mailhog.encode('üäö', 'base64', 'iso-8859-1'), 87 | 'w7zDpMO2', 88 | 'Returns base64 encoding of iso-8859-1 string' 89 | ) 90 | }) 91 | 92 | it('base64 encoding of long utf8 string', function () { 93 | assert.strictEqual( 94 | mailhog.encode('üäö'.repeat(10), 'base64', 'utf8'), 95 | 'w7zDpMO2'.repeat(9) + 'w7zD\r\npMO2', 96 | 'Returns wrapped base64 encoding of utf8 string' 97 | ) 98 | }) 99 | 100 | it('base64 encoding of utf8 string, linelength:10', function () { 101 | assert.strictEqual( 102 | mailhog.encode('üäöüäö', 'base64', 'utf8', 10), 103 | 'w7zDpMO2w7\r\nzDpMO2', 104 | 'Returns wrapped base64 encoding of utf8 string' 105 | ) 106 | }) 107 | }) 108 | 109 | describe('decode', function () { 110 | it('quoted-printable decoding to utf8 string', function () { 111 | assert.strictEqual( 112 | mailhog.decode('=C3=BC=C3=A4=C3=B6', 'quoted-printable'), 113 | 'üäö', 114 | 'Returns utf8 string from quoted-printable input string' 115 | ) 116 | }) 117 | 118 | it('quoted-printable decoding to iso-8859-1 string', function () { 119 | assert.strictEqual( 120 | mailhog.decode('=C3=BC=C3=A4=C3=B6', 'quoted-printable', 'iso-8859-1'), 121 | 'üäö', 122 | 'Returns iso-8859-1 string from quoted-printable input string' 123 | ) 124 | }) 125 | 126 | it('quoted-printable decoding of wrapped string to utf8 string', function () { 127 | assert.strictEqual( 128 | mailhog.decode( 129 | [ 130 | '=C3=BC=C3=A4=C3=B6'.repeat(4), 131 | '=C3=BC=C3=A4=C3=B6'.repeat(4), 132 | '=C3=BC=C3=A4=C3=B6'.repeat(2) 133 | ].join('=\r\n'), 134 | 'quoted-printable' 135 | ), 136 | 'üäö'.repeat(10), 137 | 'Returns utf8 string from wrapped quoted-printable input string' 138 | ) 139 | }) 140 | 141 | it('base64 decoding to utf8 string', function () { 142 | assert.strictEqual( 143 | mailhog.decode('w7zDpMO2', 'base64'), 144 | 'üäö', 145 | 'Returns utf8 string from base64 input string' 146 | ) 147 | }) 148 | 149 | it('base64 decoding to iso-8859-1 string', function () { 150 | assert.strictEqual( 151 | mailhog.decode('w7zDpMO2', 'base64', 'iso-8859-1'), 152 | 'üäö', 153 | 'Returns iso-8859-1 string from base64 input string' 154 | ) 155 | }) 156 | 157 | it('base64 decoding of wrapped string to utf8 string', function () { 158 | assert.strictEqual( 159 | mailhog.decode('w7zDpMO2'.repeat(9) + 'w7zD\r\npMO2', 'base64'), 160 | 'üäö'.repeat(10), 161 | 'Returns utf8 string from wrapped base64 input string' 162 | ) 163 | }) 164 | }) 165 | 166 | describe('multipart', function () { 167 | it('parses quoted-printable encoded text content', async function () { 168 | const result = await mailhog.latestTo('ueaeoe@example.org') 169 | assert.strictEqual(result.text, 'ü\r\näö', 'Returns plain text content') 170 | }) 171 | 172 | it('parses quoted-printable encoded HTML content', async function () { 173 | const result = await mailhog.latestTo('ueaeoe@example.org') 174 | assert.strictEqual( 175 | result.html, 176 | 'ü
äö
', 177 | 'Returns HTML content' 178 | ) 179 | }) 180 | 181 | it('parses attachments', async function () { 182 | const result = await mailhog.latestTo('ueaeoe@example.org') 183 | assert.strictEqual(result.attachments.length, 2, 'Returns attachments') 184 | assert.strictEqual(result.attachments[0].name, 'black-80x60.gif') 185 | assert.strictEqual(result.attachments[0].type, 'image/gif') 186 | assert.strictEqual( 187 | Buffer.from( 188 | result.attachments[0].Body, 189 | /** @type {BufferEncoding} */ 190 | (result.attachments[0].encoding) 191 | ).toString('base64'), 192 | 'R0lGODdhUAA8AIABAAAAAP///ywAAAAAUAA8AAACS4SPqcvtD6OctNqLs968+w+G4kiW5o' + 193 | 'mm6sq27gvH8kzX9o3n+s73/g8MCofEovGITCqXzKbzCY1Kp9Sq9YrNarfcrvcLDovH5P' + 194 | 'KsAAA7' 195 | ) 196 | assert.strictEqual(result.attachments[1].name, 'white-2x1.jpg') 197 | assert.strictEqual(result.attachments[1].type, 'image/jpeg') 198 | assert.strictEqual( 199 | Buffer.from( 200 | result.attachments[1].Body, 201 | /** @type {BufferEncoding} */ 202 | (result.attachments[1].encoding) 203 | ).toString('base64'), 204 | '/9j/4AAQSkZJRgABAQEAYABgAAD/4QAiRXhpZgAASUkqAAgAAAABABIBAwABAAAABgASAA' + 205 | 'AAAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAA8cAgUACm9iamVjdG5hbWUA/9' + 206 | 'sAQwABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQ' + 207 | 'EBAQEBAQEBAQEBAQEBAQEB/9sAQwEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQ' + 208 | 'EBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB/8AAEQgAAQACAwEiAAIRAQ' + 209 | 'MRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQ' + 210 | 'QEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJS' + 211 | 'YnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiY' + 212 | 'qSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5e' + 213 | 'bn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EAL' + 214 | 'URAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFW' + 215 | 'Jy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdH' + 216 | 'V2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJyt' + 217 | 'LT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/aAAwDAQACEQMRAD8A/v4ooooA/9k=' 218 | ) 219 | }) 220 | }) 221 | 222 | describe('charset', function () { 223 | it('parses mail with utf8 charset', async function () { 224 | const result = await mailhog.latestTo('nihon@example.org') 225 | assert.strictEqual(result.text, '日本\n', 'Parses mail with utf8 charset') 226 | }) 227 | 228 | it('parses mail with ISO-8859-1 charset', async function () { 229 | const result = await mailhog.latestTo('iso-8859-1@example.org') 230 | assert.strictEqual(result.text, 'üäö', 'Returns plain text content') 231 | }) 232 | 233 | it('parses mail without charset definition', async function () { 234 | const result = await mailhog.latestTo('no-charset@example.org') 235 | assert.strictEqual( 236 | result.text, 237 | 'text content', 238 | 'Returns plain text content' 239 | ) 240 | }) 241 | }) 242 | 243 | describe('headers', function () { 244 | it('parses the mail Cc header', async function () { 245 | const result = await mailhog.latestTo('nihon@example.org') 246 | assert.strictEqual( 247 | result.cc, 248 | '日本 ', 249 | 'Returns the decoded mail Cc header' 250 | ) 251 | }) 252 | 253 | it('parses the mail Bcc header', async function () { 254 | const result = await mailhog.latestTo('nihon@example.org') 255 | assert.strictEqual( 256 | result.bcc, 257 | '日本 ', 258 | 'Returns the decoded mail Bcc header' 259 | ) 260 | }) 261 | 262 | it('parses the mail Reply-To header', async function () { 263 | const result = await mailhog.latestTo('nihon@example.org') 264 | assert.strictEqual( 265 | result.replyTo, 266 | '日本 ', 267 | 'Returns the decoded mail Reply-To header' 268 | ) 269 | }) 270 | 271 | it('parses the mail Date header', async function () { 272 | const result = await mailhog.latestTo('nihon@example.org') 273 | assert.ok( 274 | result.deliveryDate instanceof Date, 275 | 'Returns mail Date header as Date object' 276 | ) 277 | assert.strictEqual( 278 | result.date.getTime(), 279 | Date.parse('2016-10-23T18:59:40.000Z'), 280 | 'Returns mail Date header as defined in the composed mail' 281 | ) 282 | }) 283 | 284 | it('parses the mail Delivery-Date header', async function () { 285 | const result = await mailhog.latestTo('nihon@example.org') 286 | assert.ok( 287 | result.deliveryDate instanceof Date, 288 | 'Returns mail Delivery-Date header as Date object' 289 | ) 290 | assert.ok( 291 | result.deliveryDate > result.date, 292 | 'Returns mail with Delivery-Date later than Date' 293 | ) 294 | }) 295 | }) 296 | 297 | describe('messages', function () { 298 | it('retrieve mails', async function () { 299 | const result = await mailhog.messages() 300 | assert.strictEqual(result.count, 4, 'Returns all emails') 301 | assert.strictEqual( 302 | result.items[0].subject, 303 | 'Mail without charset', 304 | 'Returns the decoded mail Subject header for the first mail in the set' 305 | ) 306 | assert.strictEqual( 307 | result.items[1].subject, 308 | 'ISO-8859-1', 309 | 'Returns the decoded mail Subject header for the second mail in the set' 310 | ) 311 | assert.strictEqual( 312 | result.items[2].subject, 313 | '日本', 314 | 'Returns the decoded mail Subject header for the third mail in the set' 315 | ) 316 | assert.strictEqual( 317 | result.items[3].subject, 318 | 'üäö', 319 | 'Returns the decoded mail Subject header for the fourth mail in the set' 320 | ) 321 | }) 322 | 323 | it('limit the messages range', async function () { 324 | const result = await mailhog.messages(3, 1) 325 | assert.strictEqual(result.count, 1, 'Returns a set for the given range') 326 | assert.strictEqual( 327 | result.items[0].subject, 328 | 'üäö', 329 | 'Returns the decoded mail Subject header for mail from the given range' 330 | ) 331 | }) 332 | }) 333 | 334 | describe('search', function () { 335 | it('search mails containing the query', async function () { 336 | const result = await mailhog.search( 337 | mailhog.encode('üäö', 'quoted-printable') 338 | ) 339 | assert.strictEqual(result.count, 1, 'Returns a list of matching emails') 340 | assert.strictEqual( 341 | result.items[0].subject, 342 | 'üäö', 343 | 'Returns the decoded mail Subject header' 344 | ) 345 | }) 346 | 347 | it('search mails from the given user', async function () { 348 | const result = await mailhog.search('test@example.org', 'from') 349 | assert.strictEqual(result.count, 4, 'Returns a list of matching emails') 350 | assert.strictEqual( 351 | result.items[0].subject, 352 | 'Mail without charset', 353 | 'Returns the decoded mail Subject header sent from the given user' 354 | ) 355 | }) 356 | 357 | it('search mails to the given user', async function () { 358 | const result = await mailhog.search('nihon@example.org', 'to') 359 | assert.strictEqual(result.count, 1, 'Returns a list of matching emails') 360 | assert.strictEqual( 361 | result.items[0].subject, 362 | '日本', 363 | 'Returns the decoded mail Subject header sent to the given user' 364 | ) 365 | }) 366 | 367 | it('limit the search results range', async function () { 368 | const result = await mailhog.search('example.org', undefined, 3, 1) 369 | assert.strictEqual(result.count, 1, 'Returns a set for the given range') 370 | assert.strictEqual( 371 | result.items[0].subject, 372 | 'üäö', 373 | 'Returns the decoded mail Subject header for mail from the given range' 374 | ) 375 | }) 376 | }) 377 | 378 | describe('latestFrom', function () { 379 | it('latest mail from a given user', async function () { 380 | const result = await mailhog.latestFrom('test@example.org') 381 | assert.strictEqual( 382 | result.from, 383 | 'Test ', 384 | 'Returns the decoded mail From header sent from the given user' 385 | ) 386 | }) 387 | }) 388 | 389 | describe('latestTo', function () { 390 | it('latest mail to a given user', async function () { 391 | const result = await mailhog.latestTo('nihon@example.org') 392 | assert.strictEqual( 393 | result.to, 394 | '日本 ', 395 | 'Returns the decoded mail To header sent to the given user' 396 | ) 397 | }) 398 | }) 399 | 400 | describe('latestContaining', function () { 401 | it('latest mail with the query text in the body content', async function () { 402 | const result = await mailhog.latestContaining('text content') 403 | assert.strictEqual( 404 | result.text, 405 | 'text content', 406 | 'Returns the decoded plain text mail content containing the given query' 407 | ) 408 | }) 409 | 410 | it('latest mail with query text in the Subject header', async function () { 411 | const result = await mailhog.latestContaining('ISO-8859-1') 412 | assert.strictEqual( 413 | result.subject, 414 | 'ISO-8859-1', 415 | 'Returns the decoded mail Subject header containing the given query' 416 | ) 417 | }) 418 | 419 | it('latest mail with the query text in the From header', async function () { 420 | const result = await mailhog.latestContaining('test@example.org') 421 | assert.strictEqual( 422 | result.from, 423 | 'Test ', 424 | 'Returns the decoded mail From header containing the given query' 425 | ) 426 | }) 427 | 428 | it('latest mail with the query text in the To header', async function () { 429 | const result = await mailhog.latestContaining('nihon@example.org') 430 | assert.strictEqual( 431 | result.to, 432 | '日本 ', 433 | 'Returns the decoded mail To header containing the given query' 434 | ) 435 | }) 436 | 437 | it('latest mail with query text in attachment filename', async function () { 438 | const result = await mailhog.latestContaining('black-80x60.gif') 439 | assert.strictEqual( 440 | result.attachments[0].name, 441 | 'black-80x60.gif', 442 | 'Finds mail for a given attachment filename' 443 | ) 444 | }) 445 | }) 446 | 447 | describe('releaseMessage', function () { 448 | after(deleteAllMail) 449 | after(sendAllMail) 450 | 451 | it('releases the given mail to an outgoing SMTP server', async function () { 452 | const result = await mailhog.latestTo('nihon@example.org') 453 | const response = await mailhog.releaseMessage(result.ID, { 454 | host: 'localhost', 455 | port: '1025', 456 | email: 'nihon@example.org' 457 | }) 458 | assert.strictEqual( 459 | response.statusCode, 460 | 200, 461 | 'Responds with status code 200' 462 | ) 463 | const newResult = await mailhog.latestTo('nihon@example.org') 464 | assert.notStrictEqual( 465 | result.ID, 466 | newResult.ID, 467 | 'New search for the same query returns a new ID' 468 | ) 469 | const listResult = await mailhog.messages() 470 | assert.strictEqual( 471 | listResult.count, 472 | 5, 473 | 'Number of mails stored has increased by 1' 474 | ) 475 | }) 476 | }) 477 | 478 | describe('deleteMessage', function () { 479 | after(deleteAllMail) 480 | after(sendAllMail) 481 | 482 | it('deletes the given mail from MailHog storage', async function () { 483 | const result = await mailhog.latestTo('nihon@example.org') 484 | const response = await mailhog.deleteMessage(result.ID) 485 | assert.strictEqual( 486 | response.statusCode, 487 | 200, 488 | 'Responds with status code 200' 489 | ) 490 | const newResult = await mailhog.latestTo('nihon@example.org') 491 | assert.strictEqual( 492 | newResult, 493 | 0, 494 | 'New search for the deleted mail returns 0' 495 | ) 496 | const listResult = await mailhog.messages() 497 | assert.strictEqual( 498 | listResult.count, 499 | 3, 500 | 'Number of mails stored has decreased by 1' 501 | ) 502 | }) 503 | }) 504 | 505 | describe('deleteAll', function () { 506 | after(deleteAllMail) 507 | after(sendAllMail) 508 | 509 | it('deletes all mail from MailHog storage', async function () { 510 | const response = await mailhog.deleteAll() 511 | assert.strictEqual( 512 | response.statusCode, 513 | 200, 514 | 'Responds with status code 200' 515 | ) 516 | const listResult = await mailhog.messages() 517 | assert.strictEqual( 518 | listResult.count, 519 | 0, 520 | 'Number of mails stored has decreased to 0' 521 | ) 522 | }) 523 | }) 524 | -------------------------------------------------------------------------------- /libqp/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Andris Reinman 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /libqp/README.md: -------------------------------------------------------------------------------- 1 | # libqp 2 | Encode and decode quoted-printable strings according to 3 | [RFC2045](http://tools.ietf.org/html/rfc2045#section-6.7). 4 | 5 | ## Installation 6 | ```sh 7 | npm install mailhog 8 | ``` 9 | 10 | ## Usage 11 | ```js 12 | const libqp = require('mailhog/libqp') 13 | ``` 14 | 15 | ### Encode values 16 | Encode Buffer objects or unicode strings with 17 | 18 | ```js 19 | libqp.encode(val) 20 | ``` 21 | 22 | Where 23 | * **val** is a Buffer or an unicode string 24 | 25 | **Example** 26 | 27 | ```js 28 | libqp.encode('jõgeva') 29 | // j=C3=B5geva 30 | ``` 31 | 32 | ### Wrap encoded values 33 | Quoted-Printable encoded lines are limited to 76 characters but `encode` method 34 | might return lines longer than the limit. 35 | 36 | To enforce soft line breaks on lines longer than 76 (or any other length) 37 | characters, use `wrap` 38 | 39 | ```js 40 | llibqp.wrap(str[, lineLength]) 41 | ``` 42 | 43 | Where 44 | * **str** is a Quoted-Printable encoded string 45 | * **lineLength** (defaults to `76`) is the maximum allowed line length. 46 | Any longer line will be soft wrapped 47 | 48 | **Example** 49 | 50 | ```js 51 | libqp.wrap('abc j=C3=B5geva', 10) 52 | // abc j=\r\n 53 | // =C3=B5geva 54 | ``` 55 | 56 | ## License 57 | Released under the [MIT license](https://opensource.org/licenses/MIT). 58 | 59 | ## Author 60 | [Andris Reinman](https://github.com/andris9) with minor edits from 61 | [Sebastian Tschan](https://blueimp.net/). 62 | -------------------------------------------------------------------------------- /libqp/index.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Encodes a Buffer or String into a Quoted-Printable encoded string 3 | * 4 | * @param {Buffer|string} buffer Buffer or String to convert 5 | * @returns {string} Quoted-Printable encoded string 6 | */ 7 | export function encode(buffer: Buffer | string): string; 8 | /** 9 | * Decodes a Quoted-Printable encoded string to a Buffer object 10 | * 11 | * @param {string} input Quoted-Printable encoded string 12 | * @returns {Buffer} Decoded value 13 | */ 14 | export function decode(input: string): Buffer; 15 | /** 16 | * Adds soft line breaks to a Quoted-Printable string 17 | * 18 | * @param {string} str Quoted-Printable encoded string 19 | * @param {number} [lineLength=76] Maximum allowed length for a line 20 | * @returns {string} Soft-wrapped Quoted-Printable encoded string 21 | */ 22 | export function wrap(str: string, lineLength?: number): string; 23 | -------------------------------------------------------------------------------- /libqp/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /* eslint-disable no-useless-escape */ 4 | 5 | /** 6 | * Helper function to check if a number is inside provided ranges 7 | * 8 | * @param {number} nr Number to check for 9 | * @param {Array} ranges An Array of allowed values 10 | * @returns {boolean} True if value was found inside allowed ranges, else false 11 | */ 12 | function checkRanges(nr, ranges) { 13 | for (let i = ranges.length - 1; i >= 0; i--) { 14 | if (!ranges[i].length) { 15 | continue 16 | } 17 | if (ranges[i].length === 1 && nr === ranges[i][0]) { 18 | return true 19 | } 20 | if (ranges[i].length === 2 && nr >= ranges[i][0] && nr <= ranges[i][1]) { 21 | return true 22 | } 23 | } 24 | return false 25 | } 26 | 27 | /** 28 | * Encodes a Buffer or String into a Quoted-Printable encoded string 29 | * 30 | * @param {Buffer|string} buffer Buffer or String to convert 31 | * @returns {string} Quoted-Printable encoded string 32 | */ 33 | function encode(buffer) { 34 | const buf = typeof buffer === 'string' ? Buffer.from(buffer) : buffer 35 | // usable characters that do not need encoding 36 | const ranges = [ 37 | // https://tools.ietf.org/html/rfc2045#section-6.7 38 | [0x09], // 39 | [0x0a], // 40 | [0x0d], // 41 | [0x20, 0x3c], // !"#$%&'()*+,-./0123456789:; 42 | // >?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|} 43 | [0x3e, 0x7e] 44 | ] 45 | let result = '' 46 | let ord 47 | 48 | for (let i = 0, len = buf.length; i < len; i++) { 49 | ord = buf[i] 50 | // if the char is in allowed range, then keep as is, 51 | // unless it is a ws in the end of a line 52 | if ( 53 | checkRanges(ord, ranges) && 54 | !( 55 | (ord === 0x20 || ord === 0x09) && 56 | (i === len - 1 || buf[i + 1] === 0x0a || buf[i + 1] === 0x0d) 57 | ) 58 | ) { 59 | result += String.fromCharCode(ord) 60 | continue 61 | } 62 | result += '=' + (ord < 0x10 ? '0' : '') + ord.toString(16).toUpperCase() 63 | } 64 | 65 | return result 66 | } 67 | 68 | /** 69 | * Decodes a Quoted-Printable encoded string to a Buffer object 70 | * 71 | * @param {string} input Quoted-Printable encoded string 72 | * @returns {Buffer} Decoded value 73 | */ 74 | function decode(input) { 75 | const str = (input || '') 76 | .toString() 77 | // remove invalid whitespace from the end of lines 78 | .replace(/[\t ]+$/gm, '') 79 | // remove soft line breaks 80 | .replace(/\=(?:\r?\n|$)/g, '') 81 | 82 | const encodedBytesCount = (str.match(/\=[\da-fA-F]{2}/g) || []).length 83 | const bufferLength = str.length - encodedBytesCount * 2 84 | const buffer = Buffer.alloc(bufferLength) 85 | let bufferPos = 0 86 | let chr 87 | let hex 88 | 89 | for (let i = 0, len = str.length; i < len; i++) { 90 | chr = str.charAt(i) 91 | if ( 92 | chr === '=' && 93 | (hex = str.substr(i + 1, 2)) && 94 | /[\da-fA-F]{2}/.test(hex) 95 | ) { 96 | buffer[bufferPos++] = parseInt(hex, 16) 97 | i += 2 98 | continue 99 | } 100 | buffer[bufferPos++] = chr.charCodeAt(0) 101 | } 102 | 103 | return buffer 104 | } 105 | 106 | /** 107 | * Adds soft line breaks to a Quoted-Printable string 108 | * 109 | * @param {string} str Quoted-Printable encoded string 110 | * @param {number} [lineLength=76] Maximum allowed length for a line 111 | * @returns {string} Soft-wrapped Quoted-Printable encoded string 112 | */ 113 | function wrap(str, lineLength) { 114 | const input = (str || '').toString() 115 | const maxLength = lineLength || 76 116 | 117 | if (input.length <= maxLength) { 118 | return input 119 | } 120 | 121 | const lineMargin = Math.floor(maxLength / 3) 122 | const len = input.length 123 | let pos = 0 124 | let match 125 | let code 126 | let line 127 | let result = '' 128 | 129 | // insert soft linebreaks where needed 130 | while (pos < len) { 131 | line = input.substr(pos, maxLength) 132 | if ((match = line.match(/\r\n/))) { 133 | line = line.substr(0, match.index + match[0].length) 134 | result += line 135 | pos += line.length 136 | continue 137 | } 138 | 139 | if (line.substr(-1) === '\n') { 140 | // nothing to change here 141 | result += line 142 | pos += line.length 143 | continue 144 | } else if ((match = line.substr(-lineMargin).match(/\n.*?$/))) { 145 | // truncate to nearest line break 146 | line = line.substr(0, line.length - (match[0].length - 1)) 147 | result += line 148 | pos += line.length 149 | continue 150 | } else if ( 151 | line.length > maxLength - lineMargin && 152 | (match = line.substr(-lineMargin).match(/[ \t\.,!\?][^ \t\.,!\?]*$/)) 153 | ) { 154 | // truncate to nearest space 155 | line = line.substr(0, line.length - (match[0].length - 1)) 156 | } else { 157 | if (line.match(/\=[\da-f]{0,2}$/i)) { 158 | // push incomplete encoding sequences to the next line 159 | if ((match = line.match(/\=[\da-f]{0,1}$/i))) { 160 | line = line.substr(0, line.length - match[0].length) 161 | } 162 | 163 | // ensure that utf-8 sequences are not split 164 | while ( 165 | line.length > 3 && 166 | line.length < len - pos && 167 | !line.match(/^(?:=[\da-f]{2}){1,4}$/i) && 168 | (match = line.match(/\=[\da-f]{2}$/gi)) 169 | ) { 170 | code = parseInt(match[0].substr(1, 2), 16) 171 | if (code < 128) { 172 | break 173 | } 174 | 175 | line = line.substr(0, line.length - 3) 176 | 177 | if (code >= 0xc0) { 178 | break 179 | } 180 | } 181 | } 182 | } 183 | 184 | if (pos + line.length < len && line.substr(-1) !== '\n') { 185 | if (line.length === maxLength && line.match(/\=[\da-f]{2}$/i)) { 186 | line = line.substr(0, line.length - 3) 187 | } else if (line.length === maxLength) { 188 | line = line.substr(0, line.length - 1) 189 | } 190 | pos += line.length 191 | line += '=\r\n' 192 | } else { 193 | pos += line.length 194 | } 195 | 196 | result += line 197 | } 198 | 199 | return result 200 | } 201 | 202 | module.exports = { 203 | encode: encode, 204 | decode: decode, 205 | wrap: wrap 206 | } 207 | -------------------------------------------------------------------------------- /mail/01.eml: -------------------------------------------------------------------------------- 1 | Content-Type: multipart/mixed; boundary=next-part-zuB0VEK66qG0M6t4 2 | Date: Sun, 23 Oct 2016 20:59:40 +0200 3 | From: Test 4 | To: =?utf-8?Q?=C3=BC=C3=A4=C3=B6?= 5 | Subject: =?utf-8?Q?=C3=BC=C3=A4=C3=B6?= 6 | 7 | This is a message with multiple parts in MIME format. 8 | --next-part-zuB0VEK66qG0M6t4 9 | Content-Type: text/plain; charset=utf-8 10 | MIME-Version: 1.0 11 | Content-Transfer-Encoding: quoted-printable 12 | 13 | =C3=BC 14 | =C3=A4=C3=B6 15 | --next-part-zuB0VEK66qG0M6t4 16 | Content-Type: text/html; charset=utf-8 17 | MIME-Version: 1.0 18 | Content-Transfer-Encoding: quoted-printable 19 | 20 | =C3=BC
=C3=A4=C3=B6
21 | --next-part-zuB0VEK66qG0M6t4 22 | Content-Transfer-Encoding: base64 23 | MIME-Version: 1.0 24 | Content-Type: image/gif 25 | Content-Disposition: attachment; filename="black-80x60.gif" 26 | 27 | R0lGODdhUAA8AIABAAAAAP///ywAAAAAUAA8AAACS4SPqcvtD6OctNqLs968+w+G4kiW5omm6sq2 28 | 7gvH8kzX9o3n+s73/g8MCofEovGITCqXzKbzCY1Kp9Sq9YrNarfcrvcLDovH5PKsAAA7 29 | --next-part-zuB0VEK66qG0M6t4 30 | Content-Transfer-Encoding: base64 31 | MIME-Version: 1.0 32 | Content-Type: image/jpeg 33 | Content-Disposition: attachment; filename="white-2x1.jpg" 34 | 35 | /9j/4AAQSkZJRgABAQEAYABgAAD/4QAiRXhpZgAASUkqAAgAAAABABIBAwABAAAABgASAAAAAAD/ 36 | 7QAsUGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAA8cAgUACm9iamVjdG5hbWUA/9sAQwABAQEBAQEB 37 | AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB 38 | /9sAQwEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB 39 | AQEBAQEBAQEBAQEB/8AAEQgAAQACAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgME 40 | BQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEV 41 | UtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3 42 | eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh 43 | 4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALUR 44 | AAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDTh 45 | JfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJ 46 | ipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz 47 | 9PX29/j5+v/aAAwDAQACEQMRAD8A/v4ooooA/9k= 48 | --next-part-zuB0VEK66qG0M6t4-- 49 | -------------------------------------------------------------------------------- /mail/02.eml: -------------------------------------------------------------------------------- 1 | Content-Transfer-Encoding: base64 2 | Content-Type: text/plain; charset=utf-8 3 | Date: Sun, 23 Oct 2016 20:59:40 +0200 4 | From: Test 5 | To: =?utf-8?B?5pel5pys?= 6 | Cc: =?utf-8?B?5pel5pys?= 7 | Bcc: =?utf-8?B?5pel5pys?= 8 | Reply-To: =?utf-8?B?5pel5pys?= 9 | Subject: =?utf-8?B?5pel5pys?= 10 | 11 | 5pel5pysCg== 12 | -------------------------------------------------------------------------------- /mail/03.eml: -------------------------------------------------------------------------------- 1 | Content-Transfer-Encoding: quoted-printable 2 | Content-Type: text/plain; charset=ISO-8859-1 3 | Date: Sun, 23 Oct 2016 20:59:40 +0200 4 | From: Test 5 | To: ISO-8859-1 6 | Subject: ISO-8859-1 7 | 8 | =FC=E4=F6= 9 | -------------------------------------------------------------------------------- /mail/04.eml: -------------------------------------------------------------------------------- 1 | Content-Transfer-Encoding: 7bit 2 | Content-Type: text/plain 3 | Date: Tue, 04 Dec 2018 15:54:33 +0000 4 | From: Test 5 | To: no-charset@example.org 6 | Subject: Mail without charset 7 | 8 | text content 9 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mailhog", 3 | "version": "4.16.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "mailhog", 9 | "version": "4.16.0", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "@types/mocha": "9", 13 | "@types/node": "16", 14 | "eslint": "7", 15 | "eslint-config-blueimp": "2", 16 | "eslint-config-prettier": "8", 17 | "eslint-plugin-jsdoc": "36", 18 | "eslint-plugin-node": "11", 19 | "eslint-plugin-prettier": "4", 20 | "prettier": "2", 21 | "typescript": "4" 22 | }, 23 | "engines": { 24 | "node": ">=10.0.0" 25 | }, 26 | "optionalDependencies": { 27 | "iconv-lite": "^0.6" 28 | } 29 | }, 30 | "node_modules/@babel/code-frame": { 31 | "version": "7.12.11", 32 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", 33 | "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", 34 | "dev": true, 35 | "dependencies": { 36 | "@babel/highlight": "^7.10.4" 37 | } 38 | }, 39 | "node_modules/@babel/helper-validator-identifier": { 40 | "version": "7.15.7", 41 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", 42 | "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", 43 | "dev": true, 44 | "engines": { 45 | "node": ">=6.9.0" 46 | } 47 | }, 48 | "node_modules/@babel/highlight": { 49 | "version": "7.14.5", 50 | "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", 51 | "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", 52 | "dev": true, 53 | "dependencies": { 54 | "@babel/helper-validator-identifier": "^7.14.5", 55 | "chalk": "^2.0.0", 56 | "js-tokens": "^4.0.0" 57 | }, 58 | "engines": { 59 | "node": ">=6.9.0" 60 | } 61 | }, 62 | "node_modules/@babel/highlight/node_modules/ansi-styles": { 63 | "version": "3.2.1", 64 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 65 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 66 | "dev": true, 67 | "dependencies": { 68 | "color-convert": "^1.9.0" 69 | }, 70 | "engines": { 71 | "node": ">=4" 72 | } 73 | }, 74 | "node_modules/@babel/highlight/node_modules/chalk": { 75 | "version": "2.4.2", 76 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 77 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 78 | "dev": true, 79 | "dependencies": { 80 | "ansi-styles": "^3.2.1", 81 | "escape-string-regexp": "^1.0.5", 82 | "supports-color": "^5.3.0" 83 | }, 84 | "engines": { 85 | "node": ">=4" 86 | } 87 | }, 88 | "node_modules/@babel/highlight/node_modules/color-convert": { 89 | "version": "1.9.3", 90 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 91 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 92 | "dev": true, 93 | "dependencies": { 94 | "color-name": "1.1.3" 95 | } 96 | }, 97 | "node_modules/@babel/highlight/node_modules/color-name": { 98 | "version": "1.1.3", 99 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 100 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 101 | "dev": true 102 | }, 103 | "node_modules/@babel/highlight/node_modules/escape-string-regexp": { 104 | "version": "1.0.5", 105 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 106 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 107 | "dev": true, 108 | "engines": { 109 | "node": ">=0.8.0" 110 | } 111 | }, 112 | "node_modules/@babel/highlight/node_modules/has-flag": { 113 | "version": "3.0.0", 114 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 115 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 116 | "dev": true, 117 | "engines": { 118 | "node": ">=4" 119 | } 120 | }, 121 | "node_modules/@babel/highlight/node_modules/supports-color": { 122 | "version": "5.5.0", 123 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 124 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 125 | "dev": true, 126 | "dependencies": { 127 | "has-flag": "^3.0.0" 128 | }, 129 | "engines": { 130 | "node": ">=4" 131 | } 132 | }, 133 | "node_modules/@es-joy/jsdoccomment": { 134 | "version": "0.10.8", 135 | "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.10.8.tgz", 136 | "integrity": "sha512-3P1JiGL4xaR9PoTKUHa2N/LKwa2/eUdRqGwijMWWgBqbFEqJUVpmaOi2TcjcemrsRMgFLBzQCK4ToPhrSVDiFQ==", 137 | "dev": true, 138 | "dependencies": { 139 | "comment-parser": "1.2.4", 140 | "esquery": "^1.4.0", 141 | "jsdoc-type-pratt-parser": "1.1.1" 142 | }, 143 | "engines": { 144 | "node": "^12 || ^14 || ^16" 145 | } 146 | }, 147 | "node_modules/@eslint/eslintrc": { 148 | "version": "0.4.3", 149 | "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", 150 | "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", 151 | "dev": true, 152 | "dependencies": { 153 | "ajv": "^6.12.4", 154 | "debug": "^4.1.1", 155 | "espree": "^7.3.0", 156 | "globals": "^13.9.0", 157 | "ignore": "^4.0.6", 158 | "import-fresh": "^3.2.1", 159 | "js-yaml": "^3.13.1", 160 | "minimatch": "^3.0.4", 161 | "strip-json-comments": "^3.1.1" 162 | }, 163 | "engines": { 164 | "node": "^10.12.0 || >=12.0.0" 165 | } 166 | }, 167 | "node_modules/@humanwhocodes/config-array": { 168 | "version": "0.5.0", 169 | "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", 170 | "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", 171 | "dev": true, 172 | "dependencies": { 173 | "@humanwhocodes/object-schema": "^1.2.0", 174 | "debug": "^4.1.1", 175 | "minimatch": "^3.0.4" 176 | }, 177 | "engines": { 178 | "node": ">=10.10.0" 179 | } 180 | }, 181 | "node_modules/@humanwhocodes/object-schema": { 182 | "version": "1.2.0", 183 | "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz", 184 | "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", 185 | "dev": true 186 | }, 187 | "node_modules/@types/mocha": { 188 | "version": "9.0.0", 189 | "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.0.0.tgz", 190 | "integrity": "sha512-scN0hAWyLVAvLR9AyW7HoFF5sJZglyBsbPuHO4fv7JRvfmPBMfp1ozWqOf/e4wwPNxezBZXRfWzMb6iFLgEVRA==", 191 | "dev": true 192 | }, 193 | "node_modules/@types/node": { 194 | "version": "16.9.6", 195 | "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.6.tgz", 196 | "integrity": "sha512-YHUZhBOMTM3mjFkXVcK+WwAcYmyhe1wL4lfqNtzI0b3qAy7yuSetnM7QJazgE5PFmgVTNGiLOgRFfJMqW7XpSQ==", 197 | "dev": true 198 | }, 199 | "node_modules/acorn": { 200 | "version": "7.4.1", 201 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", 202 | "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", 203 | "dev": true, 204 | "bin": { 205 | "acorn": "bin/acorn" 206 | }, 207 | "engines": { 208 | "node": ">=0.4.0" 209 | } 210 | }, 211 | "node_modules/acorn-jsx": { 212 | "version": "5.3.2", 213 | "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", 214 | "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", 215 | "dev": true, 216 | "peerDependencies": { 217 | "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" 218 | } 219 | }, 220 | "node_modules/ajv": { 221 | "version": "6.12.6", 222 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", 223 | "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", 224 | "dev": true, 225 | "dependencies": { 226 | "fast-deep-equal": "^3.1.1", 227 | "fast-json-stable-stringify": "^2.0.0", 228 | "json-schema-traverse": "^0.4.1", 229 | "uri-js": "^4.2.2" 230 | }, 231 | "funding": { 232 | "type": "github", 233 | "url": "https://github.com/sponsors/epoberezkin" 234 | } 235 | }, 236 | "node_modules/ansi-colors": { 237 | "version": "4.1.1", 238 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", 239 | "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", 240 | "dev": true, 241 | "engines": { 242 | "node": ">=6" 243 | } 244 | }, 245 | "node_modules/ansi-regex": { 246 | "version": "5.0.1", 247 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 248 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 249 | "dev": true, 250 | "engines": { 251 | "node": ">=8" 252 | } 253 | }, 254 | "node_modules/ansi-styles": { 255 | "version": "4.3.0", 256 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 257 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 258 | "dev": true, 259 | "dependencies": { 260 | "color-convert": "^2.0.1" 261 | }, 262 | "engines": { 263 | "node": ">=8" 264 | }, 265 | "funding": { 266 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 267 | } 268 | }, 269 | "node_modules/argparse": { 270 | "version": "1.0.10", 271 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 272 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 273 | "dev": true, 274 | "dependencies": { 275 | "sprintf-js": "~1.0.2" 276 | } 277 | }, 278 | "node_modules/astral-regex": { 279 | "version": "2.0.0", 280 | "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", 281 | "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", 282 | "dev": true, 283 | "engines": { 284 | "node": ">=8" 285 | } 286 | }, 287 | "node_modules/balanced-match": { 288 | "version": "1.0.2", 289 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 290 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 291 | "dev": true 292 | }, 293 | "node_modules/brace-expansion": { 294 | "version": "1.1.11", 295 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 296 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 297 | "dev": true, 298 | "dependencies": { 299 | "balanced-match": "^1.0.0", 300 | "concat-map": "0.0.1" 301 | } 302 | }, 303 | "node_modules/callsites": { 304 | "version": "3.1.0", 305 | "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", 306 | "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", 307 | "dev": true, 308 | "engines": { 309 | "node": ">=6" 310 | } 311 | }, 312 | "node_modules/chalk": { 313 | "version": "4.1.2", 314 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 315 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 316 | "dev": true, 317 | "dependencies": { 318 | "ansi-styles": "^4.1.0", 319 | "supports-color": "^7.1.0" 320 | }, 321 | "engines": { 322 | "node": ">=10" 323 | }, 324 | "funding": { 325 | "url": "https://github.com/chalk/chalk?sponsor=1" 326 | } 327 | }, 328 | "node_modules/color-convert": { 329 | "version": "2.0.1", 330 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 331 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 332 | "dev": true, 333 | "dependencies": { 334 | "color-name": "~1.1.4" 335 | }, 336 | "engines": { 337 | "node": ">=7.0.0" 338 | } 339 | }, 340 | "node_modules/color-name": { 341 | "version": "1.1.4", 342 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 343 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 344 | "dev": true 345 | }, 346 | "node_modules/comment-parser": { 347 | "version": "1.2.4", 348 | "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.2.4.tgz", 349 | "integrity": "sha512-pm0b+qv+CkWNriSTMsfnjChF9kH0kxz55y44Wo5le9qLxMj5xDQAaEd9ZN1ovSuk9CsrncWaFwgpOMg7ClJwkw==", 350 | "dev": true, 351 | "engines": { 352 | "node": ">= 12.0.0" 353 | } 354 | }, 355 | "node_modules/concat-map": { 356 | "version": "0.0.1", 357 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 358 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 359 | "dev": true 360 | }, 361 | "node_modules/cross-spawn": { 362 | "version": "7.0.3", 363 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", 364 | "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", 365 | "dev": true, 366 | "dependencies": { 367 | "path-key": "^3.1.0", 368 | "shebang-command": "^2.0.0", 369 | "which": "^2.0.1" 370 | }, 371 | "engines": { 372 | "node": ">= 8" 373 | } 374 | }, 375 | "node_modules/debug": { 376 | "version": "4.3.2", 377 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", 378 | "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", 379 | "dev": true, 380 | "dependencies": { 381 | "ms": "2.1.2" 382 | }, 383 | "engines": { 384 | "node": ">=6.0" 385 | }, 386 | "peerDependenciesMeta": { 387 | "supports-color": { 388 | "optional": true 389 | } 390 | } 391 | }, 392 | "node_modules/deep-is": { 393 | "version": "0.1.4", 394 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", 395 | "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", 396 | "dev": true 397 | }, 398 | "node_modules/doctrine": { 399 | "version": "3.0.0", 400 | "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", 401 | "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", 402 | "dev": true, 403 | "dependencies": { 404 | "esutils": "^2.0.2" 405 | }, 406 | "engines": { 407 | "node": ">=6.0.0" 408 | } 409 | }, 410 | "node_modules/emoji-regex": { 411 | "version": "8.0.0", 412 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 413 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 414 | "dev": true 415 | }, 416 | "node_modules/enquirer": { 417 | "version": "2.3.6", 418 | "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", 419 | "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", 420 | "dev": true, 421 | "dependencies": { 422 | "ansi-colors": "^4.1.1" 423 | }, 424 | "engines": { 425 | "node": ">=8.6" 426 | } 427 | }, 428 | "node_modules/escape-string-regexp": { 429 | "version": "4.0.0", 430 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 431 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 432 | "dev": true, 433 | "engines": { 434 | "node": ">=10" 435 | }, 436 | "funding": { 437 | "url": "https://github.com/sponsors/sindresorhus" 438 | } 439 | }, 440 | "node_modules/eslint": { 441 | "version": "7.32.0", 442 | "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", 443 | "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", 444 | "dev": true, 445 | "dependencies": { 446 | "@babel/code-frame": "7.12.11", 447 | "@eslint/eslintrc": "^0.4.3", 448 | "@humanwhocodes/config-array": "^0.5.0", 449 | "ajv": "^6.10.0", 450 | "chalk": "^4.0.0", 451 | "cross-spawn": "^7.0.2", 452 | "debug": "^4.0.1", 453 | "doctrine": "^3.0.0", 454 | "enquirer": "^2.3.5", 455 | "escape-string-regexp": "^4.0.0", 456 | "eslint-scope": "^5.1.1", 457 | "eslint-utils": "^2.1.0", 458 | "eslint-visitor-keys": "^2.0.0", 459 | "espree": "^7.3.1", 460 | "esquery": "^1.4.0", 461 | "esutils": "^2.0.2", 462 | "fast-deep-equal": "^3.1.3", 463 | "file-entry-cache": "^6.0.1", 464 | "functional-red-black-tree": "^1.0.1", 465 | "glob-parent": "^5.1.2", 466 | "globals": "^13.6.0", 467 | "ignore": "^4.0.6", 468 | "import-fresh": "^3.0.0", 469 | "imurmurhash": "^0.1.4", 470 | "is-glob": "^4.0.0", 471 | "js-yaml": "^3.13.1", 472 | "json-stable-stringify-without-jsonify": "^1.0.1", 473 | "levn": "^0.4.1", 474 | "lodash.merge": "^4.6.2", 475 | "minimatch": "^3.0.4", 476 | "natural-compare": "^1.4.0", 477 | "optionator": "^0.9.1", 478 | "progress": "^2.0.0", 479 | "regexpp": "^3.1.0", 480 | "semver": "^7.2.1", 481 | "strip-ansi": "^6.0.0", 482 | "strip-json-comments": "^3.1.0", 483 | "table": "^6.0.9", 484 | "text-table": "^0.2.0", 485 | "v8-compile-cache": "^2.0.3" 486 | }, 487 | "bin": { 488 | "eslint": "bin/eslint.js" 489 | }, 490 | "engines": { 491 | "node": "^10.12.0 || >=12.0.0" 492 | }, 493 | "funding": { 494 | "url": "https://opencollective.com/eslint" 495 | } 496 | }, 497 | "node_modules/eslint-config-blueimp": { 498 | "version": "2.3.0", 499 | "resolved": "https://registry.npmjs.org/eslint-config-blueimp/-/eslint-config-blueimp-2.3.0.tgz", 500 | "integrity": "sha512-OC1+7YHBpXYdl/Jt2PZMpIPAUogHf4iDnqf8vVMlmkKls1Xemu7DAZqdFgdYhZxgaJ/d+qXH8b66L/D/pU4btA==", 501 | "dev": true, 502 | "engines": { 503 | "node": ">=10.0.0" 504 | }, 505 | "peerDependencies": { 506 | "eslint": ">=7" 507 | } 508 | }, 509 | "node_modules/eslint-config-prettier": { 510 | "version": "8.3.0", 511 | "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz", 512 | "integrity": "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==", 513 | "dev": true, 514 | "bin": { 515 | "eslint-config-prettier": "bin/cli.js" 516 | }, 517 | "peerDependencies": { 518 | "eslint": ">=7.0.0" 519 | } 520 | }, 521 | "node_modules/eslint-plugin-es": { 522 | "version": "3.0.1", 523 | "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", 524 | "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", 525 | "dev": true, 526 | "dependencies": { 527 | "eslint-utils": "^2.0.0", 528 | "regexpp": "^3.0.0" 529 | }, 530 | "engines": { 531 | "node": ">=8.10.0" 532 | }, 533 | "funding": { 534 | "url": "https://github.com/sponsors/mysticatea" 535 | }, 536 | "peerDependencies": { 537 | "eslint": ">=4.19.1" 538 | } 539 | }, 540 | "node_modules/eslint-plugin-jsdoc": { 541 | "version": "36.1.0", 542 | "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-36.1.0.tgz", 543 | "integrity": "sha512-Qpied2AJCQcScxfzTObLKRiP5QgLXjMU/ITjBagEV5p2Q/HpumD1EQtazdRYdjDSwPmXhwOl2yquwOGQ4HOJNw==", 544 | "dev": true, 545 | "dependencies": { 546 | "@es-joy/jsdoccomment": "0.10.8", 547 | "comment-parser": "1.2.4", 548 | "debug": "^4.3.2", 549 | "esquery": "^1.4.0", 550 | "jsdoc-type-pratt-parser": "^1.1.1", 551 | "lodash": "^4.17.21", 552 | "regextras": "^0.8.0", 553 | "semver": "^7.3.5", 554 | "spdx-expression-parse": "^3.0.1" 555 | }, 556 | "engines": { 557 | "node": "^12 || ^14 || ^16" 558 | }, 559 | "peerDependencies": { 560 | "eslint": "^6.0.0 || ^7.0.0" 561 | } 562 | }, 563 | "node_modules/eslint-plugin-node": { 564 | "version": "11.1.0", 565 | "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", 566 | "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", 567 | "dev": true, 568 | "dependencies": { 569 | "eslint-plugin-es": "^3.0.0", 570 | "eslint-utils": "^2.0.0", 571 | "ignore": "^5.1.1", 572 | "minimatch": "^3.0.4", 573 | "resolve": "^1.10.1", 574 | "semver": "^6.1.0" 575 | }, 576 | "engines": { 577 | "node": ">=8.10.0" 578 | }, 579 | "peerDependencies": { 580 | "eslint": ">=5.16.0" 581 | } 582 | }, 583 | "node_modules/eslint-plugin-node/node_modules/ignore": { 584 | "version": "5.1.8", 585 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", 586 | "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", 587 | "dev": true, 588 | "engines": { 589 | "node": ">= 4" 590 | } 591 | }, 592 | "node_modules/eslint-plugin-node/node_modules/semver": { 593 | "version": "6.3.0", 594 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", 595 | "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", 596 | "dev": true, 597 | "bin": { 598 | "semver": "bin/semver.js" 599 | } 600 | }, 601 | "node_modules/eslint-plugin-prettier": { 602 | "version": "4.0.0", 603 | "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.0.0.tgz", 604 | "integrity": "sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ==", 605 | "dev": true, 606 | "dependencies": { 607 | "prettier-linter-helpers": "^1.0.0" 608 | }, 609 | "engines": { 610 | "node": ">=6.0.0" 611 | }, 612 | "peerDependencies": { 613 | "eslint": ">=7.28.0", 614 | "prettier": ">=2.0.0" 615 | }, 616 | "peerDependenciesMeta": { 617 | "eslint-config-prettier": { 618 | "optional": true 619 | } 620 | } 621 | }, 622 | "node_modules/eslint-scope": { 623 | "version": "5.1.1", 624 | "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", 625 | "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", 626 | "dev": true, 627 | "dependencies": { 628 | "esrecurse": "^4.3.0", 629 | "estraverse": "^4.1.1" 630 | }, 631 | "engines": { 632 | "node": ">=8.0.0" 633 | } 634 | }, 635 | "node_modules/eslint-utils": { 636 | "version": "2.1.0", 637 | "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", 638 | "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", 639 | "dev": true, 640 | "dependencies": { 641 | "eslint-visitor-keys": "^1.1.0" 642 | }, 643 | "engines": { 644 | "node": ">=6" 645 | }, 646 | "funding": { 647 | "url": "https://github.com/sponsors/mysticatea" 648 | } 649 | }, 650 | "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { 651 | "version": "1.3.0", 652 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", 653 | "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", 654 | "dev": true, 655 | "engines": { 656 | "node": ">=4" 657 | } 658 | }, 659 | "node_modules/eslint-visitor-keys": { 660 | "version": "2.1.0", 661 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", 662 | "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", 663 | "dev": true, 664 | "engines": { 665 | "node": ">=10" 666 | } 667 | }, 668 | "node_modules/espree": { 669 | "version": "7.3.1", 670 | "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", 671 | "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", 672 | "dev": true, 673 | "dependencies": { 674 | "acorn": "^7.4.0", 675 | "acorn-jsx": "^5.3.1", 676 | "eslint-visitor-keys": "^1.3.0" 677 | }, 678 | "engines": { 679 | "node": "^10.12.0 || >=12.0.0" 680 | } 681 | }, 682 | "node_modules/espree/node_modules/eslint-visitor-keys": { 683 | "version": "1.3.0", 684 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", 685 | "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", 686 | "dev": true, 687 | "engines": { 688 | "node": ">=4" 689 | } 690 | }, 691 | "node_modules/esprima": { 692 | "version": "4.0.1", 693 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 694 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", 695 | "dev": true, 696 | "bin": { 697 | "esparse": "bin/esparse.js", 698 | "esvalidate": "bin/esvalidate.js" 699 | }, 700 | "engines": { 701 | "node": ">=4" 702 | } 703 | }, 704 | "node_modules/esquery": { 705 | "version": "1.4.0", 706 | "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", 707 | "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", 708 | "dev": true, 709 | "dependencies": { 710 | "estraverse": "^5.1.0" 711 | }, 712 | "engines": { 713 | "node": ">=0.10" 714 | } 715 | }, 716 | "node_modules/esquery/node_modules/estraverse": { 717 | "version": "5.2.0", 718 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", 719 | "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", 720 | "dev": true, 721 | "engines": { 722 | "node": ">=4.0" 723 | } 724 | }, 725 | "node_modules/esrecurse": { 726 | "version": "4.3.0", 727 | "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", 728 | "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", 729 | "dev": true, 730 | "dependencies": { 731 | "estraverse": "^5.2.0" 732 | }, 733 | "engines": { 734 | "node": ">=4.0" 735 | } 736 | }, 737 | "node_modules/esrecurse/node_modules/estraverse": { 738 | "version": "5.2.0", 739 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", 740 | "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", 741 | "dev": true, 742 | "engines": { 743 | "node": ">=4.0" 744 | } 745 | }, 746 | "node_modules/estraverse": { 747 | "version": "4.3.0", 748 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", 749 | "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", 750 | "dev": true, 751 | "engines": { 752 | "node": ">=4.0" 753 | } 754 | }, 755 | "node_modules/esutils": { 756 | "version": "2.0.3", 757 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", 758 | "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", 759 | "dev": true, 760 | "engines": { 761 | "node": ">=0.10.0" 762 | } 763 | }, 764 | "node_modules/fast-deep-equal": { 765 | "version": "3.1.3", 766 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 767 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", 768 | "dev": true 769 | }, 770 | "node_modules/fast-diff": { 771 | "version": "1.2.0", 772 | "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", 773 | "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", 774 | "dev": true 775 | }, 776 | "node_modules/fast-json-stable-stringify": { 777 | "version": "2.1.0", 778 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 779 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", 780 | "dev": true 781 | }, 782 | "node_modules/fast-levenshtein": { 783 | "version": "2.0.6", 784 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 785 | "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", 786 | "dev": true 787 | }, 788 | "node_modules/file-entry-cache": { 789 | "version": "6.0.1", 790 | "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", 791 | "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", 792 | "dev": true, 793 | "dependencies": { 794 | "flat-cache": "^3.0.4" 795 | }, 796 | "engines": { 797 | "node": "^10.12.0 || >=12.0.0" 798 | } 799 | }, 800 | "node_modules/flat-cache": { 801 | "version": "3.0.4", 802 | "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", 803 | "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", 804 | "dev": true, 805 | "dependencies": { 806 | "flatted": "^3.1.0", 807 | "rimraf": "^3.0.2" 808 | }, 809 | "engines": { 810 | "node": "^10.12.0 || >=12.0.0" 811 | } 812 | }, 813 | "node_modules/flatted": { 814 | "version": "3.2.2", 815 | "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.2.tgz", 816 | "integrity": "sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==", 817 | "dev": true 818 | }, 819 | "node_modules/fs.realpath": { 820 | "version": "1.0.0", 821 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 822 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 823 | "dev": true 824 | }, 825 | "node_modules/function-bind": { 826 | "version": "1.1.1", 827 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 828 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 829 | "dev": true 830 | }, 831 | "node_modules/functional-red-black-tree": { 832 | "version": "1.0.1", 833 | "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", 834 | "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", 835 | "dev": true 836 | }, 837 | "node_modules/glob": { 838 | "version": "7.2.0", 839 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", 840 | "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", 841 | "dev": true, 842 | "dependencies": { 843 | "fs.realpath": "^1.0.0", 844 | "inflight": "^1.0.4", 845 | "inherits": "2", 846 | "minimatch": "^3.0.4", 847 | "once": "^1.3.0", 848 | "path-is-absolute": "^1.0.0" 849 | }, 850 | "engines": { 851 | "node": "*" 852 | }, 853 | "funding": { 854 | "url": "https://github.com/sponsors/isaacs" 855 | } 856 | }, 857 | "node_modules/glob-parent": { 858 | "version": "5.1.2", 859 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 860 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 861 | "dev": true, 862 | "dependencies": { 863 | "is-glob": "^4.0.1" 864 | }, 865 | "engines": { 866 | "node": ">= 6" 867 | } 868 | }, 869 | "node_modules/globals": { 870 | "version": "13.11.0", 871 | "resolved": "https://registry.npmjs.org/globals/-/globals-13.11.0.tgz", 872 | "integrity": "sha512-08/xrJ7wQjK9kkkRoI3OFUBbLx4f+6x3SGwcPvQ0QH6goFDrOU2oyAWrmh3dJezu65buo+HBMzAMQy6rovVC3g==", 873 | "dev": true, 874 | "dependencies": { 875 | "type-fest": "^0.20.2" 876 | }, 877 | "engines": { 878 | "node": ">=8" 879 | }, 880 | "funding": { 881 | "url": "https://github.com/sponsors/sindresorhus" 882 | } 883 | }, 884 | "node_modules/has": { 885 | "version": "1.0.3", 886 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 887 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 888 | "dev": true, 889 | "dependencies": { 890 | "function-bind": "^1.1.1" 891 | }, 892 | "engines": { 893 | "node": ">= 0.4.0" 894 | } 895 | }, 896 | "node_modules/has-flag": { 897 | "version": "4.0.0", 898 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 899 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 900 | "dev": true, 901 | "engines": { 902 | "node": ">=8" 903 | } 904 | }, 905 | "node_modules/iconv-lite": { 906 | "version": "0.6.3", 907 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", 908 | "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", 909 | "optional": true, 910 | "dependencies": { 911 | "safer-buffer": ">= 2.1.2 < 3.0.0" 912 | }, 913 | "engines": { 914 | "node": ">=0.10.0" 915 | } 916 | }, 917 | "node_modules/ignore": { 918 | "version": "4.0.6", 919 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", 920 | "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", 921 | "dev": true, 922 | "engines": { 923 | "node": ">= 4" 924 | } 925 | }, 926 | "node_modules/import-fresh": { 927 | "version": "3.3.0", 928 | "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", 929 | "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", 930 | "dev": true, 931 | "dependencies": { 932 | "parent-module": "^1.0.0", 933 | "resolve-from": "^4.0.0" 934 | }, 935 | "engines": { 936 | "node": ">=6" 937 | }, 938 | "funding": { 939 | "url": "https://github.com/sponsors/sindresorhus" 940 | } 941 | }, 942 | "node_modules/imurmurhash": { 943 | "version": "0.1.4", 944 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 945 | "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", 946 | "dev": true, 947 | "engines": { 948 | "node": ">=0.8.19" 949 | } 950 | }, 951 | "node_modules/inflight": { 952 | "version": "1.0.6", 953 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 954 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 955 | "dev": true, 956 | "dependencies": { 957 | "once": "^1.3.0", 958 | "wrappy": "1" 959 | } 960 | }, 961 | "node_modules/inherits": { 962 | "version": "2.0.4", 963 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 964 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 965 | "dev": true 966 | }, 967 | "node_modules/is-core-module": { 968 | "version": "2.6.0", 969 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.6.0.tgz", 970 | "integrity": "sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ==", 971 | "dev": true, 972 | "dependencies": { 973 | "has": "^1.0.3" 974 | }, 975 | "funding": { 976 | "url": "https://github.com/sponsors/ljharb" 977 | } 978 | }, 979 | "node_modules/is-extglob": { 980 | "version": "2.1.1", 981 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 982 | "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", 983 | "dev": true, 984 | "engines": { 985 | "node": ">=0.10.0" 986 | } 987 | }, 988 | "node_modules/is-fullwidth-code-point": { 989 | "version": "3.0.0", 990 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 991 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 992 | "dev": true, 993 | "engines": { 994 | "node": ">=8" 995 | } 996 | }, 997 | "node_modules/is-glob": { 998 | "version": "4.0.1", 999 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", 1000 | "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", 1001 | "dev": true, 1002 | "dependencies": { 1003 | "is-extglob": "^2.1.1" 1004 | }, 1005 | "engines": { 1006 | "node": ">=0.10.0" 1007 | } 1008 | }, 1009 | "node_modules/isexe": { 1010 | "version": "2.0.0", 1011 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 1012 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 1013 | "dev": true 1014 | }, 1015 | "node_modules/js-tokens": { 1016 | "version": "4.0.0", 1017 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 1018 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 1019 | "dev": true 1020 | }, 1021 | "node_modules/js-yaml": { 1022 | "version": "3.14.1", 1023 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", 1024 | "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", 1025 | "dev": true, 1026 | "dependencies": { 1027 | "argparse": "^1.0.7", 1028 | "esprima": "^4.0.0" 1029 | }, 1030 | "bin": { 1031 | "js-yaml": "bin/js-yaml.js" 1032 | } 1033 | }, 1034 | "node_modules/jsdoc-type-pratt-parser": { 1035 | "version": "1.1.1", 1036 | "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-1.1.1.tgz", 1037 | "integrity": "sha512-uelRmpghNwPBuZScwgBG/OzodaFk5RbO5xaivBdsAY70icWfShwZ7PCMO0x1zSkOa8T1FzHThmrdoyg/0AwV5g==", 1038 | "dev": true, 1039 | "engines": { 1040 | "node": ">=12.0.0" 1041 | } 1042 | }, 1043 | "node_modules/json-schema-traverse": { 1044 | "version": "0.4.1", 1045 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 1046 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 1047 | "dev": true 1048 | }, 1049 | "node_modules/json-stable-stringify-without-jsonify": { 1050 | "version": "1.0.1", 1051 | "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", 1052 | "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", 1053 | "dev": true 1054 | }, 1055 | "node_modules/levn": { 1056 | "version": "0.4.1", 1057 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", 1058 | "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", 1059 | "dev": true, 1060 | "dependencies": { 1061 | "prelude-ls": "^1.2.1", 1062 | "type-check": "~0.4.0" 1063 | }, 1064 | "engines": { 1065 | "node": ">= 0.8.0" 1066 | } 1067 | }, 1068 | "node_modules/lodash": { 1069 | "version": "4.17.21", 1070 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 1071 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", 1072 | "dev": true 1073 | }, 1074 | "node_modules/lodash.clonedeep": { 1075 | "version": "4.5.0", 1076 | "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", 1077 | "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", 1078 | "dev": true 1079 | }, 1080 | "node_modules/lodash.merge": { 1081 | "version": "4.6.2", 1082 | "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", 1083 | "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", 1084 | "dev": true 1085 | }, 1086 | "node_modules/lodash.truncate": { 1087 | "version": "4.4.2", 1088 | "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", 1089 | "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", 1090 | "dev": true 1091 | }, 1092 | "node_modules/lru-cache": { 1093 | "version": "6.0.0", 1094 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", 1095 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", 1096 | "dev": true, 1097 | "dependencies": { 1098 | "yallist": "^4.0.0" 1099 | }, 1100 | "engines": { 1101 | "node": ">=10" 1102 | } 1103 | }, 1104 | "node_modules/minimatch": { 1105 | "version": "3.0.4", 1106 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 1107 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 1108 | "dev": true, 1109 | "dependencies": { 1110 | "brace-expansion": "^1.1.7" 1111 | }, 1112 | "engines": { 1113 | "node": "*" 1114 | } 1115 | }, 1116 | "node_modules/ms": { 1117 | "version": "2.1.2", 1118 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1119 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 1120 | "dev": true 1121 | }, 1122 | "node_modules/natural-compare": { 1123 | "version": "1.4.0", 1124 | "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", 1125 | "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", 1126 | "dev": true 1127 | }, 1128 | "node_modules/once": { 1129 | "version": "1.4.0", 1130 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1131 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 1132 | "dev": true, 1133 | "dependencies": { 1134 | "wrappy": "1" 1135 | } 1136 | }, 1137 | "node_modules/optionator": { 1138 | "version": "0.9.1", 1139 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", 1140 | "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", 1141 | "dev": true, 1142 | "dependencies": { 1143 | "deep-is": "^0.1.3", 1144 | "fast-levenshtein": "^2.0.6", 1145 | "levn": "^0.4.1", 1146 | "prelude-ls": "^1.2.1", 1147 | "type-check": "^0.4.0", 1148 | "word-wrap": "^1.2.3" 1149 | }, 1150 | "engines": { 1151 | "node": ">= 0.8.0" 1152 | } 1153 | }, 1154 | "node_modules/parent-module": { 1155 | "version": "1.0.1", 1156 | "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", 1157 | "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", 1158 | "dev": true, 1159 | "dependencies": { 1160 | "callsites": "^3.0.0" 1161 | }, 1162 | "engines": { 1163 | "node": ">=6" 1164 | } 1165 | }, 1166 | "node_modules/path-is-absolute": { 1167 | "version": "1.0.1", 1168 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1169 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 1170 | "dev": true, 1171 | "engines": { 1172 | "node": ">=0.10.0" 1173 | } 1174 | }, 1175 | "node_modules/path-key": { 1176 | "version": "3.1.1", 1177 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 1178 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 1179 | "dev": true, 1180 | "engines": { 1181 | "node": ">=8" 1182 | } 1183 | }, 1184 | "node_modules/path-parse": { 1185 | "version": "1.0.7", 1186 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 1187 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 1188 | "dev": true 1189 | }, 1190 | "node_modules/prelude-ls": { 1191 | "version": "1.2.1", 1192 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", 1193 | "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", 1194 | "dev": true, 1195 | "engines": { 1196 | "node": ">= 0.8.0" 1197 | } 1198 | }, 1199 | "node_modules/prettier": { 1200 | "version": "2.4.1", 1201 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz", 1202 | "integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==", 1203 | "dev": true, 1204 | "bin": { 1205 | "prettier": "bin-prettier.js" 1206 | }, 1207 | "engines": { 1208 | "node": ">=10.13.0" 1209 | } 1210 | }, 1211 | "node_modules/prettier-linter-helpers": { 1212 | "version": "1.0.0", 1213 | "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", 1214 | "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", 1215 | "dev": true, 1216 | "dependencies": { 1217 | "fast-diff": "^1.1.2" 1218 | }, 1219 | "engines": { 1220 | "node": ">=6.0.0" 1221 | } 1222 | }, 1223 | "node_modules/progress": { 1224 | "version": "2.0.3", 1225 | "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", 1226 | "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", 1227 | "dev": true, 1228 | "engines": { 1229 | "node": ">=0.4.0" 1230 | } 1231 | }, 1232 | "node_modules/punycode": { 1233 | "version": "2.1.1", 1234 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 1235 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", 1236 | "dev": true, 1237 | "engines": { 1238 | "node": ">=6" 1239 | } 1240 | }, 1241 | "node_modules/regexpp": { 1242 | "version": "3.2.0", 1243 | "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", 1244 | "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", 1245 | "dev": true, 1246 | "engines": { 1247 | "node": ">=8" 1248 | }, 1249 | "funding": { 1250 | "url": "https://github.com/sponsors/mysticatea" 1251 | } 1252 | }, 1253 | "node_modules/regextras": { 1254 | "version": "0.8.0", 1255 | "resolved": "https://registry.npmjs.org/regextras/-/regextras-0.8.0.tgz", 1256 | "integrity": "sha512-k519uI04Z3SaY0fLX843MRXnDeG2+vHOFsyhiPZvNLe7r8rD2YNRjq4BQLZZ0oAr2NrtvZlICsXysGNFPGa3CQ==", 1257 | "dev": true, 1258 | "engines": { 1259 | "node": ">=0.1.14" 1260 | } 1261 | }, 1262 | "node_modules/require-from-string": { 1263 | "version": "2.0.2", 1264 | "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", 1265 | "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", 1266 | "dev": true, 1267 | "engines": { 1268 | "node": ">=0.10.0" 1269 | } 1270 | }, 1271 | "node_modules/resolve": { 1272 | "version": "1.20.0", 1273 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", 1274 | "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", 1275 | "dev": true, 1276 | "dependencies": { 1277 | "is-core-module": "^2.2.0", 1278 | "path-parse": "^1.0.6" 1279 | }, 1280 | "funding": { 1281 | "url": "https://github.com/sponsors/ljharb" 1282 | } 1283 | }, 1284 | "node_modules/resolve-from": { 1285 | "version": "4.0.0", 1286 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", 1287 | "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", 1288 | "dev": true, 1289 | "engines": { 1290 | "node": ">=4" 1291 | } 1292 | }, 1293 | "node_modules/rimraf": { 1294 | "version": "3.0.2", 1295 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", 1296 | "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", 1297 | "dev": true, 1298 | "dependencies": { 1299 | "glob": "^7.1.3" 1300 | }, 1301 | "bin": { 1302 | "rimraf": "bin.js" 1303 | }, 1304 | "funding": { 1305 | "url": "https://github.com/sponsors/isaacs" 1306 | } 1307 | }, 1308 | "node_modules/safer-buffer": { 1309 | "version": "2.1.2", 1310 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1311 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", 1312 | "optional": true 1313 | }, 1314 | "node_modules/semver": { 1315 | "version": "7.3.5", 1316 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", 1317 | "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", 1318 | "dev": true, 1319 | "dependencies": { 1320 | "lru-cache": "^6.0.0" 1321 | }, 1322 | "bin": { 1323 | "semver": "bin/semver.js" 1324 | }, 1325 | "engines": { 1326 | "node": ">=10" 1327 | } 1328 | }, 1329 | "node_modules/shebang-command": { 1330 | "version": "2.0.0", 1331 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 1332 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 1333 | "dev": true, 1334 | "dependencies": { 1335 | "shebang-regex": "^3.0.0" 1336 | }, 1337 | "engines": { 1338 | "node": ">=8" 1339 | } 1340 | }, 1341 | "node_modules/shebang-regex": { 1342 | "version": "3.0.0", 1343 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 1344 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 1345 | "dev": true, 1346 | "engines": { 1347 | "node": ">=8" 1348 | } 1349 | }, 1350 | "node_modules/slice-ansi": { 1351 | "version": "4.0.0", 1352 | "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", 1353 | "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", 1354 | "dev": true, 1355 | "dependencies": { 1356 | "ansi-styles": "^4.0.0", 1357 | "astral-regex": "^2.0.0", 1358 | "is-fullwidth-code-point": "^3.0.0" 1359 | }, 1360 | "engines": { 1361 | "node": ">=10" 1362 | }, 1363 | "funding": { 1364 | "url": "https://github.com/chalk/slice-ansi?sponsor=1" 1365 | } 1366 | }, 1367 | "node_modules/spdx-exceptions": { 1368 | "version": "2.3.0", 1369 | "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", 1370 | "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", 1371 | "dev": true 1372 | }, 1373 | "node_modules/spdx-expression-parse": { 1374 | "version": "3.0.1", 1375 | "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", 1376 | "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", 1377 | "dev": true, 1378 | "dependencies": { 1379 | "spdx-exceptions": "^2.1.0", 1380 | "spdx-license-ids": "^3.0.0" 1381 | } 1382 | }, 1383 | "node_modules/spdx-license-ids": { 1384 | "version": "3.0.10", 1385 | "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.10.tgz", 1386 | "integrity": "sha512-oie3/+gKf7QtpitB0LYLETe+k8SifzsX4KixvpOsbI6S0kRiRQ5MKOio8eMSAKQ17N06+wdEOXRiId+zOxo0hA==", 1387 | "dev": true 1388 | }, 1389 | "node_modules/sprintf-js": { 1390 | "version": "1.0.3", 1391 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 1392 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", 1393 | "dev": true 1394 | }, 1395 | "node_modules/string-width": { 1396 | "version": "4.2.3", 1397 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 1398 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 1399 | "dev": true, 1400 | "dependencies": { 1401 | "emoji-regex": "^8.0.0", 1402 | "is-fullwidth-code-point": "^3.0.0", 1403 | "strip-ansi": "^6.0.1" 1404 | }, 1405 | "engines": { 1406 | "node": ">=8" 1407 | } 1408 | }, 1409 | "node_modules/strip-ansi": { 1410 | "version": "6.0.1", 1411 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 1412 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 1413 | "dev": true, 1414 | "dependencies": { 1415 | "ansi-regex": "^5.0.1" 1416 | }, 1417 | "engines": { 1418 | "node": ">=8" 1419 | } 1420 | }, 1421 | "node_modules/strip-json-comments": { 1422 | "version": "3.1.1", 1423 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 1424 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 1425 | "dev": true, 1426 | "engines": { 1427 | "node": ">=8" 1428 | }, 1429 | "funding": { 1430 | "url": "https://github.com/sponsors/sindresorhus" 1431 | } 1432 | }, 1433 | "node_modules/supports-color": { 1434 | "version": "7.2.0", 1435 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 1436 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 1437 | "dev": true, 1438 | "dependencies": { 1439 | "has-flag": "^4.0.0" 1440 | }, 1441 | "engines": { 1442 | "node": ">=8" 1443 | } 1444 | }, 1445 | "node_modules/table": { 1446 | "version": "6.7.1", 1447 | "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", 1448 | "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", 1449 | "dev": true, 1450 | "dependencies": { 1451 | "ajv": "^8.0.1", 1452 | "lodash.clonedeep": "^4.5.0", 1453 | "lodash.truncate": "^4.4.2", 1454 | "slice-ansi": "^4.0.0", 1455 | "string-width": "^4.2.0", 1456 | "strip-ansi": "^6.0.0" 1457 | }, 1458 | "engines": { 1459 | "node": ">=10.0.0" 1460 | } 1461 | }, 1462 | "node_modules/table/node_modules/ajv": { 1463 | "version": "8.6.3", 1464 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.3.tgz", 1465 | "integrity": "sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw==", 1466 | "dev": true, 1467 | "dependencies": { 1468 | "fast-deep-equal": "^3.1.1", 1469 | "json-schema-traverse": "^1.0.0", 1470 | "require-from-string": "^2.0.2", 1471 | "uri-js": "^4.2.2" 1472 | }, 1473 | "funding": { 1474 | "type": "github", 1475 | "url": "https://github.com/sponsors/epoberezkin" 1476 | } 1477 | }, 1478 | "node_modules/table/node_modules/json-schema-traverse": { 1479 | "version": "1.0.0", 1480 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", 1481 | "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", 1482 | "dev": true 1483 | }, 1484 | "node_modules/text-table": { 1485 | "version": "0.2.0", 1486 | "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", 1487 | "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", 1488 | "dev": true 1489 | }, 1490 | "node_modules/type-check": { 1491 | "version": "0.4.0", 1492 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", 1493 | "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", 1494 | "dev": true, 1495 | "dependencies": { 1496 | "prelude-ls": "^1.2.1" 1497 | }, 1498 | "engines": { 1499 | "node": ">= 0.8.0" 1500 | } 1501 | }, 1502 | "node_modules/type-fest": { 1503 | "version": "0.20.2", 1504 | "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", 1505 | "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", 1506 | "dev": true, 1507 | "engines": { 1508 | "node": ">=10" 1509 | }, 1510 | "funding": { 1511 | "url": "https://github.com/sponsors/sindresorhus" 1512 | } 1513 | }, 1514 | "node_modules/typescript": { 1515 | "version": "4.4.3", 1516 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.3.tgz", 1517 | "integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==", 1518 | "dev": true, 1519 | "bin": { 1520 | "tsc": "bin/tsc", 1521 | "tsserver": "bin/tsserver" 1522 | }, 1523 | "engines": { 1524 | "node": ">=4.2.0" 1525 | } 1526 | }, 1527 | "node_modules/uri-js": { 1528 | "version": "4.4.1", 1529 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", 1530 | "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", 1531 | "dev": true, 1532 | "dependencies": { 1533 | "punycode": "^2.1.0" 1534 | } 1535 | }, 1536 | "node_modules/v8-compile-cache": { 1537 | "version": "2.3.0", 1538 | "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", 1539 | "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", 1540 | "dev": true 1541 | }, 1542 | "node_modules/which": { 1543 | "version": "2.0.2", 1544 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 1545 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 1546 | "dev": true, 1547 | "dependencies": { 1548 | "isexe": "^2.0.0" 1549 | }, 1550 | "bin": { 1551 | "node-which": "bin/node-which" 1552 | }, 1553 | "engines": { 1554 | "node": ">= 8" 1555 | } 1556 | }, 1557 | "node_modules/word-wrap": { 1558 | "version": "1.2.3", 1559 | "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", 1560 | "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", 1561 | "dev": true, 1562 | "engines": { 1563 | "node": ">=0.10.0" 1564 | } 1565 | }, 1566 | "node_modules/wrappy": { 1567 | "version": "1.0.2", 1568 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1569 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 1570 | "dev": true 1571 | }, 1572 | "node_modules/yallist": { 1573 | "version": "4.0.0", 1574 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", 1575 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", 1576 | "dev": true 1577 | } 1578 | }, 1579 | "dependencies": { 1580 | "@babel/code-frame": { 1581 | "version": "7.12.11", 1582 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", 1583 | "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", 1584 | "dev": true, 1585 | "requires": { 1586 | "@babel/highlight": "^7.10.4" 1587 | } 1588 | }, 1589 | "@babel/helper-validator-identifier": { 1590 | "version": "7.15.7", 1591 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", 1592 | "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", 1593 | "dev": true 1594 | }, 1595 | "@babel/highlight": { 1596 | "version": "7.14.5", 1597 | "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", 1598 | "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", 1599 | "dev": true, 1600 | "requires": { 1601 | "@babel/helper-validator-identifier": "^7.14.5", 1602 | "chalk": "^2.0.0", 1603 | "js-tokens": "^4.0.0" 1604 | }, 1605 | "dependencies": { 1606 | "ansi-styles": { 1607 | "version": "3.2.1", 1608 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 1609 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 1610 | "dev": true, 1611 | "requires": { 1612 | "color-convert": "^1.9.0" 1613 | } 1614 | }, 1615 | "chalk": { 1616 | "version": "2.4.2", 1617 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 1618 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 1619 | "dev": true, 1620 | "requires": { 1621 | "ansi-styles": "^3.2.1", 1622 | "escape-string-regexp": "^1.0.5", 1623 | "supports-color": "^5.3.0" 1624 | } 1625 | }, 1626 | "color-convert": { 1627 | "version": "1.9.3", 1628 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 1629 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 1630 | "dev": true, 1631 | "requires": { 1632 | "color-name": "1.1.3" 1633 | } 1634 | }, 1635 | "color-name": { 1636 | "version": "1.1.3", 1637 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 1638 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 1639 | "dev": true 1640 | }, 1641 | "escape-string-regexp": { 1642 | "version": "1.0.5", 1643 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 1644 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 1645 | "dev": true 1646 | }, 1647 | "has-flag": { 1648 | "version": "3.0.0", 1649 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 1650 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 1651 | "dev": true 1652 | }, 1653 | "supports-color": { 1654 | "version": "5.5.0", 1655 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 1656 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 1657 | "dev": true, 1658 | "requires": { 1659 | "has-flag": "^3.0.0" 1660 | } 1661 | } 1662 | } 1663 | }, 1664 | "@es-joy/jsdoccomment": { 1665 | "version": "0.10.8", 1666 | "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.10.8.tgz", 1667 | "integrity": "sha512-3P1JiGL4xaR9PoTKUHa2N/LKwa2/eUdRqGwijMWWgBqbFEqJUVpmaOi2TcjcemrsRMgFLBzQCK4ToPhrSVDiFQ==", 1668 | "dev": true, 1669 | "requires": { 1670 | "comment-parser": "1.2.4", 1671 | "esquery": "^1.4.0", 1672 | "jsdoc-type-pratt-parser": "1.1.1" 1673 | } 1674 | }, 1675 | "@eslint/eslintrc": { 1676 | "version": "0.4.3", 1677 | "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", 1678 | "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", 1679 | "dev": true, 1680 | "requires": { 1681 | "ajv": "^6.12.4", 1682 | "debug": "^4.1.1", 1683 | "espree": "^7.3.0", 1684 | "globals": "^13.9.0", 1685 | "ignore": "^4.0.6", 1686 | "import-fresh": "^3.2.1", 1687 | "js-yaml": "^3.13.1", 1688 | "minimatch": "^3.0.4", 1689 | "strip-json-comments": "^3.1.1" 1690 | } 1691 | }, 1692 | "@humanwhocodes/config-array": { 1693 | "version": "0.5.0", 1694 | "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", 1695 | "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", 1696 | "dev": true, 1697 | "requires": { 1698 | "@humanwhocodes/object-schema": "^1.2.0", 1699 | "debug": "^4.1.1", 1700 | "minimatch": "^3.0.4" 1701 | } 1702 | }, 1703 | "@humanwhocodes/object-schema": { 1704 | "version": "1.2.0", 1705 | "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz", 1706 | "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", 1707 | "dev": true 1708 | }, 1709 | "@types/mocha": { 1710 | "version": "9.0.0", 1711 | "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.0.0.tgz", 1712 | "integrity": "sha512-scN0hAWyLVAvLR9AyW7HoFF5sJZglyBsbPuHO4fv7JRvfmPBMfp1ozWqOf/e4wwPNxezBZXRfWzMb6iFLgEVRA==", 1713 | "dev": true 1714 | }, 1715 | "@types/node": { 1716 | "version": "16.9.6", 1717 | "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.6.tgz", 1718 | "integrity": "sha512-YHUZhBOMTM3mjFkXVcK+WwAcYmyhe1wL4lfqNtzI0b3qAy7yuSetnM7QJazgE5PFmgVTNGiLOgRFfJMqW7XpSQ==", 1719 | "dev": true 1720 | }, 1721 | "acorn": { 1722 | "version": "7.4.1", 1723 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", 1724 | "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", 1725 | "dev": true 1726 | }, 1727 | "acorn-jsx": { 1728 | "version": "5.3.2", 1729 | "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", 1730 | "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", 1731 | "dev": true, 1732 | "requires": {} 1733 | }, 1734 | "ajv": { 1735 | "version": "6.12.6", 1736 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", 1737 | "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", 1738 | "dev": true, 1739 | "requires": { 1740 | "fast-deep-equal": "^3.1.1", 1741 | "fast-json-stable-stringify": "^2.0.0", 1742 | "json-schema-traverse": "^0.4.1", 1743 | "uri-js": "^4.2.2" 1744 | } 1745 | }, 1746 | "ansi-colors": { 1747 | "version": "4.1.1", 1748 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", 1749 | "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", 1750 | "dev": true 1751 | }, 1752 | "ansi-regex": { 1753 | "version": "5.0.1", 1754 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 1755 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 1756 | "dev": true 1757 | }, 1758 | "ansi-styles": { 1759 | "version": "4.3.0", 1760 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 1761 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 1762 | "dev": true, 1763 | "requires": { 1764 | "color-convert": "^2.0.1" 1765 | } 1766 | }, 1767 | "argparse": { 1768 | "version": "1.0.10", 1769 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 1770 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 1771 | "dev": true, 1772 | "requires": { 1773 | "sprintf-js": "~1.0.2" 1774 | } 1775 | }, 1776 | "astral-regex": { 1777 | "version": "2.0.0", 1778 | "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", 1779 | "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", 1780 | "dev": true 1781 | }, 1782 | "balanced-match": { 1783 | "version": "1.0.2", 1784 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 1785 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 1786 | "dev": true 1787 | }, 1788 | "brace-expansion": { 1789 | "version": "1.1.11", 1790 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 1791 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 1792 | "dev": true, 1793 | "requires": { 1794 | "balanced-match": "^1.0.0", 1795 | "concat-map": "0.0.1" 1796 | } 1797 | }, 1798 | "callsites": { 1799 | "version": "3.1.0", 1800 | "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", 1801 | "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", 1802 | "dev": true 1803 | }, 1804 | "chalk": { 1805 | "version": "4.1.2", 1806 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 1807 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 1808 | "dev": true, 1809 | "requires": { 1810 | "ansi-styles": "^4.1.0", 1811 | "supports-color": "^7.1.0" 1812 | } 1813 | }, 1814 | "color-convert": { 1815 | "version": "2.0.1", 1816 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 1817 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 1818 | "dev": true, 1819 | "requires": { 1820 | "color-name": "~1.1.4" 1821 | } 1822 | }, 1823 | "color-name": { 1824 | "version": "1.1.4", 1825 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 1826 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 1827 | "dev": true 1828 | }, 1829 | "comment-parser": { 1830 | "version": "1.2.4", 1831 | "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.2.4.tgz", 1832 | "integrity": "sha512-pm0b+qv+CkWNriSTMsfnjChF9kH0kxz55y44Wo5le9qLxMj5xDQAaEd9ZN1ovSuk9CsrncWaFwgpOMg7ClJwkw==", 1833 | "dev": true 1834 | }, 1835 | "concat-map": { 1836 | "version": "0.0.1", 1837 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 1838 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 1839 | "dev": true 1840 | }, 1841 | "cross-spawn": { 1842 | "version": "7.0.3", 1843 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", 1844 | "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", 1845 | "dev": true, 1846 | "requires": { 1847 | "path-key": "^3.1.0", 1848 | "shebang-command": "^2.0.0", 1849 | "which": "^2.0.1" 1850 | } 1851 | }, 1852 | "debug": { 1853 | "version": "4.3.2", 1854 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", 1855 | "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", 1856 | "dev": true, 1857 | "requires": { 1858 | "ms": "2.1.2" 1859 | } 1860 | }, 1861 | "deep-is": { 1862 | "version": "0.1.4", 1863 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", 1864 | "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", 1865 | "dev": true 1866 | }, 1867 | "doctrine": { 1868 | "version": "3.0.0", 1869 | "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", 1870 | "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", 1871 | "dev": true, 1872 | "requires": { 1873 | "esutils": "^2.0.2" 1874 | } 1875 | }, 1876 | "emoji-regex": { 1877 | "version": "8.0.0", 1878 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 1879 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 1880 | "dev": true 1881 | }, 1882 | "enquirer": { 1883 | "version": "2.3.6", 1884 | "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", 1885 | "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", 1886 | "dev": true, 1887 | "requires": { 1888 | "ansi-colors": "^4.1.1" 1889 | } 1890 | }, 1891 | "escape-string-regexp": { 1892 | "version": "4.0.0", 1893 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 1894 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 1895 | "dev": true 1896 | }, 1897 | "eslint": { 1898 | "version": "7.32.0", 1899 | "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", 1900 | "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", 1901 | "dev": true, 1902 | "requires": { 1903 | "@babel/code-frame": "7.12.11", 1904 | "@eslint/eslintrc": "^0.4.3", 1905 | "@humanwhocodes/config-array": "^0.5.0", 1906 | "ajv": "^6.10.0", 1907 | "chalk": "^4.0.0", 1908 | "cross-spawn": "^7.0.2", 1909 | "debug": "^4.0.1", 1910 | "doctrine": "^3.0.0", 1911 | "enquirer": "^2.3.5", 1912 | "escape-string-regexp": "^4.0.0", 1913 | "eslint-scope": "^5.1.1", 1914 | "eslint-utils": "^2.1.0", 1915 | "eslint-visitor-keys": "^2.0.0", 1916 | "espree": "^7.3.1", 1917 | "esquery": "^1.4.0", 1918 | "esutils": "^2.0.2", 1919 | "fast-deep-equal": "^3.1.3", 1920 | "file-entry-cache": "^6.0.1", 1921 | "functional-red-black-tree": "^1.0.1", 1922 | "glob-parent": "^5.1.2", 1923 | "globals": "^13.6.0", 1924 | "ignore": "^4.0.6", 1925 | "import-fresh": "^3.0.0", 1926 | "imurmurhash": "^0.1.4", 1927 | "is-glob": "^4.0.0", 1928 | "js-yaml": "^3.13.1", 1929 | "json-stable-stringify-without-jsonify": "^1.0.1", 1930 | "levn": "^0.4.1", 1931 | "lodash.merge": "^4.6.2", 1932 | "minimatch": "^3.0.4", 1933 | "natural-compare": "^1.4.0", 1934 | "optionator": "^0.9.1", 1935 | "progress": "^2.0.0", 1936 | "regexpp": "^3.1.0", 1937 | "semver": "^7.2.1", 1938 | "strip-ansi": "^6.0.0", 1939 | "strip-json-comments": "^3.1.0", 1940 | "table": "^6.0.9", 1941 | "text-table": "^0.2.0", 1942 | "v8-compile-cache": "^2.0.3" 1943 | } 1944 | }, 1945 | "eslint-config-blueimp": { 1946 | "version": "2.3.0", 1947 | "resolved": "https://registry.npmjs.org/eslint-config-blueimp/-/eslint-config-blueimp-2.3.0.tgz", 1948 | "integrity": "sha512-OC1+7YHBpXYdl/Jt2PZMpIPAUogHf4iDnqf8vVMlmkKls1Xemu7DAZqdFgdYhZxgaJ/d+qXH8b66L/D/pU4btA==", 1949 | "dev": true, 1950 | "requires": {} 1951 | }, 1952 | "eslint-config-prettier": { 1953 | "version": "8.3.0", 1954 | "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz", 1955 | "integrity": "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==", 1956 | "dev": true, 1957 | "requires": {} 1958 | }, 1959 | "eslint-plugin-es": { 1960 | "version": "3.0.1", 1961 | "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", 1962 | "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", 1963 | "dev": true, 1964 | "requires": { 1965 | "eslint-utils": "^2.0.0", 1966 | "regexpp": "^3.0.0" 1967 | } 1968 | }, 1969 | "eslint-plugin-jsdoc": { 1970 | "version": "36.1.0", 1971 | "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-36.1.0.tgz", 1972 | "integrity": "sha512-Qpied2AJCQcScxfzTObLKRiP5QgLXjMU/ITjBagEV5p2Q/HpumD1EQtazdRYdjDSwPmXhwOl2yquwOGQ4HOJNw==", 1973 | "dev": true, 1974 | "requires": { 1975 | "@es-joy/jsdoccomment": "0.10.8", 1976 | "comment-parser": "1.2.4", 1977 | "debug": "^4.3.2", 1978 | "esquery": "^1.4.0", 1979 | "jsdoc-type-pratt-parser": "^1.1.1", 1980 | "lodash": "^4.17.21", 1981 | "regextras": "^0.8.0", 1982 | "semver": "^7.3.5", 1983 | "spdx-expression-parse": "^3.0.1" 1984 | } 1985 | }, 1986 | "eslint-plugin-node": { 1987 | "version": "11.1.0", 1988 | "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", 1989 | "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", 1990 | "dev": true, 1991 | "requires": { 1992 | "eslint-plugin-es": "^3.0.0", 1993 | "eslint-utils": "^2.0.0", 1994 | "ignore": "^5.1.1", 1995 | "minimatch": "^3.0.4", 1996 | "resolve": "^1.10.1", 1997 | "semver": "^6.1.0" 1998 | }, 1999 | "dependencies": { 2000 | "ignore": { 2001 | "version": "5.1.8", 2002 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", 2003 | "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", 2004 | "dev": true 2005 | }, 2006 | "semver": { 2007 | "version": "6.3.0", 2008 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", 2009 | "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", 2010 | "dev": true 2011 | } 2012 | } 2013 | }, 2014 | "eslint-plugin-prettier": { 2015 | "version": "4.0.0", 2016 | "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.0.0.tgz", 2017 | "integrity": "sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ==", 2018 | "dev": true, 2019 | "requires": { 2020 | "prettier-linter-helpers": "^1.0.0" 2021 | } 2022 | }, 2023 | "eslint-scope": { 2024 | "version": "5.1.1", 2025 | "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", 2026 | "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", 2027 | "dev": true, 2028 | "requires": { 2029 | "esrecurse": "^4.3.0", 2030 | "estraverse": "^4.1.1" 2031 | } 2032 | }, 2033 | "eslint-utils": { 2034 | "version": "2.1.0", 2035 | "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", 2036 | "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", 2037 | "dev": true, 2038 | "requires": { 2039 | "eslint-visitor-keys": "^1.1.0" 2040 | }, 2041 | "dependencies": { 2042 | "eslint-visitor-keys": { 2043 | "version": "1.3.0", 2044 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", 2045 | "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", 2046 | "dev": true 2047 | } 2048 | } 2049 | }, 2050 | "eslint-visitor-keys": { 2051 | "version": "2.1.0", 2052 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", 2053 | "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", 2054 | "dev": true 2055 | }, 2056 | "espree": { 2057 | "version": "7.3.1", 2058 | "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", 2059 | "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", 2060 | "dev": true, 2061 | "requires": { 2062 | "acorn": "^7.4.0", 2063 | "acorn-jsx": "^5.3.1", 2064 | "eslint-visitor-keys": "^1.3.0" 2065 | }, 2066 | "dependencies": { 2067 | "eslint-visitor-keys": { 2068 | "version": "1.3.0", 2069 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", 2070 | "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", 2071 | "dev": true 2072 | } 2073 | } 2074 | }, 2075 | "esprima": { 2076 | "version": "4.0.1", 2077 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 2078 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", 2079 | "dev": true 2080 | }, 2081 | "esquery": { 2082 | "version": "1.4.0", 2083 | "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", 2084 | "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", 2085 | "dev": true, 2086 | "requires": { 2087 | "estraverse": "^5.1.0" 2088 | }, 2089 | "dependencies": { 2090 | "estraverse": { 2091 | "version": "5.2.0", 2092 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", 2093 | "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", 2094 | "dev": true 2095 | } 2096 | } 2097 | }, 2098 | "esrecurse": { 2099 | "version": "4.3.0", 2100 | "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", 2101 | "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", 2102 | "dev": true, 2103 | "requires": { 2104 | "estraverse": "^5.2.0" 2105 | }, 2106 | "dependencies": { 2107 | "estraverse": { 2108 | "version": "5.2.0", 2109 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", 2110 | "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", 2111 | "dev": true 2112 | } 2113 | } 2114 | }, 2115 | "estraverse": { 2116 | "version": "4.3.0", 2117 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", 2118 | "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", 2119 | "dev": true 2120 | }, 2121 | "esutils": { 2122 | "version": "2.0.3", 2123 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", 2124 | "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", 2125 | "dev": true 2126 | }, 2127 | "fast-deep-equal": { 2128 | "version": "3.1.3", 2129 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 2130 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", 2131 | "dev": true 2132 | }, 2133 | "fast-diff": { 2134 | "version": "1.2.0", 2135 | "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", 2136 | "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", 2137 | "dev": true 2138 | }, 2139 | "fast-json-stable-stringify": { 2140 | "version": "2.1.0", 2141 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 2142 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", 2143 | "dev": true 2144 | }, 2145 | "fast-levenshtein": { 2146 | "version": "2.0.6", 2147 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 2148 | "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", 2149 | "dev": true 2150 | }, 2151 | "file-entry-cache": { 2152 | "version": "6.0.1", 2153 | "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", 2154 | "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", 2155 | "dev": true, 2156 | "requires": { 2157 | "flat-cache": "^3.0.4" 2158 | } 2159 | }, 2160 | "flat-cache": { 2161 | "version": "3.0.4", 2162 | "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", 2163 | "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", 2164 | "dev": true, 2165 | "requires": { 2166 | "flatted": "^3.1.0", 2167 | "rimraf": "^3.0.2" 2168 | } 2169 | }, 2170 | "flatted": { 2171 | "version": "3.2.2", 2172 | "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.2.tgz", 2173 | "integrity": "sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==", 2174 | "dev": true 2175 | }, 2176 | "fs.realpath": { 2177 | "version": "1.0.0", 2178 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 2179 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 2180 | "dev": true 2181 | }, 2182 | "function-bind": { 2183 | "version": "1.1.1", 2184 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 2185 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 2186 | "dev": true 2187 | }, 2188 | "functional-red-black-tree": { 2189 | "version": "1.0.1", 2190 | "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", 2191 | "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", 2192 | "dev": true 2193 | }, 2194 | "glob": { 2195 | "version": "7.2.0", 2196 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", 2197 | "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", 2198 | "dev": true, 2199 | "requires": { 2200 | "fs.realpath": "^1.0.0", 2201 | "inflight": "^1.0.4", 2202 | "inherits": "2", 2203 | "minimatch": "^3.0.4", 2204 | "once": "^1.3.0", 2205 | "path-is-absolute": "^1.0.0" 2206 | } 2207 | }, 2208 | "glob-parent": { 2209 | "version": "5.1.2", 2210 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 2211 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 2212 | "dev": true, 2213 | "requires": { 2214 | "is-glob": "^4.0.1" 2215 | } 2216 | }, 2217 | "globals": { 2218 | "version": "13.11.0", 2219 | "resolved": "https://registry.npmjs.org/globals/-/globals-13.11.0.tgz", 2220 | "integrity": "sha512-08/xrJ7wQjK9kkkRoI3OFUBbLx4f+6x3SGwcPvQ0QH6goFDrOU2oyAWrmh3dJezu65buo+HBMzAMQy6rovVC3g==", 2221 | "dev": true, 2222 | "requires": { 2223 | "type-fest": "^0.20.2" 2224 | } 2225 | }, 2226 | "has": { 2227 | "version": "1.0.3", 2228 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 2229 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 2230 | "dev": true, 2231 | "requires": { 2232 | "function-bind": "^1.1.1" 2233 | } 2234 | }, 2235 | "has-flag": { 2236 | "version": "4.0.0", 2237 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 2238 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 2239 | "dev": true 2240 | }, 2241 | "iconv-lite": { 2242 | "version": "0.6.3", 2243 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", 2244 | "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", 2245 | "optional": true, 2246 | "requires": { 2247 | "safer-buffer": ">= 2.1.2 < 3.0.0" 2248 | } 2249 | }, 2250 | "ignore": { 2251 | "version": "4.0.6", 2252 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", 2253 | "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", 2254 | "dev": true 2255 | }, 2256 | "import-fresh": { 2257 | "version": "3.3.0", 2258 | "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", 2259 | "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", 2260 | "dev": true, 2261 | "requires": { 2262 | "parent-module": "^1.0.0", 2263 | "resolve-from": "^4.0.0" 2264 | } 2265 | }, 2266 | "imurmurhash": { 2267 | "version": "0.1.4", 2268 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 2269 | "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", 2270 | "dev": true 2271 | }, 2272 | "inflight": { 2273 | "version": "1.0.6", 2274 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 2275 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 2276 | "dev": true, 2277 | "requires": { 2278 | "once": "^1.3.0", 2279 | "wrappy": "1" 2280 | } 2281 | }, 2282 | "inherits": { 2283 | "version": "2.0.4", 2284 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 2285 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 2286 | "dev": true 2287 | }, 2288 | "is-core-module": { 2289 | "version": "2.6.0", 2290 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.6.0.tgz", 2291 | "integrity": "sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ==", 2292 | "dev": true, 2293 | "requires": { 2294 | "has": "^1.0.3" 2295 | } 2296 | }, 2297 | "is-extglob": { 2298 | "version": "2.1.1", 2299 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 2300 | "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", 2301 | "dev": true 2302 | }, 2303 | "is-fullwidth-code-point": { 2304 | "version": "3.0.0", 2305 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 2306 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 2307 | "dev": true 2308 | }, 2309 | "is-glob": { 2310 | "version": "4.0.1", 2311 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", 2312 | "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", 2313 | "dev": true, 2314 | "requires": { 2315 | "is-extglob": "^2.1.1" 2316 | } 2317 | }, 2318 | "isexe": { 2319 | "version": "2.0.0", 2320 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 2321 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 2322 | "dev": true 2323 | }, 2324 | "js-tokens": { 2325 | "version": "4.0.0", 2326 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 2327 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 2328 | "dev": true 2329 | }, 2330 | "js-yaml": { 2331 | "version": "3.14.1", 2332 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", 2333 | "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", 2334 | "dev": true, 2335 | "requires": { 2336 | "argparse": "^1.0.7", 2337 | "esprima": "^4.0.0" 2338 | } 2339 | }, 2340 | "jsdoc-type-pratt-parser": { 2341 | "version": "1.1.1", 2342 | "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-1.1.1.tgz", 2343 | "integrity": "sha512-uelRmpghNwPBuZScwgBG/OzodaFk5RbO5xaivBdsAY70icWfShwZ7PCMO0x1zSkOa8T1FzHThmrdoyg/0AwV5g==", 2344 | "dev": true 2345 | }, 2346 | "json-schema-traverse": { 2347 | "version": "0.4.1", 2348 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 2349 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 2350 | "dev": true 2351 | }, 2352 | "json-stable-stringify-without-jsonify": { 2353 | "version": "1.0.1", 2354 | "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", 2355 | "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", 2356 | "dev": true 2357 | }, 2358 | "levn": { 2359 | "version": "0.4.1", 2360 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", 2361 | "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", 2362 | "dev": true, 2363 | "requires": { 2364 | "prelude-ls": "^1.2.1", 2365 | "type-check": "~0.4.0" 2366 | } 2367 | }, 2368 | "lodash": { 2369 | "version": "4.17.21", 2370 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 2371 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", 2372 | "dev": true 2373 | }, 2374 | "lodash.clonedeep": { 2375 | "version": "4.5.0", 2376 | "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", 2377 | "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", 2378 | "dev": true 2379 | }, 2380 | "lodash.merge": { 2381 | "version": "4.6.2", 2382 | "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", 2383 | "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", 2384 | "dev": true 2385 | }, 2386 | "lodash.truncate": { 2387 | "version": "4.4.2", 2388 | "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", 2389 | "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", 2390 | "dev": true 2391 | }, 2392 | "lru-cache": { 2393 | "version": "6.0.0", 2394 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", 2395 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", 2396 | "dev": true, 2397 | "requires": { 2398 | "yallist": "^4.0.0" 2399 | } 2400 | }, 2401 | "minimatch": { 2402 | "version": "3.0.4", 2403 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 2404 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 2405 | "dev": true, 2406 | "requires": { 2407 | "brace-expansion": "^1.1.7" 2408 | } 2409 | }, 2410 | "ms": { 2411 | "version": "2.1.2", 2412 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 2413 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 2414 | "dev": true 2415 | }, 2416 | "natural-compare": { 2417 | "version": "1.4.0", 2418 | "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", 2419 | "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", 2420 | "dev": true 2421 | }, 2422 | "once": { 2423 | "version": "1.4.0", 2424 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 2425 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 2426 | "dev": true, 2427 | "requires": { 2428 | "wrappy": "1" 2429 | } 2430 | }, 2431 | "optionator": { 2432 | "version": "0.9.1", 2433 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", 2434 | "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", 2435 | "dev": true, 2436 | "requires": { 2437 | "deep-is": "^0.1.3", 2438 | "fast-levenshtein": "^2.0.6", 2439 | "levn": "^0.4.1", 2440 | "prelude-ls": "^1.2.1", 2441 | "type-check": "^0.4.0", 2442 | "word-wrap": "^1.2.3" 2443 | } 2444 | }, 2445 | "parent-module": { 2446 | "version": "1.0.1", 2447 | "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", 2448 | "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", 2449 | "dev": true, 2450 | "requires": { 2451 | "callsites": "^3.0.0" 2452 | } 2453 | }, 2454 | "path-is-absolute": { 2455 | "version": "1.0.1", 2456 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 2457 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 2458 | "dev": true 2459 | }, 2460 | "path-key": { 2461 | "version": "3.1.1", 2462 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 2463 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 2464 | "dev": true 2465 | }, 2466 | "path-parse": { 2467 | "version": "1.0.7", 2468 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 2469 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 2470 | "dev": true 2471 | }, 2472 | "prelude-ls": { 2473 | "version": "1.2.1", 2474 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", 2475 | "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", 2476 | "dev": true 2477 | }, 2478 | "prettier": { 2479 | "version": "2.4.1", 2480 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz", 2481 | "integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==", 2482 | "dev": true 2483 | }, 2484 | "prettier-linter-helpers": { 2485 | "version": "1.0.0", 2486 | "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", 2487 | "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", 2488 | "dev": true, 2489 | "requires": { 2490 | "fast-diff": "^1.1.2" 2491 | } 2492 | }, 2493 | "progress": { 2494 | "version": "2.0.3", 2495 | "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", 2496 | "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", 2497 | "dev": true 2498 | }, 2499 | "punycode": { 2500 | "version": "2.1.1", 2501 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 2502 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", 2503 | "dev": true 2504 | }, 2505 | "regexpp": { 2506 | "version": "3.2.0", 2507 | "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", 2508 | "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", 2509 | "dev": true 2510 | }, 2511 | "regextras": { 2512 | "version": "0.8.0", 2513 | "resolved": "https://registry.npmjs.org/regextras/-/regextras-0.8.0.tgz", 2514 | "integrity": "sha512-k519uI04Z3SaY0fLX843MRXnDeG2+vHOFsyhiPZvNLe7r8rD2YNRjq4BQLZZ0oAr2NrtvZlICsXysGNFPGa3CQ==", 2515 | "dev": true 2516 | }, 2517 | "require-from-string": { 2518 | "version": "2.0.2", 2519 | "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", 2520 | "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", 2521 | "dev": true 2522 | }, 2523 | "resolve": { 2524 | "version": "1.20.0", 2525 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", 2526 | "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", 2527 | "dev": true, 2528 | "requires": { 2529 | "is-core-module": "^2.2.0", 2530 | "path-parse": "^1.0.6" 2531 | } 2532 | }, 2533 | "resolve-from": { 2534 | "version": "4.0.0", 2535 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", 2536 | "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", 2537 | "dev": true 2538 | }, 2539 | "rimraf": { 2540 | "version": "3.0.2", 2541 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", 2542 | "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", 2543 | "dev": true, 2544 | "requires": { 2545 | "glob": "^7.1.3" 2546 | } 2547 | }, 2548 | "safer-buffer": { 2549 | "version": "2.1.2", 2550 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 2551 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", 2552 | "optional": true 2553 | }, 2554 | "semver": { 2555 | "version": "7.3.5", 2556 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", 2557 | "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", 2558 | "dev": true, 2559 | "requires": { 2560 | "lru-cache": "^6.0.0" 2561 | } 2562 | }, 2563 | "shebang-command": { 2564 | "version": "2.0.0", 2565 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 2566 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 2567 | "dev": true, 2568 | "requires": { 2569 | "shebang-regex": "^3.0.0" 2570 | } 2571 | }, 2572 | "shebang-regex": { 2573 | "version": "3.0.0", 2574 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 2575 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 2576 | "dev": true 2577 | }, 2578 | "slice-ansi": { 2579 | "version": "4.0.0", 2580 | "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", 2581 | "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", 2582 | "dev": true, 2583 | "requires": { 2584 | "ansi-styles": "^4.0.0", 2585 | "astral-regex": "^2.0.0", 2586 | "is-fullwidth-code-point": "^3.0.0" 2587 | } 2588 | }, 2589 | "spdx-exceptions": { 2590 | "version": "2.3.0", 2591 | "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", 2592 | "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", 2593 | "dev": true 2594 | }, 2595 | "spdx-expression-parse": { 2596 | "version": "3.0.1", 2597 | "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", 2598 | "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", 2599 | "dev": true, 2600 | "requires": { 2601 | "spdx-exceptions": "^2.1.0", 2602 | "spdx-license-ids": "^3.0.0" 2603 | } 2604 | }, 2605 | "spdx-license-ids": { 2606 | "version": "3.0.10", 2607 | "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.10.tgz", 2608 | "integrity": "sha512-oie3/+gKf7QtpitB0LYLETe+k8SifzsX4KixvpOsbI6S0kRiRQ5MKOio8eMSAKQ17N06+wdEOXRiId+zOxo0hA==", 2609 | "dev": true 2610 | }, 2611 | "sprintf-js": { 2612 | "version": "1.0.3", 2613 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 2614 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", 2615 | "dev": true 2616 | }, 2617 | "string-width": { 2618 | "version": "4.2.3", 2619 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 2620 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 2621 | "dev": true, 2622 | "requires": { 2623 | "emoji-regex": "^8.0.0", 2624 | "is-fullwidth-code-point": "^3.0.0", 2625 | "strip-ansi": "^6.0.1" 2626 | } 2627 | }, 2628 | "strip-ansi": { 2629 | "version": "6.0.1", 2630 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 2631 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 2632 | "dev": true, 2633 | "requires": { 2634 | "ansi-regex": "^5.0.1" 2635 | } 2636 | }, 2637 | "strip-json-comments": { 2638 | "version": "3.1.1", 2639 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 2640 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 2641 | "dev": true 2642 | }, 2643 | "supports-color": { 2644 | "version": "7.2.0", 2645 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 2646 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 2647 | "dev": true, 2648 | "requires": { 2649 | "has-flag": "^4.0.0" 2650 | } 2651 | }, 2652 | "table": { 2653 | "version": "6.7.1", 2654 | "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", 2655 | "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", 2656 | "dev": true, 2657 | "requires": { 2658 | "ajv": "^8.0.1", 2659 | "lodash.clonedeep": "^4.5.0", 2660 | "lodash.truncate": "^4.4.2", 2661 | "slice-ansi": "^4.0.0", 2662 | "string-width": "^4.2.0", 2663 | "strip-ansi": "^6.0.0" 2664 | }, 2665 | "dependencies": { 2666 | "ajv": { 2667 | "version": "8.6.3", 2668 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.3.tgz", 2669 | "integrity": "sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw==", 2670 | "dev": true, 2671 | "requires": { 2672 | "fast-deep-equal": "^3.1.1", 2673 | "json-schema-traverse": "^1.0.0", 2674 | "require-from-string": "^2.0.2", 2675 | "uri-js": "^4.2.2" 2676 | } 2677 | }, 2678 | "json-schema-traverse": { 2679 | "version": "1.0.0", 2680 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", 2681 | "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", 2682 | "dev": true 2683 | } 2684 | } 2685 | }, 2686 | "text-table": { 2687 | "version": "0.2.0", 2688 | "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", 2689 | "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", 2690 | "dev": true 2691 | }, 2692 | "type-check": { 2693 | "version": "0.4.0", 2694 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", 2695 | "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", 2696 | "dev": true, 2697 | "requires": { 2698 | "prelude-ls": "^1.2.1" 2699 | } 2700 | }, 2701 | "type-fest": { 2702 | "version": "0.20.2", 2703 | "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", 2704 | "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", 2705 | "dev": true 2706 | }, 2707 | "typescript": { 2708 | "version": "4.4.3", 2709 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.3.tgz", 2710 | "integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==", 2711 | "dev": true 2712 | }, 2713 | "uri-js": { 2714 | "version": "4.4.1", 2715 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", 2716 | "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", 2717 | "dev": true, 2718 | "requires": { 2719 | "punycode": "^2.1.0" 2720 | } 2721 | }, 2722 | "v8-compile-cache": { 2723 | "version": "2.3.0", 2724 | "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", 2725 | "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", 2726 | "dev": true 2727 | }, 2728 | "which": { 2729 | "version": "2.0.2", 2730 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 2731 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 2732 | "dev": true, 2733 | "requires": { 2734 | "isexe": "^2.0.0" 2735 | } 2736 | }, 2737 | "word-wrap": { 2738 | "version": "1.2.3", 2739 | "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", 2740 | "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", 2741 | "dev": true 2742 | }, 2743 | "wrappy": { 2744 | "version": "1.0.2", 2745 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 2746 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 2747 | "dev": true 2748 | }, 2749 | "yallist": { 2750 | "version": "4.0.0", 2751 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", 2752 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", 2753 | "dev": true 2754 | } 2755 | } 2756 | } 2757 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mailhog", 3 | "version": "4.16.0", 4 | "title": "MailHog NodeJS library", 5 | "description": "A NodeJS library to interact with the MailHog API", 6 | "keywords": [ 7 | "mailhog", 8 | "nodejs", 9 | "library", 10 | "api" 11 | ], 12 | "homepage": "https://github.com/blueimp/mailhog-node", 13 | "author": { 14 | "name": "Sebastian Tschan", 15 | "url": "https://blueimp.net" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git://github.com/blueimp/mailhog-node.git" 20 | }, 21 | "license": "MIT", 22 | "engines": { 23 | "node": ">=10.0.0" 24 | }, 25 | "optionalDependencies": { 26 | "iconv-lite": "^0.6" 27 | }, 28 | "devDependencies": { 29 | "@types/mocha": "9", 30 | "@types/node": "16", 31 | "eslint": "7", 32 | "eslint-config-blueimp": "2", 33 | "eslint-config-prettier": "8", 34 | "eslint-plugin-jsdoc": "36", 35 | "eslint-plugin-node": "11", 36 | "eslint-plugin-prettier": "4", 37 | "prettier": "2", 38 | "typescript": "4" 39 | }, 40 | "eslintConfig": { 41 | "extends": [ 42 | "blueimp", 43 | "plugin:jsdoc/recommended", 44 | "plugin:node/recommended", 45 | "plugin:prettier/recommended" 46 | ] 47 | }, 48 | "prettier": { 49 | "arrowParens": "avoid", 50 | "proseWrap": "always", 51 | "semi": false, 52 | "singleQuote": true, 53 | "trailingComma": "none" 54 | }, 55 | "scripts": { 56 | "pretest": "eslint . && tsc", 57 | "test": "docker-compose run --rm test", 58 | "posttest": "docker-compose down", 59 | "build": "rm -f libqp/index.d.ts index.d.ts && tsc -p tsconfig.d.json", 60 | "preversion": "npm test", 61 | "version": "npm run build && git add -A libqp/index.d.ts index.d.ts", 62 | "postversion": "git push --tags origin HEAD && npm publish" 63 | }, 64 | "files": [ 65 | "libqp/index.d.ts", 66 | "libqp/index.js", 67 | "index.d.ts", 68 | "index.js" 69 | ], 70 | "main": "index.js" 71 | } 72 | -------------------------------------------------------------------------------- /sendmail.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | cd "$(dirname "$0")" 6 | 7 | for MAIL in mail/*.eml; do 8 | sendmail "$@" < "$MAIL" 9 | done 10 | -------------------------------------------------------------------------------- /tsconfig.d.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig", 3 | "compilerOptions": { 4 | "noEmit": false, 5 | "declaration": true, 6 | "emitDeclarationOnly": true 7 | }, 8 | "include": ["libqp/index.js", "index.js"] 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "checkJs": true, 5 | "noEmit": true 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /wait-for-hosts.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Waits for the given host(s) to be available before executing a given command. 5 | # Tests for availability by using netcat to connect to the hosts via TCP. 6 | # 7 | # Usage: 8 | # ./wait-for-hosts.sh [-q] [-t seconds] [host:port] [...] [-- command args...] 9 | # 10 | # The script accepts multiple `host:port` combinations as arguments or defined 11 | # as WAIT_FOR_HOSTS environment variable, separating the `host:port` 12 | # combinations via spaces. 13 | # 14 | # The status output can be made quiet by adding the `-q` argument or by setting 15 | # the environment variable WAIT_FOR_HOSTS_QUIET to `1`. 16 | # 17 | # The default timeout of 10 seconds can be changed via `-t seconds` argument or 18 | # by setting the WAIT_FOR_HOSTS_TIMEOUT environment variable to the desired 19 | # number of seconds. 20 | # 21 | # The command defined after the `--` argument separator will be executed if all 22 | # the given hosts are reachable. 23 | # 24 | # Copyright 2016, Sebastian Tschan 25 | # https://blueimp.net 26 | # 27 | # Licensed under the MIT license: 28 | # https://opensource.org/licenses/MIT 29 | # 30 | 31 | set -e 32 | 33 | is_integer() { 34 | test "$1" -eq "$1" 2> /dev/null 35 | } 36 | 37 | set_timeout() { 38 | if ! is_integer "$1"; then 39 | printf 'Error: "%s" is not a valid timeout value.\n' "$1" >&2 40 | return 1 41 | fi 42 | TIMEOUT="$1" 43 | } 44 | 45 | connect_to_service() { 46 | nc -w 1 -z "$1" "$2" 47 | } 48 | 49 | quiet_echo() { 50 | if [ "$QUIET" -ne 1 ]; then echo "$@" >&2; fi 51 | } 52 | 53 | wait_for_host() { 54 | HOST="${1%:*}" 55 | PORT="${1#*:}" 56 | if ! is_integer "$PORT"; then 57 | printf 'Error: "%s" is not a valid host:port combination.\n' "$1" >&2 58 | return 1 59 | fi 60 | if [ "$QUIET" -ne 1 ]; then 61 | printf "Waiting for host: %-${PADDING}s ... " "$1" >&2 62 | fi 63 | TIME_LIMIT=$(($(date +%s)+TIMEOUT)) 64 | while ! OUTPUT="$(connect_to_service "$HOST" "$PORT" 2>&1)"; do 65 | if [ "$(date +%s)" -ge "$TIME_LIMIT" ]; then 66 | quiet_echo timeout 67 | if [ -n "$OUTPUT" ]; then 68 | quiet_echo "$OUTPUT" 69 | fi 70 | return 1 71 | fi 72 | sleep 1 73 | done 74 | quiet_echo ok 75 | } 76 | 77 | set_padding() { 78 | PADDING=0 79 | while [ $# != 0 ]; do 80 | case "$1" in 81 | -t) shift 2;; 82 | -q) break;; 83 | --) break;; 84 | *) test ${#1} -gt $PADDING && PADDING=${#1}; shift;; 85 | esac 86 | done 87 | } 88 | 89 | QUIET=${WAIT_FOR_HOSTS_QUIET:-0} 90 | set_timeout "${WAIT_FOR_HOSTS_TIMEOUT:-10}" 91 | 92 | if [ "$QUIET" -ne 1 ]; then 93 | # shellcheck disable=SC2086 94 | set_padding $WAIT_FOR_HOSTS "$@" 95 | fi 96 | 97 | while [ $# != 0 ]; do 98 | case "$1" in 99 | -t) set_timeout "$2"; shift 2;; 100 | -q) QUIET=1; shift;; 101 | --) shift; break;; 102 | *) wait_for_host "$1"; shift;; 103 | esac 104 | done 105 | 106 | for HOST in $WAIT_FOR_HOSTS; do 107 | wait_for_host "$HOST" 108 | done 109 | 110 | exec "$@" 111 | --------------------------------------------------------------------------------