├── .github └── workflows │ └── test.yaml ├── .gitignore ├── .npmignore ├── .prettierrc ├── LICENSE.md ├── README.md ├── jest.config.mjs ├── package.json ├── src ├── AbortError.test.ts ├── AbortError.ts ├── abortable.test.ts ├── abortable.ts ├── all.test.ts ├── all.ts ├── delay.ts ├── execute.test.ts ├── execute.ts ├── forever.test.ts ├── forever.ts ├── index.ts ├── proactiveRetry.ts ├── race.test.ts ├── race.ts ├── retry.ts ├── run.ts ├── spawn.test.ts ├── spawn.ts ├── utils │ └── nextTick.ts ├── waitForEvent.test.ts └── waitForEvent.ts ├── tsconfig.build.json ├── tsconfig.es.json ├── tsconfig.json └── yarn.lock /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: Test 2 | on: 3 | push: 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-20.04 8 | 9 | strategy: 10 | matrix: 11 | environment: 12 | - node-version: '14.17' 13 | node-options: --experimental-abortcontroller 14 | - node-version: 16.x 15 | - node-version: 18.x 16 | 17 | 18 | steps: 19 | - uses: actions/checkout@v2 20 | 21 | - name: Get yarn cache directory path 22 | id: yarn-cache-dir-path 23 | run: echo "::set-output name=dir::$(yarn cache dir)" 24 | 25 | - uses: actions/cache@v2 26 | with: 27 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }} 28 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 29 | restore-keys: | 30 | ${{ runner.os }}-yarn- 31 | 32 | - name: Use Node.js ${{ matrix.environment.node-version }} 33 | uses: actions/setup-node@v2 34 | with: 35 | node-version: ${{ matrix.environment.node-version }} 36 | 37 | - run: yarn install --frozen-lockfile 38 | 39 | - env: 40 | NODE_OPTIONS: ${{ matrix.environment.node-options }} 41 | run: yarn test 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | lib 3 | es 4 | yarn-error.log 5 | .idea 6 | .direnv 7 | .envrc -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | **/*.test.ts 2 | jest.config.ts 3 | .travis.yml 4 | .gitignore 5 | .prettierrc 6 | yarn-error.log 7 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "bracketSpacing": false, 4 | "trailingComma": "all", 5 | "proseWrap": "always", 6 | "arrowParens": "avoid", 7 | "endOfLine": "auto" 8 | } 9 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Deeplay 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, 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, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Abort Controller Extras [![npm version][npm-image]][npm-url] 2 | 3 | Abortable async function primitives and combinators. 4 | 5 | - [Installation](#installation) 6 | - [Abort Controller](#abort-controller) 7 | - [Abortable Functions](#abortable-functions) 8 | - [Composing Abortable Functions](#composing-abortable-functions) 9 | - [Companion Packages](#companion-packages) 10 | - [API](#api) 11 | - [`all`](#all) 12 | - [`race`](#race) 13 | - [`delay`](#delay) 14 | - [`waitForEvent`](#waitforevent) 15 | - [`forever`](#forever) 16 | - [`spawn`](#spawn) 17 | - [`retry`](#retry) 18 | - [`proactiveRetry`](#proactive-retry) 19 | - [`execute`](#execute) 20 | - [`abortable`](#abortable) 21 | - [`run`](#run) 22 | - [`AbortError`](#aborterror) 23 | - [`isAbortError`](#isaborterror) 24 | - [`throwIfAborted`](#throwifaborted) 25 | - [`rethrowAbortError`](#rethrowaborterror) 26 | - [`catchAbortError`](#catchaborterror) 27 | 28 | ## Installation 29 | 30 | ``` 31 | yarn add abort-controller-x 32 | ``` 33 | 34 | ## Abort Controller 35 | 36 | See 37 | [`AbortController` MDN page](https://developer.mozilla.org/en-US/docs/Web/API/AbortController). 38 | AbortController is 39 | [available in NodeJS](https://nodejs.org/api/globals.html#class-abortcontroller) 40 | since 15.0.0, NodeJS 14.17+ requires the 41 | [--experimental-abortcontroller](https://nodejs.org/docs/latest-v14.x/api/cli.html#cli_experimental_abortcontroller) 42 | flag. A [polyfill](https://www.npmjs.com/package/abort-controller) is available 43 | for older NodeJS versions and browsers. 44 | 45 | ## Abortable Functions 46 | 47 | We define _abortable function_ as a function that obeys following rules: 48 | 49 | - It must accept `AbortSignal` in its arguments. 50 | - It must return a `Promise`. 51 | - It must add 52 | [`abort`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/abort_event) 53 | event listener to the `AbortSignal`. Once the `AbortSignal` is aborted, the 54 | returned `Promise` must reject with `AbortError` either immediately, or after 55 | doing any async cleanup. It's also possible to reject with other errors that 56 | happen during cleanup. 57 | - Once the returned `Promise` is fulfilled or rejected, it must remove 58 | [`abort`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/abort_event) 59 | event listener. 60 | 61 | An example of _abortable function_ is the standard 62 | [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) function. 63 | 64 | ## Composing Abortable Functions 65 | 66 | This library provides a way to build complex abortable functions using standard 67 | `async`/`await` syntax, without the burden of manually managing 68 | [`abort`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/abort_event) 69 | event listeners. You can reuse a single `AbortSignal` between many operations 70 | inside a parent function: 71 | 72 | ```ts 73 | /** 74 | * Make requests repeatedly with a delay between consecutive requests 75 | */ 76 | async function makeRequests(signal: AbortSignal): Promise { 77 | while (true) { 78 | await fetch('...', {signal}); 79 | await delay(signal, 1000); 80 | } 81 | } 82 | 83 | const abortController = new AbortController(); 84 | 85 | makeRequests(abortController.signal).catch(catchAbortError); 86 | 87 | process.on('SIGTERM', () => { 88 | abortController.abort(); 89 | }); 90 | ``` 91 | 92 | The above example can be rewritten in a more ergonomic way using [`run`](#run) 93 | helper. 94 | 95 | Usually you should only create `AbortController` somewhere on the top level, and 96 | in regular code use `async`/`await` and pass `AbortSignal` to abortable 97 | functions provided by this library or custom ones composed of other abortable 98 | functions. 99 | 100 | ## Companion Packages 101 | 102 | - [`abort-controller-x-rxjs`](https://github.com/deeplay-io/abort-controller-x-rxjs) 103 | — Abortable helpers for RxJS. 104 | - [`abort-controller-x-reactive-store`](https://github.com/deeplay-io/abort-controller-x-reactive-store) 105 | — Reactive store primitive and helpers. 106 | 107 | ## API 108 | 109 | ### `all` 110 | 111 | ```ts 112 | function all( 113 | signal: AbortSignal, 114 | executor: (innerSignal: AbortSignal) => readonly PromiseLike[], 115 | ): Promise; 116 | ``` 117 | 118 | Abortable version of `Promise.all`. 119 | 120 | Creates new inner `AbortSignal` and passes it to `executor`. That signal is 121 | aborted when `signal` is aborted or any of the promises returned from `executor` 122 | are rejected. 123 | 124 | Returns a promise that fulfills with an array of results when all of the 125 | promises returned from `executor` fulfill, rejects when any of the promises 126 | returned from `executor` are rejected, and rejects with `AbortError` when 127 | `signal` is aborted. 128 | 129 | The promises returned from `executor` must be abortable, i.e. once `innerSignal` 130 | is aborted, they must reject with `AbortError` either immediately, or after 131 | doing any async cleanup. 132 | 133 | Example: 134 | 135 | ```ts 136 | const [result1, result2] = await all(signal, signal => [ 137 | makeRequest(signal, params1), 138 | makeRequest(signal, params2), 139 | ]); 140 | ``` 141 | 142 | ### `race` 143 | 144 | ```ts 145 | function race( 146 | signal: AbortSignal, 147 | executor: (innerSignal: AbortSignal) => readonly PromiseLike[], 148 | ): Promise; 149 | ``` 150 | 151 | Abortable version of `Promise.race`. 152 | 153 | Creates new inner `AbortSignal` and passes it to `executor`. That signal is 154 | aborted when `signal` is aborted or any of the promises returned from `executor` 155 | are fulfilled or rejected. 156 | 157 | Returns a promise that fulfills or rejects when any of the promises returned 158 | from `executor` are fulfilled or rejected, and rejects with `AbortError` when 159 | `signal` is aborted. 160 | 161 | The promises returned from `executor` must be abortable, i.e. once `innerSignal` 162 | is aborted, they must reject with `AbortError` either immediately, or after 163 | doing any async cleanup. 164 | 165 | Example: 166 | 167 | ```ts 168 | const result = await race(signal, signal => [ 169 | delay(signal, 1000).then(() => ({status: 'timeout'})), 170 | makeRequest(signal, params).then(value => ({status: 'success', value})), 171 | ]); 172 | 173 | if (result.status === 'timeout') { 174 | // request timed out 175 | } else { 176 | const response = result.value; 177 | } 178 | ``` 179 | 180 | ### `delay` 181 | 182 | ```ts 183 | function delay(signal: AbortSignal, dueTime: number | Date): Promise; 184 | ``` 185 | 186 | Return a promise that resolves after delay and rejects with `AbortError` once 187 | `signal` is aborted. 188 | 189 | The delay time is specified as a `Date` object or as an integer denoting 190 | milliseconds to wait. 191 | 192 | Example: 193 | 194 | ```ts 195 | // Make a request repeatedly with a delay between consecutive requests 196 | while (true) { 197 | await makeRequest(signal, params); 198 | await delay(signal, 1000); 199 | } 200 | ``` 201 | 202 | Example: 203 | 204 | ```ts 205 | // Make a request repeatedly with a fixed interval 206 | import {addMilliseconds} from 'date-fns'; 207 | 208 | let date = new Date(); 209 | 210 | while (true) { 211 | await makeRequest(signal, params); 212 | 213 | date = addMilliseconds(date, 1000); 214 | await delay(signal, date); 215 | } 216 | ``` 217 | 218 | ### `waitForEvent` 219 | 220 | ```ts 221 | function waitForEvent( 222 | signal: AbortSignal, 223 | target: EventTargetLike, 224 | eventName: string, 225 | options?: EventListenerOptions, 226 | ): Promise; 227 | ``` 228 | 229 | Returns a promise that fulfills when an event of specific type is emitted from 230 | given event target and rejects with `AbortError` once `signal` is aborted. 231 | 232 | Example: 233 | 234 | ```ts 235 | // Create a WebSocket and wait for connection 236 | const webSocket = new WebSocket(url); 237 | 238 | const openEvent = await race(signal, signal => [ 239 | waitForEvent(signal, webSocket, 'open'), 240 | waitForEvent(signal, webSocket, 'close').then( 241 | event => { 242 | throw new Error(`Failed to connect to ${url}: ${event.reason}`); 243 | }, 244 | ), 245 | ]); 246 | ``` 247 | 248 | ### `forever` 249 | 250 | ```ts 251 | function forever(signal: AbortSignal): Promise; 252 | ``` 253 | 254 | Return a promise that never fulfills and only rejects with `AbortError` once 255 | `signal` is aborted. 256 | 257 | ### `spawn` 258 | 259 | ```ts 260 | function spawn( 261 | signal: AbortSignal, 262 | fn: (signal: AbortSignal, effects: SpawnEffects) => Promise, 263 | ): Promise; 264 | 265 | type SpawnEffects = { 266 | defer(fn: () => void | Promise): void; 267 | fork(fn: (signal: AbortSignal) => Promise): ForkTask; 268 | }; 269 | 270 | type ForkTask = { 271 | abort(): void; 272 | join(): Promise; 273 | }; 274 | ``` 275 | 276 | Run an abortable function with `fork` and `defer` effects attached to it. 277 | 278 | `spawn` allows to write Go-style coroutines. 279 | 280 | - `SpawnEffects.defer` 281 | 282 | Schedules a function to run after spawned function finishes. 283 | 284 | Deferred functions run serially in last-in-first-out order. 285 | 286 | Promise returned from `spawn` resolves or rejects only after all deferred 287 | functions finish. 288 | 289 | - `SpawnEffects.fork` 290 | 291 | Executes an abortable function in background. 292 | 293 | If a forked function throws an exception, spawned function and other forks are 294 | aborted and promise returned from `spawn` rejects with that exception. 295 | 296 | When spawned function finishes, all forks are aborted. 297 | 298 | - `ForkTask.abort` 299 | 300 | Abort a forked function. 301 | 302 | - `ForkTask.join` 303 | 304 | Returns a promise returned from a forked function. 305 | 306 | Example: 307 | 308 | ```ts 309 | // Connect to a database, then start a server, then block until abort. 310 | // On abort, gracefully shutdown the server, and once done, disconnect 311 | // from the database. 312 | spawn(signal, async (signal, {defer}) => { 313 | const db = await connectToDb(); 314 | 315 | defer(async () => { 316 | await db.close(); 317 | }); 318 | 319 | const server = await startServer(db); 320 | 321 | defer(async () => { 322 | await server.close(); 323 | }); 324 | 325 | await forever(signal); 326 | }); 327 | ``` 328 | 329 | Example: 330 | 331 | ```ts 332 | // Connect to a database, then start an infinite polling loop. 333 | // On abort, disconnect from the database. 334 | spawn(signal, async (signal, {defer}) => { 335 | const db = await connectToDb(); 336 | 337 | defer(async () => { 338 | await db.close(); 339 | }); 340 | 341 | while (true) { 342 | await poll(signal, db); 343 | await delay(signal, 5000); 344 | } 345 | }); 346 | ``` 347 | 348 | Example: 349 | 350 | ```ts 351 | // Acquire a lock and execute a function. 352 | // Extend the lock while the function is running. 353 | // Once the function finishes or the signal is aborted, stop extending 354 | // the lock and release it. 355 | import Redlock = require('redlock'); 356 | 357 | const lockTtl = 30_000; 358 | 359 | function withLock( 360 | signal: AbortSignal, 361 | redlock: Redlock, 362 | key: string, 363 | fn: (signal: AbortSignal) => Promise, 364 | ): Promise { 365 | return spawn(signal, async (signal, {fork, defer}) => { 366 | const lock = await redlock.lock(key, lockTtl); 367 | 368 | defer(() => lock.unlock()); 369 | 370 | fork(async signal => { 371 | while (true) { 372 | await delay(signal, lockTtl / 10); 373 | await lock.extend(lockTtl); 374 | } 375 | }); 376 | 377 | return await fn(signal); 378 | }); 379 | } 380 | 381 | const redlock = new Redlock([redis], { 382 | retryCount: -1, 383 | }); 384 | 385 | await withLock(signal, redlock, 'the-lock-key', async signal => { 386 | // ... 387 | }); 388 | ``` 389 | 390 | ### `retry` 391 | 392 | ```ts 393 | function retry( 394 | signal: AbortSignal, 395 | fn: (signal: AbortSignal, attempt: number, reset: () => void) => Promise, 396 | options?: RetryOptions, 397 | ): Promise; 398 | 399 | type RetryOptions = { 400 | baseMs?: number; 401 | maxDelayMs?: number; 402 | maxAttempts?: number; 403 | onError?: (error: unknown, attempt: number, delayMs: number) => void; 404 | }; 405 | ``` 406 | 407 | Retry a function with exponential backoff. 408 | 409 | - `fn` 410 | 411 | A function that will be called and retried in case of error. It receives: 412 | 413 | - `signal` 414 | 415 | `AbortSignal` that is aborted when the signal passed to `retry` is aborted. 416 | 417 | - `attempt` 418 | 419 | Attempt number starting with 0. 420 | 421 | - `reset` 422 | 423 | Function that sets attempt number to -1 so that the next attempt will be 424 | made without delay. 425 | 426 | - `RetryOptions.baseMs` 427 | 428 | Starting delay before first retry attempt in milliseconds. 429 | 430 | Defaults to 1000. 431 | 432 | Example: if `baseMs` is 100, then retries will be attempted in 100ms, 200ms, 433 | 400ms etc (not counting jitter). 434 | 435 | - `RetryOptions.maxDelayMs` 436 | 437 | Maximum delay between attempts in milliseconds. 438 | 439 | Defaults to 30 seconds. 440 | 441 | Example: if `baseMs` is 1000 and `maxDelayMs` is 3000, then retries will be 442 | attempted in 1000ms, 2000ms, 3000ms, 3000ms etc (not counting jitter). 443 | 444 | - `RetryOptions.maxAttempts` 445 | 446 | Maximum for the total number of attempts. 447 | 448 | Defaults to `Infinity`. 449 | 450 | - `RetryOptions.onError` 451 | 452 | Called after each failed attempt before setting delay timer. 453 | 454 | Rethrow error from this callback to prevent further retries. 455 | 456 | ### `proactiveRetry` 457 | 458 | ```ts 459 | function proactiveRetry( 460 | signal: AbortSignal, 461 | fn: (signal: AbortSignal, attempt: number) => Promise, 462 | options?: ProactiveRetryOptions, 463 | ): Promise; 464 | 465 | type ProactiveRetryOptions = { 466 | baseMs?: number; 467 | maxAttempts?: number; 468 | onError?: (error: unknown, attempt: number) => void; 469 | }; 470 | ``` 471 | 472 | Proactively retry a function with exponential backoff. 473 | 474 | Also known as hedging. 475 | 476 | The function will be called multiple times in parallel until it succeeds, in 477 | which case all the other calls will be aborted. 478 | 479 | - `fn` 480 | 481 | A function that will be called multiple times in parallel until it succeeds. 482 | It receives: 483 | 484 | - `signal` 485 | 486 | `AbortSignal` that is aborted when the signal passed to `retry` is aborted, 487 | or when the function succeeds. 488 | 489 | - `attempt` 490 | 491 | Attempt number starting with 0. 492 | 493 | - `ProactiveRetryOptions.baseMs` 494 | 495 | Base delay between attempts in milliseconds. 496 | 497 | Defaults to 1000. 498 | 499 | Example: if `baseMs` is 100, then retries will be attempted in 100ms, 200ms, 500 | 400ms etc (not counting jitter). 501 | 502 | - `ProactiveRetryOptions.maxAttempts` 503 | 504 | Maximum for the total number of attempts. 505 | 506 | Defaults to `Infinity`. 507 | 508 | - `ProactiveRetryOptions.onError` 509 | 510 | Called after each failed attempt. 511 | 512 | Rethrow error from this callback to prevent further retries. 513 | 514 | ### `execute` 515 | 516 | ```ts 517 | function execute( 518 | signal: AbortSignal, 519 | executor: ( 520 | resolve: (value: T) => void, 521 | reject: (reason?: any) => void, 522 | ) => () => void | PromiseLike, 523 | ): Promise; 524 | ``` 525 | 526 | Similar to `new Promise(executor)`, but allows executor to return abort callback 527 | that is called once `signal` is aborted. 528 | 529 | Returned promise rejects with `AbortError` once `signal` is aborted. 530 | 531 | Callback can return a promise, e.g. for doing any async cleanup. In this case, 532 | the promise returned from `execute` rejects with `AbortError` after that promise 533 | fulfills. 534 | 535 | ### `abortable` 536 | 537 | ```ts 538 | function abortable(signal: AbortSignal, promise: PromiseLike): Promise; 539 | ``` 540 | 541 | Wrap a promise to reject with `AbortError` once `signal` is aborted. 542 | 543 | Useful to wrap non-abortable promises. Note that underlying process will NOT be 544 | aborted. 545 | 546 | ### `run` 547 | 548 | ```ts 549 | function run(fn: (signal: AbortSignal) => Promise): () => Promise; 550 | ``` 551 | 552 | Invokes an abortable function with implicitly created `AbortSignal`. 553 | 554 | Returns a function that aborts that signal and waits until passed function 555 | finishes. 556 | 557 | Any error other than `AbortError` thrown from passed function will result in 558 | unhandled promise rejection. 559 | 560 | Example: 561 | 562 | ```ts 563 | const stop = run(async signal => { 564 | try { 565 | while (true) { 566 | await delay(signal, 1000); 567 | console.log('tick'); 568 | } 569 | } finally { 570 | await doCleanup(); 571 | } 572 | }); 573 | 574 | // abort and wait until cleanup is done 575 | await stop(); 576 | ``` 577 | 578 | This function is also useful with React `useEffect` hook: 579 | 580 | ```ts 581 | // make requests periodically while the component is mounted 582 | useEffect( 583 | () => 584 | run(async signal => { 585 | while (true) { 586 | await makeRequest(signal); 587 | await delay(signal, 1000); 588 | } 589 | }), 590 | [], 591 | ); 592 | ``` 593 | 594 | ### `AbortError` 595 | 596 | ```ts 597 | class AbortError extends Error 598 | ``` 599 | 600 | Thrown when an abortable function was aborted. 601 | 602 | **Warning**: do not use `instanceof` with this class. Instead, use 603 | `isAbortError` function. 604 | 605 | ### `isAbortError` 606 | 607 | ```ts 608 | function isAbortError(error: unknown): boolean; 609 | ``` 610 | 611 | Checks whether given `error` is an `AbortError`. 612 | 613 | ### `throwIfAborted` 614 | 615 | ```ts 616 | function throwIfAborted(signal: AbortSignal): void; 617 | ``` 618 | 619 | If `signal` is aborted, throws `AbortError`. Otherwise does nothing. 620 | 621 | ### `rethrowAbortError` 622 | 623 | ```ts 624 | function rethrowAbortError(error: unknown): void; 625 | ``` 626 | 627 | If `error` is `AbortError`, throws it. Otherwise does nothing. 628 | 629 | Useful for `try/catch` blocks around abortable code: 630 | 631 | ```ts 632 | try { 633 | await somethingAbortable(signal); 634 | } catch (err) { 635 | rethrowAbortError(err); 636 | 637 | // do normal error handling 638 | } 639 | ``` 640 | 641 | ### `catchAbortError` 642 | 643 | ```ts 644 | function catchAbortError(error: unknown): void; 645 | ``` 646 | 647 | If `error` is `AbortError`, does nothing. Otherwise throws it. 648 | 649 | Useful for invoking top-level abortable functions: 650 | 651 | ```ts 652 | somethingAbortable(signal).catch(catchAbortError); 653 | ``` 654 | 655 | Without `catchAbortError`, aborting would result in unhandled promise rejection. 656 | 657 | [npm-image]: https://badge.fury.io/js/abort-controller-x.svg 658 | [npm-url]: https://badge.fury.io/js/abort-controller-x 659 | -------------------------------------------------------------------------------- /jest.config.mjs: -------------------------------------------------------------------------------- 1 | export default { 2 | preset: 'ts-jest', 3 | extensionsToTreatAsEsm: ['.ts'], 4 | globals: { 5 | 'ts-jest': { 6 | useESM: true, 7 | }, 8 | }, 9 | moduleNameMapper: { 10 | '^(\\.{1,2}/.*)\\.js$': '$1', 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "abort-controller-x", 3 | "version": "0.4.3", 4 | "description": "Abortable async function helpers", 5 | "keywords": [ 6 | "abort", 7 | "abortable", 8 | "cancel", 9 | "cancelable", 10 | "cancellable", 11 | "abort-controller", 12 | "async", 13 | "coroutine" 14 | ], 15 | "repository": "deeplay-io/abort-controller-x", 16 | "sideEffects": false, 17 | "main": "lib/index.js", 18 | "module": "es/index.js", 19 | "typings": "lib/index.d.ts", 20 | "files": [ 21 | "src", 22 | "lib", 23 | "es" 24 | ], 25 | "scripts": { 26 | "clean": "rimraf lib es", 27 | "test": "jest", 28 | "build:lib": "tsc -P tsconfig.build.json", 29 | "build:es": "tsc -P tsconfig.es.json", 30 | "build": "npm run build:lib && npm run build:es", 31 | "prepublishOnly": "npm test && npm run clean && npm run build" 32 | }, 33 | "author": "Daniel Lytkin ", 34 | "license": "MIT", 35 | "devDependencies": { 36 | "@types/defer-promise": "^1.0.0", 37 | "@types/jest": "^28.1.6", 38 | "@types/node": "^14.17.0", 39 | "defer-promise": "^2.0.1", 40 | "jest": "^28.1.3", 41 | "prettier": "^2.1.2", 42 | "rimraf": "^2.6.3", 43 | "ts-jest": "^28.0.7", 44 | "typescript": "^4.7.4" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/AbortError.test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AbortError, 3 | catchAbortError, 4 | isAbortError, 5 | rethrowAbortError, 6 | throwIfAborted, 7 | } from './AbortError'; 8 | 9 | test('isAbortError', () => { 10 | expect(isAbortError({})).toBe(false); 11 | expect(isAbortError(undefined)).toBe(false); 12 | expect(isAbortError(null)).toBe(false); 13 | 14 | expect(isAbortError(new AbortError())).toBe(true); 15 | }); 16 | 17 | test('throwIfAborted', () => { 18 | const abortController = new AbortController(); 19 | 20 | expect(() => throwIfAborted(abortController.signal)).not.toThrow(); 21 | 22 | abortController.abort(); 23 | 24 | expect(() => throwIfAborted(abortController.signal)).toThrow(AbortError); 25 | }); 26 | 27 | test('rethrowAbortError', () => { 28 | expect(() => rethrowAbortError(new AbortError())).toThrow(AbortError); 29 | expect(() => rethrowAbortError(new Error())).not.toThrow(); 30 | }); 31 | 32 | test('catchAbortError', () => { 33 | expect(() => catchAbortError(new AbortError())).not.toThrow(); 34 | expect(() => catchAbortError(new Error())).toThrow(); 35 | }); 36 | -------------------------------------------------------------------------------- /src/AbortError.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Thrown when an abortable function was aborted. 3 | * 4 | * **Warning**: do not use `instanceof` with this class. Instead, use 5 | * `isAbortError` function. 6 | */ 7 | export class AbortError extends Error { 8 | constructor() { 9 | super('The operation has been aborted'); 10 | 11 | this.message = 'The operation has been aborted'; 12 | 13 | this.name = 'AbortError'; 14 | 15 | if (typeof Error.captureStackTrace === 'function') { 16 | Error.captureStackTrace(this, this.constructor); 17 | } 18 | } 19 | } 20 | 21 | /** 22 | * Checks whether given `error` is an `AbortError`. 23 | */ 24 | export function isAbortError(error: unknown): error is Error { 25 | return ( 26 | typeof error === 'object' && 27 | error !== null && 28 | (error as any).name === 'AbortError' 29 | ); 30 | } 31 | 32 | /** 33 | * If `signal` is aborted, throws `AbortError`. Otherwise does nothing. 34 | */ 35 | export function throwIfAborted(signal: AbortSignal): void { 36 | if (signal.aborted) { 37 | throw new AbortError(); 38 | } 39 | } 40 | 41 | /** 42 | * If `error` is `AbortError`, throws it. Otherwise does nothing. 43 | * 44 | * Useful for `try/catch` blocks around abortable code: 45 | * 46 | * try { 47 | * await somethingAbortable(signal); 48 | * } catch (err) { 49 | * rethrowAbortError(err); 50 | * 51 | * // do normal error handling 52 | * } 53 | */ 54 | export function rethrowAbortError(error: unknown): void { 55 | if (isAbortError(error)) { 56 | throw error; 57 | } 58 | 59 | return; 60 | } 61 | 62 | /** 63 | * If `error` is `AbortError`, does nothing. Otherwise throws it. 64 | * 65 | * Useful for invoking top-level abortable functions: 66 | * 67 | * somethingAbortable(signal).catch(catchAbortError) 68 | * 69 | * Without `catchAbortError`, aborting would result in unhandled promise 70 | * rejection. 71 | */ 72 | export function catchAbortError(error: unknown): void { 73 | if (isAbortError(error)) { 74 | return; 75 | } 76 | 77 | throw error; 78 | } 79 | -------------------------------------------------------------------------------- /src/abortable.test.ts: -------------------------------------------------------------------------------- 1 | import {abortable} from './abortable'; 2 | import {nextTick} from './utils/nextTick'; 3 | 4 | test('abortable endless promise', async () => { 5 | const abortController = new AbortController(); 6 | const signal = abortController.signal; 7 | signal.addEventListener = jest.fn(signal.addEventListener); 8 | signal.removeEventListener = jest.fn(signal.removeEventListener); 9 | 10 | let result: PromiseSettledResult | undefined; 11 | 12 | abortable( 13 | signal, 14 | new Promise(() => {}), 15 | ).then( 16 | value => { 17 | result = {status: 'fulfilled', value}; 18 | }, 19 | reason => { 20 | result = {status: 'rejected', reason}; 21 | }, 22 | ); 23 | 24 | await nextTick(); 25 | 26 | expect(result).toBeUndefined(); 27 | 28 | abortController.abort(); 29 | 30 | await nextTick(); 31 | 32 | expect(result).toMatchObject({ 33 | status: 'rejected', 34 | reason: {name: 'AbortError'}, 35 | }); 36 | 37 | expect(signal.addEventListener).toHaveBeenCalledTimes(1); 38 | expect(signal.removeEventListener).toHaveBeenCalledTimes(1); 39 | }); 40 | 41 | test('abort before reject', async () => { 42 | const abortController = new AbortController(); 43 | const signal = abortController.signal; 44 | signal.addEventListener = jest.fn(signal.addEventListener); 45 | signal.removeEventListener = jest.fn(signal.removeEventListener); 46 | 47 | abortController.abort(); 48 | 49 | let result: PromiseSettledResult | undefined; 50 | 51 | abortable( 52 | signal, 53 | new Promise((resolve, reject) => { 54 | reject('test'); 55 | }), 56 | ).then( 57 | value => { 58 | result = {status: 'fulfilled', value}; 59 | }, 60 | reason => { 61 | result = {status: 'rejected', reason}; 62 | }, 63 | ); 64 | 65 | await nextTick(); 66 | 67 | expect(result).toMatchObject({ 68 | status: 'rejected', 69 | reason: {name: 'AbortError'}, 70 | }); 71 | 72 | expect(signal.addEventListener).toHaveBeenCalledTimes(0); 73 | expect(signal.removeEventListener).toHaveBeenCalledTimes(0); 74 | }); 75 | -------------------------------------------------------------------------------- /src/abortable.ts: -------------------------------------------------------------------------------- 1 | import {execute} from './execute'; 2 | 3 | /** 4 | * Wrap a promise to reject with `AbortError` once `signal` is aborted. 5 | * 6 | * Useful to wrap non-abortable promises. 7 | * Note that underlying process will NOT be aborted. 8 | */ 9 | export function abortable( 10 | signal: AbortSignal, 11 | promise: PromiseLike, 12 | ): Promise { 13 | if (signal.aborted) { 14 | // prevent unhandled rejection 15 | const noop = () => {}; 16 | promise.then(noop, noop); 17 | } 18 | 19 | return execute(signal, (resolve, reject) => { 20 | promise.then(resolve, reject); 21 | 22 | return () => {}; 23 | }); 24 | } 25 | -------------------------------------------------------------------------------- /src/all.test.ts: -------------------------------------------------------------------------------- 1 | import defer from 'defer-promise'; 2 | import {AbortError} from './AbortError'; 3 | import {all} from './all'; 4 | import {nextTick} from './utils/nextTick'; 5 | 6 | test('external abort', async () => { 7 | const abortController = new AbortController(); 8 | const signal = abortController.signal; 9 | signal.addEventListener = jest.fn(signal.addEventListener); 10 | signal.removeEventListener = jest.fn(signal.removeEventListener); 11 | 12 | const deferred1 = defer(); 13 | const deferred2 = defer(); 14 | 15 | let result: PromiseSettledResult<[string, number]> | undefined; 16 | let innerSignal: AbortSignal; 17 | 18 | all(signal, signal => { 19 | innerSignal = signal; 20 | return [deferred1.promise, deferred2.promise]; 21 | }).then( 22 | value => { 23 | result = {status: 'fulfilled', value}; 24 | }, 25 | reason => { 26 | result = {status: 'rejected', reason}; 27 | }, 28 | ); 29 | 30 | abortController.abort(); 31 | 32 | expect(innerSignal!.aborted).toBe(true); 33 | 34 | await nextTick(); 35 | 36 | expect(result).toBeUndefined(); 37 | 38 | deferred1.reject(new AbortError()); 39 | await nextTick(); 40 | 41 | expect(result).toBeUndefined(); 42 | 43 | deferred2.reject(new AbortError()); 44 | await nextTick(); 45 | 46 | expect(result).toMatchObject({ 47 | status: 'rejected', 48 | reason: {name: 'AbortError'}, 49 | }); 50 | 51 | expect(signal.addEventListener).toHaveBeenCalledTimes(1); 52 | expect(signal.removeEventListener).toHaveBeenCalledTimes(1); 53 | }); 54 | 55 | test('fulfill', async () => { 56 | const abortController = new AbortController(); 57 | const signal = abortController.signal; 58 | signal.addEventListener = jest.fn(signal.addEventListener); 59 | signal.removeEventListener = jest.fn(signal.removeEventListener); 60 | 61 | const deferred1 = defer(); 62 | const deferred2 = defer(); 63 | 64 | let result: PromiseSettledResult<[string, number]> | undefined; 65 | let innerSignal: AbortSignal; 66 | 67 | all(signal, signal => { 68 | innerSignal = signal; 69 | return [deferred1.promise, deferred2.promise]; 70 | }).then( 71 | value => { 72 | result = {status: 'fulfilled', value}; 73 | }, 74 | reason => { 75 | result = {status: 'rejected', reason}; 76 | }, 77 | ); 78 | 79 | await nextTick(); 80 | 81 | expect(result).toBeUndefined(); 82 | 83 | // resolve `deferred2` first to test ordering 84 | deferred2.resolve(42); 85 | await nextTick(); 86 | 87 | expect(result).toBeUndefined(); 88 | 89 | deferred1.resolve('test'); 90 | await nextTick(); 91 | 92 | expect(innerSignal!.aborted).toBe(false); 93 | expect(result).toMatchObject({ 94 | status: 'fulfilled', 95 | value: ['test', 42], 96 | }); 97 | 98 | expect(signal.addEventListener).toHaveBeenCalledTimes(1); 99 | expect(signal.removeEventListener).toHaveBeenCalledTimes(1); 100 | }); 101 | 102 | test('reject', async () => { 103 | const abortController = new AbortController(); 104 | const signal = abortController.signal; 105 | signal.addEventListener = jest.fn(signal.addEventListener); 106 | signal.removeEventListener = jest.fn(signal.removeEventListener); 107 | 108 | const deferred1 = defer(); 109 | const deferred2 = defer(); 110 | 111 | let result: PromiseSettledResult<[string, number]> | undefined; 112 | let innerSignal: AbortSignal; 113 | 114 | all(signal, signal => { 115 | innerSignal = signal; 116 | return [deferred1.promise, deferred2.promise]; 117 | }).then( 118 | value => { 119 | result = {status: 'fulfilled', value}; 120 | }, 121 | reason => { 122 | result = {status: 'rejected', reason}; 123 | }, 124 | ); 125 | 126 | await nextTick(); 127 | 128 | expect(result).toBeUndefined(); 129 | expect(innerSignal!.aborted).toBe(false); 130 | 131 | deferred1.reject('test'); 132 | await nextTick(); 133 | 134 | expect(result).toBeUndefined(); 135 | expect(innerSignal!.aborted).toBe(true); 136 | 137 | deferred2.reject(new AbortError()); 138 | await nextTick(); 139 | 140 | expect(result).toMatchObject({ 141 | status: 'rejected', 142 | reason: 'test', 143 | }); 144 | 145 | expect(signal.addEventListener).toHaveBeenCalledTimes(1); 146 | expect(signal.removeEventListener).toHaveBeenCalledTimes(1); 147 | }); 148 | 149 | test('reject during cleanup', async () => { 150 | const abortController = new AbortController(); 151 | const signal = abortController.signal; 152 | signal.addEventListener = jest.fn(signal.addEventListener); 153 | signal.removeEventListener = jest.fn(signal.removeEventListener); 154 | 155 | const deferred1 = defer(); 156 | const deferred2 = defer(); 157 | 158 | let result: PromiseSettledResult<[string, number]> | undefined; 159 | let innerSignal: AbortSignal; 160 | 161 | all(signal, signal => { 162 | innerSignal = signal; 163 | return [deferred1.promise, deferred2.promise]; 164 | }).then( 165 | value => { 166 | result = {status: 'fulfilled', value}; 167 | }, 168 | reason => { 169 | result = {status: 'rejected', reason}; 170 | }, 171 | ); 172 | 173 | abortController.abort(); 174 | 175 | expect(innerSignal!.aborted).toBe(true); 176 | 177 | await nextTick(); 178 | 179 | expect(result).toBeUndefined(); 180 | 181 | deferred1.reject(new AbortError()); 182 | await nextTick(); 183 | 184 | expect(result).toBeUndefined(); 185 | 186 | deferred2.reject(new Error('test')); 187 | await nextTick(); 188 | 189 | expect(result).toMatchObject({ 190 | status: 'rejected', 191 | reason: {message: 'test'}, 192 | }); 193 | 194 | expect(signal.addEventListener).toHaveBeenCalledTimes(1); 195 | expect(signal.removeEventListener).toHaveBeenCalledTimes(1); 196 | }); 197 | 198 | test('empty', async () => { 199 | const abortController = new AbortController(); 200 | const signal = abortController.signal; 201 | signal.addEventListener = jest.fn(signal.addEventListener); 202 | signal.removeEventListener = jest.fn(signal.removeEventListener); 203 | 204 | await expect(all(signal, signal => [])).resolves.toEqual([]); 205 | 206 | expect(signal.addEventListener).toHaveBeenCalledTimes(0); 207 | expect(signal.removeEventListener).toHaveBeenCalledTimes(0); 208 | }); 209 | -------------------------------------------------------------------------------- /src/all.ts: -------------------------------------------------------------------------------- 1 | import {AbortError, isAbortError} from './AbortError'; 2 | 3 | /** 4 | * Abortable version of `Promise.all`. 5 | * 6 | * Creates new inner `AbortSignal` and passes it to `executor`. That signal is 7 | * aborted when `signal` is aborted or any of the promises returned from 8 | * `executor` are rejected. 9 | * 10 | * Returns a promise that fulfills with an array of results when all of the 11 | * promises returned from `executor` fulfill, rejects when any of the 12 | * promises returned from `executor` are rejected, and rejects with `AbortError` 13 | * when `signal` is aborted. 14 | * 15 | * The promises returned from `executor` must be abortable, i.e. once 16 | * `innerSignal` is aborted, they must reject with `AbortError` either 17 | * immediately, or after doing any async cleanup. 18 | * 19 | * Example: 20 | * 21 | * const [result1, result2] = await all(signal, signal => [ 22 | * makeRequest(signal, params1), 23 | * makeRequest(signal, params2), 24 | * ]); 25 | */ 26 | export function all( 27 | signal: AbortSignal, 28 | executor: ( 29 | innerSignal: AbortSignal, 30 | ) => readonly [ 31 | PromiseLike, 32 | PromiseLike, 33 | PromiseLike, 34 | PromiseLike, 35 | PromiseLike, 36 | PromiseLike, 37 | PromiseLike, 38 | PromiseLike, 39 | PromiseLike, 40 | PromiseLike, 41 | ], 42 | ): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]>; 43 | export function all( 44 | signal: AbortSignal, 45 | executor: ( 46 | innerSignal: AbortSignal, 47 | ) => readonly [ 48 | PromiseLike, 49 | PromiseLike, 50 | PromiseLike, 51 | PromiseLike, 52 | PromiseLike, 53 | PromiseLike, 54 | PromiseLike, 55 | PromiseLike, 56 | PromiseLike, 57 | ], 58 | ): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9]>; 59 | export function all( 60 | signal: AbortSignal, 61 | executor: ( 62 | innerSignal: AbortSignal, 63 | ) => readonly [ 64 | PromiseLike, 65 | PromiseLike, 66 | PromiseLike, 67 | PromiseLike, 68 | PromiseLike, 69 | PromiseLike, 70 | PromiseLike, 71 | PromiseLike, 72 | ], 73 | ): Promise<[T1, T2, T3, T4, T5, T6, T7, T8]>; 74 | export function all( 75 | signal: AbortSignal, 76 | executor: ( 77 | innerSignal: AbortSignal, 78 | ) => readonly [ 79 | PromiseLike, 80 | PromiseLike, 81 | PromiseLike, 82 | PromiseLike, 83 | PromiseLike, 84 | PromiseLike, 85 | PromiseLike, 86 | ], 87 | ): Promise<[T1, T2, T3, T4, T5, T6, T7]>; 88 | export function all( 89 | signal: AbortSignal, 90 | executor: ( 91 | innerSignal: AbortSignal, 92 | ) => readonly [ 93 | PromiseLike, 94 | PromiseLike, 95 | PromiseLike, 96 | PromiseLike, 97 | PromiseLike, 98 | PromiseLike, 99 | ], 100 | ): Promise<[T1, T2, T3, T4, T5, T6]>; 101 | export function all( 102 | signal: AbortSignal, 103 | executor: ( 104 | innerSignal: AbortSignal, 105 | ) => readonly [ 106 | PromiseLike, 107 | PromiseLike, 108 | PromiseLike, 109 | PromiseLike, 110 | PromiseLike, 111 | ], 112 | ): Promise<[T1, T2, T3, T4, T5]>; 113 | export function all( 114 | signal: AbortSignal, 115 | executor: ( 116 | innerSignal: AbortSignal, 117 | ) => readonly [ 118 | PromiseLike, 119 | PromiseLike, 120 | PromiseLike, 121 | PromiseLike, 122 | ], 123 | ): Promise<[T1, T2, T3, T4]>; 124 | export function all( 125 | signal: AbortSignal, 126 | executor: ( 127 | innerSignal: AbortSignal, 128 | ) => readonly [PromiseLike, PromiseLike, PromiseLike], 129 | ): Promise<[T1, T2, T3]>; 130 | export function all( 131 | signal: AbortSignal, 132 | executor: ( 133 | innerSignal: AbortSignal, 134 | ) => readonly [PromiseLike, PromiseLike], 135 | ): Promise<[T1, T2]>; 136 | export function all( 137 | signal: AbortSignal, 138 | executor: (innerSignal: AbortSignal) => readonly PromiseLike[], 139 | ): Promise; 140 | export function all( 141 | signal: AbortSignal, 142 | executor: (innerSignal: AbortSignal) => readonly PromiseLike[], 143 | ): Promise { 144 | return new Promise((resolve, reject) => { 145 | if (signal.aborted) { 146 | reject(new AbortError()); 147 | return; 148 | } 149 | 150 | const innerAbortController = new AbortController(); 151 | 152 | const promises = executor(innerAbortController.signal); 153 | 154 | if (promises.length === 0) { 155 | resolve([]); 156 | return; 157 | } 158 | 159 | const abortListener = () => { 160 | innerAbortController.abort(); 161 | }; 162 | 163 | signal.addEventListener('abort', abortListener); 164 | 165 | let rejection: {reason: any} | undefined; 166 | const results = new Array(promises.length); 167 | 168 | let settledCount = 0; 169 | 170 | function settled() { 171 | settledCount += 1; 172 | 173 | if (settledCount === promises.length) { 174 | signal.removeEventListener('abort', abortListener); 175 | 176 | if (rejection != null) { 177 | reject(rejection.reason); 178 | } else { 179 | resolve(results); 180 | } 181 | } 182 | } 183 | 184 | for (const [i, promise] of promises.entries()) { 185 | promise.then( 186 | value => { 187 | results[i] = value; 188 | 189 | settled(); 190 | }, 191 | reason => { 192 | innerAbortController.abort(); 193 | 194 | if ( 195 | rejection == null || 196 | (!isAbortError(reason) && isAbortError(rejection.reason)) 197 | ) { 198 | rejection = {reason}; 199 | } 200 | 201 | settled(); 202 | }, 203 | ); 204 | } 205 | }); 206 | } 207 | -------------------------------------------------------------------------------- /src/delay.ts: -------------------------------------------------------------------------------- 1 | import {execute} from './execute'; 2 | 3 | /** 4 | * Returns a promise that fulfills after delay and rejects with 5 | * `AbortError` once `signal` is aborted. 6 | * 7 | * The delay time is specified as a `Date` object or as an integer denoting 8 | * milliseconds to wait. 9 | * 10 | * Example: 11 | * 12 | * // Make requests repeatedly with a delay between consecutive requests 13 | * while (true) { 14 | * await makeRequest(signal, params); 15 | * await delay(signal, 1000); 16 | * } 17 | * 18 | * Example: 19 | * 20 | * // Make requests repeatedly with a fixed interval 21 | * import {addMilliseconds} from 'date-fns'; 22 | * 23 | * let date = new Date(); 24 | * 25 | * while (true) { 26 | * await makeRequest(signal, params); 27 | * 28 | * date = addMilliseconds(date, 1000); 29 | * await delay(signal, date); 30 | * } 31 | */ 32 | export function delay( 33 | signal: AbortSignal, 34 | dueTime: number | Date, 35 | ): Promise { 36 | return execute(signal, resolve => { 37 | const ms = 38 | typeof dueTime === 'number' ? dueTime : dueTime.getTime() - Date.now(); 39 | 40 | const timer = setTimeout(resolve, ms); 41 | 42 | return () => { 43 | clearTimeout(timer); 44 | }; 45 | }); 46 | } 47 | -------------------------------------------------------------------------------- /src/execute.test.ts: -------------------------------------------------------------------------------- 1 | import defer from 'defer-promise'; 2 | import {execute} from './execute'; 3 | import {nextTick} from './utils/nextTick'; 4 | 5 | test('resolve immediately', async () => { 6 | const abortController = new AbortController(); 7 | const signal = abortController.signal; 8 | signal.addEventListener = jest.fn(signal.addEventListener); 9 | signal.removeEventListener = jest.fn(signal.removeEventListener); 10 | 11 | const callback = jest.fn(() => {}); 12 | 13 | await expect( 14 | execute(signal, (resolve, reject) => { 15 | resolve('test'); 16 | 17 | return callback; 18 | }), 19 | ).resolves.toEqual('test'); 20 | 21 | expect(callback).not.toHaveBeenCalled(); 22 | 23 | abortController.abort(); 24 | 25 | await nextTick(); 26 | 27 | expect(callback).not.toHaveBeenCalled(); 28 | 29 | expect(signal.addEventListener).not.toHaveBeenCalled(); 30 | expect(signal.removeEventListener).not.toHaveBeenCalled(); 31 | }); 32 | 33 | test('resolve before abort', async () => { 34 | const abortController = new AbortController(); 35 | const signal = abortController.signal; 36 | signal.addEventListener = jest.fn(signal.addEventListener); 37 | signal.removeEventListener = jest.fn(signal.removeEventListener); 38 | 39 | let resolve: (value: string) => void; 40 | 41 | const callback = jest.fn(() => {}); 42 | 43 | let result: PromiseSettledResult | undefined; 44 | 45 | execute(signal, (resolve_, reject) => { 46 | resolve = resolve_; 47 | 48 | return callback; 49 | }).then( 50 | value => { 51 | result = {status: 'fulfilled', value}; 52 | }, 53 | reason => { 54 | result = {status: 'rejected', reason}; 55 | }, 56 | ); 57 | 58 | resolve!('test'); 59 | 60 | abortController.abort(); 61 | 62 | await nextTick(); 63 | 64 | expect(callback).not.toHaveBeenCalled(); 65 | expect(result).toEqual({status: 'fulfilled', value: 'test'}); 66 | 67 | expect(signal.addEventListener).toHaveBeenCalledTimes(1); 68 | expect(signal.removeEventListener).toHaveBeenCalledTimes(1); 69 | }); 70 | 71 | test('abort before resolve', async () => { 72 | const abortController = new AbortController(); 73 | const signal = abortController.signal; 74 | signal.addEventListener = jest.fn(signal.addEventListener); 75 | signal.removeEventListener = jest.fn(signal.removeEventListener); 76 | 77 | let resolve: (value: string) => void; 78 | 79 | const callback = jest.fn(() => {}); 80 | 81 | let result: PromiseSettledResult | undefined; 82 | 83 | execute(signal, (resolve_, reject) => { 84 | resolve = resolve_; 85 | 86 | return callback; 87 | }).then( 88 | value => { 89 | result = {status: 'fulfilled', value}; 90 | }, 91 | reason => { 92 | result = {status: 'rejected', reason}; 93 | }, 94 | ); 95 | 96 | abortController.abort(); 97 | resolve!('test'); 98 | 99 | await nextTick(); 100 | 101 | expect(callback).toHaveBeenCalledTimes(1); 102 | expect(result).toMatchObject({ 103 | status: 'rejected', 104 | reason: {name: 'AbortError'}, 105 | }); 106 | 107 | expect(signal.addEventListener).toHaveBeenCalledTimes(1); 108 | expect(signal.removeEventListener).toHaveBeenCalledTimes(1); 109 | }); 110 | 111 | test('abort before execute', async () => { 112 | const abortController = new AbortController(); 113 | const signal = abortController.signal; 114 | signal.addEventListener = jest.fn(signal.addEventListener); 115 | signal.removeEventListener = jest.fn(signal.removeEventListener); 116 | abortController.abort(); 117 | 118 | const executor = jest.fn( 119 | ( 120 | resolve: (value: string) => void, 121 | reject: (reason?: any) => void, 122 | ): (() => void | PromiseLike) => { 123 | return () => {}; 124 | }, 125 | ); 126 | 127 | await expect(execute(signal, executor)).rejects.toMatchObject({ 128 | name: 'AbortError', 129 | }); 130 | 131 | expect(executor).not.toHaveBeenCalled(); 132 | 133 | expect(signal.addEventListener).not.toHaveBeenCalled(); 134 | expect(signal.removeEventListener).not.toHaveBeenCalled(); 135 | }); 136 | 137 | test('reject immediately', async () => { 138 | const abortController = new AbortController(); 139 | const signal = abortController.signal; 140 | signal.addEventListener = jest.fn(signal.addEventListener); 141 | signal.removeEventListener = jest.fn(signal.removeEventListener); 142 | 143 | const callback = jest.fn(() => {}); 144 | 145 | await expect( 146 | execute(signal, (resolve, reject) => { 147 | reject('test'); 148 | 149 | return callback; 150 | }), 151 | ).rejects.toEqual('test'); 152 | 153 | expect(callback).not.toHaveBeenCalled(); 154 | 155 | abortController.abort(); 156 | 157 | await nextTick(); 158 | 159 | expect(callback).not.toHaveBeenCalled(); 160 | 161 | expect(signal.addEventListener).not.toHaveBeenCalled(); 162 | expect(signal.removeEventListener).not.toHaveBeenCalled(); 163 | }); 164 | 165 | test('reject before abort', async () => { 166 | const abortController = new AbortController(); 167 | const signal = abortController.signal; 168 | signal.addEventListener = jest.fn(signal.addEventListener); 169 | signal.removeEventListener = jest.fn(signal.removeEventListener); 170 | 171 | let reject: (value: string) => void; 172 | 173 | const callback = jest.fn(() => {}); 174 | 175 | let result: PromiseSettledResult | undefined; 176 | 177 | execute(signal, (resolve, reject_) => { 178 | reject = reject_; 179 | 180 | return callback; 181 | }).then( 182 | value => { 183 | result = {status: 'fulfilled', value}; 184 | }, 185 | reason => { 186 | result = {status: 'rejected', reason}; 187 | }, 188 | ); 189 | 190 | reject!('test'); 191 | 192 | abortController.abort(); 193 | 194 | await nextTick(); 195 | 196 | expect(callback).not.toHaveBeenCalled(); 197 | expect(result).toEqual({status: 'rejected', reason: 'test'}); 198 | 199 | expect(signal.addEventListener).toHaveBeenCalledTimes(1); 200 | expect(signal.removeEventListener).toHaveBeenCalledTimes(1); 201 | }); 202 | 203 | test('abort before reject', async () => { 204 | const abortController = new AbortController(); 205 | const signal = abortController.signal; 206 | signal.addEventListener = jest.fn(signal.addEventListener); 207 | signal.removeEventListener = jest.fn(signal.removeEventListener); 208 | 209 | let reject: (value: string) => void; 210 | 211 | const callback = jest.fn(() => {}); 212 | 213 | let result: PromiseSettledResult | undefined; 214 | 215 | execute(signal, (resolve, reject_) => { 216 | reject = reject_; 217 | 218 | return callback; 219 | }).then( 220 | value => { 221 | result = {status: 'fulfilled', value}; 222 | }, 223 | reason => { 224 | result = {status: 'rejected', reason}; 225 | }, 226 | ); 227 | 228 | abortController.abort(); 229 | reject!('test'); 230 | 231 | await nextTick(); 232 | 233 | expect(callback).toHaveBeenCalledTimes(1); 234 | expect(result).toMatchObject({ 235 | status: 'rejected', 236 | reason: {name: 'AbortError'}, 237 | }); 238 | 239 | expect(signal.addEventListener).toHaveBeenCalledTimes(1); 240 | expect(signal.removeEventListener).toHaveBeenCalledTimes(1); 241 | }); 242 | 243 | test('async abort callback', async () => { 244 | const abortController = new AbortController(); 245 | const signal = abortController.signal; 246 | signal.addEventListener = jest.fn(signal.addEventListener); 247 | signal.removeEventListener = jest.fn(signal.removeEventListener); 248 | 249 | const callbackDeferred = defer(); 250 | 251 | const callback = jest.fn(() => callbackDeferred.promise); 252 | 253 | let result: PromiseSettledResult | undefined; 254 | 255 | execute(signal, (resolve, reject) => { 256 | return callback; 257 | }).then( 258 | value => { 259 | result = {status: 'fulfilled', value}; 260 | }, 261 | reason => { 262 | result = {status: 'rejected', reason}; 263 | }, 264 | ); 265 | 266 | abortController.abort(); 267 | 268 | await nextTick(); 269 | 270 | expect(result).toBeUndefined(); 271 | 272 | callbackDeferred.resolve(); 273 | 274 | await nextTick(); 275 | 276 | expect(result).toMatchObject({ 277 | status: 'rejected', 278 | reason: {name: 'AbortError'}, 279 | }); 280 | expect(callback).toHaveBeenCalledTimes(1); 281 | 282 | expect(signal.addEventListener).toHaveBeenCalledTimes(1); 283 | expect(signal.removeEventListener).toHaveBeenCalledTimes(1); 284 | }); 285 | 286 | test('async abort callback rejection', async () => { 287 | const abortController = new AbortController(); 288 | const signal = abortController.signal; 289 | signal.addEventListener = jest.fn(signal.addEventListener); 290 | signal.removeEventListener = jest.fn(signal.removeEventListener); 291 | 292 | const callback = jest.fn(() => Promise.reject('test')); 293 | 294 | let result: PromiseSettledResult | undefined; 295 | 296 | execute(signal, (resolve, reject) => { 297 | return callback; 298 | }).then( 299 | value => { 300 | result = {status: 'fulfilled', value}; 301 | }, 302 | reason => { 303 | result = {status: 'rejected', reason}; 304 | }, 305 | ); 306 | 307 | abortController.abort(); 308 | 309 | await nextTick(); 310 | 311 | expect(result).toMatchObject({ 312 | status: 'rejected', 313 | reason: 'test', 314 | }); 315 | expect(callback).toHaveBeenCalledTimes(1); 316 | 317 | expect(signal.addEventListener).toHaveBeenCalledTimes(1); 318 | expect(signal.removeEventListener).toHaveBeenCalledTimes(1); 319 | }); 320 | -------------------------------------------------------------------------------- /src/execute.ts: -------------------------------------------------------------------------------- 1 | import {AbortError} from './AbortError'; 2 | 3 | /** 4 | * Similar to `new Promise(executor)`, but allows executor to return abort 5 | * callback that is called once `signal` is aborted. 6 | * 7 | * Returned promise rejects with `AbortError` once `signal` is aborted. 8 | * 9 | * Callback can return a promise, e.g. for doing any async cleanup. In this 10 | * case, the promise returned from `execute` rejects with `AbortError` after 11 | * that promise fulfills. 12 | */ 13 | export function execute( 14 | signal: AbortSignal, 15 | executor: ( 16 | resolve: (value: T) => void, 17 | reject: (reason?: any) => void, 18 | ) => () => void | PromiseLike, 19 | ): Promise { 20 | return new Promise((resolve, reject) => { 21 | if (signal.aborted) { 22 | reject(new AbortError()); 23 | return; 24 | } 25 | 26 | let removeAbortListener: (() => void) | undefined; 27 | let finished = false; 28 | 29 | function finish() { 30 | if (!finished) { 31 | finished = true; 32 | if (removeAbortListener != null) { 33 | removeAbortListener(); 34 | } 35 | } 36 | } 37 | 38 | const callback = executor( 39 | value => { 40 | resolve(value); 41 | finish(); 42 | }, 43 | reason => { 44 | reject(reason); 45 | finish(); 46 | }, 47 | ); 48 | 49 | if (!finished) { 50 | const listener = () => { 51 | const callbackResult = callback(); 52 | 53 | if (callbackResult == null) { 54 | reject(new AbortError()); 55 | } else { 56 | callbackResult.then( 57 | () => { 58 | reject(new AbortError()); 59 | }, 60 | reason => { 61 | reject(reason); 62 | }, 63 | ); 64 | } 65 | 66 | finish(); 67 | }; 68 | 69 | signal.addEventListener('abort', listener); 70 | 71 | removeAbortListener = () => { 72 | signal.removeEventListener('abort', listener); 73 | }; 74 | } 75 | }); 76 | } 77 | -------------------------------------------------------------------------------- /src/forever.test.ts: -------------------------------------------------------------------------------- 1 | import {forever} from './forever'; 2 | import {nextTick} from './utils/nextTick'; 3 | 4 | test('forever', async () => { 5 | const abortController = new AbortController(); 6 | const signal = abortController.signal; 7 | signal.addEventListener = jest.fn(signal.addEventListener); 8 | signal.removeEventListener = jest.fn(signal.removeEventListener); 9 | 10 | let result: PromiseSettledResult | undefined; 11 | 12 | forever(signal).then( 13 | value => { 14 | result = {status: 'fulfilled', value}; 15 | }, 16 | reason => { 17 | result = {status: 'rejected', reason}; 18 | }, 19 | ); 20 | 21 | await nextTick(); 22 | 23 | expect(result).toBeUndefined(); 24 | 25 | abortController.abort(); 26 | 27 | await nextTick(); 28 | 29 | expect(result).toMatchObject({ 30 | status: 'rejected', 31 | reason: {name: 'AbortError'}, 32 | }); 33 | 34 | expect(signal.addEventListener).toHaveBeenCalledTimes(1); 35 | expect(signal.removeEventListener).toHaveBeenCalledTimes(1); 36 | }); 37 | -------------------------------------------------------------------------------- /src/forever.ts: -------------------------------------------------------------------------------- 1 | import {execute} from './execute'; 2 | 3 | /** 4 | * Return a promise that never fulfills and only rejects with `AbortError` once 5 | * `signal` is aborted. 6 | */ 7 | export function forever(signal: AbortSignal): Promise { 8 | return execute(signal, () => () => {}); 9 | } 10 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './abortable'; 2 | export * from './AbortError'; 3 | export * from './delay'; 4 | export * from './execute'; 5 | export * from './forever'; 6 | export * from './waitForEvent'; 7 | export * from './all'; 8 | export * from './race'; 9 | export * from './retry'; 10 | export * from './spawn'; 11 | export * from './run'; 12 | export * from './proactiveRetry'; 13 | -------------------------------------------------------------------------------- /src/proactiveRetry.ts: -------------------------------------------------------------------------------- 1 | import {isAbortError, catchAbortError} from './AbortError'; 2 | import {delay} from './delay'; 3 | import {execute} from './execute'; 4 | 5 | export type ProactiveRetryOptions = { 6 | /** 7 | * Base delay between attempts in milliseconds. 8 | * 9 | * Defaults to 1000. 10 | * 11 | * Example: if `baseMs` is 100, then retries will be attempted in 100ms, 12 | * 200ms, 400ms etc (not counting jitter). 13 | */ 14 | baseMs?: number; 15 | /** 16 | * Maximum for the total number of attempts. 17 | * 18 | * Defaults to `Infinity`. 19 | */ 20 | maxAttempts?: number; 21 | /** 22 | * Called after each failed attempt. 23 | * 24 | * Rethrow error from this callback to prevent further retries. 25 | */ 26 | onError?: (error: unknown, attempt: number) => void; 27 | }; 28 | 29 | /** 30 | * Proactively retry a function with exponential backoff. 31 | * 32 | * Also known as hedging. 33 | * 34 | * The function will be called multiple times in parallel until it succeeds, in 35 | * which case all the other calls will be aborted. 36 | */ 37 | export function proactiveRetry( 38 | signal: AbortSignal, 39 | fn: (signal: AbortSignal, attempt: number) => Promise, 40 | options: ProactiveRetryOptions = {}, 41 | ): Promise { 42 | const {baseMs = 1000, onError, maxAttempts = Infinity} = options; 43 | 44 | return execute(signal, (resolve, reject) => { 45 | const innerAbortController = new AbortController(); 46 | let attemptsExhausted = false; 47 | 48 | const promises = new Map>(); 49 | 50 | function handleFulfilled(value: T) { 51 | innerAbortController.abort(); 52 | promises.clear(); 53 | 54 | resolve(value); 55 | } 56 | 57 | function handleRejected(err: unknown, attempt: number) { 58 | promises.delete(attempt); 59 | 60 | if (attemptsExhausted && promises.size === 0) { 61 | reject(err); 62 | 63 | return; 64 | } 65 | 66 | if (isAbortError(err)) { 67 | return; 68 | } 69 | 70 | if (onError) { 71 | try { 72 | onError(err, attempt); 73 | } catch (err) { 74 | innerAbortController.abort(); 75 | promises.clear(); 76 | 77 | reject(err); 78 | } 79 | } 80 | } 81 | 82 | async function makeAttempts(signal: AbortSignal) { 83 | for (let attempt = 0; ; attempt++) { 84 | const promise = fn(signal, attempt); 85 | 86 | promises.set(attempt, promise); 87 | 88 | promise.then(handleFulfilled, err => handleRejected(err, attempt)); 89 | 90 | if (attempt + 1 >= maxAttempts) { 91 | break; 92 | } 93 | 94 | // https://aws.amazon.com/ru/blogs/architecture/exponential-backoff-and-jitter/ 95 | const backoff = Math.pow(2, attempt) * baseMs; 96 | const delayMs = Math.round((backoff * (1 + Math.random())) / 2); 97 | 98 | await delay(signal, delayMs); 99 | } 100 | 101 | attemptsExhausted = true; 102 | } 103 | 104 | makeAttempts(innerAbortController.signal).catch(catchAbortError); 105 | 106 | return () => { 107 | innerAbortController.abort(); 108 | }; 109 | }); 110 | } 111 | -------------------------------------------------------------------------------- /src/race.test.ts: -------------------------------------------------------------------------------- 1 | import defer from 'defer-promise'; 2 | import {AbortError} from './AbortError'; 3 | import {race} from './race'; 4 | import {nextTick} from './utils/nextTick'; 5 | 6 | test('external abort', async () => { 7 | const abortController = new AbortController(); 8 | const signal = abortController.signal; 9 | signal.addEventListener = jest.fn(signal.addEventListener); 10 | signal.removeEventListener = jest.fn(signal.removeEventListener); 11 | 12 | const deferred1 = defer(); 13 | const deferred2 = defer(); 14 | 15 | let result: PromiseSettledResult | undefined; 16 | let innerSignal: AbortSignal; 17 | 18 | race(signal, signal => { 19 | innerSignal = signal; 20 | return [deferred1.promise, deferred2.promise]; 21 | }).then( 22 | value => { 23 | result = {status: 'fulfilled', value}; 24 | }, 25 | reason => { 26 | result = {status: 'rejected', reason}; 27 | }, 28 | ); 29 | 30 | abortController.abort(); 31 | 32 | expect(innerSignal!.aborted).toBe(true); 33 | 34 | await nextTick(); 35 | 36 | expect(result).toBeUndefined(); 37 | 38 | deferred1.reject(new AbortError()); 39 | await nextTick(); 40 | 41 | expect(result).toBeUndefined(); 42 | 43 | deferred2.reject(new AbortError()); 44 | await nextTick(); 45 | 46 | expect(result).toMatchObject({ 47 | status: 'rejected', 48 | reason: {name: 'AbortError'}, 49 | }); 50 | 51 | expect(signal.addEventListener).toHaveBeenCalledTimes(1); 52 | expect(signal.removeEventListener).toHaveBeenCalledTimes(1); 53 | }); 54 | 55 | test('fulfill', async () => { 56 | const abortController = new AbortController(); 57 | const signal = abortController.signal; 58 | signal.addEventListener = jest.fn(signal.addEventListener); 59 | signal.removeEventListener = jest.fn(signal.removeEventListener); 60 | 61 | const deferred1 = defer(); 62 | const deferred2 = defer(); 63 | 64 | let result: PromiseSettledResult | undefined; 65 | let innerSignal: AbortSignal; 66 | 67 | race(signal, signal => { 68 | innerSignal = signal; 69 | return [deferred1.promise, deferred2.promise]; 70 | }).then( 71 | value => { 72 | result = {status: 'fulfilled', value}; 73 | }, 74 | reason => { 75 | result = {status: 'rejected', reason}; 76 | }, 77 | ); 78 | 79 | await nextTick(); 80 | 81 | expect(result).toBeUndefined(); 82 | expect(innerSignal!.aborted).toBe(false); 83 | 84 | deferred1.resolve('test'); 85 | await nextTick(); 86 | 87 | expect(result).toBeUndefined(); 88 | expect(innerSignal!.aborted).toBe(true); 89 | 90 | deferred2.reject(new AbortError()); 91 | await nextTick(); 92 | 93 | expect(result).toMatchObject({ 94 | status: 'fulfilled', 95 | value: 'test', 96 | }); 97 | 98 | expect(signal.addEventListener).toHaveBeenCalledTimes(1); 99 | expect(signal.removeEventListener).toHaveBeenCalledTimes(1); 100 | }); 101 | 102 | test('reject', async () => { 103 | const abortController = new AbortController(); 104 | const signal = abortController.signal; 105 | signal.addEventListener = jest.fn(signal.addEventListener); 106 | signal.removeEventListener = jest.fn(signal.removeEventListener); 107 | 108 | const deferred1 = defer(); 109 | const deferred2 = defer(); 110 | 111 | let result: PromiseSettledResult | undefined; 112 | let innerSignal: AbortSignal; 113 | 114 | race(signal, signal => { 115 | innerSignal = signal; 116 | return [deferred1.promise, deferred2.promise]; 117 | }).then( 118 | value => { 119 | result = {status: 'fulfilled', value}; 120 | }, 121 | reason => { 122 | result = {status: 'rejected', reason}; 123 | }, 124 | ); 125 | 126 | await nextTick(); 127 | 128 | expect(result).toBeUndefined(); 129 | expect(innerSignal!.aborted).toBe(false); 130 | 131 | deferred1.reject('test'); 132 | await nextTick(); 133 | 134 | expect(result).toBeUndefined(); 135 | expect(innerSignal!.aborted).toBe(true); 136 | 137 | deferred2.reject(new AbortError()); 138 | await nextTick(); 139 | 140 | expect(result).toMatchObject({ 141 | status: 'rejected', 142 | reason: 'test', 143 | }); 144 | 145 | expect(signal.addEventListener).toHaveBeenCalledTimes(1); 146 | expect(signal.removeEventListener).toHaveBeenCalledTimes(1); 147 | }); 148 | 149 | test('reject during cleanup', async () => { 150 | const abortController = new AbortController(); 151 | const signal = abortController.signal; 152 | signal.addEventListener = jest.fn(signal.addEventListener); 153 | signal.removeEventListener = jest.fn(signal.removeEventListener); 154 | 155 | const deferred1 = defer(); 156 | const deferred2 = defer(); 157 | 158 | let result: PromiseSettledResult | undefined; 159 | let innerSignal: AbortSignal; 160 | 161 | race(signal, signal => { 162 | innerSignal = signal; 163 | return [deferred1.promise, deferred2.promise]; 164 | }).then( 165 | value => { 166 | result = {status: 'fulfilled', value}; 167 | }, 168 | reason => { 169 | result = {status: 'rejected', reason}; 170 | }, 171 | ); 172 | 173 | abortController.abort(); 174 | 175 | expect(innerSignal!.aborted).toBe(true); 176 | 177 | await nextTick(); 178 | 179 | expect(result).toBeUndefined(); 180 | 181 | deferred1.reject(new AbortError()); 182 | await nextTick(); 183 | 184 | expect(result).toBeUndefined(); 185 | 186 | deferred2.reject(new Error('test')); 187 | await nextTick(); 188 | 189 | expect(result).toMatchObject({ 190 | status: 'rejected', 191 | reason: {message: 'test'}, 192 | }); 193 | 194 | expect(signal.addEventListener).toHaveBeenCalledTimes(1); 195 | expect(signal.removeEventListener).toHaveBeenCalledTimes(1); 196 | }); 197 | -------------------------------------------------------------------------------- /src/race.ts: -------------------------------------------------------------------------------- 1 | import {AbortError, isAbortError} from './AbortError'; 2 | 3 | /** 4 | * Abortable version of `Promise.race`. 5 | * 6 | * Creates new inner `AbortSignal` and passes it to `executor`. That signal is 7 | * aborted when `signal` is aborted or any of the promises returned from 8 | * `executor` are fulfilled or rejected. 9 | * 10 | * Returns a promise that fulfills or rejects when any of the promises returned 11 | * from `executor` are fulfilled or rejected, and rejects with `AbortError` when 12 | * `signal` is aborted. 13 | * 14 | * The promises returned from `executor` must be abortable, i.e. once 15 | * `innerSignal` is aborted, they must reject with `AbortError` either 16 | * immediately, or after doing any async cleanup. 17 | * 18 | * Example: 19 | * 20 | * const result = await race(signal, signal => [ 21 | * delay(signal, 1000).then(() => ({status: 'timeout'})), 22 | * makeRequest(signal, params).then(value => ({status: 'success', value})), 23 | * ]); 24 | * 25 | * if (result.status === 'timeout') { 26 | * // request timed out 27 | * } else { 28 | * const response = result.value; 29 | * } 30 | */ 31 | export function race>( 32 | signal: AbortSignal, 33 | executor: (innerSignal: AbortSignal) => readonly T[], 34 | ): Promise ? U : never> { 35 | return new Promise((resolve, reject) => { 36 | if (signal.aborted) { 37 | reject(new AbortError()); 38 | return; 39 | } 40 | 41 | const innerAbortController = new AbortController(); 42 | 43 | const promises = executor(innerAbortController.signal); 44 | 45 | const abortListener = () => { 46 | innerAbortController.abort(); 47 | }; 48 | 49 | signal.addEventListener('abort', abortListener); 50 | 51 | let settledCount = 0; 52 | 53 | function settled( 54 | result: PromiseSettledResult ? U : never>, 55 | ) { 56 | innerAbortController.abort(); 57 | 58 | settledCount += 1; 59 | 60 | if (settledCount === promises.length) { 61 | signal.removeEventListener('abort', abortListener); 62 | 63 | if (result.status === 'fulfilled') { 64 | resolve(result.value); 65 | } else { 66 | reject(result.reason); 67 | } 68 | } 69 | } 70 | 71 | let result: 72 | | PromiseSettledResult ? U : never> 73 | | undefined; 74 | 75 | for (const promise of promises) { 76 | promise.then( 77 | value => { 78 | if (result == null) { 79 | result = {status: 'fulfilled', value}; 80 | } 81 | 82 | settled(result); 83 | }, 84 | reason => { 85 | if ( 86 | result == null || 87 | (!isAbortError(reason) && 88 | (result.status === 'fulfilled' || isAbortError(result.reason))) 89 | ) { 90 | result = {status: 'rejected', reason}; 91 | } 92 | 93 | settled(result); 94 | }, 95 | ); 96 | } 97 | }); 98 | } 99 | -------------------------------------------------------------------------------- /src/retry.ts: -------------------------------------------------------------------------------- 1 | import {delay} from './delay'; 2 | import {rethrowAbortError} from './AbortError'; 3 | 4 | export type RetryOptions = { 5 | /** 6 | * Starting delay before first retry attempt in milliseconds. 7 | * 8 | * Defaults to 1000. 9 | * 10 | * Example: if `baseMs` is 100, then retries will be attempted in 100ms, 11 | * 200ms, 400ms etc (not counting jitter). 12 | */ 13 | baseMs?: number; 14 | /** 15 | * Maximum delay between attempts in milliseconds. 16 | * 17 | * Defaults to 30 seconds. 18 | * 19 | * Example: if `baseMs` is 1000 and `maxDelayMs` is 3000, then retries will be 20 | * attempted in 1000ms, 2000ms, 3000ms, 3000ms etc (not counting jitter). 21 | */ 22 | maxDelayMs?: number; 23 | /** 24 | * Maximum for the total number of attempts. 25 | * 26 | * Defaults to `Infinity`. 27 | */ 28 | maxAttempts?: number; 29 | /** 30 | * Called after each failed attempt before setting delay timer. 31 | * 32 | * Rethrow error from this callback to prevent further retries. 33 | */ 34 | onError?: (error: unknown, attempt: number, delayMs: number) => void; 35 | }; 36 | 37 | /** 38 | * Retry function with exponential backoff. 39 | * 40 | * The function receives AbortSignal, attempt number starting with 0, and reset 41 | * function that sets attempt number to -1 so that the next attempt will be 42 | * made without delay. 43 | */ 44 | export async function retry( 45 | signal: AbortSignal, 46 | fn: (signal: AbortSignal, attempt: number, reset: () => void) => Promise, 47 | options: RetryOptions = {}, 48 | ): Promise { 49 | const { 50 | baseMs = 1000, 51 | maxDelayMs = 30000, 52 | onError, 53 | maxAttempts = Infinity, 54 | } = options; 55 | 56 | let attempt = 0; 57 | 58 | const reset = () => { 59 | attempt = -1; 60 | }; 61 | 62 | while (true) { 63 | try { 64 | return await fn(signal, attempt, reset); 65 | } catch (error) { 66 | rethrowAbortError(error); 67 | 68 | if (attempt >= maxAttempts) { 69 | throw error; 70 | } 71 | 72 | let delayMs: number; 73 | 74 | if (attempt === -1) { 75 | delayMs = 0; 76 | } else { 77 | // https://aws.amazon.com/ru/blogs/architecture/exponential-backoff-and-jitter/ 78 | const backoff = Math.min(maxDelayMs, Math.pow(2, attempt) * baseMs); 79 | delayMs = Math.round((backoff * (1 + Math.random())) / 2); 80 | } 81 | 82 | if (onError) { 83 | onError(error, attempt, delayMs); 84 | } 85 | 86 | if (delayMs !== 0) { 87 | await delay(signal, delayMs); 88 | } 89 | 90 | attempt += 1; 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/run.ts: -------------------------------------------------------------------------------- 1 | import {catchAbortError} from './AbortError'; 2 | 3 | /** 4 | * Invokes an abortable function with implicitly created `AbortSignal`. 5 | * 6 | * Returns a function that aborts that signal and waits until passed function 7 | * finishes. 8 | * 9 | * Any error other than `AbortError` thrown from passed function will result in 10 | * unhandled promise rejection. 11 | * 12 | * Example: 13 | * 14 | * const stop = run(async signal => { 15 | * try { 16 | * while (true) { 17 | * await delay(signal, 1000); 18 | * console.log('tick'); 19 | * } 20 | * } finally { 21 | * await doCleanup(); 22 | * } 23 | * }); 24 | * 25 | * // abort and wait until cleanup is done 26 | * await stop(); 27 | */ 28 | export function run( 29 | fn: (signal: AbortSignal) => Promise, 30 | ): () => Promise { 31 | const abortController = new AbortController(); 32 | 33 | const promise = fn(abortController.signal).catch(catchAbortError); 34 | 35 | return () => { 36 | abortController.abort(); 37 | return promise; 38 | }; 39 | } 40 | -------------------------------------------------------------------------------- /src/spawn.test.ts: -------------------------------------------------------------------------------- 1 | import {spawn} from './spawn'; 2 | import {forever} from './forever'; 3 | import {delay} from './delay'; 4 | 5 | test('fork manual abort', async () => { 6 | const abortController = new AbortController(); 7 | const signal = abortController.signal; 8 | signal.addEventListener = jest.fn(signal.addEventListener); 9 | signal.removeEventListener = jest.fn(signal.removeEventListener); 10 | 11 | const actions: string[] = []; 12 | 13 | await spawn(signal, async (signal, {fork}) => { 14 | const task = fork(async signal => { 15 | actions.push('fork start'); 16 | try { 17 | await forever(signal); 18 | } catch (err: any) { 19 | actions.push(`fork abort: ${err.message}`); 20 | } 21 | }); 22 | 23 | actions.push('post fork'); 24 | await delay(signal, 0); 25 | actions.push('pre task abort'); 26 | task.abort(); 27 | await delay(signal, 0); 28 | actions.push('post task abort'); 29 | }); 30 | 31 | expect(actions).toMatchInlineSnapshot(` 32 | Array [ 33 | "fork start", 34 | "post fork", 35 | "pre task abort", 36 | "fork abort: The operation has been aborted", 37 | "post task abort", 38 | ] 39 | `); 40 | 41 | expect(signal.addEventListener).toHaveBeenCalledTimes(1); 42 | expect(signal.removeEventListener).toHaveBeenCalledTimes(1); 43 | }); 44 | 45 | test('fork abort on spawn finish', async () => { 46 | const abortController = new AbortController(); 47 | const signal = abortController.signal; 48 | signal.addEventListener = jest.fn(signal.addEventListener); 49 | signal.removeEventListener = jest.fn(signal.removeEventListener); 50 | 51 | const actions: string[] = []; 52 | 53 | await spawn(signal, async (signal, {fork}) => { 54 | fork(async signal => { 55 | actions.push('fork start'); 56 | try { 57 | await forever(signal); 58 | } catch (err: any) { 59 | actions.push(`fork abort: ${err.message}`); 60 | } 61 | }); 62 | 63 | actions.push('post fork'); 64 | await delay(signal, 0); 65 | actions.push('spawn finish'); 66 | }); 67 | 68 | expect(actions).toMatchInlineSnapshot(` 69 | Array [ 70 | "fork start", 71 | "post fork", 72 | "spawn finish", 73 | "fork abort: The operation has been aborted", 74 | ] 75 | `); 76 | 77 | expect(signal.addEventListener).toHaveBeenCalledTimes(1); 78 | expect(signal.removeEventListener).toHaveBeenCalledTimes(1); 79 | }); 80 | 81 | test('fork abort on spawn error', async () => { 82 | const abortController = new AbortController(); 83 | const signal = abortController.signal; 84 | signal.addEventListener = jest.fn(signal.addEventListener); 85 | signal.removeEventListener = jest.fn(signal.removeEventListener); 86 | 87 | const actions: string[] = []; 88 | 89 | await spawn(signal, async (signal, {fork}) => { 90 | fork(async signal => { 91 | actions.push('fork start'); 92 | try { 93 | await forever(signal); 94 | } catch (err: any) { 95 | actions.push(`fork abort: ${err.message}`); 96 | } 97 | }); 98 | 99 | actions.push('post fork'); 100 | await delay(signal, 0); 101 | actions.push('spawn finish'); 102 | throw new Error('the-error'); 103 | }).catch(err => { 104 | actions.push(`spawn throw: ${err.message}`); 105 | }); 106 | 107 | expect(actions).toMatchInlineSnapshot(` 108 | Array [ 109 | "fork start", 110 | "post fork", 111 | "spawn finish", 112 | "fork abort: The operation has been aborted", 113 | "spawn throw: the-error", 114 | ] 115 | `); 116 | 117 | expect(signal.addEventListener).toHaveBeenCalledTimes(1); 118 | expect(signal.removeEventListener).toHaveBeenCalledTimes(1); 119 | }); 120 | 121 | test('error thrown from fork', async () => { 122 | const abortController = new AbortController(); 123 | const signal = abortController.signal; 124 | signal.addEventListener = jest.fn(signal.addEventListener); 125 | signal.removeEventListener = jest.fn(signal.removeEventListener); 126 | 127 | const actions: string[] = []; 128 | 129 | await spawn(signal, async (signal, {fork}) => { 130 | fork(async signal => { 131 | actions.push('fork start'); 132 | await delay(signal, 0); 133 | actions.push('fork finish'); 134 | throw new Error('the-error'); 135 | }); 136 | 137 | actions.push('post fork'); 138 | 139 | try { 140 | await forever(signal); 141 | } catch (err: any) { 142 | actions.push(`spawn abort: ${err.message}`); 143 | throw err; 144 | } 145 | }).catch(err => { 146 | actions.push(`spawn throw: ${err.message}`); 147 | }); 148 | 149 | expect(actions).toMatchInlineSnapshot(` 150 | Array [ 151 | "fork start", 152 | "post fork", 153 | "fork finish", 154 | "spawn abort: The operation has been aborted", 155 | "spawn throw: the-error", 156 | ] 157 | `); 158 | 159 | expect(signal.addEventListener).toHaveBeenCalledTimes(1); 160 | expect(signal.removeEventListener).toHaveBeenCalledTimes(1); 161 | }); 162 | 163 | test('async defer', async () => { 164 | const abortController = new AbortController(); 165 | const signal = abortController.signal; 166 | 167 | const deferredFn = jest.fn(); 168 | 169 | await spawn(signal, async (signal, {defer}) => { 170 | await delay(signal, 0); 171 | 172 | defer(() => { 173 | deferredFn(); 174 | }); 175 | }); 176 | 177 | expect(deferredFn).toHaveBeenCalledTimes(1); 178 | }); 179 | 180 | test('abort before spawn', async () => { 181 | const abortController = new AbortController(); 182 | const signal = abortController.signal; 183 | signal.addEventListener = jest.fn(signal.addEventListener); 184 | signal.removeEventListener = jest.fn(signal.removeEventListener); 185 | abortController.abort(); 186 | 187 | const executor = jest.fn(async (signal: AbortSignal) => {}); 188 | 189 | await expect(spawn(signal, executor)).rejects.toMatchObject({ 190 | name: 'AbortError', 191 | }); 192 | 193 | expect(executor).not.toHaveBeenCalled(); 194 | 195 | expect(signal.addEventListener).not.toHaveBeenCalled(); 196 | expect(signal.removeEventListener).not.toHaveBeenCalled(); 197 | }); 198 | -------------------------------------------------------------------------------- /src/spawn.ts: -------------------------------------------------------------------------------- 1 | import {AbortError, catchAbortError, isAbortError} from './AbortError'; 2 | 3 | export type SpawnEffects = { 4 | /** 5 | * Schedules a function to run after spawned function finishes. 6 | * 7 | * Deferred functions run serially in last-in-first-out order. 8 | * 9 | * Promise returned from `spawn` fulfills or rejects only after all deferred 10 | * functions finish. 11 | */ 12 | defer(fn: () => void | Promise): void; 13 | 14 | /** 15 | * Executes an abortable function in background. 16 | * 17 | * If a forked function throws an exception, spawned function and other forks 18 | * are aborted and promise returned from `spawn` rejects with that exception. 19 | * 20 | * When spawned function finishes, all forks are aborted. 21 | */ 22 | fork(fn: (signal: AbortSignal) => Promise): ForkTask; 23 | }; 24 | 25 | export type ForkTask = { 26 | /** 27 | * Abort a forked function. 28 | */ 29 | abort(): void; 30 | /** 31 | * Returns a promise returned from a forked function. 32 | */ 33 | join(): Promise; 34 | }; 35 | 36 | /** 37 | * Run an abortable function with `fork` and `defer` effects attached to it. 38 | * 39 | * `spawn` allows to write Go-style coroutines. 40 | * 41 | * Example: 42 | * 43 | * // Connect to a database, then start a server, then block until abort. 44 | * // On abort, gracefully shutdown the server, and once done, disconnect 45 | * // from the database. 46 | * spawn(signal, async (signal, {defer}) => { 47 | * const db = await connectToDb(); 48 | * 49 | * defer(async () => { 50 | * await db.close(); 51 | * }); 52 | * 53 | * const server = await startServer(db); 54 | * 55 | * defer(async () => { 56 | * await server.close(); 57 | * }); 58 | * 59 | * await forever(signal); 60 | * }); 61 | * 62 | * Example: 63 | * 64 | * // Connect to a database, then start an infinite polling loop. 65 | * // On abort, disconnect from the database. 66 | * spawn(signal, async (signal, {defer}) => { 67 | * const db = await connectToDb(); 68 | * 69 | * defer(async () => { 70 | * await db.close(); 71 | * }); 72 | * 73 | * while (true) { 74 | * await poll(signal, db); 75 | * await delay(signal, 5000); 76 | * } 77 | * }); 78 | * 79 | * Example: 80 | * 81 | * // Acquire a lock and execute a function. 82 | * // Extend the lock while the function is running. 83 | * // Once the function finishes or the signal is aborted, stop extending 84 | * // the lock and release it. 85 | * import Redlock = require('redlock'); 86 | * 87 | * const lockTtl = 30_000; 88 | * 89 | * function withLock( 90 | * signal: AbortSignal, 91 | * redlock: Redlock, 92 | * key: string, 93 | * fn: (signal: AbortSignal) => Promise, 94 | * ): Promise { 95 | * return spawn(signal, async (signal, {fork, defer}) => { 96 | * const lock = await redlock.lock(key, lockTtl); 97 | * 98 | * defer(() => lock.unlock()); 99 | * ​ 100 | * fork(async signal => { 101 | * while (true) { 102 | * await delay(signal, lockTtl / 10); 103 | * await lock.extend(lockTtl); 104 | * } 105 | * }); 106 | * 107 | * return await fn(signal); 108 | * }); 109 | * } 110 | * 111 | * const redlock = new Redlock([redis], { 112 | * retryCount: -1, 113 | * }); 114 | * 115 | * await withLock(signal, redlock, 'the-lock-key', async signal => { 116 | * // ... 117 | * }); 118 | */ 119 | export function spawn( 120 | signal: AbortSignal, 121 | fn: (signal: AbortSignal, effects: SpawnEffects) => Promise, 122 | ): Promise { 123 | if (signal.aborted) { 124 | return Promise.reject(new AbortError()); 125 | } 126 | 127 | const deferredFunctions: Array<() => void | Promise> = []; 128 | 129 | /** 130 | * Aborted when spawned function finishes 131 | * or one of forked functions throws 132 | * or parent signal aborted. 133 | */ 134 | const spawnAbortController = new AbortController(); 135 | const spawnSignal = spawnAbortController.signal; 136 | 137 | const abortSpawn = () => { 138 | spawnAbortController.abort(); 139 | }; 140 | signal.addEventListener('abort', abortSpawn); 141 | const removeAbortListener = () => { 142 | signal.removeEventListener('abort', abortSpawn); 143 | }; 144 | 145 | const tasks = new Set>(); 146 | 147 | const abortTasks = () => { 148 | for (const task of tasks) { 149 | task.abort(); 150 | } 151 | }; 152 | spawnSignal.addEventListener('abort', abortTasks); 153 | const removeSpawnAbortListener = () => { 154 | spawnSignal.removeEventListener('abort', abortTasks); 155 | }; 156 | 157 | let promise = new Promise((resolve, reject) => { 158 | let result: {value: T} | undefined; 159 | let failure: {error: unknown} | undefined; 160 | 161 | fork(signal => 162 | fn(signal, { 163 | defer(fn: () => void | Promise) { 164 | deferredFunctions.push(fn); 165 | }, 166 | 167 | fork, 168 | }), 169 | ) 170 | .join() 171 | .then( 172 | value => { 173 | spawnAbortController.abort(); 174 | result = {value}; 175 | }, 176 | error => { 177 | spawnAbortController.abort(); 178 | 179 | if (!isAbortError(error) || failure == null) { 180 | failure = {error}; 181 | } 182 | }, 183 | ); 184 | 185 | function fork(forkFn: (signal: AbortSignal) => Promise): ForkTask { 186 | if (spawnSignal.aborted) { 187 | // return already aborted task 188 | return { 189 | abort() {}, 190 | async join() { 191 | throw new AbortError(); 192 | }, 193 | }; 194 | } 195 | 196 | const taskAbortController = new AbortController(); 197 | const taskSignal = taskAbortController.signal; 198 | 199 | const taskPromise = forkFn(taskSignal); 200 | 201 | const task: ForkTask = { 202 | abort() { 203 | taskAbortController.abort(); 204 | }, 205 | join: () => taskPromise, 206 | }; 207 | 208 | tasks.add(task); 209 | 210 | taskPromise 211 | .catch(catchAbortError) 212 | .catch(error => { 213 | failure = {error}; 214 | 215 | // error in forked function 216 | spawnAbortController.abort(); 217 | }) 218 | .finally(() => { 219 | tasks.delete(task); 220 | 221 | if (tasks.size === 0) { 222 | if (failure != null) { 223 | reject(failure.error); 224 | } else { 225 | resolve(result!.value); 226 | } 227 | } 228 | }); 229 | 230 | return task; 231 | } 232 | }); 233 | 234 | promise = promise.finally(() => { 235 | removeAbortListener(); 236 | removeSpawnAbortListener(); 237 | 238 | let deferPromise = Promise.resolve(); 239 | 240 | for (let i = deferredFunctions.length - 1; i >= 0; i--) { 241 | deferPromise = deferPromise.finally(deferredFunctions[i]); 242 | } 243 | 244 | return deferPromise; 245 | }); 246 | 247 | return promise; 248 | } 249 | -------------------------------------------------------------------------------- /src/utils/nextTick.ts: -------------------------------------------------------------------------------- 1 | export function nextTick() { 2 | return new Promise(resolve => setTimeout(resolve, 0)); 3 | } 4 | -------------------------------------------------------------------------------- /src/waitForEvent.test.ts: -------------------------------------------------------------------------------- 1 | import {nextTick} from './utils/nextTick'; 2 | import {waitForEvent} from './waitForEvent'; 3 | 4 | test('external abort', async () => { 5 | const abortController = new AbortController(); 6 | const signal = abortController.signal; 7 | signal.addEventListener = jest.fn(signal.addEventListener); 8 | signal.removeEventListener = jest.fn(signal.removeEventListener); 9 | 10 | const eventTarget = new AbortController().signal; 11 | eventTarget.removeEventListener = jest.fn(eventTarget.removeEventListener); 12 | 13 | let result: PromiseSettledResult | undefined; 14 | 15 | waitForEvent(signal, eventTarget, 'test').then( 16 | value => { 17 | result = {status: 'fulfilled', value}; 18 | }, 19 | reason => { 20 | result = {status: 'rejected', reason}; 21 | }, 22 | ); 23 | 24 | expect(eventTarget.removeEventListener).toHaveBeenCalledTimes(0); 25 | 26 | abortController.abort(); 27 | 28 | await nextTick(); 29 | 30 | expect(result).toMatchObject({ 31 | status: 'rejected', 32 | reason: {name: 'AbortError'}, 33 | }); 34 | expect(eventTarget.removeEventListener).toHaveBeenCalledTimes(1); 35 | 36 | expect(signal.addEventListener).toHaveBeenCalledTimes(1); 37 | expect(signal.removeEventListener).toHaveBeenCalledTimes(1); 38 | }); 39 | 40 | test('fulfill', async () => { 41 | const abortController = new AbortController(); 42 | const signal = abortController.signal; 43 | signal.addEventListener = jest.fn(signal.addEventListener); 44 | signal.removeEventListener = jest.fn(signal.removeEventListener); 45 | 46 | const eventTargetController = new AbortController(); 47 | const eventTarget = eventTargetController.signal; 48 | eventTarget.removeEventListener = jest.fn(eventTarget.removeEventListener); 49 | 50 | let result: PromiseSettledResult | undefined; 51 | 52 | waitForEvent(signal, eventTarget, 'abort').then( 53 | value => { 54 | result = {status: 'fulfilled', value}; 55 | }, 56 | reason => { 57 | result = {status: 'rejected', reason}; 58 | }, 59 | ); 60 | 61 | eventTargetController.abort(); 62 | 63 | await nextTick(); 64 | 65 | expect(result).toMatchObject({ 66 | status: 'fulfilled', 67 | value: {type: 'abort'}, 68 | }); 69 | expect(eventTarget.removeEventListener).toHaveBeenCalledTimes(1); 70 | 71 | expect(signal.addEventListener).toHaveBeenCalledTimes(1); 72 | expect(signal.removeEventListener).toHaveBeenCalledTimes(1); 73 | }); 74 | -------------------------------------------------------------------------------- /src/waitForEvent.ts: -------------------------------------------------------------------------------- 1 | import { execute } from './execute'; 2 | 3 | export type EventTargetLike = 4 | | EventTargetLike.HasEventTargetAddRemove 5 | | EventTargetLike.NodeStyleEventEmitter 6 | | EventTargetLike.NodeCompatibleEventEmitter 7 | | EventTargetLike.JQueryStyleEventEmitter; 8 | 9 | /** 10 | * Returns a promise that fulfills when an event of specific type is emitted 11 | * from given event target and rejects with `AbortError` once `signal` is 12 | * aborted. 13 | * 14 | * Example: 15 | * 16 | * // Create a WebSocket and wait for connection 17 | * const webSocket = new WebSocket(url); 18 | * 19 | * const openEvent = await race(signal, signal => [ 20 | * waitForEvent(signal, webSocket, 'open'), 21 | * waitForEvent(signal, webSocket, 'close').then( 22 | * event => { 23 | * throw new Error(`Failed to connect to ${url}: ${event.reason}`); 24 | * }, 25 | * ), 26 | * ]); 27 | */ 28 | export function waitForEvent( 29 | signal: AbortSignal, 30 | target: EventTargetLike, 31 | eventName: string | symbol, 32 | options?: EventTargetLike.EventListenerOptions, 33 | ): Promise { 34 | return execute(signal, resolve => { 35 | let unlisten: (() => void) | undefined; 36 | let finished = false; 37 | 38 | const handler = (...args: any[]) => { 39 | resolve(args.length > 1 ? args : args[0]); 40 | finished = true; 41 | 42 | if (unlisten != null) { 43 | unlisten(); 44 | } 45 | }; 46 | 47 | unlisten = listen(target, eventName, handler, options); 48 | 49 | if (finished) { 50 | unlisten(); 51 | } 52 | 53 | return () => { 54 | finished = true; 55 | 56 | if (unlisten != null) { 57 | unlisten(); 58 | } 59 | }; 60 | }); 61 | } 62 | 63 | // gratefully copied from RxJS' fromEvent 64 | export namespace EventTargetLike { 65 | export interface NodeStyleEventEmitter { 66 | addListener: ( 67 | eventName: string | symbol, 68 | handler: NodeEventHandler, 69 | ) => this; 70 | removeListener: ( 71 | eventName: string | symbol, 72 | handler: NodeEventHandler, 73 | ) => this; 74 | } 75 | 76 | export type NodeEventHandler = (...args: any[]) => void; 77 | 78 | // For APIs that implement `addListener` and `removeListener` methods that may 79 | // not use the same arguments or return EventEmitter values 80 | // such as React Native 81 | export interface NodeCompatibleEventEmitter { 82 | addListener: (eventName: string, handler: NodeEventHandler) => void | {}; 83 | removeListener: (eventName: string, handler: NodeEventHandler) => void | {}; 84 | } 85 | 86 | // Use handler types like those in @types/jquery. See: 87 | // https://github.com/DefinitelyTyped/DefinitelyTyped/blob/847731ba1d7fa6db6b911c0e43aa0afe596e7723/types/jquery/misc.d.ts#L6395 88 | export interface JQueryStyleEventEmitter { 89 | on: ( 90 | eventName: string | symbol, 91 | handler: (this: TContext, t: T, ...args: any[]) => any, 92 | ) => void; 93 | off: ( 94 | eventName: string | symbol, 95 | handler: (this: TContext, t: T, ...args: any[]) => any, 96 | ) => void; 97 | } 98 | 99 | export interface HasEventTargetAddRemove { 100 | addEventListener( 101 | type: string | symbol, 102 | listener: ((evt: E) => void) | null, 103 | options?: boolean | AddEventListenerOptions, 104 | ): void; 105 | removeEventListener( 106 | type: string | symbol, 107 | listener: ((evt: E) => void) | null, 108 | options?: EventListenerOptions | boolean, 109 | ): void; 110 | } 111 | 112 | export interface EventListenerOptions { 113 | capture?: boolean; 114 | passive?: boolean; 115 | once?: boolean; 116 | } 117 | 118 | export interface AddEventListenerOptions extends EventListenerOptions { 119 | once?: boolean; 120 | passive?: boolean; 121 | } 122 | } 123 | 124 | function listen( 125 | target: EventTargetLike, 126 | eventName: string | symbol, 127 | handler: (...args: any[]) => void, 128 | options?: EventTargetLike.EventListenerOptions, 129 | ) { 130 | if (isEventTarget(target)) { 131 | target.addEventListener(eventName, handler, options); 132 | return () => target.removeEventListener(eventName, handler, options); 133 | } 134 | 135 | if (isJQueryStyleEventEmitter(target)) { 136 | target.on(eventName, handler); 137 | return () => target.off(eventName, handler); 138 | } 139 | 140 | if (isNodeStyleEventEmitter(target)) { 141 | target.addListener(eventName, handler); 142 | return () => target.removeListener(eventName, handler); 143 | } 144 | 145 | throw new Error('Invalid event target'); 146 | } 147 | 148 | function isNodeStyleEventEmitter( 149 | sourceObj: any, 150 | ): sourceObj is EventTargetLike.NodeStyleEventEmitter { 151 | return ( 152 | isFunction(sourceObj.addListener) && isFunction(sourceObj.removeListener) 153 | ); 154 | } 155 | 156 | function isJQueryStyleEventEmitter( 157 | sourceObj: any, 158 | ): sourceObj is EventTargetLike.JQueryStyleEventEmitter { 159 | return isFunction(sourceObj.on) && isFunction(sourceObj.off); 160 | } 161 | 162 | function isEventTarget( 163 | sourceObj: any, 164 | ): sourceObj is EventTargetLike.HasEventTargetAddRemove { 165 | return ( 166 | isFunction(sourceObj.addEventListener) && 167 | isFunction(sourceObj.removeEventListener) 168 | ); 169 | } 170 | 171 | const isFunction = (obj: any) => typeof obj === 'function'; 172 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig", 3 | "exclude": [ 4 | "src/**/*.test.ts" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /tsconfig.es.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig", 3 | "compilerOptions": { 4 | "module": "es6", 5 | "outDir": "es" 6 | }, 7 | "exclude": [ 8 | "src/**/*.test.ts" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "lib": ["es2020", "dom"], 5 | "module": "commonjs", 6 | "moduleResolution": "node", 7 | "outDir": "lib", 8 | "sourceMap": true, 9 | "declaration": true, 10 | "strict": true, 11 | "noImplicitReturns": true, 12 | "noUnusedLocals": true, 13 | "esModuleInterop": true 14 | }, 15 | "files": ["src/index.ts"], 16 | "include": ["src/**/*.test.ts"] 17 | } 18 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@ampproject/remapping@^2.1.0": 6 | version "2.2.0" 7 | resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" 8 | integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== 9 | dependencies: 10 | "@jridgewell/gen-mapping" "^0.1.0" 11 | "@jridgewell/trace-mapping" "^0.3.9" 12 | 13 | "@babel/code-frame@^7.0.0": 14 | version "7.0.0" 15 | resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0.tgz#06e2ab19bdb535385559aabb5ba59729482800f8" 16 | integrity sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA== 17 | dependencies: 18 | "@babel/highlight" "^7.0.0" 19 | 20 | "@babel/code-frame@^7.10.4": 21 | version "7.10.4" 22 | resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" 23 | integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== 24 | dependencies: 25 | "@babel/highlight" "^7.10.4" 26 | 27 | "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6": 28 | version "7.18.6" 29 | resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" 30 | integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== 31 | dependencies: 32 | "@babel/highlight" "^7.18.6" 33 | 34 | "@babel/compat-data@^7.18.8": 35 | version "7.18.8" 36 | resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.18.8.tgz#2483f565faca607b8535590e84e7de323f27764d" 37 | integrity sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ== 38 | 39 | "@babel/core@^7.11.6", "@babel/core@^7.12.3": 40 | version "7.18.9" 41 | resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.9.tgz#805461f967c77ff46c74ca0460ccf4fe933ddd59" 42 | integrity sha512-1LIb1eL8APMy91/IMW+31ckrfBM4yCoLaVzoDhZUKSM4cu1L1nIidyxkCgzPAgrC5WEz36IPEr/eSeSF9pIn+g== 43 | dependencies: 44 | "@ampproject/remapping" "^2.1.0" 45 | "@babel/code-frame" "^7.18.6" 46 | "@babel/generator" "^7.18.9" 47 | "@babel/helper-compilation-targets" "^7.18.9" 48 | "@babel/helper-module-transforms" "^7.18.9" 49 | "@babel/helpers" "^7.18.9" 50 | "@babel/parser" "^7.18.9" 51 | "@babel/template" "^7.18.6" 52 | "@babel/traverse" "^7.18.9" 53 | "@babel/types" "^7.18.9" 54 | convert-source-map "^1.7.0" 55 | debug "^4.1.0" 56 | gensync "^1.0.0-beta.2" 57 | json5 "^2.2.1" 58 | semver "^6.3.0" 59 | 60 | "@babel/generator@^7.18.9", "@babel/generator@^7.7.2": 61 | version "7.18.9" 62 | resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.9.tgz#68337e9ea8044d6ddc690fb29acae39359cca0a5" 63 | integrity sha512-wt5Naw6lJrL1/SGkipMiFxJjtyczUWTP38deiP1PO60HsBjDeKk08CGC3S8iVuvf0FmTdgKwU1KIXzSKL1G0Ug== 64 | dependencies: 65 | "@babel/types" "^7.18.9" 66 | "@jridgewell/gen-mapping" "^0.3.2" 67 | jsesc "^2.5.1" 68 | 69 | "@babel/helper-compilation-targets@^7.18.9": 70 | version "7.18.9" 71 | resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz#69e64f57b524cde3e5ff6cc5a9f4a387ee5563bf" 72 | integrity sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg== 73 | dependencies: 74 | "@babel/compat-data" "^7.18.8" 75 | "@babel/helper-validator-option" "^7.18.6" 76 | browserslist "^4.20.2" 77 | semver "^6.3.0" 78 | 79 | "@babel/helper-environment-visitor@^7.18.9": 80 | version "7.18.9" 81 | resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" 82 | integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== 83 | 84 | "@babel/helper-function-name@^7.18.9": 85 | version "7.18.9" 86 | resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz#940e6084a55dee867d33b4e487da2676365e86b0" 87 | integrity sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A== 88 | dependencies: 89 | "@babel/template" "^7.18.6" 90 | "@babel/types" "^7.18.9" 91 | 92 | "@babel/helper-hoist-variables@^7.18.6": 93 | version "7.18.6" 94 | resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" 95 | integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== 96 | dependencies: 97 | "@babel/types" "^7.18.6" 98 | 99 | "@babel/helper-module-imports@^7.18.6": 100 | version "7.18.6" 101 | resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" 102 | integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA== 103 | dependencies: 104 | "@babel/types" "^7.18.6" 105 | 106 | "@babel/helper-module-transforms@^7.18.9": 107 | version "7.18.9" 108 | resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz#5a1079c005135ed627442df31a42887e80fcb712" 109 | integrity sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g== 110 | dependencies: 111 | "@babel/helper-environment-visitor" "^7.18.9" 112 | "@babel/helper-module-imports" "^7.18.6" 113 | "@babel/helper-simple-access" "^7.18.6" 114 | "@babel/helper-split-export-declaration" "^7.18.6" 115 | "@babel/helper-validator-identifier" "^7.18.6" 116 | "@babel/template" "^7.18.6" 117 | "@babel/traverse" "^7.18.9" 118 | "@babel/types" "^7.18.9" 119 | 120 | "@babel/helper-plugin-utils@^7.0.0": 121 | version "7.0.0" 122 | resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz#bbb3fbee98661c569034237cc03967ba99b4f250" 123 | integrity sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA== 124 | 125 | "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.8.0": 126 | version "7.10.4" 127 | resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz#2f75a831269d4f677de49986dff59927533cf375" 128 | integrity sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg== 129 | 130 | "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6": 131 | version "7.18.9" 132 | resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz#4b8aea3b069d8cb8a72cdfe28ddf5ceca695ef2f" 133 | integrity sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w== 134 | 135 | "@babel/helper-simple-access@^7.18.6": 136 | version "7.18.6" 137 | resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz#d6d8f51f4ac2978068df934b569f08f29788c7ea" 138 | integrity sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g== 139 | dependencies: 140 | "@babel/types" "^7.18.6" 141 | 142 | "@babel/helper-split-export-declaration@^7.18.6": 143 | version "7.18.6" 144 | resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" 145 | integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== 146 | dependencies: 147 | "@babel/types" "^7.18.6" 148 | 149 | "@babel/helper-validator-identifier@^7.10.4": 150 | version "7.10.4" 151 | resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" 152 | integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== 153 | 154 | "@babel/helper-validator-identifier@^7.18.6": 155 | version "7.18.6" 156 | resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076" 157 | integrity sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g== 158 | 159 | "@babel/helper-validator-option@^7.18.6": 160 | version "7.18.6" 161 | resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" 162 | integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== 163 | 164 | "@babel/helpers@^7.18.9": 165 | version "7.18.9" 166 | resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.18.9.tgz#4bef3b893f253a1eced04516824ede94dcfe7ff9" 167 | integrity sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ== 168 | dependencies: 169 | "@babel/template" "^7.18.6" 170 | "@babel/traverse" "^7.18.9" 171 | "@babel/types" "^7.18.9" 172 | 173 | "@babel/highlight@^7.0.0": 174 | version "7.0.0" 175 | resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0.tgz#f710c38c8d458e6dd9a201afb637fcb781ce99e4" 176 | integrity sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw== 177 | dependencies: 178 | chalk "^2.0.0" 179 | esutils "^2.0.2" 180 | js-tokens "^4.0.0" 181 | 182 | "@babel/highlight@^7.10.4": 183 | version "7.10.4" 184 | resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143" 185 | integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA== 186 | dependencies: 187 | "@babel/helper-validator-identifier" "^7.10.4" 188 | chalk "^2.0.0" 189 | js-tokens "^4.0.0" 190 | 191 | "@babel/highlight@^7.18.6": 192 | version "7.18.6" 193 | resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" 194 | integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== 195 | dependencies: 196 | "@babel/helper-validator-identifier" "^7.18.6" 197 | chalk "^2.0.0" 198 | js-tokens "^4.0.0" 199 | 200 | "@babel/parser@^7.1.0": 201 | version "7.4.5" 202 | resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.4.5.tgz#04af8d5d5a2b044a2a1bffacc1e5e6673544e872" 203 | integrity sha512-9mUqkL1FF5T7f0WDFfAoDdiMVPWsdD1gZYzSnaXsxUCUqzuch/8of9G3VUSNiZmMBoRxT3neyVsqeiL/ZPcjew== 204 | 205 | "@babel/parser@^7.10.4": 206 | version "7.11.5" 207 | resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.5.tgz#c7ff6303df71080ec7a4f5b8c003c58f1cf51037" 208 | integrity sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q== 209 | 210 | "@babel/parser@^7.14.7", "@babel/parser@^7.18.6", "@babel/parser@^7.18.9": 211 | version "7.18.9" 212 | resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.9.tgz#f2dde0c682ccc264a9a8595efd030a5cc8fd2539" 213 | integrity sha512-9uJveS9eY9DJ0t64YbIBZICtJy8a5QrDEVdiLCG97fVLpDTpGX7t8mMSb6OWw6Lrnjqj4O8zwjELX3dhoMgiBg== 214 | 215 | "@babel/plugin-syntax-async-generators@^7.8.4": 216 | version "7.8.4" 217 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" 218 | integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== 219 | dependencies: 220 | "@babel/helper-plugin-utils" "^7.8.0" 221 | 222 | "@babel/plugin-syntax-bigint@^7.8.3": 223 | version "7.8.3" 224 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" 225 | integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== 226 | dependencies: 227 | "@babel/helper-plugin-utils" "^7.8.0" 228 | 229 | "@babel/plugin-syntax-class-properties@^7.8.3": 230 | version "7.10.4" 231 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.4.tgz#6644e6a0baa55a61f9e3231f6c9eeb6ee46c124c" 232 | integrity sha512-GCSBF7iUle6rNugfURwNmCGG3Z/2+opxAMLs1nND4bhEG5PuxTIggDBoeYYSujAlLtsupzOHYJQgPS3pivwXIA== 233 | dependencies: 234 | "@babel/helper-plugin-utils" "^7.10.4" 235 | 236 | "@babel/plugin-syntax-import-meta@^7.8.3": 237 | version "7.10.4" 238 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" 239 | integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== 240 | dependencies: 241 | "@babel/helper-plugin-utils" "^7.10.4" 242 | 243 | "@babel/plugin-syntax-json-strings@^7.8.3": 244 | version "7.8.3" 245 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" 246 | integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== 247 | dependencies: 248 | "@babel/helper-plugin-utils" "^7.8.0" 249 | 250 | "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": 251 | version "7.10.4" 252 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" 253 | integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== 254 | dependencies: 255 | "@babel/helper-plugin-utils" "^7.10.4" 256 | 257 | "@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": 258 | version "7.8.3" 259 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" 260 | integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== 261 | dependencies: 262 | "@babel/helper-plugin-utils" "^7.8.0" 263 | 264 | "@babel/plugin-syntax-numeric-separator@^7.8.3": 265 | version "7.10.4" 266 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" 267 | integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== 268 | dependencies: 269 | "@babel/helper-plugin-utils" "^7.10.4" 270 | 271 | "@babel/plugin-syntax-object-rest-spread@^7.8.3": 272 | version "7.8.3" 273 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" 274 | integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== 275 | dependencies: 276 | "@babel/helper-plugin-utils" "^7.8.0" 277 | 278 | "@babel/plugin-syntax-optional-catch-binding@^7.8.3": 279 | version "7.8.3" 280 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" 281 | integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== 282 | dependencies: 283 | "@babel/helper-plugin-utils" "^7.8.0" 284 | 285 | "@babel/plugin-syntax-optional-chaining@^7.8.3": 286 | version "7.8.3" 287 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" 288 | integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== 289 | dependencies: 290 | "@babel/helper-plugin-utils" "^7.8.0" 291 | 292 | "@babel/plugin-syntax-top-level-await@^7.8.3": 293 | version "7.14.5" 294 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" 295 | integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== 296 | dependencies: 297 | "@babel/helper-plugin-utils" "^7.14.5" 298 | 299 | "@babel/plugin-syntax-typescript@^7.7.2": 300 | version "7.18.6" 301 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz#1c09cd25795c7c2b8a4ba9ae49394576d4133285" 302 | integrity sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA== 303 | dependencies: 304 | "@babel/helper-plugin-utils" "^7.18.6" 305 | 306 | "@babel/template@^7.18.6": 307 | version "7.18.6" 308 | resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.6.tgz#1283f4993e00b929d6e2d3c72fdc9168a2977a31" 309 | integrity sha512-JoDWzPe+wgBsTTgdnIma3iHNFC7YVJoPssVBDjiHfNlyt4YcunDtcDOUmfVDfCK5MfdsaIoX9PkijPhjH3nYUw== 310 | dependencies: 311 | "@babel/code-frame" "^7.18.6" 312 | "@babel/parser" "^7.18.6" 313 | "@babel/types" "^7.18.6" 314 | 315 | "@babel/template@^7.3.3": 316 | version "7.10.4" 317 | resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278" 318 | integrity sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA== 319 | dependencies: 320 | "@babel/code-frame" "^7.10.4" 321 | "@babel/parser" "^7.10.4" 322 | "@babel/types" "^7.10.4" 323 | 324 | "@babel/traverse@^7.18.9", "@babel/traverse@^7.7.2": 325 | version "7.18.9" 326 | resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.9.tgz#deeff3e8f1bad9786874cb2feda7a2d77a904f98" 327 | integrity sha512-LcPAnujXGwBgv3/WHv01pHtb2tihcyW1XuL9wd7jqh1Z8AQkTd+QVjMrMijrln0T7ED3UXLIy36P9Ao7W75rYg== 328 | dependencies: 329 | "@babel/code-frame" "^7.18.6" 330 | "@babel/generator" "^7.18.9" 331 | "@babel/helper-environment-visitor" "^7.18.9" 332 | "@babel/helper-function-name" "^7.18.9" 333 | "@babel/helper-hoist-variables" "^7.18.6" 334 | "@babel/helper-split-export-declaration" "^7.18.6" 335 | "@babel/parser" "^7.18.9" 336 | "@babel/types" "^7.18.9" 337 | debug "^4.1.0" 338 | globals "^11.1.0" 339 | 340 | "@babel/types@^7.0.0", "@babel/types@^7.3.0": 341 | version "7.4.4" 342 | resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.4.4.tgz#8db9e9a629bb7c29370009b4b779ed93fe57d5f0" 343 | integrity sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ== 344 | dependencies: 345 | esutils "^2.0.2" 346 | lodash "^4.17.11" 347 | to-fast-properties "^2.0.0" 348 | 349 | "@babel/types@^7.10.4", "@babel/types@^7.3.3": 350 | version "7.11.5" 351 | resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.11.5.tgz#d9de577d01252d77c6800cee039ee64faf75662d" 352 | integrity sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q== 353 | dependencies: 354 | "@babel/helper-validator-identifier" "^7.10.4" 355 | lodash "^4.17.19" 356 | to-fast-properties "^2.0.0" 357 | 358 | "@babel/types@^7.18.6", "@babel/types@^7.18.9": 359 | version "7.18.9" 360 | resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.9.tgz#7148d64ba133d8d73a41b3172ac4b83a1452205f" 361 | integrity sha512-WwMLAg2MvJmt/rKEVQBBhIVffMmnilX4oe0sRe7iPOHIGsqpruFHHdrfj4O1CMMtgMtCU4oPafZjDPCRgO57Wg== 362 | dependencies: 363 | "@babel/helper-validator-identifier" "^7.18.6" 364 | to-fast-properties "^2.0.0" 365 | 366 | "@bcoe/v8-coverage@^0.2.3": 367 | version "0.2.3" 368 | resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" 369 | integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== 370 | 371 | "@istanbuljs/load-nyc-config@^1.0.0": 372 | version "1.1.0" 373 | resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" 374 | integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== 375 | dependencies: 376 | camelcase "^5.3.1" 377 | find-up "^4.1.0" 378 | get-package-type "^0.1.0" 379 | js-yaml "^3.13.1" 380 | resolve-from "^5.0.0" 381 | 382 | "@istanbuljs/schema@^0.1.2": 383 | version "0.1.2" 384 | resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd" 385 | integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw== 386 | 387 | "@jest/console@^28.1.3": 388 | version "28.1.3" 389 | resolved "https://registry.yarnpkg.com/@jest/console/-/console-28.1.3.tgz#2030606ec03a18c31803b8a36382762e447655df" 390 | integrity sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw== 391 | dependencies: 392 | "@jest/types" "^28.1.3" 393 | "@types/node" "*" 394 | chalk "^4.0.0" 395 | jest-message-util "^28.1.3" 396 | jest-util "^28.1.3" 397 | slash "^3.0.0" 398 | 399 | "@jest/core@^28.1.3": 400 | version "28.1.3" 401 | resolved "https://registry.yarnpkg.com/@jest/core/-/core-28.1.3.tgz#0ebf2bd39840f1233cd5f2d1e6fc8b71bd5a1ac7" 402 | integrity sha512-CIKBrlaKOzA7YG19BEqCw3SLIsEwjZkeJzf5bdooVnW4bH5cktqe3JX+G2YV1aK5vP8N9na1IGWFzYaTp6k6NA== 403 | dependencies: 404 | "@jest/console" "^28.1.3" 405 | "@jest/reporters" "^28.1.3" 406 | "@jest/test-result" "^28.1.3" 407 | "@jest/transform" "^28.1.3" 408 | "@jest/types" "^28.1.3" 409 | "@types/node" "*" 410 | ansi-escapes "^4.2.1" 411 | chalk "^4.0.0" 412 | ci-info "^3.2.0" 413 | exit "^0.1.2" 414 | graceful-fs "^4.2.9" 415 | jest-changed-files "^28.1.3" 416 | jest-config "^28.1.3" 417 | jest-haste-map "^28.1.3" 418 | jest-message-util "^28.1.3" 419 | jest-regex-util "^28.0.2" 420 | jest-resolve "^28.1.3" 421 | jest-resolve-dependencies "^28.1.3" 422 | jest-runner "^28.1.3" 423 | jest-runtime "^28.1.3" 424 | jest-snapshot "^28.1.3" 425 | jest-util "^28.1.3" 426 | jest-validate "^28.1.3" 427 | jest-watcher "^28.1.3" 428 | micromatch "^4.0.4" 429 | pretty-format "^28.1.3" 430 | rimraf "^3.0.0" 431 | slash "^3.0.0" 432 | strip-ansi "^6.0.0" 433 | 434 | "@jest/environment@^28.1.3": 435 | version "28.1.3" 436 | resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-28.1.3.tgz#abed43a6b040a4c24fdcb69eab1f97589b2d663e" 437 | integrity sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA== 438 | dependencies: 439 | "@jest/fake-timers" "^28.1.3" 440 | "@jest/types" "^28.1.3" 441 | "@types/node" "*" 442 | jest-mock "^28.1.3" 443 | 444 | "@jest/expect-utils@^28.1.3": 445 | version "28.1.3" 446 | resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-28.1.3.tgz#58561ce5db7cd253a7edddbc051fb39dda50f525" 447 | integrity sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA== 448 | dependencies: 449 | jest-get-type "^28.0.2" 450 | 451 | "@jest/expect@^28.1.3": 452 | version "28.1.3" 453 | resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-28.1.3.tgz#9ac57e1d4491baca550f6bdbd232487177ad6a72" 454 | integrity sha512-lzc8CpUbSoE4dqT0U+g1qODQjBRHPpCPXissXD4mS9+sWQdmmpeJ9zSH1rS1HEkrsMN0fb7nKrJ9giAR1d3wBw== 455 | dependencies: 456 | expect "^28.1.3" 457 | jest-snapshot "^28.1.3" 458 | 459 | "@jest/fake-timers@^28.1.3": 460 | version "28.1.3" 461 | resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-28.1.3.tgz#230255b3ad0a3d4978f1d06f70685baea91c640e" 462 | integrity sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw== 463 | dependencies: 464 | "@jest/types" "^28.1.3" 465 | "@sinonjs/fake-timers" "^9.1.2" 466 | "@types/node" "*" 467 | jest-message-util "^28.1.3" 468 | jest-mock "^28.1.3" 469 | jest-util "^28.1.3" 470 | 471 | "@jest/globals@^28.1.3": 472 | version "28.1.3" 473 | resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-28.1.3.tgz#a601d78ddc5fdef542728309894895b4a42dc333" 474 | integrity sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA== 475 | dependencies: 476 | "@jest/environment" "^28.1.3" 477 | "@jest/expect" "^28.1.3" 478 | "@jest/types" "^28.1.3" 479 | 480 | "@jest/reporters@^28.1.3": 481 | version "28.1.3" 482 | resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-28.1.3.tgz#9adf6d265edafc5fc4a434cfb31e2df5a67a369a" 483 | integrity sha512-JuAy7wkxQZVNU/V6g9xKzCGC5LVXx9FDcABKsSXp5MiKPEE2144a/vXTEDoyzjUpZKfVwp08Wqg5A4WfTMAzjg== 484 | dependencies: 485 | "@bcoe/v8-coverage" "^0.2.3" 486 | "@jest/console" "^28.1.3" 487 | "@jest/test-result" "^28.1.3" 488 | "@jest/transform" "^28.1.3" 489 | "@jest/types" "^28.1.3" 490 | "@jridgewell/trace-mapping" "^0.3.13" 491 | "@types/node" "*" 492 | chalk "^4.0.0" 493 | collect-v8-coverage "^1.0.0" 494 | exit "^0.1.2" 495 | glob "^7.1.3" 496 | graceful-fs "^4.2.9" 497 | istanbul-lib-coverage "^3.0.0" 498 | istanbul-lib-instrument "^5.1.0" 499 | istanbul-lib-report "^3.0.0" 500 | istanbul-lib-source-maps "^4.0.0" 501 | istanbul-reports "^3.1.3" 502 | jest-message-util "^28.1.3" 503 | jest-util "^28.1.3" 504 | jest-worker "^28.1.3" 505 | slash "^3.0.0" 506 | string-length "^4.0.1" 507 | strip-ansi "^6.0.0" 508 | terminal-link "^2.0.0" 509 | v8-to-istanbul "^9.0.1" 510 | 511 | "@jest/schemas@^28.1.3": 512 | version "28.1.3" 513 | resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-28.1.3.tgz#ad8b86a66f11f33619e3d7e1dcddd7f2d40ff905" 514 | integrity sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg== 515 | dependencies: 516 | "@sinclair/typebox" "^0.24.1" 517 | 518 | "@jest/source-map@^28.1.2": 519 | version "28.1.2" 520 | resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-28.1.2.tgz#7fe832b172b497d6663cdff6c13b0a920e139e24" 521 | integrity sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww== 522 | dependencies: 523 | "@jridgewell/trace-mapping" "^0.3.13" 524 | callsites "^3.0.0" 525 | graceful-fs "^4.2.9" 526 | 527 | "@jest/test-result@^28.1.3": 528 | version "28.1.3" 529 | resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-28.1.3.tgz#5eae945fd9f4b8fcfce74d239e6f725b6bf076c5" 530 | integrity sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg== 531 | dependencies: 532 | "@jest/console" "^28.1.3" 533 | "@jest/types" "^28.1.3" 534 | "@types/istanbul-lib-coverage" "^2.0.0" 535 | collect-v8-coverage "^1.0.0" 536 | 537 | "@jest/test-sequencer@^28.1.3": 538 | version "28.1.3" 539 | resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-28.1.3.tgz#9d0c283d906ac599c74bde464bc0d7e6a82886c3" 540 | integrity sha512-NIMPEqqa59MWnDi1kvXXpYbqsfQmSJsIbnd85mdVGkiDfQ9WQQTXOLsvISUfonmnBT+w85WEgneCigEEdHDFxw== 541 | dependencies: 542 | "@jest/test-result" "^28.1.3" 543 | graceful-fs "^4.2.9" 544 | jest-haste-map "^28.1.3" 545 | slash "^3.0.0" 546 | 547 | "@jest/transform@^28.1.3": 548 | version "28.1.3" 549 | resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-28.1.3.tgz#59d8098e50ab07950e0f2fc0fc7ec462371281b0" 550 | integrity sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA== 551 | dependencies: 552 | "@babel/core" "^7.11.6" 553 | "@jest/types" "^28.1.3" 554 | "@jridgewell/trace-mapping" "^0.3.13" 555 | babel-plugin-istanbul "^6.1.1" 556 | chalk "^4.0.0" 557 | convert-source-map "^1.4.0" 558 | fast-json-stable-stringify "^2.0.0" 559 | graceful-fs "^4.2.9" 560 | jest-haste-map "^28.1.3" 561 | jest-regex-util "^28.0.2" 562 | jest-util "^28.1.3" 563 | micromatch "^4.0.4" 564 | pirates "^4.0.4" 565 | slash "^3.0.0" 566 | write-file-atomic "^4.0.1" 567 | 568 | "@jest/types@^28.1.3": 569 | version "28.1.3" 570 | resolved "https://registry.yarnpkg.com/@jest/types/-/types-28.1.3.tgz#b05de80996ff12512bc5ceb1d208285a7d11748b" 571 | integrity sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ== 572 | dependencies: 573 | "@jest/schemas" "^28.1.3" 574 | "@types/istanbul-lib-coverage" "^2.0.0" 575 | "@types/istanbul-reports" "^3.0.0" 576 | "@types/node" "*" 577 | "@types/yargs" "^17.0.8" 578 | chalk "^4.0.0" 579 | 580 | "@jridgewell/gen-mapping@^0.1.0": 581 | version "0.1.1" 582 | resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996" 583 | integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== 584 | dependencies: 585 | "@jridgewell/set-array" "^1.0.0" 586 | "@jridgewell/sourcemap-codec" "^1.4.10" 587 | 588 | "@jridgewell/gen-mapping@^0.3.2": 589 | version "0.3.2" 590 | resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" 591 | integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== 592 | dependencies: 593 | "@jridgewell/set-array" "^1.0.1" 594 | "@jridgewell/sourcemap-codec" "^1.4.10" 595 | "@jridgewell/trace-mapping" "^0.3.9" 596 | 597 | "@jridgewell/resolve-uri@^3.0.3": 598 | version "3.1.0" 599 | resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" 600 | integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== 601 | 602 | "@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": 603 | version "1.1.2" 604 | resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" 605 | integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== 606 | 607 | "@jridgewell/sourcemap-codec@^1.4.10": 608 | version "1.4.14" 609 | resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" 610 | integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== 611 | 612 | "@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.13", "@jridgewell/trace-mapping@^0.3.9": 613 | version "0.3.14" 614 | resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz#b231a081d8f66796e475ad588a1ef473112701ed" 615 | integrity sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ== 616 | dependencies: 617 | "@jridgewell/resolve-uri" "^3.0.3" 618 | "@jridgewell/sourcemap-codec" "^1.4.10" 619 | 620 | "@sinclair/typebox@^0.24.1": 621 | version "0.24.20" 622 | resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.20.tgz#11a657875de6008622d53f56e063a6347c51a6dd" 623 | integrity sha512-kVaO5aEFZb33nPMTZBxiPEkY+slxiPtqC7QX8f9B3eGOMBvEfuMfxp9DSTTCsRJPumPKjrge4yagyssO4q6qzQ== 624 | 625 | "@sinonjs/commons@^1.7.0": 626 | version "1.8.1" 627 | resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.1.tgz#e7df00f98a203324f6dc7cc606cad9d4a8ab2217" 628 | integrity sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw== 629 | dependencies: 630 | type-detect "4.0.8" 631 | 632 | "@sinonjs/fake-timers@^9.1.2": 633 | version "9.1.2" 634 | resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz#4eaab737fab77332ab132d396a3c0d364bd0ea8c" 635 | integrity sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw== 636 | dependencies: 637 | "@sinonjs/commons" "^1.7.0" 638 | 639 | "@types/babel__core@^7.1.14": 640 | version "7.1.19" 641 | resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.19.tgz#7b497495b7d1b4812bdb9d02804d0576f43ee460" 642 | integrity sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw== 643 | dependencies: 644 | "@babel/parser" "^7.1.0" 645 | "@babel/types" "^7.0.0" 646 | "@types/babel__generator" "*" 647 | "@types/babel__template" "*" 648 | "@types/babel__traverse" "*" 649 | 650 | "@types/babel__generator@*": 651 | version "7.0.2" 652 | resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.0.2.tgz#d2112a6b21fad600d7674274293c85dce0cb47fc" 653 | integrity sha512-NHcOfab3Zw4q5sEE2COkpfXjoE7o+PmqD9DQW4koUT3roNxwziUdXGnRndMat/LJNUtePwn1TlP4do3uoe3KZQ== 654 | dependencies: 655 | "@babel/types" "^7.0.0" 656 | 657 | "@types/babel__template@*": 658 | version "7.0.2" 659 | resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.0.2.tgz#4ff63d6b52eddac1de7b975a5223ed32ecea9307" 660 | integrity sha512-/K6zCpeW7Imzgab2bLkLEbz0+1JlFSrUMdw7KoIIu+IUdu51GWaBZpd3y1VXGVXzynvGa4DaIaxNZHiON3GXUg== 661 | dependencies: 662 | "@babel/parser" "^7.1.0" 663 | "@babel/types" "^7.0.0" 664 | 665 | "@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": 666 | version "7.0.7" 667 | resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.0.7.tgz#2496e9ff56196cc1429c72034e07eab6121b6f3f" 668 | integrity sha512-CeBpmX1J8kWLcDEnI3Cl2Eo6RfbGvzUctA+CjZUhOKDFbLfcr7fc4usEqLNWetrlJd7RhAkyYe2czXop4fICpw== 669 | dependencies: 670 | "@babel/types" "^7.3.0" 671 | 672 | "@types/defer-promise@^1.0.0": 673 | version "1.0.0" 674 | resolved "https://registry.yarnpkg.com/@types/defer-promise/-/defer-promise-1.0.0.tgz#9c5054b7ef63fd0f0497d17b0ea1a849f9c6323b" 675 | integrity sha512-NJAgn+jlo8V2TD0rj8zQy1nRzNLX73MQARTSQzWxco94lwzTOwRp7FPu7r4/Cnz715DxCWMGf3epjo7wbDBhVA== 676 | 677 | "@types/graceful-fs@^4.1.3": 678 | version "4.1.5" 679 | resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" 680 | integrity sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw== 681 | dependencies: 682 | "@types/node" "*" 683 | 684 | "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": 685 | version "2.0.1" 686 | resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#42995b446db9a48a11a07ec083499a860e9138ff" 687 | integrity sha512-hRJD2ahnnpLgsj6KWMYSrmXkM3rm2Dl1qkx6IOFD5FnuNPXJIG5L0dhgKXCYTRMGzU4n0wImQ/xfmRc4POUFlg== 688 | 689 | "@types/istanbul-lib-coverage@^2.0.1": 690 | version "2.0.3" 691 | resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" 692 | integrity sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw== 693 | 694 | "@types/istanbul-lib-report@*": 695 | version "1.1.1" 696 | resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-1.1.1.tgz#e5471e7fa33c61358dd38426189c037a58433b8c" 697 | integrity sha512-3BUTyMzbZa2DtDI2BkERNC6jJw2Mr2Y0oGI7mRxYNBPxppbtEK1F66u3bKwU2g+wxwWI7PAoRpJnOY1grJqzHg== 698 | dependencies: 699 | "@types/istanbul-lib-coverage" "*" 700 | 701 | "@types/istanbul-reports@^3.0.0": 702 | version "3.0.0" 703 | resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz#508b13aa344fa4976234e75dddcc34925737d821" 704 | integrity sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA== 705 | dependencies: 706 | "@types/istanbul-lib-report" "*" 707 | 708 | "@types/jest@^28.1.6": 709 | version "28.1.6" 710 | resolved "https://registry.yarnpkg.com/@types/jest/-/jest-28.1.6.tgz#d6a9cdd38967d2d746861fb5be6b120e38284dd4" 711 | integrity sha512-0RbGAFMfcBJKOmqRazM8L98uokwuwD5F8rHrv/ZMbrZBwVOWZUyPG6VFNscjYr/vjM3Vu4fRrCPbOs42AfemaQ== 712 | dependencies: 713 | jest-matcher-utils "^28.0.0" 714 | pretty-format "^28.0.0" 715 | 716 | "@types/node@*": 717 | version "14.11.5" 718 | resolved "https://registry.yarnpkg.com/@types/node/-/node-14.11.5.tgz#fecad41c041cae7f2404ad4b2d0742fdb628b305" 719 | integrity sha512-jVFzDV6NTbrLMxm4xDSIW/gKnk8rQLF9wAzLWIOg+5nU6ACrIMndeBdXci0FGtqJbP9tQvm6V39eshc96TO2wQ== 720 | 721 | "@types/node@^14.17.0": 722 | version "14.18.22" 723 | resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.22.tgz#fd2a15dca290fc9ad565b672fde746191cd0c6e6" 724 | integrity sha512-qzaYbXVzin6EPjghf/hTdIbnVW1ErMx8rPzwRNJhlbyJhu2SyqlvjGOY/tbUt6VFyzg56lROcOeSQRInpt63Yw== 725 | 726 | "@types/prettier@^2.1.5": 727 | version "2.6.3" 728 | resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.6.3.tgz#68ada76827b0010d0db071f739314fa429943d0a" 729 | integrity sha512-ymZk3LEC/fsut+/Q5qejp6R9O1rMxz3XaRHDV6kX8MrGAhOSPqVARbDi+EZvInBpw+BnCX3TD240byVkOfQsHg== 730 | 731 | "@types/stack-utils@^2.0.0": 732 | version "2.0.0" 733 | resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.0.tgz#7036640b4e21cc2f259ae826ce843d277dad8cff" 734 | integrity sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw== 735 | 736 | "@types/yargs-parser@*": 737 | version "15.0.0" 738 | resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d" 739 | integrity sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw== 740 | 741 | "@types/yargs@^17.0.8": 742 | version "17.0.10" 743 | resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.10.tgz#591522fce85d8739bca7b8bb90d048e4478d186a" 744 | integrity sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA== 745 | dependencies: 746 | "@types/yargs-parser" "*" 747 | 748 | ansi-escapes@^4.2.1: 749 | version "4.3.1" 750 | resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61" 751 | integrity sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA== 752 | dependencies: 753 | type-fest "^0.11.0" 754 | 755 | ansi-regex@^5.0.0: 756 | version "5.0.0" 757 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" 758 | integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== 759 | 760 | ansi-regex@^5.0.1: 761 | version "5.0.1" 762 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" 763 | integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== 764 | 765 | ansi-styles@^3.2.1: 766 | version "3.2.1" 767 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" 768 | integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== 769 | dependencies: 770 | color-convert "^1.9.0" 771 | 772 | ansi-styles@^4.0.0, ansi-styles@^4.1.0: 773 | version "4.3.0" 774 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" 775 | integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== 776 | dependencies: 777 | color-convert "^2.0.1" 778 | 779 | ansi-styles@^5.0.0: 780 | version "5.2.0" 781 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" 782 | integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== 783 | 784 | anymatch@^3.0.3: 785 | version "3.1.1" 786 | resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" 787 | integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== 788 | dependencies: 789 | normalize-path "^3.0.0" 790 | picomatch "^2.0.4" 791 | 792 | argparse@^1.0.7: 793 | version "1.0.10" 794 | resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" 795 | integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== 796 | dependencies: 797 | sprintf-js "~1.0.2" 798 | 799 | babel-jest@^28.1.3: 800 | version "28.1.3" 801 | resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-28.1.3.tgz#c1187258197c099072156a0a121c11ee1e3917d5" 802 | integrity sha512-epUaPOEWMk3cWX0M/sPvCHHCe9fMFAa/9hXEgKP8nFfNl/jlGkE9ucq9NqkZGXLDduCJYS0UvSlPUwC0S+rH6Q== 803 | dependencies: 804 | "@jest/transform" "^28.1.3" 805 | "@types/babel__core" "^7.1.14" 806 | babel-plugin-istanbul "^6.1.1" 807 | babel-preset-jest "^28.1.3" 808 | chalk "^4.0.0" 809 | graceful-fs "^4.2.9" 810 | slash "^3.0.0" 811 | 812 | babel-plugin-istanbul@^6.1.1: 813 | version "6.1.1" 814 | resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" 815 | integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== 816 | dependencies: 817 | "@babel/helper-plugin-utils" "^7.0.0" 818 | "@istanbuljs/load-nyc-config" "^1.0.0" 819 | "@istanbuljs/schema" "^0.1.2" 820 | istanbul-lib-instrument "^5.0.4" 821 | test-exclude "^6.0.0" 822 | 823 | babel-plugin-jest-hoist@^28.1.3: 824 | version "28.1.3" 825 | resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-28.1.3.tgz#1952c4d0ea50f2d6d794353762278d1d8cca3fbe" 826 | integrity sha512-Ys3tUKAmfnkRUpPdpa98eYrAR0nV+sSFUZZEGuQ2EbFd1y4SOLtD5QDNHAq+bb9a+bbXvYQC4b+ID/THIMcU6Q== 827 | dependencies: 828 | "@babel/template" "^7.3.3" 829 | "@babel/types" "^7.3.3" 830 | "@types/babel__core" "^7.1.14" 831 | "@types/babel__traverse" "^7.0.6" 832 | 833 | babel-preset-current-node-syntax@^1.0.0: 834 | version "1.0.1" 835 | resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" 836 | integrity sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ== 837 | dependencies: 838 | "@babel/plugin-syntax-async-generators" "^7.8.4" 839 | "@babel/plugin-syntax-bigint" "^7.8.3" 840 | "@babel/plugin-syntax-class-properties" "^7.8.3" 841 | "@babel/plugin-syntax-import-meta" "^7.8.3" 842 | "@babel/plugin-syntax-json-strings" "^7.8.3" 843 | "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3" 844 | "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" 845 | "@babel/plugin-syntax-numeric-separator" "^7.8.3" 846 | "@babel/plugin-syntax-object-rest-spread" "^7.8.3" 847 | "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" 848 | "@babel/plugin-syntax-optional-chaining" "^7.8.3" 849 | "@babel/plugin-syntax-top-level-await" "^7.8.3" 850 | 851 | babel-preset-jest@^28.1.3: 852 | version "28.1.3" 853 | resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-28.1.3.tgz#5dfc20b99abed5db994406c2b9ab94c73aaa419d" 854 | integrity sha512-L+fupJvlWAHbQfn74coNX3zf60LXMJsezNvvx8eIh7iOR1luJ1poxYgQk1F8PYtNq/6QODDHCqsSnTFSWC491A== 855 | dependencies: 856 | babel-plugin-jest-hoist "^28.1.3" 857 | babel-preset-current-node-syntax "^1.0.0" 858 | 859 | balanced-match@^1.0.0: 860 | version "1.0.0" 861 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" 862 | integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= 863 | 864 | brace-expansion@^1.1.7: 865 | version "1.1.11" 866 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 867 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== 868 | dependencies: 869 | balanced-match "^1.0.0" 870 | concat-map "0.0.1" 871 | 872 | braces@^3.0.2: 873 | version "3.0.2" 874 | resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" 875 | integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== 876 | dependencies: 877 | fill-range "^7.0.1" 878 | 879 | browserslist@^4.20.2: 880 | version "4.21.2" 881 | resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.2.tgz#59a400757465535954946a400b841ed37e2b4ecf" 882 | integrity sha512-MonuOgAtUB46uP5CezYbRaYKBNt2LxP0yX+Pmj4LkcDFGkn9Cbpi83d9sCjwQDErXsIJSzY5oKGDbgOlF/LPAA== 883 | dependencies: 884 | caniuse-lite "^1.0.30001366" 885 | electron-to-chromium "^1.4.188" 886 | node-releases "^2.0.6" 887 | update-browserslist-db "^1.0.4" 888 | 889 | bs-logger@0.x: 890 | version "0.2.6" 891 | resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" 892 | integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== 893 | dependencies: 894 | fast-json-stable-stringify "2.x" 895 | 896 | bser@^2.0.0: 897 | version "2.0.0" 898 | resolved "https://registry.yarnpkg.com/bser/-/bser-2.0.0.tgz#9ac78d3ed5d915804fd87acb158bc797147a1719" 899 | integrity sha1-mseNPtXZFYBP2HrLFYvHlxR6Fxk= 900 | dependencies: 901 | node-int64 "^0.4.0" 902 | 903 | buffer-from@^1.0.0: 904 | version "1.1.1" 905 | resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" 906 | integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== 907 | 908 | callsites@^3.0.0: 909 | version "3.1.0" 910 | resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" 911 | integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== 912 | 913 | camelcase@^5.3.1: 914 | version "5.3.1" 915 | resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" 916 | integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== 917 | 918 | camelcase@^6.2.0: 919 | version "6.3.0" 920 | resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" 921 | integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== 922 | 923 | caniuse-lite@^1.0.30001366: 924 | version "1.0.30001368" 925 | resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001368.tgz#c5c06381c6051cd863c45021475434e81936f713" 926 | integrity sha512-wgfRYa9DenEomLG/SdWgQxpIyvdtH3NW8Vq+tB6AwR9e56iOIcu1im5F/wNdDf04XlKHXqIx4N8Jo0PemeBenQ== 927 | 928 | chalk@^2.0.0: 929 | version "2.4.2" 930 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" 931 | integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== 932 | dependencies: 933 | ansi-styles "^3.2.1" 934 | escape-string-regexp "^1.0.5" 935 | supports-color "^5.3.0" 936 | 937 | chalk@^4.0.0: 938 | version "4.1.0" 939 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" 940 | integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== 941 | dependencies: 942 | ansi-styles "^4.1.0" 943 | supports-color "^7.1.0" 944 | 945 | char-regex@^1.0.2: 946 | version "1.0.2" 947 | resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" 948 | integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== 949 | 950 | ci-info@^3.2.0: 951 | version "3.3.2" 952 | resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.2.tgz#6d2967ffa407466481c6c90b6e16b3098f080128" 953 | integrity sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg== 954 | 955 | cjs-module-lexer@^1.0.0: 956 | version "1.2.2" 957 | resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" 958 | integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== 959 | 960 | cliui@^7.0.2: 961 | version "7.0.4" 962 | resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" 963 | integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== 964 | dependencies: 965 | string-width "^4.2.0" 966 | strip-ansi "^6.0.0" 967 | wrap-ansi "^7.0.0" 968 | 969 | co@^4.6.0: 970 | version "4.6.0" 971 | resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" 972 | integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= 973 | 974 | collect-v8-coverage@^1.0.0: 975 | version "1.0.1" 976 | resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" 977 | integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg== 978 | 979 | color-convert@^1.9.0: 980 | version "1.9.3" 981 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" 982 | integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== 983 | dependencies: 984 | color-name "1.1.3" 985 | 986 | color-convert@^2.0.1: 987 | version "2.0.1" 988 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" 989 | integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== 990 | dependencies: 991 | color-name "~1.1.4" 992 | 993 | color-name@1.1.3: 994 | version "1.1.3" 995 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" 996 | integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= 997 | 998 | color-name@~1.1.4: 999 | version "1.1.4" 1000 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" 1001 | integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== 1002 | 1003 | concat-map@0.0.1: 1004 | version "0.0.1" 1005 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 1006 | integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= 1007 | 1008 | convert-source-map@^1.4.0: 1009 | version "1.6.0" 1010 | resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.6.0.tgz#51b537a8c43e0f04dec1993bffcdd504e758ac20" 1011 | integrity sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A== 1012 | dependencies: 1013 | safe-buffer "~5.1.1" 1014 | 1015 | convert-source-map@^1.6.0, convert-source-map@^1.7.0: 1016 | version "1.7.0" 1017 | resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" 1018 | integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== 1019 | dependencies: 1020 | safe-buffer "~5.1.1" 1021 | 1022 | cross-spawn@^7.0.3: 1023 | version "7.0.3" 1024 | resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" 1025 | integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== 1026 | dependencies: 1027 | path-key "^3.1.0" 1028 | shebang-command "^2.0.0" 1029 | which "^2.0.1" 1030 | 1031 | debug@^4.1.0, debug@^4.1.1: 1032 | version "4.1.1" 1033 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" 1034 | integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== 1035 | dependencies: 1036 | ms "^2.1.1" 1037 | 1038 | dedent@^0.7.0: 1039 | version "0.7.0" 1040 | resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" 1041 | integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== 1042 | 1043 | deepmerge@^4.2.2: 1044 | version "4.2.2" 1045 | resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" 1046 | integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== 1047 | 1048 | defer-promise@^2.0.1: 1049 | version "2.0.1" 1050 | resolved "https://registry.yarnpkg.com/defer-promise/-/defer-promise-2.0.1.tgz#7e6c67a55a17a9a7079fbd524cf2c1dadae3590b" 1051 | integrity sha512-zoKtBNZZQSv+45zx6Bjx5+N2DSloiTlTdAZt7COAG103Ulesn2I5GHG2nBcl/uKGp6atDWa/6Z99cfTCzPNopg== 1052 | 1053 | detect-newline@^3.0.0: 1054 | version "3.1.0" 1055 | resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" 1056 | integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== 1057 | 1058 | diff-sequences@^28.1.1: 1059 | version "28.1.1" 1060 | resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-28.1.1.tgz#9989dc731266dc2903457a70e996f3a041913ac6" 1061 | integrity sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw== 1062 | 1063 | electron-to-chromium@^1.4.188: 1064 | version "1.4.196" 1065 | resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.196.tgz#e18cdc5c1c2c2ebf78da237d0c374cc3b244d4cb" 1066 | integrity sha512-uxMa/Dt7PQsLBVXwH+t6JvpHJnrsYBaxWKi/J6HE+/nBtoHENhwBoNkgkm226/Kfxeg0z1eMQLBRPPKcDH8xWA== 1067 | 1068 | emittery@^0.10.2: 1069 | version "0.10.2" 1070 | resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.10.2.tgz#902eec8aedb8c41938c46e9385e9db7e03182933" 1071 | integrity sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw== 1072 | 1073 | emoji-regex@^8.0.0: 1074 | version "8.0.0" 1075 | resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" 1076 | integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== 1077 | 1078 | error-ex@^1.3.1: 1079 | version "1.3.2" 1080 | resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" 1081 | integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== 1082 | dependencies: 1083 | is-arrayish "^0.2.1" 1084 | 1085 | escalade@^3.1.1: 1086 | version "3.1.1" 1087 | resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" 1088 | integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== 1089 | 1090 | escape-string-regexp@^1.0.5: 1091 | version "1.0.5" 1092 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 1093 | integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= 1094 | 1095 | escape-string-regexp@^2.0.0: 1096 | version "2.0.0" 1097 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" 1098 | integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== 1099 | 1100 | esprima@^4.0.0: 1101 | version "4.0.1" 1102 | resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" 1103 | integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== 1104 | 1105 | esutils@^2.0.2: 1106 | version "2.0.2" 1107 | resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" 1108 | integrity sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs= 1109 | 1110 | execa@^5.0.0: 1111 | version "5.1.1" 1112 | resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" 1113 | integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== 1114 | dependencies: 1115 | cross-spawn "^7.0.3" 1116 | get-stream "^6.0.0" 1117 | human-signals "^2.1.0" 1118 | is-stream "^2.0.0" 1119 | merge-stream "^2.0.0" 1120 | npm-run-path "^4.0.1" 1121 | onetime "^5.1.2" 1122 | signal-exit "^3.0.3" 1123 | strip-final-newline "^2.0.0" 1124 | 1125 | exit@^0.1.2: 1126 | version "0.1.2" 1127 | resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" 1128 | integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw= 1129 | 1130 | expect@^28.1.3: 1131 | version "28.1.3" 1132 | resolved "https://registry.yarnpkg.com/expect/-/expect-28.1.3.tgz#90a7c1a124f1824133dd4533cce2d2bdcb6603ec" 1133 | integrity sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g== 1134 | dependencies: 1135 | "@jest/expect-utils" "^28.1.3" 1136 | jest-get-type "^28.0.2" 1137 | jest-matcher-utils "^28.1.3" 1138 | jest-message-util "^28.1.3" 1139 | jest-util "^28.1.3" 1140 | 1141 | fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: 1142 | version "2.0.0" 1143 | resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" 1144 | integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= 1145 | 1146 | fb-watchman@^2.0.0: 1147 | version "2.0.0" 1148 | resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.0.tgz#54e9abf7dfa2f26cd9b1636c588c1afc05de5d58" 1149 | integrity sha1-VOmr99+i8mzZsWNsWIwa/AXeXVg= 1150 | dependencies: 1151 | bser "^2.0.0" 1152 | 1153 | fill-range@^7.0.1: 1154 | version "7.0.1" 1155 | resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" 1156 | integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== 1157 | dependencies: 1158 | to-regex-range "^5.0.1" 1159 | 1160 | find-up@^4.0.0, find-up@^4.1.0: 1161 | version "4.1.0" 1162 | resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" 1163 | integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== 1164 | dependencies: 1165 | locate-path "^5.0.0" 1166 | path-exists "^4.0.0" 1167 | 1168 | fs.realpath@^1.0.0: 1169 | version "1.0.0" 1170 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 1171 | integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= 1172 | 1173 | fsevents@^2.3.2: 1174 | version "2.3.2" 1175 | resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" 1176 | integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== 1177 | 1178 | function-bind@^1.1.1: 1179 | version "1.1.1" 1180 | resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" 1181 | integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== 1182 | 1183 | gensync@^1.0.0-beta.2: 1184 | version "1.0.0-beta.2" 1185 | resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" 1186 | integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== 1187 | 1188 | get-caller-file@^2.0.5: 1189 | version "2.0.5" 1190 | resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" 1191 | integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== 1192 | 1193 | get-package-type@^0.1.0: 1194 | version "0.1.0" 1195 | resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" 1196 | integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== 1197 | 1198 | get-stream@^6.0.0: 1199 | version "6.0.1" 1200 | resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" 1201 | integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== 1202 | 1203 | glob@^7.1.3: 1204 | version "7.1.4" 1205 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" 1206 | integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A== 1207 | dependencies: 1208 | fs.realpath "^1.0.0" 1209 | inflight "^1.0.4" 1210 | inherits "2" 1211 | minimatch "^3.0.4" 1212 | once "^1.3.0" 1213 | path-is-absolute "^1.0.0" 1214 | 1215 | glob@^7.1.4: 1216 | version "7.1.6" 1217 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" 1218 | integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== 1219 | dependencies: 1220 | fs.realpath "^1.0.0" 1221 | inflight "^1.0.4" 1222 | inherits "2" 1223 | minimatch "^3.0.4" 1224 | once "^1.3.0" 1225 | path-is-absolute "^1.0.0" 1226 | 1227 | globals@^11.1.0: 1228 | version "11.12.0" 1229 | resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" 1230 | integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== 1231 | 1232 | graceful-fs@^4.2.9: 1233 | version "4.2.10" 1234 | resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" 1235 | integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== 1236 | 1237 | has-flag@^3.0.0: 1238 | version "3.0.0" 1239 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" 1240 | integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= 1241 | 1242 | has-flag@^4.0.0: 1243 | version "4.0.0" 1244 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" 1245 | integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== 1246 | 1247 | has@^1.0.3: 1248 | version "1.0.3" 1249 | resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" 1250 | integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== 1251 | dependencies: 1252 | function-bind "^1.1.1" 1253 | 1254 | html-escaper@^2.0.0: 1255 | version "2.0.2" 1256 | resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" 1257 | integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== 1258 | 1259 | human-signals@^2.1.0: 1260 | version "2.1.0" 1261 | resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" 1262 | integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== 1263 | 1264 | import-local@^3.0.2: 1265 | version "3.0.2" 1266 | resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.0.2.tgz#a8cfd0431d1de4a2199703d003e3e62364fa6db6" 1267 | integrity sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA== 1268 | dependencies: 1269 | pkg-dir "^4.2.0" 1270 | resolve-cwd "^3.0.0" 1271 | 1272 | imurmurhash@^0.1.4: 1273 | version "0.1.4" 1274 | resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" 1275 | integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= 1276 | 1277 | inflight@^1.0.4: 1278 | version "1.0.6" 1279 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 1280 | integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= 1281 | dependencies: 1282 | once "^1.3.0" 1283 | wrappy "1" 1284 | 1285 | inherits@2: 1286 | version "2.0.3" 1287 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 1288 | integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= 1289 | 1290 | is-arrayish@^0.2.1: 1291 | version "0.2.1" 1292 | resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" 1293 | integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== 1294 | 1295 | is-core-module@^2.9.0: 1296 | version "2.9.0" 1297 | resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" 1298 | integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== 1299 | dependencies: 1300 | has "^1.0.3" 1301 | 1302 | is-fullwidth-code-point@^3.0.0: 1303 | version "3.0.0" 1304 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" 1305 | integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== 1306 | 1307 | is-generator-fn@^2.0.0: 1308 | version "2.1.0" 1309 | resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" 1310 | integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== 1311 | 1312 | is-number@^7.0.0: 1313 | version "7.0.0" 1314 | resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" 1315 | integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== 1316 | 1317 | is-stream@^2.0.0: 1318 | version "2.0.0" 1319 | resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" 1320 | integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== 1321 | 1322 | isexe@^2.0.0: 1323 | version "2.0.0" 1324 | resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" 1325 | integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= 1326 | 1327 | istanbul-lib-coverage@^3.0.0: 1328 | version "3.0.0" 1329 | resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec" 1330 | integrity sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg== 1331 | 1332 | istanbul-lib-coverage@^3.2.0: 1333 | version "3.2.0" 1334 | resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" 1335 | integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== 1336 | 1337 | istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: 1338 | version "5.2.0" 1339 | resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.0.tgz#31d18bdd127f825dd02ea7bfdfd906f8ab840e9f" 1340 | integrity sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A== 1341 | dependencies: 1342 | "@babel/core" "^7.12.3" 1343 | "@babel/parser" "^7.14.7" 1344 | "@istanbuljs/schema" "^0.1.2" 1345 | istanbul-lib-coverage "^3.2.0" 1346 | semver "^6.3.0" 1347 | 1348 | istanbul-lib-report@^3.0.0: 1349 | version "3.0.0" 1350 | resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" 1351 | integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== 1352 | dependencies: 1353 | istanbul-lib-coverage "^3.0.0" 1354 | make-dir "^3.0.0" 1355 | supports-color "^7.1.0" 1356 | 1357 | istanbul-lib-source-maps@^4.0.0: 1358 | version "4.0.0" 1359 | resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz#75743ce6d96bb86dc7ee4352cf6366a23f0b1ad9" 1360 | integrity sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg== 1361 | dependencies: 1362 | debug "^4.1.1" 1363 | istanbul-lib-coverage "^3.0.0" 1364 | source-map "^0.6.1" 1365 | 1366 | istanbul-reports@^3.1.3: 1367 | version "3.1.5" 1368 | resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.5.tgz#cc9a6ab25cb25659810e4785ed9d9fb742578bae" 1369 | integrity sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w== 1370 | dependencies: 1371 | html-escaper "^2.0.0" 1372 | istanbul-lib-report "^3.0.0" 1373 | 1374 | jest-changed-files@^28.1.3: 1375 | version "28.1.3" 1376 | resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-28.1.3.tgz#d9aeee6792be3686c47cb988a8eaf82ff4238831" 1377 | integrity sha512-esaOfUWJXk2nfZt9SPyC8gA1kNfdKLkQWyzsMlqq8msYSlNKfmZxfRgZn4Cd4MGVUF+7v6dBs0d5TOAKa7iIiA== 1378 | dependencies: 1379 | execa "^5.0.0" 1380 | p-limit "^3.1.0" 1381 | 1382 | jest-circus@^28.1.3: 1383 | version "28.1.3" 1384 | resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-28.1.3.tgz#d14bd11cf8ee1a03d69902dc47b6bd4634ee00e4" 1385 | integrity sha512-cZ+eS5zc79MBwt+IhQhiEp0OeBddpc1n8MBo1nMB8A7oPMKEO+Sre+wHaLJexQUj9Ya/8NOBY0RESUgYjB6fow== 1386 | dependencies: 1387 | "@jest/environment" "^28.1.3" 1388 | "@jest/expect" "^28.1.3" 1389 | "@jest/test-result" "^28.1.3" 1390 | "@jest/types" "^28.1.3" 1391 | "@types/node" "*" 1392 | chalk "^4.0.0" 1393 | co "^4.6.0" 1394 | dedent "^0.7.0" 1395 | is-generator-fn "^2.0.0" 1396 | jest-each "^28.1.3" 1397 | jest-matcher-utils "^28.1.3" 1398 | jest-message-util "^28.1.3" 1399 | jest-runtime "^28.1.3" 1400 | jest-snapshot "^28.1.3" 1401 | jest-util "^28.1.3" 1402 | p-limit "^3.1.0" 1403 | pretty-format "^28.1.3" 1404 | slash "^3.0.0" 1405 | stack-utils "^2.0.3" 1406 | 1407 | jest-cli@^28.1.3: 1408 | version "28.1.3" 1409 | resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-28.1.3.tgz#558b33c577d06de55087b8448d373b9f654e46b2" 1410 | integrity sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ== 1411 | dependencies: 1412 | "@jest/core" "^28.1.3" 1413 | "@jest/test-result" "^28.1.3" 1414 | "@jest/types" "^28.1.3" 1415 | chalk "^4.0.0" 1416 | exit "^0.1.2" 1417 | graceful-fs "^4.2.9" 1418 | import-local "^3.0.2" 1419 | jest-config "^28.1.3" 1420 | jest-util "^28.1.3" 1421 | jest-validate "^28.1.3" 1422 | prompts "^2.0.1" 1423 | yargs "^17.3.1" 1424 | 1425 | jest-config@^28.1.3: 1426 | version "28.1.3" 1427 | resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-28.1.3.tgz#e315e1f73df3cac31447eed8b8740a477392ec60" 1428 | integrity sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ== 1429 | dependencies: 1430 | "@babel/core" "^7.11.6" 1431 | "@jest/test-sequencer" "^28.1.3" 1432 | "@jest/types" "^28.1.3" 1433 | babel-jest "^28.1.3" 1434 | chalk "^4.0.0" 1435 | ci-info "^3.2.0" 1436 | deepmerge "^4.2.2" 1437 | glob "^7.1.3" 1438 | graceful-fs "^4.2.9" 1439 | jest-circus "^28.1.3" 1440 | jest-environment-node "^28.1.3" 1441 | jest-get-type "^28.0.2" 1442 | jest-regex-util "^28.0.2" 1443 | jest-resolve "^28.1.3" 1444 | jest-runner "^28.1.3" 1445 | jest-util "^28.1.3" 1446 | jest-validate "^28.1.3" 1447 | micromatch "^4.0.4" 1448 | parse-json "^5.2.0" 1449 | pretty-format "^28.1.3" 1450 | slash "^3.0.0" 1451 | strip-json-comments "^3.1.1" 1452 | 1453 | jest-diff@^28.1.3: 1454 | version "28.1.3" 1455 | resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-28.1.3.tgz#948a192d86f4e7a64c5264ad4da4877133d8792f" 1456 | integrity sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw== 1457 | dependencies: 1458 | chalk "^4.0.0" 1459 | diff-sequences "^28.1.1" 1460 | jest-get-type "^28.0.2" 1461 | pretty-format "^28.1.3" 1462 | 1463 | jest-docblock@^28.1.1: 1464 | version "28.1.1" 1465 | resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-28.1.1.tgz#6f515c3bf841516d82ecd57a62eed9204c2f42a8" 1466 | integrity sha512-3wayBVNiOYx0cwAbl9rwm5kKFP8yHH3d/fkEaL02NPTkDojPtheGB7HZSFY4wzX+DxyrvhXz0KSCVksmCknCuA== 1467 | dependencies: 1468 | detect-newline "^3.0.0" 1469 | 1470 | jest-each@^28.1.3: 1471 | version "28.1.3" 1472 | resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-28.1.3.tgz#bdd1516edbe2b1f3569cfdad9acd543040028f81" 1473 | integrity sha512-arT1z4sg2yABU5uogObVPvSlSMQlDA48owx07BDPAiasW0yYpYHYOo4HHLz9q0BVzDVU4hILFjzJw0So9aCL/g== 1474 | dependencies: 1475 | "@jest/types" "^28.1.3" 1476 | chalk "^4.0.0" 1477 | jest-get-type "^28.0.2" 1478 | jest-util "^28.1.3" 1479 | pretty-format "^28.1.3" 1480 | 1481 | jest-environment-node@^28.1.3: 1482 | version "28.1.3" 1483 | resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-28.1.3.tgz#7e74fe40eb645b9d56c0c4b70ca4357faa349be5" 1484 | integrity sha512-ugP6XOhEpjAEhGYvp5Xj989ns5cB1K6ZdjBYuS30umT4CQEETaxSiPcZ/E1kFktX4GkrcM4qu07IIlDYX1gp+A== 1485 | dependencies: 1486 | "@jest/environment" "^28.1.3" 1487 | "@jest/fake-timers" "^28.1.3" 1488 | "@jest/types" "^28.1.3" 1489 | "@types/node" "*" 1490 | jest-mock "^28.1.3" 1491 | jest-util "^28.1.3" 1492 | 1493 | jest-get-type@^28.0.2: 1494 | version "28.0.2" 1495 | resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-28.0.2.tgz#34622e628e4fdcd793d46db8a242227901fcf203" 1496 | integrity sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA== 1497 | 1498 | jest-haste-map@^28.1.3: 1499 | version "28.1.3" 1500 | resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-28.1.3.tgz#abd5451129a38d9841049644f34b034308944e2b" 1501 | integrity sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA== 1502 | dependencies: 1503 | "@jest/types" "^28.1.3" 1504 | "@types/graceful-fs" "^4.1.3" 1505 | "@types/node" "*" 1506 | anymatch "^3.0.3" 1507 | fb-watchman "^2.0.0" 1508 | graceful-fs "^4.2.9" 1509 | jest-regex-util "^28.0.2" 1510 | jest-util "^28.1.3" 1511 | jest-worker "^28.1.3" 1512 | micromatch "^4.0.4" 1513 | walker "^1.0.8" 1514 | optionalDependencies: 1515 | fsevents "^2.3.2" 1516 | 1517 | jest-leak-detector@^28.1.3: 1518 | version "28.1.3" 1519 | resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-28.1.3.tgz#a6685d9b074be99e3adee816ce84fd30795e654d" 1520 | integrity sha512-WFVJhnQsiKtDEo5lG2mM0v40QWnBM+zMdHHyJs8AWZ7J0QZJS59MsyKeJHWhpBZBH32S48FOVvGyOFT1h0DlqA== 1521 | dependencies: 1522 | jest-get-type "^28.0.2" 1523 | pretty-format "^28.1.3" 1524 | 1525 | jest-matcher-utils@^28.0.0, jest-matcher-utils@^28.1.3: 1526 | version "28.1.3" 1527 | resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz#5a77f1c129dd5ba3b4d7fc20728806c78893146e" 1528 | integrity sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw== 1529 | dependencies: 1530 | chalk "^4.0.0" 1531 | jest-diff "^28.1.3" 1532 | jest-get-type "^28.0.2" 1533 | pretty-format "^28.1.3" 1534 | 1535 | jest-message-util@^28.1.3: 1536 | version "28.1.3" 1537 | resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-28.1.3.tgz#232def7f2e333f1eecc90649b5b94b0055e7c43d" 1538 | integrity sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g== 1539 | dependencies: 1540 | "@babel/code-frame" "^7.12.13" 1541 | "@jest/types" "^28.1.3" 1542 | "@types/stack-utils" "^2.0.0" 1543 | chalk "^4.0.0" 1544 | graceful-fs "^4.2.9" 1545 | micromatch "^4.0.4" 1546 | pretty-format "^28.1.3" 1547 | slash "^3.0.0" 1548 | stack-utils "^2.0.3" 1549 | 1550 | jest-mock@^28.1.3: 1551 | version "28.1.3" 1552 | resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-28.1.3.tgz#d4e9b1fc838bea595c77ab73672ebf513ab249da" 1553 | integrity sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA== 1554 | dependencies: 1555 | "@jest/types" "^28.1.3" 1556 | "@types/node" "*" 1557 | 1558 | jest-pnp-resolver@^1.2.2: 1559 | version "1.2.2" 1560 | resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" 1561 | integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== 1562 | 1563 | jest-regex-util@^28.0.2: 1564 | version "28.0.2" 1565 | resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-28.0.2.tgz#afdc377a3b25fb6e80825adcf76c854e5bf47ead" 1566 | integrity sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw== 1567 | 1568 | jest-resolve-dependencies@^28.1.3: 1569 | version "28.1.3" 1570 | resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-28.1.3.tgz#8c65d7583460df7275c6ea2791901fa975c1fe66" 1571 | integrity sha512-qa0QO2Q0XzQoNPouMbCc7Bvtsem8eQgVPNkwn9LnS+R2n8DaVDPL/U1gngC0LTl1RYXJU0uJa2BMC2DbTfFrHA== 1572 | dependencies: 1573 | jest-regex-util "^28.0.2" 1574 | jest-snapshot "^28.1.3" 1575 | 1576 | jest-resolve@^28.1.3: 1577 | version "28.1.3" 1578 | resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-28.1.3.tgz#cfb36100341ddbb061ec781426b3c31eb51aa0a8" 1579 | integrity sha512-Z1W3tTjE6QaNI90qo/BJpfnvpxtaFTFw5CDgwpyE/Kz8U/06N1Hjf4ia9quUhCh39qIGWF1ZuxFiBiJQwSEYKQ== 1580 | dependencies: 1581 | chalk "^4.0.0" 1582 | graceful-fs "^4.2.9" 1583 | jest-haste-map "^28.1.3" 1584 | jest-pnp-resolver "^1.2.2" 1585 | jest-util "^28.1.3" 1586 | jest-validate "^28.1.3" 1587 | resolve "^1.20.0" 1588 | resolve.exports "^1.1.0" 1589 | slash "^3.0.0" 1590 | 1591 | jest-runner@^28.1.3: 1592 | version "28.1.3" 1593 | resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-28.1.3.tgz#5eee25febd730b4713a2cdfd76bdd5557840f9a1" 1594 | integrity sha512-GkMw4D/0USd62OVO0oEgjn23TM+YJa2U2Wu5zz9xsQB1MxWKDOlrnykPxnMsN0tnJllfLPinHTka61u0QhaxBA== 1595 | dependencies: 1596 | "@jest/console" "^28.1.3" 1597 | "@jest/environment" "^28.1.3" 1598 | "@jest/test-result" "^28.1.3" 1599 | "@jest/transform" "^28.1.3" 1600 | "@jest/types" "^28.1.3" 1601 | "@types/node" "*" 1602 | chalk "^4.0.0" 1603 | emittery "^0.10.2" 1604 | graceful-fs "^4.2.9" 1605 | jest-docblock "^28.1.1" 1606 | jest-environment-node "^28.1.3" 1607 | jest-haste-map "^28.1.3" 1608 | jest-leak-detector "^28.1.3" 1609 | jest-message-util "^28.1.3" 1610 | jest-resolve "^28.1.3" 1611 | jest-runtime "^28.1.3" 1612 | jest-util "^28.1.3" 1613 | jest-watcher "^28.1.3" 1614 | jest-worker "^28.1.3" 1615 | p-limit "^3.1.0" 1616 | source-map-support "0.5.13" 1617 | 1618 | jest-runtime@^28.1.3: 1619 | version "28.1.3" 1620 | resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-28.1.3.tgz#a57643458235aa53e8ec7821949e728960d0605f" 1621 | integrity sha512-NU+881ScBQQLc1JHG5eJGU7Ui3kLKrmwCPPtYsJtBykixrM2OhVQlpMmFWJjMyDfdkGgBMNjXCGB/ebzsgNGQw== 1622 | dependencies: 1623 | "@jest/environment" "^28.1.3" 1624 | "@jest/fake-timers" "^28.1.3" 1625 | "@jest/globals" "^28.1.3" 1626 | "@jest/source-map" "^28.1.2" 1627 | "@jest/test-result" "^28.1.3" 1628 | "@jest/transform" "^28.1.3" 1629 | "@jest/types" "^28.1.3" 1630 | chalk "^4.0.0" 1631 | cjs-module-lexer "^1.0.0" 1632 | collect-v8-coverage "^1.0.0" 1633 | execa "^5.0.0" 1634 | glob "^7.1.3" 1635 | graceful-fs "^4.2.9" 1636 | jest-haste-map "^28.1.3" 1637 | jest-message-util "^28.1.3" 1638 | jest-mock "^28.1.3" 1639 | jest-regex-util "^28.0.2" 1640 | jest-resolve "^28.1.3" 1641 | jest-snapshot "^28.1.3" 1642 | jest-util "^28.1.3" 1643 | slash "^3.0.0" 1644 | strip-bom "^4.0.0" 1645 | 1646 | jest-snapshot@^28.1.3: 1647 | version "28.1.3" 1648 | resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-28.1.3.tgz#17467b3ab8ddb81e2f605db05583d69388fc0668" 1649 | integrity sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg== 1650 | dependencies: 1651 | "@babel/core" "^7.11.6" 1652 | "@babel/generator" "^7.7.2" 1653 | "@babel/plugin-syntax-typescript" "^7.7.2" 1654 | "@babel/traverse" "^7.7.2" 1655 | "@babel/types" "^7.3.3" 1656 | "@jest/expect-utils" "^28.1.3" 1657 | "@jest/transform" "^28.1.3" 1658 | "@jest/types" "^28.1.3" 1659 | "@types/babel__traverse" "^7.0.6" 1660 | "@types/prettier" "^2.1.5" 1661 | babel-preset-current-node-syntax "^1.0.0" 1662 | chalk "^4.0.0" 1663 | expect "^28.1.3" 1664 | graceful-fs "^4.2.9" 1665 | jest-diff "^28.1.3" 1666 | jest-get-type "^28.0.2" 1667 | jest-haste-map "^28.1.3" 1668 | jest-matcher-utils "^28.1.3" 1669 | jest-message-util "^28.1.3" 1670 | jest-util "^28.1.3" 1671 | natural-compare "^1.4.0" 1672 | pretty-format "^28.1.3" 1673 | semver "^7.3.5" 1674 | 1675 | jest-util@^28.0.0, jest-util@^28.1.3: 1676 | version "28.1.3" 1677 | resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-28.1.3.tgz#f4f932aa0074f0679943220ff9cbba7e497028b0" 1678 | integrity sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ== 1679 | dependencies: 1680 | "@jest/types" "^28.1.3" 1681 | "@types/node" "*" 1682 | chalk "^4.0.0" 1683 | ci-info "^3.2.0" 1684 | graceful-fs "^4.2.9" 1685 | picomatch "^2.2.3" 1686 | 1687 | jest-validate@^28.1.3: 1688 | version "28.1.3" 1689 | resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-28.1.3.tgz#e322267fd5e7c64cea4629612c357bbda96229df" 1690 | integrity sha512-SZbOGBWEsaTxBGCOpsRWlXlvNkvTkY0XxRfh7zYmvd8uL5Qzyg0CHAXiXKROflh801quA6+/DsT4ODDthOC/OA== 1691 | dependencies: 1692 | "@jest/types" "^28.1.3" 1693 | camelcase "^6.2.0" 1694 | chalk "^4.0.0" 1695 | jest-get-type "^28.0.2" 1696 | leven "^3.1.0" 1697 | pretty-format "^28.1.3" 1698 | 1699 | jest-watcher@^28.1.3: 1700 | version "28.1.3" 1701 | resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-28.1.3.tgz#c6023a59ba2255e3b4c57179fc94164b3e73abd4" 1702 | integrity sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g== 1703 | dependencies: 1704 | "@jest/test-result" "^28.1.3" 1705 | "@jest/types" "^28.1.3" 1706 | "@types/node" "*" 1707 | ansi-escapes "^4.2.1" 1708 | chalk "^4.0.0" 1709 | emittery "^0.10.2" 1710 | jest-util "^28.1.3" 1711 | string-length "^4.0.1" 1712 | 1713 | jest-worker@^28.1.3: 1714 | version "28.1.3" 1715 | resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-28.1.3.tgz#7e3c4ce3fa23d1bb6accb169e7f396f98ed4bb98" 1716 | integrity sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g== 1717 | dependencies: 1718 | "@types/node" "*" 1719 | merge-stream "^2.0.0" 1720 | supports-color "^8.0.0" 1721 | 1722 | jest@^28.1.3: 1723 | version "28.1.3" 1724 | resolved "https://registry.yarnpkg.com/jest/-/jest-28.1.3.tgz#e9c6a7eecdebe3548ca2b18894a50f45b36dfc6b" 1725 | integrity sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA== 1726 | dependencies: 1727 | "@jest/core" "^28.1.3" 1728 | "@jest/types" "^28.1.3" 1729 | import-local "^3.0.2" 1730 | jest-cli "^28.1.3" 1731 | 1732 | js-tokens@^4.0.0: 1733 | version "4.0.0" 1734 | resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" 1735 | integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== 1736 | 1737 | js-yaml@^3.13.1: 1738 | version "3.14.0" 1739 | resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" 1740 | integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== 1741 | dependencies: 1742 | argparse "^1.0.7" 1743 | esprima "^4.0.0" 1744 | 1745 | jsesc@^2.5.1: 1746 | version "2.5.2" 1747 | resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" 1748 | integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== 1749 | 1750 | json-parse-even-better-errors@^2.3.0: 1751 | version "2.3.1" 1752 | resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" 1753 | integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== 1754 | 1755 | json5@^2.2.1: 1756 | version "2.2.1" 1757 | resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" 1758 | integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== 1759 | 1760 | kleur@^3.0.2: 1761 | version "3.0.3" 1762 | resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" 1763 | integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== 1764 | 1765 | leven@^3.1.0: 1766 | version "3.1.0" 1767 | resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" 1768 | integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== 1769 | 1770 | lines-and-columns@^1.1.6: 1771 | version "1.2.4" 1772 | resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" 1773 | integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== 1774 | 1775 | locate-path@^5.0.0: 1776 | version "5.0.0" 1777 | resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" 1778 | integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== 1779 | dependencies: 1780 | p-locate "^4.1.0" 1781 | 1782 | lodash.memoize@4.x: 1783 | version "4.1.2" 1784 | resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" 1785 | integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= 1786 | 1787 | lodash@^4.17.11: 1788 | version "4.17.11" 1789 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" 1790 | integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== 1791 | 1792 | lodash@^4.17.19: 1793 | version "4.17.20" 1794 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" 1795 | integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== 1796 | 1797 | lru-cache@^6.0.0: 1798 | version "6.0.0" 1799 | resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" 1800 | integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== 1801 | dependencies: 1802 | yallist "^4.0.0" 1803 | 1804 | make-dir@^3.0.0: 1805 | version "3.1.0" 1806 | resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" 1807 | integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== 1808 | dependencies: 1809 | semver "^6.0.0" 1810 | 1811 | make-error@1.x: 1812 | version "1.3.5" 1813 | resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.5.tgz#efe4e81f6db28cadd605c70f29c831b58ef776c8" 1814 | integrity sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g== 1815 | 1816 | makeerror@1.0.12: 1817 | version "1.0.12" 1818 | resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" 1819 | integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== 1820 | dependencies: 1821 | tmpl "1.0.5" 1822 | 1823 | merge-stream@^2.0.0: 1824 | version "2.0.0" 1825 | resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" 1826 | integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== 1827 | 1828 | micromatch@^4.0.4: 1829 | version "4.0.5" 1830 | resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" 1831 | integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== 1832 | dependencies: 1833 | braces "^3.0.2" 1834 | picomatch "^2.3.1" 1835 | 1836 | mimic-fn@^2.1.0: 1837 | version "2.1.0" 1838 | resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" 1839 | integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== 1840 | 1841 | minimatch@^3.0.4: 1842 | version "3.0.4" 1843 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 1844 | integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== 1845 | dependencies: 1846 | brace-expansion "^1.1.7" 1847 | 1848 | ms@^2.1.1: 1849 | version "2.1.2" 1850 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" 1851 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 1852 | 1853 | natural-compare@^1.4.0: 1854 | version "1.4.0" 1855 | resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" 1856 | integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= 1857 | 1858 | node-int64@^0.4.0: 1859 | version "0.4.0" 1860 | resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" 1861 | integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= 1862 | 1863 | node-releases@^2.0.6: 1864 | version "2.0.6" 1865 | resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503" 1866 | integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg== 1867 | 1868 | normalize-path@^3.0.0: 1869 | version "3.0.0" 1870 | resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" 1871 | integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== 1872 | 1873 | npm-run-path@^4.0.1: 1874 | version "4.0.1" 1875 | resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" 1876 | integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== 1877 | dependencies: 1878 | path-key "^3.0.0" 1879 | 1880 | once@^1.3.0: 1881 | version "1.4.0" 1882 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 1883 | integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= 1884 | dependencies: 1885 | wrappy "1" 1886 | 1887 | onetime@^5.1.2: 1888 | version "5.1.2" 1889 | resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" 1890 | integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== 1891 | dependencies: 1892 | mimic-fn "^2.1.0" 1893 | 1894 | p-limit@^2.2.0: 1895 | version "2.3.0" 1896 | resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" 1897 | integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== 1898 | dependencies: 1899 | p-try "^2.0.0" 1900 | 1901 | p-limit@^3.1.0: 1902 | version "3.1.0" 1903 | resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" 1904 | integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== 1905 | dependencies: 1906 | yocto-queue "^0.1.0" 1907 | 1908 | p-locate@^4.1.0: 1909 | version "4.1.0" 1910 | resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" 1911 | integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== 1912 | dependencies: 1913 | p-limit "^2.2.0" 1914 | 1915 | p-try@^2.0.0: 1916 | version "2.2.0" 1917 | resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" 1918 | integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== 1919 | 1920 | parse-json@^5.2.0: 1921 | version "5.2.0" 1922 | resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" 1923 | integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== 1924 | dependencies: 1925 | "@babel/code-frame" "^7.0.0" 1926 | error-ex "^1.3.1" 1927 | json-parse-even-better-errors "^2.3.0" 1928 | lines-and-columns "^1.1.6" 1929 | 1930 | path-exists@^4.0.0: 1931 | version "4.0.0" 1932 | resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" 1933 | integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== 1934 | 1935 | path-is-absolute@^1.0.0: 1936 | version "1.0.1" 1937 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 1938 | integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= 1939 | 1940 | path-key@^3.0.0, path-key@^3.1.0: 1941 | version "3.1.1" 1942 | resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" 1943 | integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== 1944 | 1945 | path-parse@^1.0.7: 1946 | version "1.0.7" 1947 | resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" 1948 | integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== 1949 | 1950 | picocolors@^1.0.0: 1951 | version "1.0.0" 1952 | resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" 1953 | integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== 1954 | 1955 | picomatch@^2.0.4: 1956 | version "2.2.2" 1957 | resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" 1958 | integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== 1959 | 1960 | picomatch@^2.2.3, picomatch@^2.3.1: 1961 | version "2.3.1" 1962 | resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" 1963 | integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== 1964 | 1965 | pirates@^4.0.4: 1966 | version "4.0.5" 1967 | resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" 1968 | integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== 1969 | 1970 | pkg-dir@^4.2.0: 1971 | version "4.2.0" 1972 | resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" 1973 | integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== 1974 | dependencies: 1975 | find-up "^4.0.0" 1976 | 1977 | prettier@^2.1.2: 1978 | version "2.1.2" 1979 | resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.1.2.tgz#3050700dae2e4c8b67c4c3f666cdb8af405e1ce5" 1980 | integrity sha512-16c7K+x4qVlJg9rEbXl7HEGmQyZlG4R9AgP+oHKRMsMsuk8s+ATStlf1NpDqyBI1HpVyfjLOeMhH2LvuNvV5Vg== 1981 | 1982 | pretty-format@^28.0.0, pretty-format@^28.1.3: 1983 | version "28.1.3" 1984 | resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-28.1.3.tgz#c9fba8cedf99ce50963a11b27d982a9ae90970d5" 1985 | integrity sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q== 1986 | dependencies: 1987 | "@jest/schemas" "^28.1.3" 1988 | ansi-regex "^5.0.1" 1989 | ansi-styles "^5.0.0" 1990 | react-is "^18.0.0" 1991 | 1992 | prompts@^2.0.1: 1993 | version "2.1.0" 1994 | resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.1.0.tgz#bf90bc71f6065d255ea2bdc0fe6520485c1b45db" 1995 | integrity sha512-+x5TozgqYdOwWsQFZizE/Tra3fKvAoy037kOyU6cgz84n8f6zxngLOV4O32kTwt9FcLCxAqw0P/c8rOr9y+Gfg== 1996 | dependencies: 1997 | kleur "^3.0.2" 1998 | sisteransi "^1.0.0" 1999 | 2000 | react-is@^18.0.0: 2001 | version "18.2.0" 2002 | resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" 2003 | integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== 2004 | 2005 | require-directory@^2.1.1: 2006 | version "2.1.1" 2007 | resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" 2008 | integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= 2009 | 2010 | resolve-cwd@^3.0.0: 2011 | version "3.0.0" 2012 | resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" 2013 | integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== 2014 | dependencies: 2015 | resolve-from "^5.0.0" 2016 | 2017 | resolve-from@^5.0.0: 2018 | version "5.0.0" 2019 | resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" 2020 | integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== 2021 | 2022 | resolve.exports@^1.1.0: 2023 | version "1.1.0" 2024 | resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" 2025 | integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== 2026 | 2027 | resolve@^1.20.0: 2028 | version "1.22.1" 2029 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" 2030 | integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== 2031 | dependencies: 2032 | is-core-module "^2.9.0" 2033 | path-parse "^1.0.7" 2034 | supports-preserve-symlinks-flag "^1.0.0" 2035 | 2036 | rimraf@^2.6.3: 2037 | version "2.6.3" 2038 | resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" 2039 | integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== 2040 | dependencies: 2041 | glob "^7.1.3" 2042 | 2043 | rimraf@^3.0.0: 2044 | version "3.0.2" 2045 | resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" 2046 | integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== 2047 | dependencies: 2048 | glob "^7.1.3" 2049 | 2050 | safe-buffer@~5.1.1: 2051 | version "5.1.2" 2052 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" 2053 | integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== 2054 | 2055 | semver@7.x: 2056 | version "7.3.2" 2057 | resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" 2058 | integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== 2059 | 2060 | semver@^6.0.0: 2061 | version "6.1.1" 2062 | resolved "https://registry.yarnpkg.com/semver/-/semver-6.1.1.tgz#53f53da9b30b2103cd4f15eab3a18ecbcb210c9b" 2063 | integrity sha512-rWYq2e5iYW+fFe/oPPtYJxYgjBm8sC4rmoGdUOgBB7VnwKt6HrL793l2voH1UlsyYZpJ4g0wfjnTEO1s1NP2eQ== 2064 | 2065 | semver@^6.3.0: 2066 | version "6.3.0" 2067 | resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" 2068 | integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== 2069 | 2070 | semver@^7.3.5: 2071 | version "7.3.7" 2072 | resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" 2073 | integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== 2074 | dependencies: 2075 | lru-cache "^6.0.0" 2076 | 2077 | shebang-command@^2.0.0: 2078 | version "2.0.0" 2079 | resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" 2080 | integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== 2081 | dependencies: 2082 | shebang-regex "^3.0.0" 2083 | 2084 | shebang-regex@^3.0.0: 2085 | version "3.0.0" 2086 | resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" 2087 | integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== 2088 | 2089 | signal-exit@^3.0.3, signal-exit@^3.0.7: 2090 | version "3.0.7" 2091 | resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" 2092 | integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== 2093 | 2094 | sisteransi@^1.0.0: 2095 | version "1.0.0" 2096 | resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.0.tgz#77d9622ff909080f1c19e5f4a1df0c1b0a27b88c" 2097 | integrity sha512-N+z4pHB4AmUv0SjveWRd6q1Nj5w62m5jodv+GD8lvmbY/83T/rpbJGZOnK5T149OldDj4Db07BSv9xY4K6NTPQ== 2098 | 2099 | slash@^3.0.0: 2100 | version "3.0.0" 2101 | resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" 2102 | integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== 2103 | 2104 | source-map-support@0.5.13: 2105 | version "0.5.13" 2106 | resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" 2107 | integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== 2108 | dependencies: 2109 | buffer-from "^1.0.0" 2110 | source-map "^0.6.0" 2111 | 2112 | source-map@^0.6.0, source-map@^0.6.1: 2113 | version "0.6.1" 2114 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" 2115 | integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== 2116 | 2117 | sprintf-js@~1.0.2: 2118 | version "1.0.3" 2119 | resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" 2120 | integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= 2121 | 2122 | stack-utils@^2.0.3: 2123 | version "2.0.5" 2124 | resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.5.tgz#d25265fca995154659dbbfba3b49254778d2fdd5" 2125 | integrity sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA== 2126 | dependencies: 2127 | escape-string-regexp "^2.0.0" 2128 | 2129 | string-length@^4.0.1: 2130 | version "4.0.1" 2131 | resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.1.tgz#4a973bf31ef77c4edbceadd6af2611996985f8a1" 2132 | integrity sha512-PKyXUd0LK0ePjSOnWn34V2uD6acUWev9uy0Ft05k0E8xRW+SKcA0F7eMr7h5xlzfn+4O3N+55rduYyet3Jk+jw== 2133 | dependencies: 2134 | char-regex "^1.0.2" 2135 | strip-ansi "^6.0.0" 2136 | 2137 | string-width@^4.1.0, string-width@^4.2.0: 2138 | version "4.2.0" 2139 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" 2140 | integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== 2141 | dependencies: 2142 | emoji-regex "^8.0.0" 2143 | is-fullwidth-code-point "^3.0.0" 2144 | strip-ansi "^6.0.0" 2145 | 2146 | string-width@^4.2.3: 2147 | version "4.2.3" 2148 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" 2149 | integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== 2150 | dependencies: 2151 | emoji-regex "^8.0.0" 2152 | is-fullwidth-code-point "^3.0.0" 2153 | strip-ansi "^6.0.1" 2154 | 2155 | strip-ansi@^6.0.0: 2156 | version "6.0.0" 2157 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" 2158 | integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== 2159 | dependencies: 2160 | ansi-regex "^5.0.0" 2161 | 2162 | strip-ansi@^6.0.1: 2163 | version "6.0.1" 2164 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" 2165 | integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== 2166 | dependencies: 2167 | ansi-regex "^5.0.1" 2168 | 2169 | strip-bom@^4.0.0: 2170 | version "4.0.0" 2171 | resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" 2172 | integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== 2173 | 2174 | strip-final-newline@^2.0.0: 2175 | version "2.0.0" 2176 | resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" 2177 | integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== 2178 | 2179 | strip-json-comments@^3.1.1: 2180 | version "3.1.1" 2181 | resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" 2182 | integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== 2183 | 2184 | supports-color@^5.3.0: 2185 | version "5.5.0" 2186 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" 2187 | integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== 2188 | dependencies: 2189 | has-flag "^3.0.0" 2190 | 2191 | supports-color@^7.0.0, supports-color@^7.1.0: 2192 | version "7.2.0" 2193 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" 2194 | integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== 2195 | dependencies: 2196 | has-flag "^4.0.0" 2197 | 2198 | supports-color@^8.0.0: 2199 | version "8.1.1" 2200 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" 2201 | integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== 2202 | dependencies: 2203 | has-flag "^4.0.0" 2204 | 2205 | supports-hyperlinks@^2.0.0: 2206 | version "2.1.0" 2207 | resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz#f663df252af5f37c5d49bbd7eeefa9e0b9e59e47" 2208 | integrity sha512-zoE5/e+dnEijk6ASB6/qrK+oYdm2do1hjoLWrqUC/8WEIW1gbxFcKuBof7sW8ArN6e+AYvsE8HBGiVRWL/F5CA== 2209 | dependencies: 2210 | has-flag "^4.0.0" 2211 | supports-color "^7.0.0" 2212 | 2213 | supports-preserve-symlinks-flag@^1.0.0: 2214 | version "1.0.0" 2215 | resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" 2216 | integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== 2217 | 2218 | terminal-link@^2.0.0: 2219 | version "2.1.1" 2220 | resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" 2221 | integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== 2222 | dependencies: 2223 | ansi-escapes "^4.2.1" 2224 | supports-hyperlinks "^2.0.0" 2225 | 2226 | test-exclude@^6.0.0: 2227 | version "6.0.0" 2228 | resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" 2229 | integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== 2230 | dependencies: 2231 | "@istanbuljs/schema" "^0.1.2" 2232 | glob "^7.1.4" 2233 | minimatch "^3.0.4" 2234 | 2235 | tmpl@1.0.5: 2236 | version "1.0.5" 2237 | resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" 2238 | integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== 2239 | 2240 | to-fast-properties@^2.0.0: 2241 | version "2.0.0" 2242 | resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" 2243 | integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= 2244 | 2245 | to-regex-range@^5.0.1: 2246 | version "5.0.1" 2247 | resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" 2248 | integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== 2249 | dependencies: 2250 | is-number "^7.0.0" 2251 | 2252 | ts-jest@^28.0.7: 2253 | version "28.0.7" 2254 | resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-28.0.7.tgz#e18757a9e44693da9980a79127e5df5a98b37ac6" 2255 | integrity sha512-wWXCSmTwBVmdvWrOpYhal79bDpioDy4rTT+0vyUnE3ZzM7LOAAGG9NXwzkEL/a516rQEgnMmS/WKP9jBPCVJyA== 2256 | dependencies: 2257 | bs-logger "0.x" 2258 | fast-json-stable-stringify "2.x" 2259 | jest-util "^28.0.0" 2260 | json5 "^2.2.1" 2261 | lodash.memoize "4.x" 2262 | make-error "1.x" 2263 | semver "7.x" 2264 | yargs-parser "^21.0.1" 2265 | 2266 | type-detect@4.0.8: 2267 | version "4.0.8" 2268 | resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" 2269 | integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== 2270 | 2271 | type-fest@^0.11.0: 2272 | version "0.11.0" 2273 | resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" 2274 | integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== 2275 | 2276 | typescript@^4.7.4: 2277 | version "4.7.4" 2278 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235" 2279 | integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ== 2280 | 2281 | update-browserslist-db@^1.0.4: 2282 | version "1.0.5" 2283 | resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz#be06a5eedd62f107b7c19eb5bcefb194411abf38" 2284 | integrity sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q== 2285 | dependencies: 2286 | escalade "^3.1.1" 2287 | picocolors "^1.0.0" 2288 | 2289 | v8-to-istanbul@^9.0.1: 2290 | version "9.0.1" 2291 | resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz#b6f994b0b5d4ef255e17a0d17dc444a9f5132fa4" 2292 | integrity sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w== 2293 | dependencies: 2294 | "@jridgewell/trace-mapping" "^0.3.12" 2295 | "@types/istanbul-lib-coverage" "^2.0.1" 2296 | convert-source-map "^1.6.0" 2297 | 2298 | walker@^1.0.8: 2299 | version "1.0.8" 2300 | resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" 2301 | integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== 2302 | dependencies: 2303 | makeerror "1.0.12" 2304 | 2305 | which@^2.0.1: 2306 | version "2.0.2" 2307 | resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" 2308 | integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== 2309 | dependencies: 2310 | isexe "^2.0.0" 2311 | 2312 | wrap-ansi@^7.0.0: 2313 | version "7.0.0" 2314 | resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" 2315 | integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== 2316 | dependencies: 2317 | ansi-styles "^4.0.0" 2318 | string-width "^4.1.0" 2319 | strip-ansi "^6.0.0" 2320 | 2321 | wrappy@1: 2322 | version "1.0.2" 2323 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 2324 | integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= 2325 | 2326 | write-file-atomic@^4.0.1: 2327 | version "4.0.1" 2328 | resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.1.tgz#9faa33a964c1c85ff6f849b80b42a88c2c537c8f" 2329 | integrity sha512-nSKUxgAbyioruk6hU87QzVbY279oYT6uiwgDoujth2ju4mJ+TZau7SQBhtbTmUyuNYTuXnSyRn66FV0+eCgcrQ== 2330 | dependencies: 2331 | imurmurhash "^0.1.4" 2332 | signal-exit "^3.0.7" 2333 | 2334 | y18n@^5.0.5: 2335 | version "5.0.8" 2336 | resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" 2337 | integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== 2338 | 2339 | yallist@^4.0.0: 2340 | version "4.0.0" 2341 | resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" 2342 | integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== 2343 | 2344 | yargs-parser@^21.0.0, yargs-parser@^21.0.1: 2345 | version "21.0.1" 2346 | resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.0.1.tgz#0267f286c877a4f0f728fceb6f8a3e4cb95c6e35" 2347 | integrity sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg== 2348 | 2349 | yargs@^17.3.1: 2350 | version "17.5.1" 2351 | resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.5.1.tgz#e109900cab6fcb7fd44b1d8249166feb0b36e58e" 2352 | integrity sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA== 2353 | dependencies: 2354 | cliui "^7.0.2" 2355 | escalade "^3.1.1" 2356 | get-caller-file "^2.0.5" 2357 | require-directory "^2.1.1" 2358 | string-width "^4.2.3" 2359 | y18n "^5.0.5" 2360 | yargs-parser "^21.0.0" 2361 | 2362 | yocto-queue@^0.1.0: 2363 | version "0.1.0" 2364 | resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" 2365 | integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== 2366 | --------------------------------------------------------------------------------