From cf4e08963c86bc0a2ae91f62e95dcead1ea58314 Mon Sep 17 00:00:00 2001 From: Seb Aebischer <49983670+seb-cr@users.noreply.github.com> Date: Tue, 3 Jan 2023 11:27:07 +0000 Subject: [PATCH 01/28] feat: Migrate to TypeScript (#882) This is a first iteration of Lambda Wrapper v2, which contains a number of breaking changes. The key aims are: - Migrate to a TypeScript-only codebase - Reconsider how dependency injection should work so that dependency types can be inferred - Fully document the new version - Keep other changes to the package's API to a minimum For a complete list of changes, please read the [v2 migration guide](https://github.com/comicrelief/lambda-wrapper/blob/beta/docs/migration/v2.md). In terms of what we'll do with this moving forward: v2 will first be published on the beta release channel. We can then test it out in one or two real-life applications to see how easy or difficult it is to work with, and continue to release improvements to the beta channel until we are satisfied that it's ready for wider use. BREAKING CHANGE: See the [v2 migration guide](https://github.com/comicrelief/lambda-wrapper/blob/beta/docs/migration/v2.md) for full details. --- .eslintrc.yml | 7 +- .nycrc.yml | 6 + README.md | 219 ++- babel.config.js | 13 - docs/migration/v2.md | 155 ++ docs/services/BaseConfigService.md | 46 + docs/services/HTTPService.md | 6 + docs/services/LoggerService.md | 56 + docs/services/RequestService.md | 49 + docs/services/SQSService.md | 113 ++ docs/services/TimerService.md | 21 + jest.config.js | 9 + package.json | 46 +- src/Config/Dependencies.js | 26 - .../DependencyAware.class.js | 33 - .../DependencyInjection.class.js | 116 -- src/Model/CloudEvent.model.js | 133 -- src/Model/Model.model.js | 31 - src/Model/Response.model.js | 127 -- .../SQS/MarketingPreference.constraints.json | 28 - src/Model/SQS/MarketingPreference.model.js | 559 ------- src/Model/SQS/Message.model.js | 88 - src/Service/Timer.service.js | 40 - src/Wrapper/LambdaTermination.js | 29 - src/Wrapper/LambdaWrapper.js | 156 -- src/core/DependencyAwareClass.ts | 17 + src/core/DependencyInjection.ts | 109 ++ src/core/LambdaWrapper.ts | 160 ++ src/core/config.ts | 36 + src/index.js | 24 - src/index.ts | 70 + src/models/ResponseModel.ts | 109 ++ src/models/SQSMessageModel.ts | 79 + .../Status.model.js => models/StatusModel.ts} | 38 +- .../BaseConfigService.ts} | 97 +- .../HTTPService.ts} | 26 +- .../LoggerService.ts} | 158 +- .../RequestService.ts} | 173 +- .../SQS.service.js => services/SQSService.ts} | 281 ++-- src/services/TimerService.ts | 33 + src/utils/LambdaTermination.ts | 22 + .../PromisifiedDelay.ts} | 12 +- tests/.eslintrc.yml | 4 + tests/lib/mocks.js | 49 - tests/mocks/aws/index.ts | 6 + .../DependencyAware.class.test.js | 32 - .../DependencyInjection.class.test.js | 101 -- tests/unit/Model/CloudEvent.model.test.js | 58 - .../SQS/MarketingPreferences.model.test.js | 460 ----- tests/unit/Model/SQS/Message.model.test.js | 46 - tests/unit/Model/Status.model.test.js | 22 - .../BaseConfig.service.test.js.snap | 35 - .../__snapshots__/Logger.service.test.js.snap | 138 -- .../__snapshots__/SQS.service.test.js.snap | 7 - tests/unit/Wrapper/LambdaWrapper.test.js | 256 --- .../__snapshots__/LambdaWrapper.test.js.snap | 221 --- tests/unit/core/DependencyAwareClass.spec.ts | 19 + tests/unit/core/DependencyInjection.spec.ts | 69 + tests/unit/core/LambdaWrapper.spec.ts | 306 ++++ tests/unit/core/config.spec.ts | 52 + tests/unit/index.spec.ts | 97 ++ .../ResponseModel.spec.ts} | 48 +- tests/unit/models/SQSMessageModel.spec.ts | 59 + tests/unit/models/StatusModel.spec.ts | 39 + .../BaseConfigService.spec.ts} | 130 +- .../HTTPService.spec.ts} | 42 +- .../LoggerService.spec.ts} | 81 +- .../RequestService.spec.ts} | 144 +- .../SQSService.spec.ts} | 92 +- tests/unit/services/TimerService.spec.ts | 36 + .../BaseConfigService.spec.ts.snap | 35 + .../__snapshots__/HTTPService.spec.ts.snap} | 44 +- .../__snapshots__/LoggerService.spec.ts.snap | 138 ++ .../RequestService.spec.ts.snap} | 14 +- .../__snapshots__/SQSService.spec.ts.snap | 7 + .../LambdaTermination.spec.ts} | 20 +- tsconfig-base.json | 30 + tsconfig-build.json | 11 + tsconfig.json | 12 + types/alai.d.ts | 3 + yarn.lock | 1484 ++++------------- 81 files changed, 3299 insertions(+), 4704 deletions(-) create mode 100644 .nycrc.yml delete mode 100644 babel.config.js create mode 100644 docs/migration/v2.md create mode 100644 docs/services/BaseConfigService.md create mode 100644 docs/services/HTTPService.md create mode 100644 docs/services/LoggerService.md create mode 100644 docs/services/RequestService.md create mode 100644 docs/services/SQSService.md create mode 100644 docs/services/TimerService.md create mode 100644 jest.config.js delete mode 100644 src/Config/Dependencies.js delete mode 100644 src/DependencyInjection/DependencyAware.class.js delete mode 100644 src/DependencyInjection/DependencyInjection.class.js delete mode 100644 src/Model/CloudEvent.model.js delete mode 100644 src/Model/Model.model.js delete mode 100644 src/Model/Response.model.js delete mode 100644 src/Model/SQS/MarketingPreference.constraints.json delete mode 100644 src/Model/SQS/MarketingPreference.model.js delete mode 100644 src/Model/SQS/Message.model.js delete mode 100644 src/Service/Timer.service.js delete mode 100644 src/Wrapper/LambdaTermination.js delete mode 100644 src/Wrapper/LambdaWrapper.js create mode 100644 src/core/DependencyAwareClass.ts create mode 100644 src/core/DependencyInjection.ts create mode 100644 src/core/LambdaWrapper.ts create mode 100644 src/core/config.ts delete mode 100644 src/index.js create mode 100644 src/index.ts create mode 100644 src/models/ResponseModel.ts create mode 100644 src/models/SQSMessageModel.ts rename src/{Model/Status.model.js => models/StatusModel.ts} (59%) rename src/{Service/BaseConfig.service.js => services/BaseConfigService.ts} (52%) rename src/{Service/HTTP.service.js => services/HTTPService.ts} (55%) rename src/{Service/Logger.service.js => services/LoggerService.ts} (57%) rename src/{Service/Request.service.js => services/RequestService.ts} (54%) rename src/{Service/SQS.service.js => services/SQSService.ts} (52%) create mode 100644 src/services/TimerService.ts create mode 100644 src/utils/LambdaTermination.ts rename src/{Wrapper/PromisifiedDelay.js => utils/PromisifiedDelay.ts} (81%) delete mode 100644 tests/lib/mocks.js create mode 100644 tests/mocks/aws/index.ts delete mode 100644 tests/unit/DependencyInjection/DependencyAware.class.test.js delete mode 100644 tests/unit/DependencyInjection/DependencyInjection.class.test.js delete mode 100644 tests/unit/Model/CloudEvent.model.test.js delete mode 100644 tests/unit/Model/SQS/MarketingPreferences.model.test.js delete mode 100644 tests/unit/Model/SQS/Message.model.test.js delete mode 100644 tests/unit/Model/Status.model.test.js delete mode 100644 tests/unit/Service/__snapshots__/BaseConfig.service.test.js.snap delete mode 100644 tests/unit/Service/__snapshots__/Logger.service.test.js.snap delete mode 100644 tests/unit/Service/__snapshots__/SQS.service.test.js.snap delete mode 100644 tests/unit/Wrapper/LambdaWrapper.test.js delete mode 100644 tests/unit/Wrapper/__snapshots__/LambdaWrapper.test.js.snap create mode 100644 tests/unit/core/DependencyAwareClass.spec.ts create mode 100644 tests/unit/core/DependencyInjection.spec.ts create mode 100644 tests/unit/core/LambdaWrapper.spec.ts create mode 100644 tests/unit/core/config.spec.ts create mode 100644 tests/unit/index.spec.ts rename tests/unit/{Model/Response.model.test.js => models/ResponseModel.spec.ts} (54%) create mode 100644 tests/unit/models/SQSMessageModel.spec.ts create mode 100644 tests/unit/models/StatusModel.spec.ts rename tests/unit/{Service/BaseConfig.service.test.js => services/BaseConfigService.spec.ts} (66%) rename tests/unit/{Service/HTTP.service.test.js => services/HTTPService.spec.ts} (62%) rename tests/unit/{Service/Logger.service.test.js => services/LoggerService.spec.ts} (63%) rename tests/unit/{Service/Request.service.test.js => services/RequestService.spec.ts} (56%) rename tests/unit/{Service/SQS.service.test.js => services/SQSService.spec.ts} (84%) create mode 100644 tests/unit/services/TimerService.spec.ts create mode 100644 tests/unit/services/__snapshots__/BaseConfigService.spec.ts.snap rename tests/unit/{Service/__snapshots__/HTTP.service.test.js.snap => services/__snapshots__/HTTPService.spec.ts.snap} (68%) create mode 100644 tests/unit/services/__snapshots__/LoggerService.spec.ts.snap rename tests/unit/{Service/__snapshots__/Request.service.test.js.snap => services/__snapshots__/RequestService.spec.ts.snap} (58%) create mode 100644 tests/unit/services/__snapshots__/SQSService.spec.ts.snap rename tests/unit/{Wrapper/LambdaTermination.test.js => utils/LambdaTermination.spec.ts} (55%) create mode 100644 tsconfig-base.json create mode 100644 tsconfig-build.json create mode 100644 tsconfig.json create mode 100644 types/alai.d.ts diff --git a/.eslintrc.yml b/.eslintrc.yml index 1a98544e..794163b6 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -1,10 +1,8 @@ root: true extends: - - '@comicrelief/eslint-config' - - '@comicrelief/eslint-config/mixins/jsdoc' - -parser: '@babel/eslint-parser' + - '@comicrelief/eslint-config/mixins/base' + - '@comicrelief/eslint-config/mixins/ts' ignorePatterns: - node_modules @@ -14,3 +12,4 @@ ignorePatterns: rules: unicorn/prefer-node-protocol: off + '@typescript-eslint/no-explicit-any': off diff --git a/.nycrc.yml b/.nycrc.yml new file mode 100644 index 00000000..8fb1de64 --- /dev/null +++ b/.nycrc.yml @@ -0,0 +1,6 @@ +extends: '@istanbuljs/nyc-config-typescript' + +all: true +check-coverage: true +include: + - src/** diff --git a/README.md b/README.md index fab1d61f..2bd7288a 100644 --- a/README.md +++ b/README.md @@ -4,95 +4,220 @@ [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release) [![semantic-release](https://badge.fury.io/js/%40comicrelief%2Flambda-wrapper.svg)](https://www.npmjs.com/package/@comicrelief/lambda-wrapper) -When writing Serverless endpoints, we have found ourselves replicating a lot of boiler plate code to do basic actions, such as reading request variables or writing to SQS. The aim of this package is to provide a wrapper for our Lambda functions, to provide some level of dependency and configuration injection and to reduce time spent on project setup. +When writing Serverless applications, we have found ourselves replicating a lot of boilerplate code to do basic actions, such as reading request data or sending messages to SQS. The aim of this package is to provide a wrapper for our Lambda functions, to provide some level of dependency and configuration injection and to reduce time spent on project setup. -## Installation & usage +If you're coming from v1 and updating to v2, check out the [v2 migration guide](docs/migration/v2.md). -Install via npm: +## Getting started -```bash -npm install --save @comicrelief/lambda-wrapper -``` - -Or via yarn: +Install via npm or Yarn: ```bash +npm i @comicrelief/lambda-wrapper +# or yarn add @comicrelief/lambda-wrapper ``` -You can then wrap your lambdas as follows. +You can then wrap your Lambda handler functions like this: -```js -import { - LambdaWrapper, +```ts +// src/action/Hello.ts +import lambdaWrapper, { ResponseModel, RequestService, } from '@comicrelief/lambda-wrapper'; -export default LambdaWrapper({}, (di, request, done) => { - const response = new ResponseModel({}, 200, `hello ${request.get('name', 'nobody')}`); - done(null, response.generate()); +export default lambdaWrapper.wrap(async (di) => { + const request = di.get(RequestService); + return ResponseModel.generate( + {}, + 200, + `hello ${request.get('name', 'nobody')}`, + ); }); ``` -## Serverless Offline & SQS Emulation +Here we've used the default export `lambdaWrapper` which is a preconfigured instance that can be used out of the box. You'll likely want to add your own dependencies and service config using the `configure` method: -Serverless Offline only emulates API Gateway and Lambda, so publishing an SQS message would use the real SQS queue and trigger the consumer function (if any) in AWS. When working with offline code, you often want the local functions to be invoked instead. +```ts +// src/config/LambdaWrapper.ts +import lambdaWrapper from '@comicrelief/lambda-wrapper'; -Offline SQS behaviour can be configured by setting the `LAMBDA_WRAPPER_OFFLINE_SQS_MODE` environment variable. Available modes are: +export default lambdaWrapper.configure({ + // your config goes here +}); +``` -- `direct` (the default): invokes the consumer function directly via an offline Lambda endpoint -- `local`: send messages to an offline SQS endpoint, such as Localstack -- `aws`: no special handling of SQS offline; messages will be sent to AWS +`configure` returns a new Lambda Wrapper instance with the given configuration. You'll want to export it and then use this when wrapping your handler functions. -Details of each mode are documented in the sections below. When you send a message using `SQSService.prototype.publish`, it will check which mode to use and dispatch the message appropriately. These modes take effect only when running offline (as defined by `DependencyInjection.prototype.isOffline`). In a deployed environment, SQS messages will always be sent to AWS SQS. +Read the next section to see what goes inside the config object! -### Direct Lambda mode +If you want to start from scratch without the built-in dependencies, you can use the `LambdaWrapper` constructor directly. -This is the default mode if `LAMBDA_WRAPPER_OFFLINE_SQS_MODE` is not set. A Lambda client will be created and the message will be delivered to the offline Lambda endpoint, effectively running the consumer function _immediately_ as part of the original Lambda invocation. This works very well in the offline environment because invoking a Lambda function will trigger its whole (local) execution tree. +```ts +// src/config/LambdaWrapper.ts +import { LambdaWrapper } from '@comicrelief/lambda-wrapper'; -To take advantage of SQS emulation, you will need to define the following in the implementing service: +export default new LambdaWrapper({ + // your config goes here +}); +``` -**QUEUE_CONSUMERS** +## Dependencies -In your `src/Config/Configuration` define a `QUEUE_CONSUMERS` object. `QUEUE_CONSUMERS` will map the queue name to the fully qualified `FunctionName` that we want to trigger when messages are published to that queue. +Lambda Wrapper comes with some commonly used dependencies built in: -You will need to export `QUEUE_CONSUMERS` as part of your default export, alongside `DEFINITIONS`, `DEPENDENCIES`, `QUEUES`, `QUEUE_DEFINITIONS`, etc. +- [HTTPService](docs/services/HTTPService.md) +- [LoggerService](docs/services/LoggerService.md) +- [RequestService](docs/services/RequestService.md) +- [SQSService](docs/services/SQSService.md) +- [TimerService](docs/services/TimerService.md) -A `Configuration` example can be found in the `serverless-prize-platform` repository [here](https://github.com/comicrelief/serverless-prize-platform/blob/master/src/Config/Configuration.js). +Access these via dependency injection. You've already seen an example of this where we got `RequestService`. Pass the dependency class to `di.get()` to get its instance: -**process.env.SERVICE_LAMBDA_URL** +```ts +export default lambdaWrapper.wrap(async (di) => { + const request = di.get(RequestService); + const sqs = di.get(SQSService); + // ... +}); +``` -While creating the Lambda client, we need to point it to our offline environment. LambdaWrapper will take care of the specifics, but it will need to know the Lambda endpoint URL. This _can_ and _must_ be specified via the `SERVICE_LAMBDA_URL` environment variable. +To add your own dependencies, first extend `DependencyAwareClass`. -The URL is likely to be your localhost URL and the next available port from the offline API Gateway. So, if you are running Serverless Offline on `http://localhost:3001`, the Lambda URL is likely to be `http://localhost:3002`. You can check the port in the output during Serverless Offline startup by looking for the following line: +```ts +// src/services/MyService.ts +import { DependencyAwareClass } from '@comicrelief/lambda-wrapper'; - offline: Offline [http for lambda] listening on http://localhost:3002 +export default class MyService extends DependencyAwareClass { + doSomething() { + // ... + } +} +``` -#### Caveats +Then add it to your Lambda Wrapper configuration in the `dependencies` key. -1. You will be running the SQS-triggered lambdas in the same Serverless Offline context as your triggering lambda. Expect logs from both lambdas in the Serverless Offline output. +```ts +// src/config/LambdaWrapper.ts +import lambdaWrapper from '@comicrelief/lambda-wrapper'; -2. If you await `sqs.publish` you will effectively wait until all SQS-triggered lambdas (and possibly their own SQS-triggered lambdas) have all completed. This is necessary to avoid any pending execution (i.e. the lambda terminating before its async processes are completed). +import MyService from '@/src/services/MyService'; -3. If the triggered lambda incurs an exception, this will be propagated upstream, effectively killing the execution of the calling lambda. +export default lambdaWrapper.configure({ + dependencies: { + MyService, + }, +}); +``` + +Now you can use it inside your handler functions and other dependencies! + +```ts +// src/action/DoSomething.ts +import lambdaWrapper from '@/src/config/LambdaWrapper'; +import MyService from '@/src/services/MyService'; + +export default lambdaWrapper.wrap(async (di) => { + di.get(MyService).doSomething(); +}); +``` + +## Service config + +Some dependencies need their own config. This goes in per-service keys within your Lambda Wrapper config. For an example, see [SQSService](docs/services/SQSService.md) which uses the `sqs` key. + +```ts +export default lambdaWrapper.configure({ + dependencies: { + // your dependencies + }, + sqs: { + // your SQSService config + }, + // ... other configs ... +}); +``` + +To use config with your own dependencies, you need to do three things: + +1. Define the key and type of your config object. + + Using `SQSService` as an example, we have the `sqs` key which has the `SQSServiceConfig` type: + + ```ts + export interface SQSServiceConfig { + queues?: Record; + queueConsumers?: Record; + } + ``` + +2. Define a type that can be applied to a Lambda Wrapper config. + + This simply combines the key and type defined in step 1. Conventionally we name these `With...` types. + + ```ts + export interface WithSQSServiceConfig { + sqs?: SQSServiceConfig; + } + ``` + + In the case of `SQSService`, the `sqs` key is optional because this dependency is included by default and not all applications need it. If your dependency requires config in order to work, you can make this a required key. + +3. In your dependency constructor, cast the config to this type. + + ```ts + export default class SQSService extends DependencyAwareClass { + constructor(di: DependencyInjection) { + super(di); + + const config = (this.di.config as WithSQSServiceConfig).sqs; + // Bear in mind that because the `sqs` key is optional, the type of + // `config` will be `SQSServiceConfig | undefined`. Take care when + // accessing its properties! You can use optional chaining: + const queues = config?.queues || {}; + // ... + } + } + ``` + +When you go to configure your Lambda Wrapper, you can now include your dependency's config type in the generic for `configure` to get IntelliSense completions and type checking for your config keys. + +```ts +lambdaWrapper.configure({ + sqs: { + queues: 42 // Oops! This will be flaggeed as a type error by TypeScript + }, +}); +``` + +You can combine types for multiple dependencies if needed using `&`: + +```ts +lambdaWrapper.configure({ + sqs: { + // SQSService config + }, + other: { + // OtherService config + }, +}); +``` -### Local SQS mode +## Development -Use this mode by setting `LAMBDA_WRAPPER_OFFLINE_SQS_MODE=local`. Messages will still be sent to an SQS queue, but using a locally simulated version instead of AWS. This allows you to test your service using a tool like Localstack. +### Testing -By default, messages will be sent to a SQS service running on `localhost:4576`. If you need to change the hostname, you can set `process.env.LAMBDA_WRAPPER_OFFLINE_SQS_HOST`. -Also, if you need to change the port, you can set `process.env.LAMBDA_WRAPPER_OFFLINE_SQS_PORT`. +Run `yarn test` to run the unit tests. -### AWS SQS mode +When writing a bugfix, start by writing a test that reproduces the problem. It should fail with the current version of Lambda Wrapper, and pass once you've implemented the fix. -Use this mode by setting `LAMBDA_WRAPPER_OFFLINE_SQS_MODE=aws`. Messages will be sent to the real queue in AWS. This mode is useful when a queue is consumed by an external service, rather than another function in the service under test. +When adding a feature, ensure it's covered by tests that adequately define its behaviour. -In order for queue URLs to be correctly constructed, you must either: +### Linting -- set `AWS_ACCOUNT_ID` to the account ID that hosts your queue; or -- invoke offline functions via the Lambda API, passing a context that contains a realistic `invokedFunctionArn` including the account ID. +Run `yarn lint` to check code style complies to our standard. Many problems can be auto-fixed using `yarn lint --fix`. -## Semantic release +### Releases Release management is automated using [semantic-release](https://www.npmjs.com/package/semantic-release). diff --git a/babel.config.js b/babel.config.js deleted file mode 100644 index b57ac9e8..00000000 --- a/babel.config.js +++ /dev/null @@ -1,13 +0,0 @@ -module.exports = { - presets: [ - [ - '@babel/preset-env', - { - targets: { - node: 'current', - }, - }, - ], - ], - plugins: ['@babel/plugin-syntax-flow', '@babel/plugin-transform-flow-strip-types'], -}; diff --git a/docs/migration/v2.md b/docs/migration/v2.md new file mode 100644 index 00000000..2690540c --- /dev/null +++ b/docs/migration/v2.md @@ -0,0 +1,155 @@ +# Migrating from v1 to v2 + +This doc summarises the breaking changes introduced in v2 and what you need to do to update your projects to work with it. + +- [Configuration](#configuration) +- [Wrapping a function](#wrapping-a-function) +- [Dependency injection](#dependency-injection) +- [Models](#models) + +## Configuration + +v1 required several consts with shouty names. In v2 these are replaced with a single config object with camel-case keys. You pass this to the new `configure` method to get a configured instance of `LambdaWrapper`. + +Instead of this: + +```js +// src/config/Configuration.js +import { DEFINITIONS as CORE_DEFINITIONS } from '@comicrelief/lambda-wrapper'; + +import { MyService } from '@/src/services/MyService'; + +export const DEFINITIONS = { + ...CORE_DEFINITIONS, + MY_SERVICE: 'MY_SERVICE', +}; + +export const DEPENDENCIES = { + [DEFINITIONS.MY_SERVICE]: MyService, +}; + +export const QUEUE_DEFINITIONS = { + MY_QUEUE: 'MY_QUEUE', +}; + +export const QUEUES = { + [QUEUE_DEFINITIONS.MY_QUEUE]: process.env.SQS_MY_QUEUE, +}; + +export default { + DEFINITIONS, + DEPENDENCIES, + QUEUE_DEFINITIONS, + QUEUES, +}; +``` + +do this: + +```ts +// src/config/LambdaWrapper.ts +import lw from '@comicrelief/lambda-wrapper'; + +import { MyService } from '@/src/services/MyService'; + +export const lambdaWrapper = lw.configure({ + dependencies: { + MyService, + }, + sqs: { + queues: { + myQueue: process.env.SQS_MY_QUEUE, + }, + }, +}); +``` + +## Wrapping a function + +Rather than passing in a config object everywhere you use Lambda Wrapper, you now simply use the configured instance. + +v2 also drops support for callback-style async. Use promises instead. + +Finally, there is no longer a `request` parameter provided to your wrapped function. You can get this from `di` if you need it. + +Instead of this: + +```js +import { LambdaWrapper } from '@comicrelief/lambda-wrapper'; + +import { CONFIGURATION, DEFINITIONS } from '@/src/config/Configuration'; + +export default LambdaWrapper(CONFIGURATION, (di, request, done) => { + // ... + done(null, response); +}); +``` + +do this: + +```ts +import lambdaWrapper from '@/src/config/LambdaWrapper'; + +export default lambdaWrapper.wrap(async (di) => { + const request = di.get(RequestService); + // ... +}); +``` + +If your project doesn't add any additional services to dependency injection, you can also now use `lambdaWrapper` straight out of the box: + +```ts +import lambdaWrapper from '@comicrelief/lambda-wrapper'; + +export default lambdaWrapper.wrap(async (di) => { + // ... +}); +``` + +## Dependency injection + +As you'll have seen in the above examples, dependencies are no longer identified by a `DEFINITIONS` string. `get` now takes the dependency class directly. + +Instead of this: + +```js +import { LambdaWrapper } from '@comicrelief/lambda-wrapper'; + +import { CONFIGURATION, DEFINITIONS } from '@/src/config/Configuration'; + +export default LambdaWrapper(CONFIGURATION, (di, request, done) => { + const logger = di.get(DEFINITIONS.LOGGER); + const myService = di.get(DEFINITIONS.MY_SERVICE); + // ... +}); +``` + +do this: + +```ts +import { LoggerService, RequestService } from '@comicrelief/lambda-wrapper'; + +import lambdaWrapper from '@/src/config/LambdaWrapper'; +import { MyService } from '@/src/services/MyService'; + +export default lambdaWrapper.wrap(async (di) => { + const logger = di.get(LoggerService); + const request = di.get(RequestService); + const myService = di.get(MyService); + // ... +}); +``` + +`get` will also always throw an error when used in a constructor to avoid surprises where other dependencies may be `undefined`. Instead of storing references to dependencies in class members, `get` them just before use. + +`definitions` has been removed. + +`getEvent`, `getContext` and `getConfiguration` have been deprecated and will be removed in a future major release. Use the `event`, `context` and `config` properties directly. + +## Models + +The `Model` base class has been removed. It's hard to make it type-safe (it tries to dynamically call setter methods) and we do modelling and validation differently now, using our [data-models](https://github.com/comicrelief/data-models) repo which is based around [Yup](https://github.com/jquense/yup). + +The `MarketingPreference` model is removed, as this is application-specific and again is replaced by our [data-models](https://github.com/comicrelief/data-models) repo. + +Other models (`ResponseModel`, `SQSMessageModel`, `StatusModel`) are unaffected except that they no longer inherit from a common `Model` class. diff --git a/docs/services/BaseConfigService.md b/docs/services/BaseConfigService.md new file mode 100644 index 00000000..0a6cb3f1 --- /dev/null +++ b/docs/services/BaseConfigService.md @@ -0,0 +1,46 @@ +# BaseConfigService + +Instead of reimplementing the service status get and set logic across several services, Lambda Wrapper provides a Status service that handles these two operations for us. + +## Usage + +This class is to be extended by the implementing services so that `defaultConfig` and possibly `s3Config` can be overriden / extended. As such, it is not included as a dependency by default and must be explicitly added. + +Example implementation with validation: + +```ts +// src/services/ConfigService.ts +import { BaseConfigService } from '@comicrelief/lambda-wrapper'; + +import { ConfigModel, ConfigProps } from '@/src/models/Config'; + +export default class ConfigService extends BaseConfigService { + async put(config): Promise { + const validated = await ConfigModel.validate(config); + return super.put(validated); + } + + async get(): Promise { + const config = await super.get(); + return ConfigModel.validate(config); + } +} +``` + +Config is typed as `unknown` in the base class since you shouldn't trust what's in the bucket. Override the `get` and `put` methods to pass the results through some validation to ensure the config is valid and can safely be typed. + +Then add to your Lambda Wrapper dependencies: + +```ts +// src/config/LambdaWrapper.ts +import lambdaWrapper from '@comicrelief/lambda-wrapper'; + +import ConfigService from '@/src/services/ConfigService'; + +export default lambdaWrapper.configure({ + dependencies: { + ConfigService, + // ... + }, +}); +``` diff --git a/docs/services/HTTPService.md b/docs/services/HTTPService.md new file mode 100644 index 00000000..49e64deb --- /dev/null +++ b/docs/services/HTTPService.md @@ -0,0 +1,6 @@ +# HTTPService + +Wrapper for `axios.request` that: + +- sets a default timeout of 10 seconds +- forwards a `x-comicrelief-test-metadata` header if one was provided in the request from upstream diff --git a/docs/services/LoggerService.md b/docs/services/LoggerService.md new file mode 100644 index 00000000..5c877590 --- /dev/null +++ b/docs/services/LoggerService.md @@ -0,0 +1,56 @@ +# LoggerService + +Provides logging and integrations with our monitoring tools. + +For logging we use [Winston](https://github.com/winstonjs/winston). Errors will also be sent to [Sentry](https://sentry.io/) and [Epsagon](https://epsagon.com/) if those are configured. + +## Usage + +The logger exposes various methods that you can pass messages or objects to for logging: + +```ts +import lambdaWrapper, { LoggerService } from '@comicrelief/lambda-wrapper'; + +export default lambdaWrapper.wrap(async (di) => { + const logger = di.get(LoggerService); + + // general log message + logger.info('Doing something'); + + // tag the trace so we can find certain tracess more easily in Epsagon + logger.label('flag'); + logger.metric('transactionId', value); + + try { + // do something that might throw an error... + } catch (error) { + // log the error and flag the trace on Epsagon and Sentry + logger.error(error); + + // alternatively, use `warning` if this error is not relevant in staging + // (see Soft Warnings below) + logger.warning(error); + } +}); +``` + +## Configuration + +### Soft warnings + +The `warning` method is equivalent to `error` by default, but can be switched to use `info` by setting `LOGGER_SOFT_WARNING=1` in the environment. + +This is handy for muting certain errors in staging, where we expect our integration tests to cause a lot of errors deliberately that would otherwise spam us with Epsagon alerts. + +### Epsagon + +To configure Epsagon, set the following environment variables: + +- `EPSAGON_TOKEN` – your access token +- `EPSAGON_SERVICE_NAME` – the application name (including stage) to record traces under + +### Sentry + +To configure Sentry, set the following environment variables: + +- `RAVEN_DSN` – your Sentry DSN URL diff --git a/docs/services/RequestService.md b/docs/services/RequestService.md new file mode 100644 index 00000000..eefef05d --- /dev/null +++ b/docs/services/RequestService.md @@ -0,0 +1,49 @@ +# RequestService + +Provides access to components of the HTTP request being handled. + +## Usage + +Since Lambda Wrapper v2, the `RequestService` instance is no longer passed as an argument to your wrapped handler, and must be obtained via `di`. + +```ts +import lambdaWrapper, { RequestService } from '@comicrelief/lambda-wrapper'; + +export default lambdaWrapper.wrap(async (di) => { + const request = di.get(RequestService); + // get the 'name' request parameter, defaulting to 'world' if not set + const name = request.get('name', 'world'); + return ResponseModel.generate({}, 200, `Hello, ${name}`); +}); +``` + +### Headers + +- `getAllHeaders` returns an object containing all headers +- `getHeader` returns the value of an HTTP header +- `getAuthorizationToken` extracts a Bearer token from the `Authorization` header + +### Body + +For requests that submit data in their body (POST, PATCH, PUT), + +- `getAll` parses the body according to the `Content-Type` header +- `get` fetches a single value from the body + +### URL parameters + +For other request methods without a body (GET, HEAD, DELETE), + +- `getAll` returns an object containing all query string parameters +- `get` fetches a single query string parameter + +For all requests, + +- `getPathParameter` fetches a path parameter value + +### Client info + +Some limited information about the client making the request is available. + +- `getIp` returns the request's source IP address +- `getUserBrowserAndDevice` returns user agent details diff --git a/docs/services/SQSService.md b/docs/services/SQSService.md new file mode 100644 index 00000000..de10c1fa --- /dev/null +++ b/docs/services/SQSService.md @@ -0,0 +1,113 @@ +# SQSService + +## Usage + +SQS queues are configured inside an `sqs` key in your Lambda Wrapper config. + +The `queues` key maps short friendly names to the full SQS queue name. Usually we define queue names in our `serverless.yml` and provide them to the application via environment variables. + +```ts +import lambdaWrapper, { WithSQSServiceConfig } from '@comicrelief/lambda-wrapper'; + +export default lambdaWrapper.configure({ + sqs: { + queues: { + // add an entry for each queue mapping to its AWS name + submissions: process.env.SQS_QUEUE_SUBMISSIONS, + }, + }, +}); +``` + +This config is optional – not every application uses SQS! + +You can then send messages to a queue within your Lambda handler using the `publish` method. + +```ts +import { SQSService } from '@comicrelief/lambda-wrapper'; + +import lambdaWrapper from '@/src/config/LambdaWrapper'; + +export default lambdaWrapper.wrap(async (di) => { + const sqs = di.get(SQSService); + const message = { data: 'Hello SQS!' }; + await sqs.publish('submissions', message); +}); +``` + +## Serverless Offline & SQS Emulation + +Serverless Offline only emulates API Gateway and Lambda, so sending an SQS message would use the real SQS queue and trigger the consumer function (if any) in AWS. When working with offline code, you often want the local functions to be invoked instead. + +Offline SQS behaviour can be configured by setting the `LAMBDA_WRAPPER_OFFLINE_SQS_MODE` environment variable. Available modes are: + +- `direct` (the default): invokes the consumer function directly via an offline Lambda endpoint +- `local`: send messages to an offline SQS endpoint, such as Localstack +- `aws`: no special handling of SQS offline; messages will be sent to AWS + +Details of each mode are documented in the sections below. When you send a message using `SQSService.prototype.publish`, it will check which mode to use and dispatch the message appropriately. These modes take effect only when running offline (as defined by `DependencyInjection.prototype.isOffline`). In a deployed environment, SQS messages will always be sent to AWS SQS. + +### Direct Lambda mode + +This is the default mode if `LAMBDA_WRAPPER_OFFLINE_SQS_MODE` is not set. A Lambda client will be created and the message will be delivered to the offline Lambda endpoint, effectively running the consumer function _immediately_ as part of the original Lambda invocation. This works very well in the offline environment because invoking a Lambda function will trigger its whole (local) execution tree. + +To take advantage of SQS emulation, you will need to do the following in your project: + +- Include the `queueConsumers` key in your `SQSService` config. + + This maps the queue name to the fully qualified `FunctionName` that we want to trigger when messages are sent to that queue. + + Extending the example from above, your config might look like this: + + ```ts + const lambdaWrapper = lw.configure({ + sqs: { + queues: { + // Add an entry for each queue with its AWS name. + // Usually we define queue names in our serverless.yml and provide them + // to the application via environment variables. + submissions: process.env.SQS_QUEUE_SUBMISSIONS, + }, + queueConsumers: { + // See section below about offline SQS emulation. + submissions: 'SubmissionConsumer', + }, + } + }); + ``` + + Now when a message is sent using `sqs.publish('submissions', message)`, the `SubmissionConsumer` function will be directly invoked to consume the message. + +- Set `process.env.SERVICE_LAMBDA_URL`. + + While creating the Lambda client, we need to point it to our offline environment. Lambda Wrapper will take care of the specifics, but it will need to know the Lambda endpoint URL. This _can_ and _must_ be specified via the `SERVICE_LAMBDA_URL` environment variable. + + The URL is likely to be your localhost URL and the next available port from the offline API Gateway. So, if you are running Serverless Offline on `http://localhost:3001`, the Lambda URL is likely to be `http://localhost:3002`. You can check the port in the output during Serverless Offline startup by looking for the following line: + + ```plaintext + offline: Offline [http for lambda] listening on http://localhost:3002 + ``` + +#### Caveats + +1. You will be running the SQS-triggered lambdas in the same Serverless Offline context as your triggering lambda. Expect logs from both lambdas in the Serverless Offline output. + +2. If you await `sqs.publish` you will effectively wait until all SQS-triggered lambdas (and possibly their own SQS-triggered lambdas) have all completed. This is necessary to avoid any pending execution (i.e. the lambda terminating before its async processes are completed). + +3. If the triggered lambda incurs an exception, this will be propagated upstream, effectively killing the execution of the calling lambda. + +### Local SQS mode + +Use this mode by setting `LAMBDA_WRAPPER_OFFLINE_SQS_MODE=local`. Messages will still be sent to an SQS queue, but using a locally simulated version instead of AWS. This allows you to test your service using a tool like Localstack. + +By default, messages will be sent to a SQS service running on `localhost:4576`. If you need to change the hostname, you can set `process.env.LAMBDA_WRAPPER_OFFLINE_SQS_HOST`. +Also, if you need to change the port, you can set `process.env.LAMBDA_WRAPPER_OFFLINE_SQS_PORT`. + +### AWS SQS mode + +Use this mode by setting `LAMBDA_WRAPPER_OFFLINE_SQS_MODE=aws`. Messages will be sent to the real queue in AWS. This mode is useful when a queue is consumed by an external service, rather than another function in the service under test. + +In order for queue URLs to be correctly constructed, you must either: + +- set `AWS_ACCOUNT_ID` to the account ID that hosts your queue; or +- invoke offline functions via the Lambda API, passing a context that contains a realistic `invokedFunctionArn` including the account ID. diff --git a/docs/services/TimerService.md b/docs/services/TimerService.md new file mode 100644 index 00000000..9c256712 --- /dev/null +++ b/docs/services/TimerService.md @@ -0,0 +1,21 @@ +# TimerService + +Timer helper that can be used to measure how long operations take. + +## Usage + +Start and stop the timer using the `start` and `stop` methods. + +```ts +import lambdaWrapper, { TimerService } from '@comicrelief/lambda-wrapper'; + +export default lambdaWrapper.wrap(async (di) => { + const timer = di.get(TimerService); + + const timerId = 'someLongSlowOperation'; + timer.start(timerId); + await someLongSlowOperation(); + timer.stop(timerId); + // logs 'someLongSlowOperation took 12345 ms to complete' +}); +``` diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 00000000..4ce18349 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,9 @@ +/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + testMatch: ['/tests/unit/**/*.spec.ts'], + moduleNameMapper: { + '^@/(.*)$': '/$1', + }, +}; diff --git a/package.json b/package.json index 08eae247..2a84cf35 100644 --- a/package.json +++ b/package.json @@ -3,36 +3,46 @@ "version": "0.0.0-see.readme.for.semantic.release.process", "description": "Lambda wrapper for all Comic Relief Serverless Projects", "main": "dist/index.js", + "author": "Adam Clark", + "license": "ISC", + "repository": { + "type": "git", + "url": "git+https://github.com/comicrelief/lambda-wrapper.git" + }, + "files": [ + "dist" + ], "scripts": { + "prepare": "yarn clean && yarn build", + "build": "tsc -p tsconfig-build.json", + "clean": "rm -rf dist", "lint": "eslint src tests", - "test": "jest --coverage", - "build": "babel src --out-dir dist --copy-files", - "prepublish": "yarn build" + "test": "jest", + "coverage": "yarn test --coverage" }, - "author": "Adam Clark", - "license": "ISC", "devDependencies": { - "@babel/cli": "^7.18.10", - "@babel/core": "^7.18.10", - "@babel/eslint-parser": "^7.18.9", - "@babel/node": "^7.18.10", - "@babel/plugin-syntax-flow": "^7.18.6", - "@babel/plugin-transform-flow-strip-types": "^7.18.9", - "@babel/plugin-transform-react-jsx": "^7.18.10", - "@babel/preset-env": "^7.18.10", "@comicrelief/eslint-config": "^2.0.3", + "@istanbuljs/nyc-config-typescript": "^1.0.2", + "@types/async": "^3.2.15", + "@types/aws-lambda": "^8.10.102", "@types/jest": "^28.1.6", + "@types/node": "14", + "@types/useragent": "^2.3.1", + "@types/uuid": "^8.3.4", + "@types/xml2js": "^0.4.11", + "@typescript-eslint/eslint-plugin": "^5.33.0", + "@typescript-eslint/parser": "^5.33.0", "aws-sdk": "^2.1194.0", - "babel-jest": "^28.1.3", "eslint": "^8.22.0", - "eslint-plugin-flowtype": "^8.0.3", "eslint-plugin-import": "^2.25.2", "eslint-plugin-jsdoc": "^39.3.2", - "eslint-plugin-sonarjs": "^0.13.0", - "eslint-plugin-unicorn": "^42.0.0", "jest": "^28.1.3", "nyc": "^15.1.0", - "semantic-release": "^19.0.3" + "semantic-release": "^19.0.3", + "ts-jest": "^28.0.8", + "ts-node": "^10.9.1", + "tsconfig-paths": "^4.1.0", + "typescript": "^4.7.4" }, "peerDependencies": { "aws-sdk": "^2.831.0" diff --git a/src/Config/Dependencies.js b/src/Config/Dependencies.js deleted file mode 100644 index ab2680ee..00000000 --- a/src/Config/Dependencies.js +++ /dev/null @@ -1,26 +0,0 @@ -import HTTPService from '../Service/HTTP.service'; -import LoggerService from '../Service/Logger.service'; -import RequestService from '../Service/Request.service'; -import SQSService from '../Service/SQS.service'; -import TimerService from '../Service/Timer.service'; - -export const DEFINITIONS = { - HTTP: 'HTTP', - LOGGER: 'LOGGER', - REQUEST: 'REQUEST', - SQS: 'SQS', - TIMER: 'TIMER', -}; - -export const DEPENDENCIES = { - [DEFINITIONS.HTTP]: HTTPService, - [DEFINITIONS.LOGGER]: LoggerService, - [DEFINITIONS.REQUEST]: RequestService, - [DEFINITIONS.SQS]: SQSService, - [DEFINITIONS.TIMER]: TimerService, -}; - -export default { - DEFINITIONS, - DEPENDENCIES, -}; diff --git a/src/DependencyInjection/DependencyAware.class.js b/src/DependencyInjection/DependencyAware.class.js deleted file mode 100644 index 61d7b800..00000000 --- a/src/DependencyInjection/DependencyAware.class.js +++ /dev/null @@ -1,33 +0,0 @@ -import DependencyInjection from './DependencyInjection.class'; - -/** - * DependencyAwareClass Class - */ -export default class DependencyAwareClass { - /** - * DependencyAwareClass constructor - * - * @param {DependencyInjection} di - */ - constructor(di: DependencyInjection) { - this.di = di; - } - - /** - * Get Dependency Injection Container - * - * @returns {DependencyInjection} - */ - getContainer() { - return this.di; - } - - /** - * Shortcut for `this.getContainer().definitions` - * - * @returns {object} - */ - get definitions() { - return this.getContainer().definitions; - } -} diff --git a/src/DependencyInjection/DependencyInjection.class.js b/src/DependencyInjection/DependencyInjection.class.js deleted file mode 100644 index 16ac73bd..00000000 --- a/src/DependencyInjection/DependencyInjection.class.js +++ /dev/null @@ -1,116 +0,0 @@ -import { DEFINITIONS, DEPENDENCIES } from '../Config/Dependencies'; - -/** - * DependencyInjection class - */ -export default class DependencyInjection { - /** - * DependencyInjection constructor - * - * @param configuration - * @param event - * @param context - */ - constructor(configuration, event, context) { - this.event = event; - this.context = context; - - this.dependencies = {}; - this.configuration = configuration; - - for (let x = 0; x <= 1; x += 1) { - // Iterate over lapper dependencies and add to container - Object.keys(DEFINITIONS).forEach((dependencyKey) => { - this.dependencies[dependencyKey] = new DEPENDENCIES[dependencyKey](this); - }); - - // Iterate over child dependencies and add to container - if (typeof configuration.DEPENDENCIES !== 'undefined') { - Object.keys(configuration.DEPENDENCIES).forEach((dependencyKey) => { - this.dependencies[dependencyKey] = new configuration.DEPENDENCIES[dependencyKey](this); - }); - } - } - } - - /** - * Get Dependency - * - * @param definition - * @returns {*} - */ - get(definition) { - if (typeof this.dependencies[definition] === 'undefined') { - throw new TypeError(`${definition} does not exist in di container`); - } - - return this.dependencies[definition]; - } - - /** - * Get Event - * - * @returns {*} - */ - getEvent() { - return this.event; - } - - /** - * Get Context - * - * @returns {*} - */ - getContext() { - return this.context; - } - - /** - * Get Configuration - * - * @param definition string - * @returns {*} - */ - getConfiguration(definition = null) { - if (definition !== null && typeof this.configuration[definition] === 'undefined') { - return null; - } - if (typeof this.configuration[definition] !== 'undefined') { - return this.configuration[definition]; - } - - return this.configuration; - } - - /** - * Check whether the function - * is being executed in a serverless-offline context - * - * @returns {boolean} - */ - get isOffline() { - const context = this.getContext() || {}; - - if (!Object.prototype.hasOwnProperty.call(context, 'invokedFunctionArn')) { - return true; - } - - if (context.invokedFunctionArn.includes('offline')) { - return true; - } - - return !!process.env.USE_SERVERLESS_OFFLINE; - } - - /** - * Returns the definitions - * associated to this DependencyInjection - * so that services can refer to them - * without causing circular imports. - * - * @returns {object} - */ - get definitions() { - return this.configuration.DEFINITIONS; - } -} diff --git a/src/Model/CloudEvent.model.js b/src/Model/CloudEvent.model.js deleted file mode 100644 index 98776c98..00000000 --- a/src/Model/CloudEvent.model.js +++ /dev/null @@ -1,133 +0,0 @@ -import { v4 as UUID } from 'uuid'; - -import Model from './Model.model'; - -/** - * CloudEventModel class - * Class to implement cloud events - https://github.com/cloudevents/spec/blob/master/spec.md - */ -export default class CloudEventModel extends Model { - /** - * CloudEventModel constructor - */ - constructor() { - super(); - - this.cloudEventsVersion = '0.1'; - this.eventType = ''; - this.source = ''; - this.eventID = UUID(); - this.eventTime = new Date().toISOString(); - this.extensions = {}; - this.contentType = 'application/json'; - this.data = {}; - } - - /** - * Get Cloud Events Version - * - * @returns {number} - */ - getCloudEventsVersion(): string { - return this.cloudEventsVersion; - } - - /** - * Get event type - * - * @returns {string|*} - */ - getEventType() { - return this.eventType; - } - - /** - * Set event type - * - * @param value string - */ - setEventType(value: string) { - this.eventType = value; - } - - /** - * Get source - * - * @returns {string|*} - */ - getSource() { - return this.source; - } - - /** - * Set source - * - * @param value string - */ - setSource(value: string) { - this.source = value; - } - - /** - * Get event id - * - * @returns {*|string} - */ - getEventID() { - return this.eventID; - } - - /** - * Get event time - * - * @returns {*|string} - */ - getEventTime() { - return this.eventTime; - } - - /** - * Get extensions - * - * @returns {{}|*} - */ - getExtensions() { - return this.extensions; - } - - /** - * Set extensions - * - * @param value object - */ - setExtensions(value: object) { - this.extensions = value; - } - - /** - * Get content type - * - * @returns {string} - */ - getContentType() { - return this.contentType; - } - - /** - * Get data - * - * @returns {{}|*} - */ - getData() { - return this.data; - } - - /** - * Set data - * - * @param value object - */ - setData(value: object) { - this.data = value; - } -} diff --git a/src/Model/Model.model.js b/src/Model/Model.model.js deleted file mode 100644 index a2e17452..00000000 --- a/src/Model/Model.model.js +++ /dev/null @@ -1,31 +0,0 @@ -/* eslint-disable class-methods-use-this */ -import validate from 'validate.js/validate'; - -/** - * Model base class - */ -export default class Model { - /** - * Instantiate a function with a value if defined - * - * @param classFunctionName string - * @param value mixed - */ - instantiateFunctionWithDefinedValue(classFunctionName, value) { - if (typeof value !== 'undefined') { - this[classFunctionName](value); - } - } - - /** - * Validate values against constraints - * - * @param values object - * @param constraints object - * @returns {boolean} - */ - validateAgainstConstraints(values: object, constraints: object): boolean { - const validation = validate(values, constraints); - return typeof validation === 'undefined'; - } -} diff --git a/src/Model/Response.model.js b/src/Model/Response.model.js deleted file mode 100644 index 78fd9a26..00000000 --- a/src/Model/Response.model.js +++ /dev/null @@ -1,127 +0,0 @@ -import Model from './Model.model'; - -/** - * - * @type {object} - */ -export const RESPONSE_HEADERS = { - 'Content-Type': 'application/json', - 'Access-Control-Allow-Origin': '*', // Required for CORS support to work - 'Access-Control-Allow-Credentials': true, // Required for cookies, authorization headers with HTTPS -}; - -/** - * Default message provided as part of response - * - * @type {string} - */ -export const DEFAULT_MESSAGE = 'success'; - -/** - * class ResponseModel - */ -export default class ResponseModel extends Model { - /** - * ResponseModel Constructor - * - * @param data - * @param code - * @param message - */ - constructor(data = null, code = null, message = null) { - super(); - - this.body = { - data: data !== null ? data : {}, - message: message !== null ? message : DEFAULT_MESSAGE, - }; - this.code = code !== null ? code : {}; - } - - /** - * Add or update a body variable - * - * @param variable - * @param value - */ - setBodyVariable(variable: string, value) { - this.body[variable] = value; - } - - /** - * Set Data - * - * @param data - */ - setData(data: object) { - this.body.data = data; - } - - /** - * Set Status Code - * - * @param code - */ - setCode(code: number) { - this.code = code; - } - - /** - * Get Status Code - * - * @returns {*} - */ - getCode() { - return this.code; - } - - /** - * Set message - * - * @param message - */ - setMessage(message: string) { - this.body.message = message; - } - - /** - * Get Message - * - * @returns {string|*} - */ - getMessage() { - return this.body.message; - } - - /** - * Geneate a response - * - * @returns {object} - */ - generate() { - return { - statusCode: this.code, - headers: RESPONSE_HEADERS, - body: JSON.stringify(this.body), - }; - } - - /** - * Shorthand static method - * that generates the response immediately - * if no additional processing is required. - * - * Saves only 1 line of code - * but keeps code terse in a lot of places. - * - * @param {*} data - * @param {*} code - * @param {*} message - * @returns {object} - */ - static generate(data = null, code = null, message = null) { - const response = new this(data, code, message); - - return response.generate(); - } -} diff --git a/src/Model/SQS/MarketingPreference.constraints.json b/src/Model/SQS/MarketingPreference.constraints.json deleted file mode 100644 index d1858f81..00000000 --- a/src/Model/SQS/MarketingPreference.constraints.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "campaign": { - "presence": { - "allowEmpty": false - } - }, - "transSource": { - "presence": { - "allowEmpty": false - } - }, - "transSourceUrl": { - "presence": { - "allowEmpty": false - }, - "url": true - }, - "transType": { - "presence": { - "allowEmpty": false - } - }, - "timestamp": { - "presence": { - "allowEmpty": false - } - } -} diff --git a/src/Model/SQS/MarketingPreference.model.js b/src/Model/SQS/MarketingPreference.model.js deleted file mode 100644 index ee4cb243..00000000 --- a/src/Model/SQS/MarketingPreference.model.js +++ /dev/null @@ -1,559 +0,0 @@ -/* eslint-disable sonarjs/cognitive-complexity */ -import validate from 'validate.js'; - -import Model from '../Model.model'; -import ResponseModel from '../Response.model'; -import requestConstraints from './MarketingPreference.constraints.json'; - -// Define action specific error types -export const ERROR_TYPES = { - VALIDATION_ERROR: new ResponseModel({}, 400, 'required fields are missing'), -}; - -/** - * Marketing Preference - */ -export default class MarketingPreference extends Model { - /** - * Message constructor - * - * @param data object - */ - constructor(data = {}) { - super(); - - this.firstname = null; - this.lastname = null; - this.phone = null; - this.mobile = null; - this.address1 = null; - this.address2 = null; - this.address3 = null; - this.town = null; - this.postcode = null; - this.country = null; - this.campaign = ''; - this.transactionId = null; - this.transSource = ''; - this.transSourceUrl = ''; - this.transType = 'prefs'; - this.email = null; - this.permissionPost = null; - this.permissionEmail = null; - this.permissionPhone = null; - this.permissionSMS = null; - this.timestamp = null; - - this.instantiateFunctionWithDefinedValue('setFirstName', data.firstName); - this.instantiateFunctionWithDefinedValue('setFirstName', data.firstname); - this.instantiateFunctionWithDefinedValue('setLastName', data.lastName); - this.instantiateFunctionWithDefinedValue('setLastName', data.lastname); - this.instantiateFunctionWithDefinedValue('setPhone', data.phone); - this.instantiateFunctionWithDefinedValue('setMobile', data.mobile); - this.instantiateFunctionWithDefinedValue('setAddress1', data.address1); - this.instantiateFunctionWithDefinedValue('setAddress2', data.address2); - this.instantiateFunctionWithDefinedValue('setAddress3', data.address3); - this.instantiateFunctionWithDefinedValue('setTown', data.town); - this.instantiateFunctionWithDefinedValue('setPostcode', data.postcode); - this.instantiateFunctionWithDefinedValue('setCountry', data.country); - this.instantiateFunctionWithDefinedValue('setCampaign', data.campaign); - this.instantiateFunctionWithDefinedValue('setTransactionId', data.transactionId); - this.instantiateFunctionWithDefinedValue('setTransSource', data.transSource); - this.instantiateFunctionWithDefinedValue('setTransSourceUrl', data.transSourceUrl); - this.instantiateFunctionWithDefinedValue('setEmail', data.email); - this.instantiateFunctionWithDefinedValue('setPermissionPost', data.permissionPost); - this.instantiateFunctionWithDefinedValue('setPermissionEmail', data.permissionEmail); - this.instantiateFunctionWithDefinedValue('setPermissionPhone', data.permissionPhone); - this.instantiateFunctionWithDefinedValue('setPermissionSMS', data.permissionSMS); - if (typeof data.timestamp !== 'undefined' && data.timestamp !== '' && data.timestamp !== null) { - this.instantiateFunctionWithDefinedValue('setTimestamp', data.timestamp); - } else { - this.generateTimestamp(); - } - } - - /** - * Get First Name - * - * @returns {string|*} - */ - getFirstName() { - return this.firstname; - } - - /** - * Set First Name - * - * @param value string - */ - setFirstName(value: string) { - this.firstname = value; - } - - /** - * Get Last Name - * - * @returns {string|*} - */ - getLastName() { - return this.lastname; - } - - /** - * Set Last Name - * - * @param value string - */ - setLastName(value: string) { - this.lastname = value; - } - - /** - * Get phone - * - * @returns {string|*} - */ - getPhone() { - return this.phone; - } - - /** - * Set phone - * - * @param value string - */ - setPhone(value: string) { - this.phone = value; - } - - /** - * Get Mobile - * - * @returns {string|*} - */ - getMobile() { - return this.mobile; - } - - /** - * Set Mobile - * - * @param value string - */ - setMobile(value: string) { - this.mobile = value; - } - - /** - * Get Address Line 1 - * - * @returns {string|*} - */ - getAddress1() { - return this.address1; - } - - /** - * Set Address Line 1 - * - * @param value string - */ - setAddress1(value: string) { - this.address1 = value; - } - - /** - * Get Address Line 2 - * - * @returns {string|*} - */ - getAddress2() { - return this.address2; - } - - /** - * Set Address Line 2 - * - * @param value string - */ - setAddress2(value: string) { - this.address2 = typeof value === 'undefined' || value === '' ? null : value; - } - - /** - * Get Address Line 3 - * - * @returns {string|*} - */ - getAddress3() { - return this.address3; - } - - /** - * Set Address Line 3 - * - * @param value string - */ - setAddress3(value: string) { - this.address3 = typeof value === 'undefined' || value === '' ? null : value; - } - - /** - * Get Town - * - * @returns {string|*} - */ - getTown() { - return this.town; - } - - /** - * Set Town - * - * @param value string - */ - setTown(value: string) { - this.town = value; - } - - /** - * Get Postcode - * - * @returns {string|*} - */ - getPostcode() { - return this.postcode; - } - - /** - * Set Postcode - * - * @param value string - */ - setPostcode(value: string) { - this.postcode = value; - } - - /** - * Get Country - * - * @returns {string|*} - */ - getCountry() { - return this.country; - } - - /** - * Set Country - * - * @param value string - */ - setCountry(value: string) { - this.country = value; - } - - /** - * Get Campaign - * - * @returns {string|*} - */ - getCampaign() { - return this.campaign; - } - - /** - * Set Campaign - * - * @param value string - */ - setCampaign(value: string) { - this.campaign = value; - } - - /** - * Get Transaction Id - * - * @returns {string|*} - */ - getTransactionId() { - return this.transactionId; - } - - /** - * Set Transaction Id - * - * @param value string - */ - setTransactionId(value: string) { - this.transactionId = value; - } - - /** - * Get Transaction Source - * - * @returns {string|*} - */ - getTransSource() { - return this.transSource; - } - - /** - * Set Transaction Source - * - * @param value string - */ - setTransSource(value: string) { - this.transSource = value; - } - - /** - * Get Transaction Source URL - * - * @returns {string|*} - */ - getTransSourceUrl() { - return this.transSourceUrl; - } - - /** - * Set Transaction Source URL - * - * @param value string - */ - setTransSourceUrl(value: string) { - this.transSourceUrl = value; - } - - /** - * Get Transaction Type - * - * @returns {string|*} - */ - getTransType() { - return this.transType; - } - - /** - * Set Transaction Type - * - * @param value string - */ - setTransType(value: string) { - this.transType = value; - } - - /** - * Get Email - * - * @returns {string|*} - */ - getEmail() { - return this.email; - } - - /** - * Set Email - * - * @param value string - */ - setEmail(value: string) { - this.email = value; - } - - /** - * Get Email Permission - * - * @returns {string|*} - */ - getPermissionEmail() { - return this.permissionEmail; - } - - /** - * Set Email Permission - * - * @param value string - */ - setPermissionEmail(value: string) { - this.permissionEmail = typeof value === 'undefined' || value === '' ? null : value; - } - - /** - * Get Post Permission - * - * @returns {string|*} - */ - getPermissionPost() { - return this.permissionPost; - } - - /** - * Set Post Permission - * - * @param value string - */ - setPermissionPost(value: string) { - this.permissionPost = typeof value === 'undefined' || value === '' ? null : value; - } - - /** - * Get Phone Permission - * - * @returns {string|*} - */ - getPermissionPhone() { - return this.permissionPhone; - } - - /** - * Set Phone Permission - * - * @param value string - */ - setPermissionPhone(value: string) { - this.permissionPhone = typeof value === 'undefined' || value === '' ? null : value; - } - - /** - * Get SMS Permission - * - * @returns {string|*} - */ - getPermissionSMS() { - return this.permissionSMS; - } - - /** - * Set SMS Permission - * - * @param value string - */ - setPermissionSMS(value: string) { - this.permissionSMS = typeof value === 'undefined' || value === '' ? null : value; - } - - /** - * Get Timestamp - * - * @returns {string|*} - */ - getTimestamp() { - return this.timestamp; - } - - /** - * Set Timestamp - * - * @param value string - */ - setTimestamp(value: string) { - this.timestamp = value; - } - - /** - * Generate Timestamp - */ - generateTimestamp() { - this.timestamp = Math.floor(Date.now() / 1000); - } - - /** - * Get Base entity mappings - * - * @returns {object} - */ - getEntityMappings() { - return { - firstname: this.getFirstName(), - lastname: this.getLastName(), - phone: this.getPhone(), - mobile: this.getMobile(), - address1: this.getAddress1(), - address2: this.getAddress2(), - address3: this.getAddress3(), - town: this.getTown(), - postcode: this.getPostcode(), - country: this.getCountry(), - campaign: this.getCampaign(), - transactionId: this.getTransactionId(), - transSource: this.getTransSource(), - transSourceUrl: this.getTransSourceUrl(), - transType: this.getTransType(), - email: this.getEmail(), - permissionEmail: this.getPermissionEmail(), - permissionPost: this.getPermissionPost(), - permissionPhone: this.getPermissionPhone(), - permissionSMS: this.getPermissionSMS(), - timestamp: this.getTimestamp(), - }; - } - - /** - * Check if any permission is set - * - * @returns {boolean} - */ - isPermissionSet() { - return ( - (this.getPermissionEmail() !== null && this.getPermissionEmail() !== '') - || (this.getPermissionPost() !== null && this.getPermissionPost() !== '') - || (this.getPermissionPhone() !== null && this.getPermissionPhone() !== '') - || (this.getPermissionSMS() !== null && this.getPermissionSMS() !== '') - ); - } - - /** - * Validate the model - * - * @returns {Promise} - */ - validate() { - return new Promise((resolve, reject) => { - const requestConstraintsClone = { ...requestConstraints }; - if ( - (this.getPermissionEmail() !== null - && this.getPermissionEmail() !== '' - && this.getPermissionEmail() !== '0' - && this.getPermissionEmail() !== 0) - || this.getEmail() - ) { - if (this.getEmail()) { - requestConstraintsClone.email = { email: true }; - } else { - requestConstraintsClone.email = { presence: { allowEmpty: false }, email: true }; - } - } - // Update constraints if fields are not empty - requestConstraintsClone.firstname = this.getFirstName() !== null && this.getFirstName() !== '' - ? { format: { pattern: "[a-zA-Z.'-_ ]+", flags: 'i', message: 'can only contain alphabetical characters' } } - : ''; - requestConstraintsClone.lastname = this.getLastName() !== null && this.getLastName() !== '' - ? { format: { pattern: "[a-zA-Z.'-_ ]+", flags: 'i', message: 'can only contain alphabetical characters' } } - : ''; - requestConstraintsClone.phone = this.getPhone() !== null && this.getPhone() !== '' - ? { format: { pattern: '[0-9 ]+', flags: 'i', message: 'can only contain numerical characters' } } - : ''; - requestConstraintsClone.mobile = this.getMobile() !== null && this.getMobile() !== '' - ? { format: { pattern: '[0-9 ]+', flags: 'i', message: 'can only contain numerical characters' } } - : ''; - requestConstraintsClone.address1 = this.getAddress1() !== null && this.getAddress1() !== '' - ? { format: { pattern: "[a-zA-Z.'-_& ]+", flags: 'i', message: "can only contain alphanumeric characters and . ' - _ &" } } - : ''; - requestConstraintsClone.country = this.getCountry() !== null && this.getCountry() !== '' - ? { format: { pattern: "[a-zA-Z.'-_& ]+", flags: 'i', message: "can only contain alphabetical characters and . ' - _ &" } } - : ''; - - const validation = validate(this.getEntityMappings(), requestConstraintsClone); - - if (typeof validation === 'undefined') { - resolve(); - return; - } - - const validationErrorResponse = ERROR_TYPES.VALIDATION_ERROR; - validationErrorResponse.setBodyVariable('validation_errors', validation); - - reject(validationErrorResponse); - }); - } -} diff --git a/src/Model/SQS/Message.model.js b/src/Model/SQS/Message.model.js deleted file mode 100644 index 2bd6c087..00000000 --- a/src/Model/SQS/Message.model.js +++ /dev/null @@ -1,88 +0,0 @@ -import Model from '../Model.model'; - -/** - * Message Model - */ -export default class Message extends Model { - /** - * Message constructor - * - * @param message - */ - constructor(message) { - super(); - - this.messageId = message.MessageId; - this.receiptHandle = message.ReceiptHandle; - - this.body = JSON.parse(message.Body); - this.forDeletion = false; - this.metadata = {}; - } - - /** - * Get Message ID - * - * @returns {*} - */ - getMessageId() { - return this.messageId; - } - - /** - * Get Receipt Handle - * - * @returns {*} - */ - getReceiptHandle() { - return this.receiptHandle; - } - - /** - * Get Body - * - * @returns {any | *} - */ - getBody() { - return this.body; - } - - /** - * Set for deletion status - * - * @param forDeletion - */ - setForDeletion(forDeletion: boolean) { - this.forDeletion = forDeletion; - } - - /** - * Whether message is for deletion - * - * @returns {boolean|*} - */ - isForDeletion() { - return this.forDeletion; - } - - /** - * Get all of the message metadata - * - * @returns {{}} - */ - getMetaData() { - return this.metadata; - } - - /** - * Set message metadata value - * - * @param key - * @param value - */ - setMetaData(key, value) { - this.metadata[key] = value; - - return this; - } -} diff --git a/src/Service/Timer.service.js b/src/Service/Timer.service.js deleted file mode 100644 index 5d544444..00000000 --- a/src/Service/Timer.service.js +++ /dev/null @@ -1,40 +0,0 @@ -import { DEFINITIONS } from '../Config/Dependencies'; -import DependencyAwareClass from '../DependencyInjection/DependencyAware.class'; -import DependencyInjection from '../DependencyInjection/DependencyInjection.class'; - -/** - * TimerService class - */ -export default class TimerService extends DependencyAwareClass { - /** - * TimerService constructor - * - * @param di - */ - constructor(di: DependencyInjection) { - super(di); - this.timers = {}; - } - - /** - * Start timer - * - * @param identifier - */ - start(identifier: string) { - this.timers[identifier] = Date.now(); - } - - /** - * Stop timer - * - * @param identifier - */ - stop(identifier: string) { - if (typeof this.timers[identifier] !== 'undefined') { - const duration = Date.now() - this.timers[identifier]; - - this.getContainer().get(DEFINITIONS.LOGGER).info(`Timing - ${identifier} took ${duration}ms to complete`); - } - } -} diff --git a/src/Wrapper/LambdaTermination.js b/src/Wrapper/LambdaTermination.js deleted file mode 100644 index 09d05cfc..00000000 --- a/src/Wrapper/LambdaTermination.js +++ /dev/null @@ -1,29 +0,0 @@ -/** - * - */ -export default class LambdaTermination extends Error { - /** - * Triggers a Lambda Termination. - * Offers developer details (that are logged) - * an code for the Lambda and a front facing - * consumer message. - * - * @param {object|string} internal - * @param {number?} code - * @param {object|string?} body - * @param details - */ - constructor(internal, code = 500, body = null, details = 'unknown error') { - let stringified = internal; - - if (typeof internal !== 'string') { - stringified = JSON.stringify(internal); - } - super(stringified); - - this.internal = internal; - this.code = code; - this.body = body || 'unknown error'; - this.details = details; - } -} diff --git a/src/Wrapper/LambdaWrapper.js b/src/Wrapper/LambdaWrapper.js deleted file mode 100644 index 9e31abbb..00000000 --- a/src/Wrapper/LambdaWrapper.js +++ /dev/null @@ -1,156 +0,0 @@ -/* eslint-disable sonarjs/cognitive-complexity */ -import Epsagon from 'epsagon'; - -import { DEFINITIONS } from '../Config/Dependencies'; -import DependencyInjection from '../DependencyInjection/DependencyInjection.class'; -import ResponseModel from '../Model/Response.model'; - -/** - * Processes the outcome once we have one - * - * @param di - * @param outcome - */ -export const handleSuccess = (di, outcome) => { - const logger = di.get(DEFINITIONS.LOGGER); - - // Outcome may be undefined as not all lambdas have a return value. - logger.metric('lambda.statusCode', (outcome && outcome.statusCode) || 200); - - return outcome; -}; - -/** - * Gracefully handles an error - * logging in Epsagon and generating - * a response reflecting the `code` - * of the error, if defined. - * - * Note about Epsagon: - * Epsagon generates alerts for logs on level ERROR. - * This means that logger.error will produce an alert. - * To avoid not meaningful notifications, most likely - * coming from tests, we log INFO unless either: - * - * 1. `error.raiseOnEpsagon` is defined & truthy - * 2. `error.code` is defined and `error.code >= 500`. - * - * @param {DependencyInjection} di - * @param {Error} error - * @param {boolean} [throwError=false] - */ -export const handleError = (di, error, throwError = false) => { - const logger = di.get(DEFINITIONS.LOGGER); - - logger.metric('lambda.statusCode', error.code || 500); - - if (error.raiseOnEpsagon || !error.code || error.code >= 500) { - logger.error(error); - } else { - logger.info(error); - } - - if (throwError) { - if (error instanceof Error) { - return error; - } - - // We want to be absolutely sure - // that we are returning an error - // as Lambda sync handlers will only fail - // if the object is instanceof Error - return new Error(error); - } - - const responseDetails = { - body: error.body || {}, - code: error.code || 500, - details: error.details || 'unknown error', - }; - - return ResponseModel.generate(responseDetails.body, responseDetails.code, responseDetails.details); -}; - -/** - * Lambda Wrapper. - * - * Wraps a lambda handler, generating a new function - * that has access to the dependency injection - * for the service and handles logging and exceptions. - * - * @param configuration - * @param handler - * @param throwError - */ -export default (configuration, handler, throwError = false) => { - let instance = (event, context, callback) => { - const di = new DependencyInjection(configuration, event, context); - const request = di.get(DEFINITIONS.REQUEST); - const logger = di.get(DEFINITIONS.LOGGER); - - context.callbackWaitsForEmptyEventLoop = false; - - // If the event is to trigger a warm up, then don't bother returning the function. - if (di.getEvent().source === 'serverless-plugin-warmup') { - return callback(null, 'Lambda is warm!'); - } - - // Log the users ip address silently for use in error tracing - if (request.getIp() !== null) { - logger.metric('ipAddress', request.getIp(), true); - } - - // Add metrics with user browser information for rapid debugging - const userBrowserAndDevice = request.getUserBrowserAndDevice(); - if (userBrowserAndDevice !== null && typeof userBrowserAndDevice === 'object') { - Object.keys(userBrowserAndDevice).forEach((metricKey) => { - logger.metric(metricKey, userBrowserAndDevice[metricKey], true); - }); - } - - let outcome; - - try { - outcome = handler.call(instance, di, request, callback); - - if (outcome instanceof Promise) { - outcome = outcome - .then((value) => handleSuccess(di, value)) - .catch((error) => { - const handled = handleError(di, error, throwError); - - if (throwError) { - // AWS Lambda with async handler is looking for a rejection - // and not an error object directly - // and will treat resolved errors as successful - // as it will cast the error to JSON, i.e. `{}` - throw handled; - } - - return handled; - }); - } - } catch (error) { - outcome = handleError(di, error, throwError); - } - - return outcome; - }; - - // If the Epsagon token is enabled, then wrap the instance in the Epsagon wrapper - if ( - typeof process.env.EPSAGON_TOKEN === 'string' - && process.env.EPSAGON_TOKEN !== 'undefined' - && typeof process.env.EPSAGON_SERVICE_NAME === 'string' - && process.env.EPSAGON_SERVICE_NAME !== 'undefined' - ) { - Epsagon.init({ - token: process.env.EPSAGON_TOKEN, - appName: process.env.EPSAGON_SERVICE_NAME, - }); - - instance = Epsagon.lambdaWrapper(instance); - } - - return instance; -}; diff --git a/src/core/DependencyAwareClass.ts b/src/core/DependencyAwareClass.ts new file mode 100644 index 00000000..8957cf93 --- /dev/null +++ b/src/core/DependencyAwareClass.ts @@ -0,0 +1,17 @@ +import DependencyInjection from './DependencyInjection'; + +/** + * Base class for dependencies. + */ +export default class DependencyAwareClass { + constructor(readonly di: DependencyInjection) {} + + /** + * Get dependency injection container. + * + * @deprecated Use `this.di` instead. + */ + getContainer(): DependencyInjection { + return this.di; + } +} diff --git a/src/core/DependencyInjection.ts b/src/core/DependencyInjection.ts new file mode 100644 index 00000000..81ff20dc --- /dev/null +++ b/src/core/DependencyInjection.ts @@ -0,0 +1,109 @@ +import { Context } from 'aws-lambda'; + +import DependencyAwareClass from './DependencyAwareClass'; +import { LambdaWrapperConfig } from './config'; + +// eslint-disable-next-line no-use-before-define +type Class = new (di: DependencyInjection) => T; + +/** + * Dependency injection container. + * + * Dependencies (singleton instances of dependency-aware classes) are provided + * to the main Lambda handler and other dependencies via this class. + */ +export default class DependencyInjection { + /** + * Instantiated dependencies. + */ + readonly dependencies: Record; + + /** + * True until all dependencies have been constructed. + */ + private isConstructing = true; + + constructor( + readonly config: LambdaWrapperConfig, + readonly event: any, + readonly context: Context, + ) { + const classes = Object.values(config.dependencies); + this.dependencies = Object.fromEntries( + classes.map((Constructor) => [Constructor.name, new Constructor(this)]), + ); + + this.isConstructing = false; + } + + /** + * Get the singleton instance of the given dependency. + * + * @param dependency + */ + get(dependency: Class): T { + if (this.isConstructing) { + throw new Error( + 'Dependencies are not available in dependency class constructors.\n\n' + + 'To fix this, call `di.get` in the function where the dependency is' + + 'used instead of inside your constructor.', + ); + } + + const name = dependency.name; + + if (!this.dependencies[name]) { + throw new Error( + `${name} does not exist in dependency container\n\n` + + `Make sure you've included ${name} in the 'dependencies' key of your ` + + 'Lambda Wrapper config.', + ); + } + + return this.dependencies[name] as T; + } + + /** + * Get the event passed to AWS Lambda. + * + * @deprecated Use `di.event` instead. + */ + getEvent() { + return this.event; + } + + /** + * Get the AWS Lambda context object. + * + * @deprecated Use `di.context` instead. + */ + getContext() { + return this.context; + } + + /** + * Get Lambda Wrapper configuration. + * + * @deprecated Use `di.config` instead. + */ + getConfiguration() { + return this.config; + } + + /** + * True if the function is being executed in `serverless-offline`. + * + * We use the following checks for this: + * + * - if there is no function ARN, or the ARN includes 'offline' + * - if `process.env.USE_SERVERLESS_OFFLINE` is set + * + * TODO: This is nothing to do with dependency injection and should be moved + * somewhere else! Any ideas? + */ + get isOffline(): boolean { + return !this.context.invokedFunctionArn + || this.context.invokedFunctionArn.includes('offline') + || !!process.env.USE_SERVERLESS_OFFLINE; + } +} diff --git a/src/core/LambdaWrapper.ts b/src/core/LambdaWrapper.ts new file mode 100644 index 00000000..59085d62 --- /dev/null +++ b/src/core/LambdaWrapper.ts @@ -0,0 +1,160 @@ +import Epsagon from 'epsagon'; + +import { Context } from '../index'; +import ResponseModel from '../models/ResponseModel'; +import LoggerService from '../services/LoggerService'; +import RequestService from '../services/RequestService'; +import DependencyInjection from './DependencyInjection'; +import { LambdaWrapperConfig, mergeConfig } from './config'; + +export interface WrapOptions { + /** + * Whether uncaught errors should be handled to return an HTTP 500 response + * instead of causing a function error. (default: `true`) + * + * This is what you usually want when working on an HTTP endpoint, but in + * other contexts (e.g. queue consumers) you may want AWS Lambda to report a + * failure so that the event is retried. + */ + handleUncaughtErrors?: boolean; +} + +export default class LambdaWrapper { + constructor(readonly config: TConfig) {} + + /** + * Returns a new Lambda Wrapper with the given configuration applied. + * + * @param config + */ + configure(config: Partial & TMoreConfig): LambdaWrapper { + return new LambdaWrapper(mergeConfig(this.config, config)); + } + + /** + * Wrap the given function. + */ + wrap(handler: (di: DependencyInjection) => Promise, options?: WrapOptions) { + const { + handleUncaughtErrors = true, + } = options || {}; + + let wrapper = async (event: any, context: Context) => { + const di = new DependencyInjection(this.config, event, context); + const request = di.get(RequestService); + const logger = di.get(LoggerService); + + context.callbackWaitsForEmptyEventLoop = false; + + // if the event is a warmup, don't bother running the function + if (event.source === 'serverless-plugin-warmup') { + return 'Lambda is warm!'; + } + + // log the user's IP address silently for use in error tracing + const ipAddress = request.getIp(); + if (ipAddress) { + logger.metric('ipAddress', ipAddress, true); + } + + // add metrics with user browser information for rapid debugging + const userBrowserAndDevice = request.getUserBrowserAndDevice(); + if (userBrowserAndDevice) { + Object.entries(userBrowserAndDevice).forEach(([key, value]) => { + logger.metric(key, value, true); + }); + } + + try { + const result = await handler.call(wrapper, di); + return LambdaWrapper.handleSuccess(di, result); + } catch (error: any) { + const handled = LambdaWrapper.handleError(di, error, !handleUncaughtErrors); + + if (!handleUncaughtErrors) { + // AWS Lambda with async handler is looking for a rejection + // and not an error object directly + // and will treat resolved errors as successful + // as it will cast the error to JSON, i.e. `{}` + throw handled; + } + + return handled; + } + }; + + // If Epsagon is enabled, wrap the instance in the Epsagon wrapper + if (process.env.EPSAGON_TOKEN && process.env.EPSAGON_SERVICE_NAME) { + Epsagon.init({ + token: process.env.EPSAGON_TOKEN, + appName: process.env.EPSAGON_SERVICE_NAME, + }); + + wrapper = Epsagon.lambdaWrapper(wrapper); + } + + return wrapper; + } + + /** + * Process the result once we have one. + * + * @param di + * @param result + */ + static handleSuccess(di: DependencyInjection, result: any) { + const logger = di.get(LoggerService); + + // result may be undefined as not all lambdas have a return value + logger.metric('lambda.statusCode', result?.statusCode || 200); + + return result; + } + + /** + * Gracefully handles an error, logging in Epsagon and generating a response + * reflecting the `code` of the error, if defined. + * + * Note about Epsagon: + * Epsagon generates alerts for logs on level ERROR. This means that + * `logger.error` will produce an alert. To avoid meaningless notifications, + * most likely coming from tests, we log INFO unless either: + * + * 1. `error.raiseOnEpsagon` is defined & truthy + * 2. `error.code` is defined and `error.code >= 500`. + * + * @param di + * @param error + * @param [throwError=false] + */ + static handleError(di: DependencyInjection, error: Error, throwError = false) { + const logger = di.get(LoggerService); + + const { + code, + raiseOnEpsagon, + body = {}, + details = 'unknown error', + } = error as any; + + logger.metric('lambda.statusCode', code || 500); + + if (raiseOnEpsagon || !code || code >= 500) { + logger.error(error); + } else { + logger.info(error); + } + + if (throwError) { + if (error instanceof Error) { + return error; + } + + // We want to be absolutely sure that we are returning an error, as + // Lambda sync handlers will only fail if the object is instanceof Error + return new Error(error); + } + + return ResponseModel.generate(body, code || 500, details); + } +} diff --git a/src/core/config.ts b/src/core/config.ts new file mode 100644 index 00000000..43c5e64c --- /dev/null +++ b/src/core/config.ts @@ -0,0 +1,36 @@ +import DependencyAwareClass from './DependencyAwareClass'; + +/** + * Config for Lambda Wrapper defining dependencies and their configuration. + */ +export interface LambdaWrapperConfig { + /** + * Dependencies to be provided by dependency injection. + * + * TODO: should this just be a list instead? keys are currently unused + */ + dependencies: Record; +} + +/** + * Combine two Lambda Wrapper configs. + * + * @param old Current config. + * @param new_ New config that will override the old. + */ +export function mergeConfig< + A extends LambdaWrapperConfig, + B extends Partial, +>( + old: A, + new_: B, +): A & B { + return { + ...old, + ...new_, + dependencies: { + ...old.dependencies, + ...new_.dependencies, + }, + }; +} diff --git a/src/index.js b/src/index.js deleted file mode 100644 index 81fc1775..00000000 --- a/src/index.js +++ /dev/null @@ -1,24 +0,0 @@ -export { DEFINITIONS } from './Config/Dependencies'; - -// DependencyInjection -export { default as DependencyAwareClass } from './DependencyInjection/DependencyAware.class'; -export { default as DependencyInjection } from './DependencyInjection/DependencyInjection.class'; - -// Model -export { default as Model } from './Model/Model.model'; -export { default as ResponseModel } from './Model/Response.model'; -export { default as StatusModel, STATUS_TYPES } from './Model/Status.model'; -export { default as SQSMessageModel } from './Model/SQS/Message.model'; -export { default as MarketingPreferenceModel } from './Model/SQS/MarketingPreference.model'; - -// Service -export { default as BaseConfigService } from './Service/BaseConfig.service'; -export { default as HTTPService, COMICRELIEF_TEST_METADATA_HEADER } from './Service/HTTP.service'; -export { default as LoggerService } from './Service/Logger.service'; -export { default as RequestService } from './Service/Request.service'; -export { default as SQSService, SQS_OFFLINE_MODES, SQS_PUBLISH_FAILURE_MODES } from './Service/SQS.service'; - -// Wrapper -export { default as LambdaTermination } from './Wrapper/LambdaTermination'; -export { default as LambdaWrapper } from './Wrapper/LambdaWrapper'; -export { default as PromisifiedDelay } from './Wrapper/PromisifiedDelay'; diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 00000000..612c06e2 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,70 @@ +import LambdaWrapper from './core/LambdaWrapper'; +import { LambdaWrapperConfig } from './core/config'; +import LoggerService from './services/LoggerService'; +import RequestService from './services/RequestService'; +import SQSService, { WithSQSServiceConfig } from './services/SQSService'; +import TimerService from './services/TimerService'; + +/** + * Lambda Wrapper preconfigured with our core services that can be used + * straight out of the box. + * + * Use `lambdaWrapper.configure()` to add your own dependencies. + */ +const lambdaWrapper = new LambdaWrapper({ + dependencies: { + LoggerService, + RequestService, + SQSService, + TimerService, + }, +}); + +export default lambdaWrapper; + +export { Context, Handler } from 'aws-lambda'; + +export { LambdaWrapperConfig } from './core/config'; +export { default as DependencyAwareClass } from './core/DependencyAwareClass'; +export { default as DependencyInjection } from './core/DependencyInjection'; +export { default as LambdaWrapper, WrapOptions } from './core/LambdaWrapper'; + +export { + default as ResponseModel, +} from './models/ResponseModel'; +export { + default as SQSMessageModel, +} from './models/SQSMessageModel'; +export { + default as StatusModel, + STATUS_TYPES, +} from './models/StatusModel'; + +export { + default as BaseConfigService, +} from './services/BaseConfigService'; +export { + default as HTTPService, + COMICRELIEF_TEST_METADATA_HEADER, +} from './services/HTTPService'; +export { + default as LoggerService, +} from './services/LoggerService'; +export { + default as RequestService, + REQUEST_TYPES, + RequestFile, +} from './services/RequestService'; +export { + default as SQSService, + SQS_OFFLINE_MODES, + SQS_PUBLISH_FAILURE_MODES, + SQSServiceConfig, + WithSQSServiceConfig, +} from './services/SQSService'; +export { + default as TimerService, +} from './services/TimerService'; + +export { default as LambdaTermination } from './utils/LambdaTermination'; +export { default as PromisifiedDelay } from './utils/PromisifiedDelay'; diff --git a/src/models/ResponseModel.ts b/src/models/ResponseModel.ts new file mode 100644 index 00000000..c6697c8e --- /dev/null +++ b/src/models/ResponseModel.ts @@ -0,0 +1,109 @@ +/** + * HTTP headers to be included in all responses. + */ +export const RESPONSE_HEADERS = { + 'Content-Type': 'application/json', + /** Required for CORS support to work */ + 'Access-Control-Allow-Origin': '*', + /** Required for cookies, authorization headers with HTTPS */ + 'Access-Control-Allow-Credentials': true, +}; + +/** + * Default message provided as part of response. + */ +export const DEFAULT_MESSAGE = 'success'; + +/** + * Our standard response model for HTTP endpoints. + */ +export default class ResponseModel { + body: any; + + code: any; + + constructor(data?: any, code?: number, message?: string) { + this.body = { + data: data ?? {}, + message: message ?? DEFAULT_MESSAGE, + }; + this.code = code ?? {}; + } + + /** + * Add or update a body variable. + * + * @param key + * @param value + */ + setBodyVariable(key: string, value: any) { + this.body[key] = value; + } + + /** + * Set data. + * + * @param data + */ + setData(data: object) { + this.body.data = data; + } + + /** + * Set status code. + * + * @param code + */ + setCode(code: number) { + this.code = code; + } + + /** + * Get status code. + */ + getCode() { + return this.code; + } + + /** + * Set message. + * + * @param message + */ + setMessage(message: string) { + this.body.message = message; + } + + /** + * Get message. + */ + getMessage() { + return this.body.message; + } + + /** + * Geneate a response. + */ + generate() { + return { + statusCode: this.code, + headers: RESPONSE_HEADERS, + body: JSON.stringify(this.body), + }; + } + + /** + * Shorthand static method that generates the response immediately if no + * additional processing is required. + * + * Saves only 1 line of code but keeps code terse in a lot of places. + * + * @param data + * @param code + * @param message + */ + static generate(data?: any, code?: number, message?: string) { + const response = new this(data, code, message); + return response.generate(); + } +} diff --git a/src/models/SQSMessageModel.ts b/src/models/SQSMessageModel.ts new file mode 100644 index 00000000..58d00afe --- /dev/null +++ b/src/models/SQSMessageModel.ts @@ -0,0 +1,79 @@ +import { SQS } from 'aws-sdk'; + +/** + * Message model for SQS. + */ +export default class Message { + messageId: string; + + receiptHandle: string; + + body: string; + + forDeletion = false; + + metadata: Record = {}; + + constructor(message: SQS.Message) { + // todo: validate rather than assert the type + this.messageId = message.MessageId!; + this.receiptHandle = message.ReceiptHandle!; + this.body = JSON.parse(message.Body!); + } + + /** + * Get message ID. + */ + getMessageId() { + return this.messageId; + } + + /** + * Get message receipt handle. + */ + getReceiptHandle() { + return this.receiptHandle; + } + + /** + * Get message body. + */ + getBody() { + return this.body; + } + + /** + * Set for deletion status. + * + * @param forDeletion + */ + setForDeletion(forDeletion: boolean) { + this.forDeletion = forDeletion; + } + + /** + * Whether message is for deletion. + */ + isForDeletion() { + return this.forDeletion; + } + + /** + * Get all of the message metadata. + */ + getMetaData() { + return this.metadata; + } + + /** + * Set message metadata value + * + * @param key + * @param value + */ + setMetaData(key: string, value: any) { + this.metadata[key] = value; + + return this; + } +} diff --git a/src/Model/Status.model.js b/src/models/StatusModel.ts similarity index 59% rename from src/Model/Status.model.js rename to src/models/StatusModel.ts index cc6b0190..942bc19d 100644 --- a/src/Model/Status.model.js +++ b/src/models/StatusModel.ts @@ -1,5 +1,3 @@ -import Model from './Model.model'; - export const STATUS_TYPES = { OK: 'OK', ACCEPTABLE_FAILURE: 'ACCEPTABLE_FAILURE', @@ -7,33 +5,33 @@ export const STATUS_TYPES = { }; /** - * StatusModel Class + * Model for our status check endpoints. */ -export default class StatusModel extends Model { +export default class StatusModel { /** - * StatusModel constructor - * - * @param service - * @param status + * Service name. */ - constructor(service: string, status: string) { - super(); + service: string; - this.setService(service); - this.setStatus(status); + /** + * One of the `STATUS_TYPES` values. + */ + status: string; + + constructor(service: string, status: string) { + this.service = service; + this.status = status; } /** - * Get Service - * - * @returns {*} + * Get the service name. */ getService(): string { return this.service; } /** - * Set Service + * Set the service name. * * @param service */ @@ -42,12 +40,12 @@ export default class StatusModel extends Model { } /** - * Set the status + * Set the status. * * @param status */ setStatus(status: string) { - if (typeof STATUS_TYPES[status] === 'undefined') { + if (!(status in STATUS_TYPES)) { throw new TypeError(`${StatusModel.name} - ${status} is not a valid status type`); } @@ -55,9 +53,7 @@ export default class StatusModel extends Model { } /** - * Get status - * - * @returns {string|*} + * Get the status. */ getStatus(): string { return this.status; diff --git a/src/Service/BaseConfig.service.js b/src/services/BaseConfigService.ts similarity index 52% rename from src/Service/BaseConfig.service.js rename to src/services/BaseConfigService.ts index 65dc2ba2..0a6805d9 100644 --- a/src/Service/BaseConfig.service.js +++ b/src/services/BaseConfigService.ts @@ -1,14 +1,15 @@ import { S3 } from 'aws-sdk'; -import DependencyAwareClass from '../DependencyInjection/DependencyAware.class'; -import LambdaTermination from '../Wrapper/LambdaTermination'; +import DependencyAwareClass from '../core/DependencyAwareClass'; +import LambdaTermination from '../utils/LambdaTermination'; + /** - * Error.code for S3 404 errors + * `error.code` for S3 404 errors. */ export const S3_NO_SUCH_KEY_ERROR_CODE = 'NoSuchKey'; /** - * Represents the service states + * Represents the service states. */ export const ServiceStates = { OK: 'OK', @@ -17,7 +18,7 @@ export const ServiceStates = { }; /** - * Maps service states to HTTP codes + * Maps service states to HTTP codes. */ export const ServiceStatesHttpCodes = { [ServiceStates.OK]: 200, @@ -26,18 +27,19 @@ export const ServiceStatesHttpCodes = { }; /** - * BaseConfigService class + * This class is to be extended by the implementing services so that + * `defaultConfig` and possibly `s3Config` can be overriden / extended. * - * This class is to be extended by the implementing services - * so that `defaultConfig` and possibly `s3Config` can be - * overriden / extended. + * Config is typed as `unknown` since you shouldn't trust what's in the bucket. + * Override the `get` and `put` methods to pass the results through some + * validation to ensure the config is valid and can safely be typed. */ export default class BaseConfigService extends DependencyAwareClass { /** * Returns the basic config. - * This getter is used to set the default config - * should the service not find any - * on the configured S3 Bucket. + * + * This getter is used to set the default config should the service not find + * any on the configured S3 Bucket. * * See `getOrCreate` and `patch` methods. */ @@ -48,21 +50,18 @@ export default class BaseConfigService extends DependencyAwareClass { } /** - * Returns the S3 configuration - * used to retrieve / update the - * service configuration. + * Returns the S3 configuration used to retrieve or update the service + * configuration. */ - static get s3config() { + static get s3config(): { Bucket: string; Key: string; } { return { - Bucket: process.env.SERVICE_CONFIG_S3_BUCKET, - Key: process.env.SERVICE_CONFIG_S3_KEY, + Bucket: process.env.SERVICE_CONFIG_S3_BUCKET || '', + Key: process.env.SERVICE_CONFIG_S3_KEY || '', }; } /** - * Returns an S3 client - * - * @returns {S3} + * Returns an S3 client. */ static get client() { return new S3({ @@ -72,32 +71,30 @@ export default class BaseConfigService extends DependencyAwareClass { /** * Returns an S3 client - * - * @returns {S3} */ get client() { - return this.constructor.client; + return (this.constructor as typeof BaseConfigService).client; } /** - * Deletes the configuration stored on S3. - * Helpful in feature tests. + * Deletes the configuration stored on S3. Helpful in feature tests. */ async delete() { - return this.client.deleteObject(this.constructor.s3config).promise(); + return this.client.deleteObject( + (this.constructor as typeof BaseConfigService).s3config, + ).promise(); } /** - * Puts the given configuration on S3 + * Puts the given configuration on S3. * * @param config */ - async put(config) { + async put(config: T): Promise { await this.client.putObject({ - ...this.constructor.s3config, + ...(this.constructor as typeof BaseConfigService).s3config, Body: JSON.stringify(config), - }) - .promise(); + }).promise(); return config; } @@ -105,8 +102,10 @@ export default class BaseConfigService extends DependencyAwareClass { /** * Gets the service configuration. */ - async get() { - const response = await this.client.getObject(this.constructor.s3config).promise(); + async get(): Promise { + const response = await this.client.getObject( + (this.constructor as typeof BaseConfigService).s3config, + ).promise(); const body = String(response.Body); if (!body) { @@ -124,20 +123,19 @@ export default class BaseConfigService extends DependencyAwareClass { /** * Gets or creates the service configuration. * - * If the configuration is not found on S3 - * the default configuration - * is uploaded and returned instead. + * If the configuration is not found on S3 the default configuration is + * uploaded and returned instead. */ - async getOrCreate() { + async getOrCreate(): Promise { try { return await this.get(); - } catch (error) { + } catch (error: any) { if (error.code !== S3_NO_SUCH_KEY_ERROR_CODE) { // Throw directly any other error throw error; } - return this.put(this.constructor.defaultConfig); + return this.put((this.constructor as typeof BaseConfigService).defaultConfig); } } @@ -148,12 +146,12 @@ export default class BaseConfigService extends DependencyAwareClass { * * @param partialConfig */ - async patch(partialConfig) { - let base = this.constructor.defaultConfig; + async patch(partialConfig: any): Promise { + let base: any = (this.constructor as typeof BaseConfigService).defaultConfig; try { base = await this.get(); - } catch (error) { + } catch (error: any) { if (error.code !== S3_NO_SUCH_KEY_ERROR_CODE) { // Throw directly any other error throw error; @@ -170,27 +168,24 @@ export default class BaseConfigService extends DependencyAwareClass { } /** - * Performs a health check - * given the currentConfig. + * Performs a health check given the current config. * - * If currentConfig is not supplied - * it uses `getOrCreate` to fetch it. + * If `currentConfig` is not supplied it uses `getOrCreate` to fetch it. * * @param currentConfig */ - async healthCheck(currentConfig = null) { + async healthCheck(currentConfig?: any): Promise { const config = currentConfig || await this.getOrCreate(); return ServiceStatesHttpCodes[config.state] || 500; } /** - * Ensures that the application is healthy - * or throws a LambdaTermination + * Ensures that the application is healthy or throws a `LambdaTermination`. * * @param currentConfig */ - async ensureHealthy(currentConfig = null) { + async ensureHealthy(currentConfig: any = null) { const statusCode = await this.healthCheck(currentConfig); if (statusCode < 400) { diff --git a/src/Service/HTTP.service.js b/src/services/HTTPService.ts similarity index 55% rename from src/Service/HTTP.service.js rename to src/services/HTTPService.ts index ca6aabac..80898091 100644 --- a/src/Service/HTTP.service.js +++ b/src/services/HTTPService.ts @@ -1,15 +1,23 @@ import axios, { AxiosRequestConfig, AxiosResponse } from 'axios'; -import DependencyAwareClass from '../DependencyInjection/DependencyAware.class'; +import DependencyAwareClass from '../core/DependencyAwareClass'; +import DependencyInjection from '../core/DependencyInjection'; +import RequestService from './RequestService'; export const COMICRELIEF_TEST_METADATA_HEADER = 'x-comicrelief-test-metadata'; export const DEFAULT_HTTP_TIMEOUT = 10 * 1000; /** - * HTTPService class + * Wrapper for `axios.request` that: + * + * - sets a default timeout of 10 seconds + * - forwards a `x-comicrelief-test-metadata` header if one was provided in + * the request from upstream */ export default class HTTPService extends DependencyAwareClass { - constructor(di) { + config: AxiosRequestConfig; + + constructor(di: DependencyInjection) { super(di); this.config = { @@ -18,16 +26,16 @@ export default class HTTPService extends DependencyAwareClass { } /** - * Sets the default timeout + * Set the default timeout. * - * @param {number} ms + * @param ms */ - setDefaultTimeout(ms) { + setDefaultTimeout(ms: number) { this.config.timeout = ms; } /** - * Performs and HTTP Request + * Perform an HTTP request. * * @param config */ @@ -38,8 +46,8 @@ export default class HTTPService extends DependencyAwareClass { ...config, }; - const lambdaRequest = this.getContainer().get(this.definitions.REQUEST); - const testMetadata = lambdaRequest.getHeader(COMICRELIEF_TEST_METADATA_HEADER); + const request = this.di.get(RequestService); + const testMetadata = request.getHeader(COMICRELIEF_TEST_METADATA_HEADER); if (testMetadata) { mergedConfig.headers = mergedConfig.headers || {}; diff --git a/src/Service/Logger.service.js b/src/services/LoggerService.ts similarity index 57% rename from src/Service/Logger.service.js rename to src/services/LoggerService.ts index 18eb144b..9d82b6cf 100644 --- a/src/Service/Logger.service.js +++ b/src/services/LoggerService.ts @@ -1,13 +1,14 @@ import * as Sentry from '@sentry/node'; +import { AxiosError } from 'axios'; import Epsagon from 'epsagon'; import Winston from 'winston'; -import DependencyAwareClass from '../DependencyInjection/DependencyAware.class'; -import DependencyInjection from '../DependencyInjection/DependencyInjection.class'; +import DependencyAwareClass from '../core/DependencyAwareClass'; +import DependencyInjection from '../core/DependencyInjection'; -// Instantiate the sentry client const sentryIsAvailable = typeof process.env.RAVEN_DSN !== 'undefined' && typeof process.env.RAVEN_DSN === 'string' && process.env.RAVEN_DSN !== 'undefined'; +// initialise the Sentry client if available if (sentryIsAvailable) { Sentry.init({ dsn: process.env.RAVEN_DSN, @@ -17,30 +18,36 @@ if (sentryIsAvailable) { } /** - * LoggerService class + * Provides logging and integrations with our monitoring tools. + * + * For logging we use [Winston](https://github.com/winstonjs/winston). + * Errors will also be sent to [Sentry](https://sentry.io/) and + * [Epsagon](https://epsagon.com/) if those are available. */ export default class LoggerService extends DependencyAwareClass { + private sentry: typeof Sentry | null; + + private winston: Winston.Logger | null; + constructor(di: DependencyInjection) { super(di); + this.sentry = null; this.winston = null; - const container = this.getContainer(); - const event = container.getEvent(); - const context = container.getContext(); + const { event, context } = this.di; - // Set sentry client context - if (sentryIsAvailable && !container.isOffline) { + if (sentryIsAvailable && !di.isOffline) { Sentry.configureScope((scope) => { scope.setTags({ Event: event, - Context: context, + Context: context as any, }); scope.setExtras({ lambda: context.functionName, memory_size: context.memoryLimitInMB, - log_group: context.log_group_name, - log_stream: context.log_stream_name, + log_group: context.logGroupName, + log_stream: context.logStreamName, stage: process.env.STAGE, path: event.path, httpMethod: event.httpMethod, @@ -52,14 +59,10 @@ export default class LoggerService extends DependencyAwareClass { } /** - * Returns a Winston logger object - * configured for our lambdas. + * Returns a Winston logger configured for our lambdas. * - * Note: - * - * If the lambda is executed - * in a `serverless-offline` context - * the log output to console will be pretty printed. + * Note: If the lambda is executed in a `serverless-offline` context, the + * log output to console will be pretty-printed. */ getLogger() { const loggerFormats = [ @@ -69,21 +72,17 @@ export default class LoggerService extends DependencyAwareClass { return value.toString('base64'); } if (value instanceof Error) { - const error = {}; - - Object.getOwnPropertyNames(value).forEach((objectKey) => { - error[objectKey] = value[objectKey]; - }); - - return error; + return Object.fromEntries( + Object.getOwnPropertyNames(value) + .map((errorKey) => [errorKey, (value as any)[errorKey]]), + ); } - return value; }, }), ]; - if (this.getContainer().isOffline) { + if (this.di.isOffline) { loggerFormats.push(Winston.format.prettyPrint()); } @@ -97,9 +96,8 @@ export default class LoggerService extends DependencyAwareClass { /** * Returns the logger. * - * Uses a cached `Winston` object - * if it has been already generated, - * otherwise it generates one. + * Uses a cached Winston logger if it has been already created, otherwise it + * creates one. */ get logger() { if (!this.winston) { @@ -110,8 +108,15 @@ export default class LoggerService extends DependencyAwareClass { } /** - * While handling an error, lambda wrapper should - * recognise axios errors and trim down the information. + * Get Sentry client. + */ + getSentry() { + return this.sentry; + } + + /** + * While logging an error, we should recognise axios errors and trim down the + * information to only what is useful for debugging. * * Keep the following keys: * - message.config @@ -121,16 +126,14 @@ export default class LoggerService extends DependencyAwareClass { * * @param {object} error */ - static processAxiosError(error) { - const processed = { + static processAxiosError(error: AxiosError) { + const processed: any = { config: error.config, message: error.message, }; - // It's pretty common for axios errors - // to not have.response e.g.when there's - // a network error or timeout. - // These errors will have .request but not .response. + // It's pretty common for axios errors to not have a `response`, + // for example if there was a network error or timeout. if (error.response) { processed.response = { status: error.response.status, @@ -142,16 +145,15 @@ export default class LoggerService extends DependencyAwareClass { } /** - * Transform the original message - * before it is passed to the winston logger + * Transform the original message before it is passed to the logger. * - * @param {string|object} message + * @param message */ - processMessage(message = '') { + static processMessage(message: any) { let processed = message; - if (processed && processed.isAxiosError) { - processed = this.constructor.processAxiosError(processed); + if (processed?.isAxiosError) { + processed = LoggerService.processAxiosError(processed); } return processed; @@ -163,7 +165,7 @@ export default class LoggerService extends DependencyAwareClass { * @param error object * @param message string */ - error(error, message = '') { + error(error: any, message = '') { if (sentryIsAvailable && error instanceof Error) { Sentry.captureException(error); } @@ -178,44 +180,34 @@ export default class LoggerService extends DependencyAwareClass { Epsagon.setError(error); } - this.logger.log('error', message, { error: this.processMessage(error) }); + this.logger.log('error', message, { error: LoggerService.processMessage(error) }); this.label('error', true); this.metric('error', 'error', true); } /** - * Get sentry client + * Log an informational message. * - * @returns {null|*} + * @param message */ - getSentry() { - return this.sentry; + info(message: any) { + this.logger.log('info', LoggerService.processMessage(message)); } /** - * Log Information Message + * Log an error, using `LoggerService.error` or `LoggerService.info` based + * on `process.env.LOGGER_SOFT_WARNING`. * - * @param message string - */ - info(message) { - this.logger.log('info', this.processMessage(message)); - } - - /** - * Logs an error, using `LoggerService.error` - * or `LoggerService.info` based on - * `process.env.LOGGER_SOFT_WARNING`. - * - * Please note that `LoggerService.error` and `LoggerService.info` - * have different signatures. The function uses the shared argument - * instead of introducing ambiguity. + * Please note that `LoggerService.error` and `LoggerService.info` have + * different signatures. The function uses the shared argument instead of + * introducing ambiguity. * * @param error */ - warning(error) { + warning(error: any) { const softWarningValues = ['true', '1']; - if (softWarningValues.includes(process.env.LOGGER_SOFT_WARNING)) { + if (softWarningValues.includes(process.env.LOGGER_SOFT_WARNING || '')) { return this.info(error); } @@ -223,12 +215,12 @@ export default class LoggerService extends DependencyAwareClass { } /** - * Add a label + * Add a label to the function's Epsagon trace. * - * @param descriptor string - * @param silent boolean + * @param descriptor + * @param silent If `false`, the label will also be logged. (default: false) */ - label(descriptor, silent = false) { + label(descriptor: string, silent = false) { if ( typeof process.env.EPSAGON_TOKEN === 'string' && process.env.EPSAGON_TOKEN !== 'undefined' @@ -238,19 +230,19 @@ export default class LoggerService extends DependencyAwareClass { Epsagon.label(descriptor, true); } - if (silent === false) { + if (!silent) { this.logger.log('info', `label - ${descriptor}`); } } /** - * Add a metric + * Add a metric to the function's Epsagon trace. * - * @param descriptor string - * @param stat integer | string - * @param silent boolean + * @param descriptor + * @param stat + * @param silent If `false`, the metric will also be logged. (default: false) */ - metric(descriptor, stat, silent = false) { + metric(descriptor: string, stat: number | string, silent = false) { if ( typeof process.env.EPSAGON_TOKEN === 'string' && process.env.EPSAGON_TOKEN !== 'undefined' @@ -266,13 +258,13 @@ export default class LoggerService extends DependencyAwareClass { } /** - * Logs an object so that it can be inspected + * Log an object so that it can be inspected. * - * @param action - What are we doing with the object, i.e. 'Processing' - * @param object - The object to be stored in logs - * @param level - 'error', 'warning' or 'info' + * @param action What are we doing with the object, e.g. 'Processing' + * @param object The object to be stored in logs + * @param level 'error', 'warning' or 'info' */ - object(action, object, level = 'info') { + object(action: string, object: any, level: 'error' | 'warning' | 'info' = 'info') { if (!(['error', 'warning', 'info'].includes(level))) { throw new Error('Unrecognised log level'); } diff --git a/src/Service/Request.service.js b/src/services/RequestService.ts similarity index 54% rename from src/Service/Request.service.js rename to src/services/RequestService.ts index b6ad5533..4070ffb7 100644 --- a/src/Service/Request.service.js +++ b/src/services/RequestService.ts @@ -1,16 +1,13 @@ -/* eslint-disable class-methods-use-this */ -/* eslint-disable sonarjs/no-duplicate-string */ -/* @flow */ - import QueryString from 'querystring'; +import { APIGatewayProxyEvent } from 'aws-lambda'; import useragent from 'useragent'; import validate from 'validate.js/validate'; import XML2JS from 'xml2js'; -import { DEFINITIONS } from '../Config/Dependencies'; -import DependencyAwareClass from '../DependencyInjection/DependencyAware.class'; -import ResponseModel from '../Model/Response.model'; +import DependencyAwareClass from '../core/DependencyAwareClass'; +import ResponseModel from '../models/ResponseModel'; +import LoggerService from './LoggerService'; export const REQUEST_TYPES = { DELETE: 'DELETE', @@ -40,34 +37,42 @@ export const ERROR_TYPES = { VALIDATION_ERROR: new ResponseModel({}, 400, 'required fields are missing'), }; +export type RequestFile = { + type: string; + filename: string; + contentType: string; + content: string | Buffer; +}; + /** - * RequestService class + * Provides access to components of the HTTP request being handled. */ export default class RequestService extends DependencyAwareClass { /** * Get a parameter from the request. * * @param parameter - * @param ifNull + * @param ifNull Value to return if the parameter is not set. * @param requestType */ - get(parameter: string, ifNull = null, requestType = null) { + get(parameter: string, ifNull?: string | null, requestType?: string): string | string[] | null { const queryParameters = this.getAll(requestType); if (queryParameters === null) { - return ifNull; + return ifNull ?? null; } - return typeof queryParameters[parameter] !== 'undefined' ? queryParameters[parameter] : ifNull; + return queryParameters[parameter] ?? ifNull ?? null; } /** * Get all HTTP headers included in the request. * - * @returns {object} An object with a key for each header. + * @returns An object with a key for each header. */ getAllHeaders() { - return { ...this.getContainer().getEvent().headers }; + const event = this.getContainer().getEvent() as APIGatewayProxyEvent; + return { ...event.headers }; } /** @@ -75,12 +80,11 @@ export default class RequestService extends DependencyAwareClass { * * The header name is case-insensitive. * - * @param {string} name The name of the header. - * @param {string} [whenMissing] Value to return if the header is missing. + * @param name The name of the header. + * @param [whenMissing] Value to return if the header is missing. * (default: empty string) - * @returns {string} */ - getHeader(name: string, whenMissing: string = '') { + getHeader(name: string, whenMissing = ''): string { const headers = this.getAllHeaders(); if (!headers) { return whenMissing; @@ -91,11 +95,9 @@ export default class RequestService extends DependencyAwareClass { } /** - * Get authorization token - * - * @returns {*} + * Get an authorization token from the `Authorization` header. */ - getAuthorizationToken() { + getAuthorizationToken(): string | null { const authorization = this.getHeader('Authorization'); if (!authorization) { return null; @@ -112,22 +114,22 @@ export default class RequestService extends DependencyAwareClass { } /** - * Get a path parameter + * Get a path parameter, or all path parameters if no `parameter` is given. * * @param parameter - * @param ifNull mixed + * @param ifNull Value to return if the parameter is not set. */ - getPathParameter(parameter: string = null, ifNull = {}) { - const event = this.getContainer().getEvent(); + getPathParameter(parameter?: string, ifNull = {}): any { + const event = this.getContainer().getEvent() as APIGatewayProxyEvent; // If no parameter has been requested, return all path parameters - if (parameter === null && typeof event.pathParameters === 'object') { + if (!parameter && typeof event.pathParameters === 'object') { return event.pathParameters; } // If a specifc parameter has been requested, return the parameter if it exists if ( - typeof parameter === 'string' + parameter && typeof event.pathParameters === 'object' && event.pathParameters !== null && typeof event.pathParameters[parameter] !== 'undefined' @@ -142,42 +144,50 @@ export default class RequestService extends DependencyAwareClass { * Get all request parameters * * @param requestType - * @returns {{}} */ - // eslint-disable-next-line sonarjs/cognitive-complexity - getAll(requestType = null) { - const event = this.getContainer().getEvent(); + getAll(requestType?: string): any { + const event = this.getContainer().getEvent() as APIGatewayProxyEvent; - if (HTTP_METHODS_WITHOUT_PAYLOADS.includes(event.httpMethod) || HTTP_METHODS_WITHOUT_PAYLOADS.includes(requestType)) { + if ( + HTTP_METHODS_WITHOUT_PAYLOADS.includes(event.httpMethod) + || HTTP_METHODS_WITHOUT_PAYLOADS.includes(requestType || '') + ) { // get simple parameters - const params = { ...event.queryStringParameters }; + const params: Record = { + ...event.queryStringParameters, + }; // add array parameters as arrays - Object.keys(params) - .filter((key) => key.endsWith('[]')) - .forEach((key) => { - params[key] = event.multiValueQueryStringParameters[key]; - }); + if (event.multiValueQueryStringParameters !== null) { + Object.keys(params) + .filter((key) => key.endsWith('[]')) + .forEach((key) => { + params[key] = event.multiValueQueryStringParameters?.[key]; + }); + } return params; } - if (HTTP_METHODS_WITH_PAYLOADS.includes(event.httpMethod) || HTTP_METHODS_WITH_PAYLOADS.includes(requestType)) { + if ( + HTTP_METHODS_WITH_PAYLOADS.includes(event.httpMethod) + || HTTP_METHODS_WITH_PAYLOADS.includes(requestType || '') + ) { const contentType = this.getHeader('Content-Type'); let queryParameters = {}; if (contentType.includes('application/x-www-form-urlencoded')) { - queryParameters = QueryString.parse(event.body); + queryParameters = QueryString.parse(event.body as string); } if (contentType.includes('application/json')) { try { - queryParameters = JSON.parse(event.body); + queryParameters = JSON.parse(event.body as string); } catch { queryParameters = {}; } } if (contentType.includes('text/xml')) { - XML2JS.parseString(event.body, (error, result) => { + XML2JS.parseString(event.body as string, (error, result) => { queryParameters = error ? {} : result; }); } @@ -194,10 +204,8 @@ export default class RequestService extends DependencyAwareClass { /** * Fetch the request IP address - * - * @returns {*} */ - getIp() { + getIp(): string | null { const event = this.getContainer().getEvent(); if ( @@ -212,13 +220,11 @@ export default class RequestService extends DependencyAwareClass { } /** - * Get user agent - * - * @returns {*} + * Get user agent details from the `User-Agent` header. */ getUserBrowserAndDevice() { - const userAgent = this.getHeader('user-agent', null); - if (userAgent === null) { + const userAgent = this.getHeader('user-agent'); + if (!userAgent) { return null; } @@ -234,20 +240,20 @@ export default class RequestService extends DependencyAwareClass { 'operating-system-version': agent.os.toVersion(), }; } catch { - this.getContainer().get(DEFINITIONS.LOGGER).label('user-agent-parsing-failed'); - + this.di.get(LoggerService).label('user-agent-parsing-failed'); return null; } } /** - * Test a request against validation constraints + * Test a request against validation constraints. + * + * See [validate.js](https://validatejs.org/) for how to write constraints. * * @param constraints - * @returns {Promise} */ - validateAgainstConstraints(constraints: object) { - const Logger = this.getContainer().get(DEFINITIONS.LOGGER); + validateAgainstConstraints(constraints: object): Promise { + const logger = this.di.get(LoggerService); return new Promise((resolve, reject) => { const validation = validate(this.getAll(), constraints); @@ -255,49 +261,54 @@ export default class RequestService extends DependencyAwareClass { if (typeof validation === 'undefined') { resolve(); } else { - Logger.label('request-validation-failed'); + logger.label('request-validation-failed'); const validationErrorResponse = ERROR_TYPES.VALIDATION_ERROR; validationErrorResponse.setBodyVariable('validation_errors', validation); - reject(validationErrorResponse); } }); } /** - * Fetch the request multipart form + * Fetch the request multipart form. * - * @param useBuffer - * @returns {*} + * @param useBuffer Whether to return file content as a `Buffer`. */ parseForm(useBuffer: boolean) { - const event = this.getContainer().getEvent(); - const boundary = this.getBoundary(event); + // todo: rewrite this to use a dedicated package and add error handling + /* eslint-disable @typescript-eslint/no-non-null-assertion */ + + const event = this.getContainer().getEvent() as APIGatewayProxyEvent; + const boundary = RequestService.getBoundary(event) as string; - const body = event.isBase64Encoded ? Buffer.from(event.body, 'base64').toString('binary').trim() : event.body; + const body = event.isBase64Encoded + ? Buffer.from(event.body as string, 'base64').toString('binary').trim() + : event.body as string; - const result = {}; + const result: Record = {}; body.split(boundary).forEach((item) => { if (/filename=".+"/g.test(item)) { - result[item.match(/name=".+";/g)[0].slice(6, -2)] = { + const name = item.match(/name=".+";/g)![0].slice(6, -2); + result[name] = { type: 'file', - filename: item.match(/filename=".+"/g)[0].slice(10, -1), - contentType: item.match(/Content-Type:\s.+/g)[0].slice(14), + filename: item.match(/filename=".+"/g)![0].slice(10, -1), + contentType: item.match(/Content-Type:\s.+/g)![0].slice(14), content: useBuffer - ? Buffer.from(item.slice(item.search(/Content-Type:\s.+/g) + item.match(/Content-Type:\s.+/g)[0].length + 4, -4), 'binary') - : item.slice(item.search(/Content-Type:\s.+/g) + item.match(/Content-Type:\s.+/g)[0].length + 4, -4), + ? Buffer.from(item.slice(item.search(/Content-Type:\s.+/g) + item.match(/Content-Type:\s.+/g)![0].length + 4, -4), 'binary') + : item.slice(item.search(/Content-Type:\s.+/g) + item.match(/Content-Type:\s.+/g)![0].length + 4, -4), }; } else if (/name=".+"/g.test(item)) { - result[item.match(/name=".+"/g)[0].slice(6, -1)] = item.slice(item.search(/name=".+"/g) + item.match(/name=".+"/g)[0].length + 4, -4); + result[item.match(/name=".+"/g)![0].slice(6, -1)] = item.slice(item.search(/name=".+"/g) + item.match(/name=".+"/g)![0].length + 4, -4); } }); + + /* eslint-enable @typescript-eslint/no-non-null-assertion */ + return result; } /** * Fetch the request AWS event Records - * - * @returns {*} */ getAWSRecords() { const event = this.getContainer().getEvent(); @@ -310,15 +321,15 @@ export default class RequestService extends DependencyAwareClass { } /** - * Gets a value independently from - * the case of the key + * Gets a value independently from the case of the key. * * @param object * @param key */ - getValueIgnoringKeyCase(object, key) { - const foundKey = Object.keys(object).find((currentKey) => currentKey.toLocaleLowerCase() === key.toLowerCase()); - return object[foundKey]; + static getValueIgnoringKeyCase(object: Record, key: string): string | undefined { + const foundKey = Object.keys(object) + .find((currentKey) => currentKey.toLocaleLowerCase() === key.toLowerCase()); + return foundKey && object[foundKey]; } /** @@ -327,7 +338,7 @@ export default class RequestService extends DependencyAwareClass { * * @param event */ - getBoundary(event) { - return this.getValueIgnoringKeyCase(event.headers, 'Content-Type').split('=')[1]; + static getBoundary(event: APIGatewayProxyEvent): string | undefined { + return this.getValueIgnoringKeyCase(event.headers, 'Content-Type')?.split('=')?.[1]; } } diff --git a/src/Service/SQS.service.js b/src/services/SQSService.ts similarity index 52% rename from src/Service/SQS.service.js rename to src/services/SQSService.ts index 1d4dbbf2..1144f016 100644 --- a/src/Service/SQS.service.js +++ b/src/services/SQSService.ts @@ -1,14 +1,52 @@ -/* @flow */ import alai from 'alai'; -import each from 'async/each'; +import { each } from 'async'; import AWS from 'aws-sdk'; -import { v4 as UUID } from 'uuid'; +import { v4 as uuid } from 'uuid'; -import { DEFINITIONS } from '../Config/Dependencies'; -import DependencyAwareClass from '../DependencyInjection/DependencyAware.class'; -import DependencyInjection from '../DependencyInjection/DependencyInjection.class'; -import SQSMessageModel from '../Model/SQS/Message.model'; -import StatusModel, { STATUS_TYPES } from '../Model/Status.model'; +import DependencyAwareClass from '../core/DependencyAwareClass'; +import DependencyInjection from '../core/DependencyInjection'; +import SQSMessageModel from '../models/SQSMessageModel'; +import StatusModel, { STATUS_TYPES } from '../models/StatusModel'; +import LoggerService from './LoggerService'; +import TimerService from './TimerService'; + +export interface SQSServiceConfig { + /** + * Maps short friendly queue names to the full SQS queue name. + * + * Usually we define queue names in our `serverless.yml` and provide them to + * the application via environment variables. Example: + * + * ```ts + * { + * queues: { + * submissions: process.env.SQS_QUEUE_SUBMISSIONS, + * } + * } + * ``` + */ + queues?: Record; + /** + * Maps short friendly queue names to the queue consumer function name, for + * use with offline SQS emulation. Example: + * + * ```ts + * { + * queueConsumers: { + * submissions: 'SubmissionConsumer', + * } + * } + * ``` + * + * See the [SQSService docs](../../docs/services/SQSService.md) for details + * about how this works. + */ + queueConsumers?: Record; +} + +export interface WithSQSServiceConfig { + sqs?: SQSServiceConfig; +} /** * Allowed values for `process.env.LAMBDA_WRAPPER_OFFLINE_SQS_MODE`. @@ -34,38 +72,72 @@ export const SQS_OFFLINE_MODES = { }; /** - * Defines the preferred behaviour - * for SQSService.prototype.publish - * should AWS SQS fail. + * Defines the preferred behaviour for `SQSService.prototype.send` in case the + * AWS SQS call fails. */ export const SQS_PUBLISH_FAILURE_MODES = { /** - * Catches the exception and logs it. - * This is the default behaviour - * for LambdaWrapper 1.8.0 and below - * and for LambdaWrapper 1.8.2 and above + * Catch the exception and logs it. + * + * This is the default behaviour for Lambda Wrapper v1.8.0 and below and for + * Lambda Wrapper v1.8.2 and above. */ CATCH: 'catch', /** - * Throws the exception so that the caller - * can handle it directly. + * Throw the exception so that the caller can handle it directly. */ THROW: 'throw', -}; +} as const; /** - * SQSService class + * Helper service for working with SQS. + * + * Config for this service goes in the `sqs` key of your Lambda Wrapper config. + * The `queues` key maps short friendly names to the full SQS queue name. + * Usually we define queue names in our `serverless.yml` and provide them to + * the application via environment variables. + * + * ```ts + * const lambdaWrapper = lw.configure({ + * sqs: { + * queues: { + * // add an entry for each queue mapping to its AWS name + * submissions: process.env.SQS_QUEUE_SUBMISSIONS, + * }, + * }, + * }); + * ``` + * + * You can then send messages to a queue within your Lambda handler using the + * `publish` method. + * + * ```ts + * export default lambdaWrapper.wrap(async (di) => { + * const sqs = di.get(SQSService); + * const message = { data: 'Hello SQS!' }; + * await sqs.publish('submissions', message); + * }); + * ``` */ export default class SQSService extends DependencyAwareClass { - /** - * SQSService constructor - * - * @param di DependencyInjection - */ + readonly queues: Record; + + readonly queueConsumers: Record; + + readonly queueUrls: Record; + + private $sqs?: AWS.SQS; + + private $lambda?: AWS.Lambda; + constructor(di: DependencyInjection) { super(di); + const config = (this.di.config as WithSQSServiceConfig).sqs; + this.queues = config?.queues || {}; + this.queueConsumers = config?.queueConsumers || {}; + const { LAMBDA_WRAPPER_OFFLINE_SQS_HOST: offlineHost = 'localhost', LAMBDA_WRAPPER_OFFLINE_SQS_PORT: offlinePort = '4576', @@ -74,33 +146,22 @@ export default class SQSService extends DependencyAwareClass { REGION, } = process.env; - const container = this.getContainer(); - const context = container.getContext(); - const queues = container.getConfiguration('QUEUES'); - const accountId = (context && context.invokedFunctionArn && alai.parse(context)) || AWS_ACCOUNT_ID; - - this.queues = {}; - - this.$lambda = null; - this.$sqs = null; + const accountId = (di.context.invokedFunctionArn && alai.parse(di.context)) + || AWS_ACCOUNT_ID; - if (container.isOffline && !Object.values(SQS_OFFLINE_MODES).includes(offlineMode)) { + if (di.isOffline && !Object.values(SQS_OFFLINE_MODES).includes(offlineMode)) { throw new Error(`Invalid LAMBDA_WRAPPER_OFFLINE_SQS_MODE: ${offlineMode}\n` + `Please use one of: ${Object.values(SQS_OFFLINE_MODES).join(', ')}`); } - // Add the queues from configuration - if (queues !== null && Object.keys(queues).length > 0) { - Object.keys(queues).forEach((queueDefinition) => { - if (container.isOffline && offlineMode === SQS_OFFLINE_MODES.LOCAL) { - // custom URL when using an offline SQS service such as Localstack - this.queues[queueDefinition] = `http://${offlineHost}:${offlinePort}/queue/${queues[queueDefinition]}`; - } else { - // default AWS queue URL - this.queues[queueDefinition] = `https://sqs.${REGION}.amazonaws.com/${accountId}/${queues[queueDefinition]}`; - } - }); - } + const useLocalQueues = di.isOffline && offlineMode === SQS_OFFLINE_MODES.LOCAL; + this.queueUrls = Object.fromEntries( + Object.entries(this.queues).map(( + ([key, queueName]) => [key, useLocalQueues + ? `http://${offlineHost}:${offlinePort}/queue/${queueName}` + : `https://sqs.${REGION}.amazonaws.com/${accountId}/${queueName}`] + )), + ); } /** @@ -154,23 +215,21 @@ export default class SQSService extends DependencyAwareClass { } /** - * Batch delete messages + * Batch delete messages. * - * @param queue strung - * @param messageModels [SQSMessageModel] - * @returns {Promise} + * @param queue + * @param messageModels */ - batchDelete(queue: string, messageModels: [SQSMessageModel]) { - const container = this.getContainer(); - const queueUrl = this.queues[queue]; - const Logger = container.get(DEFINITIONS.LOGGER); - const Timer = container.get(DEFINITIONS.TIMER); - const timerId = `sqs-batch-delete-${UUID()} - Queue: '${queueUrl}'`; + batchDelete(queue: string, messageModels: SQSMessageModel[]): Promise { + const queueUrl = this.queueUrls[queue]; + const logger = this.di.get(LoggerService); + const timer = this.di.get(TimerService); + const timerId = `sqs-batch-delete-${uuid()} - Queue: '${queueUrl}'`; - return new Promise((resolve) => { - const messagesForDeletion = []; + return new Promise((resolve) => { + const messagesForDeletion: { Id: string; ReceiptHandle: string }[] = []; - Timer.start(timerId); + timer.start(timerId); // assuming openFiles is an array of file names each( messageModels, @@ -185,7 +244,7 @@ export default class SQSService extends DependencyAwareClass { }, (loopError) => { if (loopError) { - Logger.error(loopError); + logger.error(loopError); resolve(); } @@ -195,10 +254,10 @@ export default class SQSService extends DependencyAwareClass { QueueUrl: queueUrl, }, (error) => { - Timer.stop(timerId); + timer.stop(timerId); if (error) { - Logger.error(error); + logger.error(error); } resolve(); @@ -210,26 +269,23 @@ export default class SQSService extends DependencyAwareClass { } /** - * Check SQS status - * - * @returns {Promise} + * Check SQS status. */ checkStatus() { - const container = this.getContainer(); - const Logger = container.get(DEFINITIONS.LOGGER); - const Timer = container.get(DEFINITIONS.TIMER); - const timerId = `sqs-list-queues-${UUID()}`; + const logger = this.di.get(LoggerService); + const timer = this.di.get(TimerService); + const timerId = `sqs-list-queues-${uuid()}`; return new Promise((resolve) => { - Timer.start(timerId); + timer.start(timerId); this.sqs.listQueues({}, (error, data) => { - Timer.stop(timerId); + timer.stop(timerId); const statusModel = new StatusModel('SQS', STATUS_TYPES.OK); if (error) { - Logger.error(error); + logger.error(error); statusModel.setStatus(STATUS_TYPES.APPLICATION_FAILURE); } @@ -243,20 +299,18 @@ export default class SQSService extends DependencyAwareClass { } /** - * Get number of messages in a queue + * Get the approximate number of messages in a queue. * * @param queue - * @returns {Promise} */ - getMessageCount(queue: string) { - const container = this.getContainer(); - const queueUrl = this.queues[queue]; - const Logger = container.get(DEFINITIONS.LOGGER); - const Timer = container.get(DEFINITIONS.TIMER); - const timerId = `sqs-get-queue-attributes-${UUID()} - Queue: '${queueUrl}'`; + getMessageCount(queue: string): Promise { + const queueUrl = this.queueUrls[queue]; + const logger = this.di.get(LoggerService); + const timer = this.di.get(TimerService); + const timerId = `sqs-get-queue-attributes-${uuid()} - Queue: '${queueUrl}'`; return new Promise((resolve) => { - Timer.start(timerId); + timer.start(timerId); this.sqs.getQueueAttributes( { @@ -264,21 +318,22 @@ export default class SQSService extends DependencyAwareClass { QueueUrl: queueUrl, }, (error, data) => { - Timer.stop(timerId); + timer.stop(timerId); if (error) { - Logger.error(error); + logger.error(error); resolve(0); } - resolve(Number.parseInt(data.Attributes.ApproximateNumberOfMessages, 10)); + const messageCount = data.Attributes?.ApproximateNumberOfMessages || '0'; + resolve(Number.parseInt(messageCount, 10)); }, ); }); } /** - * Publish to message queue + * Publish to message queue. * * When running within serverless-offline, messages can be published to a * local Lambda or SQS service instead of to AWS, depending on the offline @@ -287,42 +342,40 @@ export default class SQSService extends DependencyAwareClass { * @param queue string * @param messageObject object * @param messageGroupId string - * @param {'catch' | 'throw'} failureMode Choose how failures are handled: + * @param failureMode Choose how failures are handled: * - `catch`: errors will be caught and logged. This is the default. * - `throw`: errors will be thrown, causing promise to reject. - * @returns {Promise} */ - async publish(queue: string, messageObject: object, messageGroupId = null, failureMode = SQS_PUBLISH_FAILURE_MODES.CATCH) { + async publish(queue: string, messageObject: object, messageGroupId = null, failureMode: 'catch' | 'throw' = SQS_PUBLISH_FAILURE_MODES.CATCH) { if (!Object.values(SQS_PUBLISH_FAILURE_MODES).includes(failureMode)) { throw new Error(`Invalid value for 'failureMode': ${failureMode}`); } - const container = this.getContainer(); - const queueUrl = this.queues[queue]; - const Timer = container.get(DEFINITIONS.TIMER); - const timerId = `sqs-send-message-${UUID()} - Queue: '${queueUrl}'`; + const queueUrl = this.queueUrls[queue]; + const timer = this.di.get(TimerService); + const timerId = `sqs-send-message-${uuid()} - Queue: '${queueUrl}'`; - Timer.start(timerId); + timer.start(timerId); - const messageParameters = { + const messageParameters: AWS.SQS.SendMessageRequest = { MessageBody: JSON.stringify(messageObject), QueueUrl: queueUrl, }; if (queueUrl.includes('.fifo')) { - messageParameters.MessageDeduplicationId = UUID(); - messageParameters.MessageGroupId = messageGroupId !== null ? messageGroupId : UUID(); + messageParameters.MessageDeduplicationId = uuid(); + messageParameters.MessageGroupId = messageGroupId !== null ? messageGroupId : uuid(); } try { - if (container.isOffline && this.constructor.offlineMode === SQS_OFFLINE_MODES.DIRECT) { + if (this.di.isOffline && SQSService.offlineMode === SQS_OFFLINE_MODES.DIRECT) { await this.publishOffline(queue, messageParameters); } else { await this.sqs.sendMessage(messageParameters).promise(); } } catch (error) { if (failureMode === SQS_PUBLISH_FAILURE_MODES.CATCH) { - container.get(DEFINITIONS.LOGGER).error(error); + this.di.get(LoggerService).error(error); return null; } throw error; @@ -341,18 +394,18 @@ export default class SQSService extends DependencyAwareClass { * @param queue * @param messageParameters */ - async publishOffline(queue: string, messageParameters) { - const container = this.getContainer(); - - if (!container.isOffline) { + async publishOffline(queue: string, messageParameters: AWS.SQS.SendMessageRequest) { + if (!this.di.isOffline) { throw new Error('Can only publishOffline while running serverless offline.'); } - const consumers = container.getConfiguration('QUEUE_CONSUMERS') || {}; - const FunctionName = consumers[queue]; + const FunctionName = this.queueConsumers[queue]; if (!FunctionName) { - throw new Error(`Queue consumer for queue ${queue} was not found. Please configure your application's QUEUE_CONSUMERS.`); + throw new Error( + `Queue consumer for queue ${queue} was not found. Please add it to ` + + 'the sqs.queueConsumers key in your Lambda Wrapper config.', + ); } const InvocationType = 'RequestResponse'; @@ -375,17 +428,15 @@ export default class SQSService extends DependencyAwareClass { * * @param queue string * @param timeout number - * @returns {Promise} */ - receive(queue: string, timeout: number = 15) { - const container = this.getContainer(); - const queueUrl = this.queues[queue]; - const Logger = container.get(DEFINITIONS.LOGGER); - const Timer = container.get(DEFINITIONS.TIMER); - const timerId = `sqs-receive-message-${UUID()} - Queue: '${queueUrl}'`; + receive(queue: string, timeout = 15): Promise { + const queueUrl = this.queueUrls[queue]; + const logger = this.di.get(LoggerService); + const timer = this.di.get(TimerService); + const timerId = `sqs-receive-message-${uuid()} - Queue: '${queueUrl}'`; return new Promise((resolve, reject) => { - Timer.start(timerId); + timer.start(timerId); this.sqs.receiveMessage( { @@ -394,10 +445,10 @@ export default class SQSService extends DependencyAwareClass { MaxNumberOfMessages: 10, }, (error, data) => { - Timer.stop(timerId); + timer.stop(timerId); if (error) { - Logger.error(error); + logger.error(error); return reject(error); } diff --git a/src/services/TimerService.ts b/src/services/TimerService.ts new file mode 100644 index 00000000..962cbb20 --- /dev/null +++ b/src/services/TimerService.ts @@ -0,0 +1,33 @@ +import DependencyAwareClass from '../core/DependencyAwareClass'; +import LoggerService from './LoggerService'; + +/** + * Timer helper that can be used to measure how long operations take. + */ +export default class TimerService extends DependencyAwareClass { + timers: Record = {}; + + /** + * Start a timer. + * + * To stop the timer, call `stop()` with the same `identifier`. + * + * @param identifier + */ + start(identifier: string) { + this.timers[identifier] = Date.now(); + } + + /** + * Stop a timer and log the elapsed time. + * + * @param identifier + */ + stop(identifier: string) { + if (identifier in this.timers) { + const logger = this.di.get(LoggerService); + const duration = Date.now() - this.timers[identifier]; + logger.info(`Timing - ${identifier} took ${duration} ms to complete`); + } + } +} diff --git a/src/utils/LambdaTermination.ts b/src/utils/LambdaTermination.ts new file mode 100644 index 00000000..cda88e36 --- /dev/null +++ b/src/utils/LambdaTermination.ts @@ -0,0 +1,22 @@ +/** + * An error that triggers a Lambda termination. + * + * Offers developer details (that are logged), a code for the Lambda and a + * front-facing consumer message. + */ +export default class LambdaTermination extends Error { + constructor( + readonly internal: object | string, + readonly code = 500, + readonly body: object | string | null = null, + readonly details = 'unknown error', + ) { + const stringified = typeof internal === 'string' + ? internal + : JSON.stringify(internal); + + super(stringified); + + this.body = body || 'unknown error'; + } +} diff --git a/src/Wrapper/PromisifiedDelay.js b/src/utils/PromisifiedDelay.ts similarity index 81% rename from src/Wrapper/PromisifiedDelay.js rename to src/utils/PromisifiedDelay.ts index 94992ea9..d2c3cbee 100644 --- a/src/Wrapper/PromisifiedDelay.js +++ b/src/utils/PromisifiedDelay.ts @@ -18,31 +18,29 @@ const HIGH_LATENCY_DELAYS = { * PromisifiedDelay class */ export default class PromisifiedDelay { + readonly delays: number[] = []; + /** * PromisifiedDelay constructor * * @param highLatency */ constructor(highLatency = true) { - this.delays = []; - const delayArray = highLatency === true ? HIGH_LATENCY_DELAYS : STANDARD_LATENCY_DELAYS; Object.keys(delayArray).forEach((delayDuration) => { - const delayIterations = delayArray[delayDuration]; + const delayIterations = (delayArray as any)[delayDuration]; for (let i = 0; i < delayIterations; i += 1) { - this.delays.push(delayDuration); + this.delays.push(Number.parseInt(delayDuration, 10)); } }); } /** * Create a promisified delay - * - * @returns {Promise} */ - get() { + get(): Promise { return new Promise((resolve) => { setTimeout(() => { resolve(); diff --git a/tests/.eslintrc.yml b/tests/.eslintrc.yml index 446a915a..4f0ac1cc 100644 --- a/tests/.eslintrc.yml +++ b/tests/.eslintrc.yml @@ -1,2 +1,6 @@ extends: - '@comicrelief/eslint-config/mixins/jest' + +rules: + max-classes-per-file: off + '@typescript-eslint/no-non-null-assertion': off diff --git a/tests/lib/mocks.js b/tests/lib/mocks.js deleted file mode 100644 index 656e57ce..00000000 --- a/tests/lib/mocks.js +++ /dev/null @@ -1,49 +0,0 @@ -import { DEFINITIONS } from '../../src/Config/Dependencies'; - -/** - * Returns a mocked logger. - * - * You can pass an overrides object - * specifying the return values - * and/or behaviour for each property. - * - * @param {object} overrides - * @param {object} di - */ -export const getMockedLogger = (overrides = {}, di = null) => { - const logger = { - di, - error: jest.fn(() => overrides.error || null), - info: jest.fn(() => overrides.info || null), - metric: jest.fn(() => overrides.metric || null), - }; - - logger.getContainer = () => logger.di; - - return logger; -}; - -/** - * Returns a mocked di. - * - * You can pass an overrides object - * specifying the behaviour of a depedendency. - * - * @param {object} overrides - */ -export const getMockedDi = (overrides = {}) => { - const deps = { - [DEFINITIONS.LOGGER]: null, - ...overrides, - }; - const di = { - deps, - get: (key) => deps[key], - }; - - if (!deps[DEFINITIONS.LOGGER]) { - deps[DEFINITIONS.LOGGER] = getMockedLogger({}, di); - } - - return di; -}; diff --git a/tests/mocks/aws/index.ts b/tests/mocks/aws/index.ts new file mode 100644 index 00000000..adcaa6f5 --- /dev/null +++ b/tests/mocks/aws/index.ts @@ -0,0 +1,6 @@ +import { Context } from '@/src'; +import context from '@/tests/mocks/aws/context.json'; +import event from '@/tests/mocks/aws/event.json'; + +export const mockContext = context as Context; +export const mockEvent = event; diff --git a/tests/unit/DependencyInjection/DependencyAware.class.test.js b/tests/unit/DependencyInjection/DependencyAware.class.test.js deleted file mode 100644 index 3b151fde..00000000 --- a/tests/unit/DependencyInjection/DependencyAware.class.test.js +++ /dev/null @@ -1,32 +0,0 @@ -import DependencyInjection from '../../../src/DependencyInjection/DependencyInjection.class'; -// The import order is relevant here to avoid circular imports -// eslint-disable-next-line import/order -import DependencyAware from '../../../src/DependencyInjection/DependencyAware.class'; -import getContext from '../../mocks/aws/context.json'; -import getEvent from '../../mocks/aws/event.json'; - -describe('DependencyInjection/DependencyAwareClass', () => { - describe('getContainer', () => { - const dependencyInjectionClass = new DependencyInjection({}, getEvent, getContext); - const dependencyAwareClass = new DependencyAware(dependencyInjectionClass); - - it('should instantiate and be able to get the dependency injection container', () => { - expect(dependencyAwareClass.getContainer()).toEqual(dependencyInjectionClass); - }); - }); - - describe('definitions', () => { - describe('Returns the provided definitions', () => { - [ - [{}, undefined], - [{ DEFINITIONS: 1 }, 1], - ].forEach(([configuration, expected]) => { - it(`With configuration: ${configuration}`, () => { - const di = new DependencyInjection(configuration); - const service = new DependencyAware(di); - expect(service.definitions).toEqual(expected); - }); - }); - }); - }); -}); diff --git a/tests/unit/DependencyInjection/DependencyInjection.class.test.js b/tests/unit/DependencyInjection/DependencyInjection.class.test.js deleted file mode 100644 index 4304f7a0..00000000 --- a/tests/unit/DependencyInjection/DependencyInjection.class.test.js +++ /dev/null @@ -1,101 +0,0 @@ -import { DEFINITIONS } from '../../../src/Config/Dependencies'; -import DependencyInjection from '../../../src/DependencyInjection/DependencyInjection.class'; -import LoggerService from '../../../src/Service/Logger.service'; -import RequestService from '../../../src/Service/Request.service'; -import getContext from '../../mocks/aws/context.json'; -import getEvent from '../../mocks/aws/event.json'; - -describe('DependencyInjection/DependencyInjectionClass', () => { - describe('should instantiate', () => { - const configuration = { - test: 123, - }; - const dependencyInjection = new DependencyInjection(configuration, getEvent, getContext); - - it('should output the event that was provided to it', () => { - expect(dependencyInjection.getEvent()).toEqual(getEvent); - }); - - it('should output the context that was provided to it', () => { - expect(dependencyInjection.getContext()).toEqual(getContext); - }); - - it('should output the configuration that was provided to it', () => { - expect(dependencyInjection.getConfiguration()).toEqual(configuration); - }); - }); - - describe('should get dependencies', () => { - const dependencyInjection = new DependencyInjection({}, getEvent, getContext); - - it('Should throw validation errors when an non existent model is requested', () => { - expect(() => dependencyInjection.get('test')).toThrow('test does not exist in di container'); - }); - - it('should fetch an instance of the logger service', () => { - expect(dependencyInjection.get(DEFINITIONS.LOGGER) instanceof LoggerService).toEqual(true); - }); - - it('should fetch an instance of the request service', () => { - const requestService = dependencyInjection.get(DEFINITIONS.REQUEST); - expect(requestService instanceof RequestService).toEqual(true); - expect(requestService.di instanceof DependencyInjection).toEqual(true); - }); - }); - - describe('isOffline', () => { - let useServerlessOffline; - - beforeAll(() => { - useServerlessOffline = process.env.USE_SERVERLESS_OFFLINE; - process.env.USE_SERVERLESS_OFFLINE = ''; - }); - - afterEach(() => { - process.env.USE_SERVERLESS_OFFLINE = ''; - }); - - afterAll(() => { - process.env.USE_SERVERLESS_OFFLINE = useServerlessOffline; - }); - - describe('is true', () => { - it("when context doesn't define an invokedFunctionArn", () => { - const di = new DependencyInjection({}, getEvent, {}); - expect(di.isOffline).toEqual(true); - }); - - it('When the invokedFunctionArn includes `offline`', () => { - const di = new DependencyInjection({}, getEvent, { invokedFunctionArn: 'my-offline-function' }); - expect(di.isOffline).toEqual(true); - }); - - it('When process.env.USE_SERVERLESS_OFFLINE is defined', () => { - process.env.USE_SERVERLESS_OFFLINE = 'true'; - const di = new DependencyInjection({}, getEvent, { invokedFunctionArn: 'my-function' }); - expect(di.isOffline).toEqual(true); - }); - }); - - describe('is false`', () => { - it("When the invokedFunctionArn doesn't contain `offline", () => { - const di = new DependencyInjection({}, getEvent, { invokedFunctionArn: 'my-function' }); - expect(di.isOffline).toEqual(false); - }); - }); - }); - - describe('definitions', () => { - describe('Returns the provided definitions', () => { - [ - [{}, undefined], - [{ DEFINITIONS: 1 }, 1], - ].forEach(([configuration, expected]) => { - it(`With configuration: ${configuration}`, () => { - const di = new DependencyInjection(configuration); - expect(di.definitions).toEqual(expected); - }); - }); - }); - }); -}); diff --git a/tests/unit/Model/CloudEvent.model.test.js b/tests/unit/Model/CloudEvent.model.test.js deleted file mode 100644 index 9e00179c..00000000 --- a/tests/unit/Model/CloudEvent.model.test.js +++ /dev/null @@ -1,58 +0,0 @@ -import CloudEventModel from '../../../src/Model/CloudEvent.model'; - -// Test definitions. -describe('Model/CloudEventModel', () => { - describe('Ensure setting and getting of variables', () => { - const model = new CloudEventModel(); - - it('should get the cloud event version', () => { - expect(model.getCloudEventsVersion()).toEqual('0.1'); - }); - - it('should set and get the event type', () => { - expect(model.getEventType()).toEqual(''); - const eventType = 'test.event'; - model.setEventType(eventType); - expect(model.getEventType()).toEqual(eventType); - }); - - it('should set and get the source', () => { - expect(model.getSource()).toEqual(''); - const source = 'test'; - model.setSource(source); - expect(model.getSource()).toEqual(source); - }); - - it('should generate a uuid as the event id', () => { - expect(model.getEventID().length).toEqual(36); - }); - - it('should generate the current timestamp as the current time', () => { - expect(new CloudEventModel().getEventTime().replace(/:[^:]+$/, '')).toEqual(new Date().toISOString().replace(/:[^:]+$/, '')); - }); - - it('should set and get the extensions', () => { - expect(model.getExtensions()).toEqual({}); - - const extensions = { - test: 'test', - }; - model.setExtensions(extensions); - expect(model.getExtensions()).toEqual(extensions); - }); - - it('should get the content type', () => { - expect(model.getContentType()).toEqual('application/json'); - }); - - it('should set and get the extensions', () => { - expect(model.getData()).toEqual({}); - - const data = { - test: 'test', - }; - model.setData(data); - expect(model.getData()).toEqual(data); - }); - }); -}); diff --git a/tests/unit/Model/SQS/MarketingPreferences.model.test.js b/tests/unit/Model/SQS/MarketingPreferences.model.test.js deleted file mode 100644 index a85b0906..00000000 --- a/tests/unit/Model/SQS/MarketingPreferences.model.test.js +++ /dev/null @@ -1,460 +0,0 @@ -/* eslint-disable sonarjs/no-identical-functions */ -/* eslint-disable sonarjs/no-duplicate-string */ -import ResponseModel from '../../../../src/Model/Response.model'; -import MarketingPreferencesModel from '../../../../src/Model/SQS/MarketingPreference.model'; - -// Test definitions. -describe('Model/MarketingPreferencesModel', () => { - describe('Ensure setting and getting of variables', () => { - const mockedData = { - firstname: 'Tim', - lastname: 'Jones', - phone: '0208 254 3062', - mobile: '07917 321 492', - address1: '32-36', - address2: "St. Smith's Avenue", - address3: '', - town: 'London', - postcode: 'sw184bx', - country: 'United Kindgom', - campaign: 'sr18', - transactionId: 'AN129MNDJDJ', - transSource: 'giftaid-sportrelief', - transSourceUrl: 'https://giftaid.sportrelief.com/', - transType: 'prefs', - email: 'tim.jones@comicrelief.com', - permissionEmail: 1, - permissionPost: 0, - permissionPhone: 0, - permissionSMS: 0, - timestamp: '1550841771', - }; - - const model = new MarketingPreferencesModel(mockedData); - - it('should set and get the firstname', () => { - expect(model.getFirstName()).toEqual(mockedData.firstname); - }); - - it('should set and get the lastname', () => { - expect(model.getLastName()).toEqual(mockedData.lastname); - }); - - it('should set and get the phone', () => { - expect(model.getPhone()).toEqual(mockedData.phone); - }); - - it('should set and get the mobile', () => { - expect(model.getMobile()).toEqual(mockedData.mobile); - }); - - it('should set and get the address1', () => { - expect(model.getAddress1()).toEqual(mockedData.address1); - }); - - it('should set and get the address2', () => { - expect(model.getAddress2()).toEqual(mockedData.address2); - }); - - it('should set and get the address3', () => { - expect(model.getAddress3()).toEqual(null); - }); - - it('should set and get the town', () => { - expect(model.getTown()).toEqual(mockedData.town); - }); - - it('should set and get the postcode', () => { - expect(model.getPostcode()).toEqual(mockedData.postcode); - }); - - it('should set and get the country', () => { - expect(model.getCountry()).toEqual(mockedData.country); - }); - - it('should set and get the campaign', () => { - expect(model.getCampaign()).toEqual(mockedData.campaign); - }); - - it('should set and get the transaction id', () => { - expect(model.getTransactionId()).toEqual(mockedData.transactionId); - }); - - it('should set and get the transSource', () => { - expect(model.getTransSource()).toEqual(mockedData.transSource); - }); - - it('should set and get the transSourceUrl', () => { - expect(model.getTransSourceUrl()).toEqual(mockedData.transSourceUrl); - }); - - it('should set and get the transType', () => { - expect(model.getTransType()).toEqual(mockedData.transType); - }); - - it('should set and get the email', () => { - expect(model.getEmail()).toEqual(mockedData.email); - }); - - it('should set and get the permissionEmail', () => { - expect(model.getPermissionEmail()).toEqual(mockedData.permissionEmail); - }); - - it('should set and get the permissionPost', () => { - expect(model.getPermissionPost()).toEqual(mockedData.permissionPost); - }); - - it('should set and get the permissionPhone', () => { - expect(model.getPermissionPhone()).toEqual(mockedData.permissionPhone); - }); - - it('should set and get the permissionSMS', () => { - expect(model.getPermissionSMS()).toEqual(mockedData.permissionSMS); - }); - - it('should set and get the Timestamp', () => { - expect(model.getTimestamp()).toEqual(mockedData.timestamp); - }); - - it('should validate the model', (done) => { - model - .validate() - .then(() => { - expect(true).toEqual(true); - done(); - }) - .catch(() => { - expect(true).toEqual(false); - done(); - }); - }); - }); - - describe('Ensure validation fails when variables are not correctly set', () => { - const mockedData = {}; - - const model = new MarketingPreferencesModel(mockedData); - - it('should validate the model and return an error response', (done) => { - model - .validate() - .then(() => { - expect(true).toEqual(false); - done(); - }) - .catch((error) => { - expect(error instanceof ResponseModel).toEqual(true); - expect(error.getCode()).toEqual(400); - expect(error.body.message).toEqual('required fields are missing'); - expect(true).toEqual(true); - done(); - }); - }); - }); - - describe('Ensure validation fails when email permission is set and no email is provided', () => { - const mockedData = { - firstname: 'Tim', - lastname: 'Jones', - mobile: '07917 321 492', - address1: '32-36', - address2: "St. Smith's Avenue", - address3: '', - town: 'London', - postcode: 'sw184bx', - country: 'United Kindgom', - campaign: 'sr18', - transactionId: 'AN129MNDJDJ', - transSource: 'giftaid-sportrelief', - transSourceUrl: 'https://giftaid.sportrelief.com/', - transType: 'prefs', - permissionEmail: 1, - permissionPost: 0, - permissionPhone: 0, - permissionSMS: 0, - timestamp: '1550841771', - }; - - const model = new MarketingPreferencesModel(mockedData); - - it('should validate the model and return an error response', (done) => { - model - .validate() - .then(() => { - expect(true).toEqual(false); - done(); - }) - .catch((error) => { - expect(error instanceof ResponseModel).toEqual(true); - expect(error.getCode()).toEqual(400); - expect(error.body.validation_errors.email[0]).toEqual("Email can't be blank"); - expect(true).toEqual(true); - done(); - }); - }); - }); - - describe('Ensure validation fails when email permission is set and invalid email is provided', () => { - const mockedData = { - firstname: 'Tim', - lastname: 'Jones', - phone: '0208 254 3062', - mobile: '07917 321 492', - address1: '32-36', - address2: "St. Smith's Avenue", - address3: '', - town: 'London', - postcode: 'sw184bx', - country: 'United Kindgom', - campaign: 'sr18', - transactionId: 'AN129MNDJDJ', - transSource: 'giftaid-sportrelief', - transSourceUrl: 'https://giftaid.sportrelief.com/', - transType: 'prefs', - email: 'tim@', - permissionEmail: 1, - permissionPost: 0, - permissionPhone: 0, - permissionSMS: 0, - }; - - const model = new MarketingPreferencesModel(mockedData); - - it('should validate the model and return an error response', (done) => { - model - .validate() - .then(() => { - expect(true).toEqual(false); - done(); - }) - .catch((error) => { - expect(error instanceof ResponseModel).toEqual(true); - expect(error.getCode()).toEqual(400); - expect(error.body.validation_errors.email[0]).toEqual('Email is not a valid email'); - expect(true).toEqual(true); - done(); - }); - }); - }); - - describe('Ensure validation passes when permissions are not set', () => { - const mockedData = { - firstname: 'Kelvin', - lastname: 'James', - phone: '0208 254 3062', - mobile: '07425253522', - address1: 'COMIC RELIEF', - address2: 'CAMELFORD HOUSE 87-90', - address3: 'ALBERT EMBANKMENT', - town: 'LONDON', - postcode: 'SE1 7TP', - country: 'GB', - campaign: 'RND19', - transactionId: 'AN129MNDJDJ', - transSource: 'RND19_GiftAid', - transSourceUrl: 'https://giftaid.sportrelief.com/', - transType: 'prefs', - confirm: 1, - permissionEmail: null, - permissionPost: null, - permissionPhone: null, - permissionSMS: null, - timestamp: '1562165588', - }; - - const model = new MarketingPreferencesModel(mockedData); - - it('should validate the model', (done) => { - model - .validate() - .then(() => { - expect(true).toEqual(true); - done(); - }) - .catch((error) => { - console.log('Error:', error); - expect(true).toEqual(false); - done(); - }); - }); - }); - - describe('Ensure validation passes when email permission is NO', () => { - const mockedData = { - firstname: 'Kelvin', - lastname: 'James', - phone: '0208 254 3062', - mobile: '07425253522', - address1: 'COMIC RELIEF', - address2: 'CAMELFORD HOUSE 87-90', - address3: 'ALBERT EMBANKMENT', - town: 'LONDON', - postcode: 'SE1 7TP', - country: 'GB', - campaign: 'RND19', - transactionId: 'AN129MNDJDJ', - transSource: 'RND19_GiftAid', - transSourceUrl: 'https://giftaid.sportrelief.com/', - transType: 'prefs', - confirm: 1, - permissionEmail: 0, - permissionPost: null, - permissionPhone: null, - permissionSMS: null, - timestamp: '1562165588', - }; - - const model = new MarketingPreferencesModel(mockedData); - - it('should validate the model', (done) => { - model - .validate() - .then(() => { - expect(true).toEqual(true); - done(); - }) - .catch((error) => { - console.log('Error:', error); - expect(true).toEqual(false); - done(); - }); - }); - }); - - describe('Ensure generating of timestamp when not set', () => { - const mockedData = { - firstname: 'Tim', - lastname: 'Jones', - phone: '0208 254 3062', - mobile: '07917 321 492', - address1: '32-36', - address2: "St. Smith's Avenue", - address3: '', - town: 'London', - postcode: 'sw184bx', - country: 'United Kindgom', - campaign: 'sr18', - transactionId: 'AN129MNDJDJ', - transSource: 'giftaid-sportrelief', - transSourceUrl: 'https://giftaid.sportrelief.com/', - transType: 'prefs', - email: 'tim.jones@comicrelief.com', - permissionEmail: 1, - permissionPost: 0, - permissionPhone: 0, - permissionSMS: 0, - }; - - const model = new MarketingPreferencesModel(mockedData); - - it('should get a timestamp', () => { - expect(model.getTimestamp() > 0).toEqual(true); - }); - - it('should validate the model', (done) => { - model - .validate() - .then(() => { - expect(true).toEqual(true); - done(); - }) - .catch(() => { - expect(true).toEqual(false); - done(); - }); - }); - }); - - describe('Ensure validation passes when nullable fields are not present', () => { - const mockedData = { - firstname: 'Tim', - lastname: 'Jones', - phone: '0208 254 3062', - mobile: '07917 321 492', - address1: "32 Smith's Avenue", - town: 'London', - postcode: 'sw184bx', - country: 'United Kindgom', - campaign: 'sr18', - transSource: 'giftaid-sportrelief', - transSourceUrl: 'https://giftaid.sportrelief.com/', - transType: 'prefs', - email: '', - }; - - const model = new MarketingPreferencesModel(mockedData); - - it('should validate the model', (done) => { - model - .validate() - .then(() => { - expect(true).toEqual(true); - done(); - }) - .catch((error) => { - console.log('Error:', error); - expect(true).toEqual(false); - done(); - }); - }); - }); - - describe('Ensure model permission evaluates to false when no permission is set', () => { - const mockedData = { - firstname: 'Tim', - lastname: 'Jones', - phone: '0208 254 3062', - mobile: '07917 321 492', - address1: "32 Smith's Avenue", - town: 'London', - postcode: 'sw184bx', - country: 'United Kindgom', - campaign: 'sr18', - transSource: 'giftaid-sportrelief', - transSourceUrl: 'https://giftaid.sportrelief.com/', - transType: 'prefs', - email: '', - permissionEmail: '', - permissionPost: '', - permissionPhone: '', - permissionSMS: '', - }; - - const model = new MarketingPreferencesModel(mockedData); - - it('should evaluate model permissions to false', (done) => { - expect(model.isPermissionSet()).toEqual(false); - done(); - }); - }); - - describe('Ensure model permission evaluates to true when at least one permission is set', () => { - const mockedData = { - firstname: 'Tim', - lastname: 'Jones', - phone: '0208 254 3062', - mobile: '07917 321 492', - address1: "32 Smith's Avenue", - town: 'London', - postcode: 'sw184bx', - country: 'United Kindgom', - campaign: 'sr18', - transactionId: 'AN129MNDJDJ', - transSource: 'giftaid-sportrelief', - transSourceUrl: 'https://giftaid.sportrelief.com/', - transType: 'prefs', - email: 'tim@example.com', - permissionEmail: 1, - permissionPost: '', - permissionPhone: '', - permissionSMS: '', - }; - - const model = new MarketingPreferencesModel(mockedData); - - it('should evaluate model permissions to true', (done) => { - expect(model.isPermissionSet()).toEqual(true); - done(); - }); - }); -}); diff --git a/tests/unit/Model/SQS/Message.model.test.js b/tests/unit/Model/SQS/Message.model.test.js deleted file mode 100644 index e2d4615c..00000000 --- a/tests/unit/Model/SQS/Message.model.test.js +++ /dev/null @@ -1,46 +0,0 @@ -import Message from '../../../../src/Model/SQS/Message.model'; - -// Test definitions. -describe('Model/SQS/Message.model', () => { - describe('Ensure setting and getting of variables', () => { - const messageData = { - test: 123, - }; - - const mockedMessage = { - MessageId: 123, - ReceiptHandle: 123, - Body: JSON.stringify(messageData), - }; - - const messageModel = new Message(mockedMessage); - - it('should set and get the message id', () => { - expect(messageModel.getMessageId()).toEqual(mockedMessage.MessageId); - }); - - it('should set and get the receipt handle', () => { - expect(messageModel.getReceiptHandle()).toEqual(mockedMessage.ReceiptHandle); - }); - - it('should set, parse the JSON and get the body', () => { - expect(messageModel.getBody()).toEqual(messageData); - }); - - it('should default to having a for deletion status of false', () => { - expect(messageModel.isForDeletion()).toEqual(false); - }); - - it('should be able to change the for deletion status to true', () => { - messageModel.setForDeletion(true); - expect(messageModel.isForDeletion()).toEqual(true); - }); - - it('should be able to set metadata', () => { - messageModel.setMetaData('test', 123); - expect(messageModel.getMetaData()).toEqual({ - test: 123, - }); - }); - }); -}); diff --git a/tests/unit/Model/Status.model.test.js b/tests/unit/Model/Status.model.test.js deleted file mode 100644 index 62d34d72..00000000 --- a/tests/unit/Model/Status.model.test.js +++ /dev/null @@ -1,22 +0,0 @@ -import StatusModel, { STATUS_TYPES } from '../../../src/Model/Status.model'; - -// Test definitions. -describe('Model/StatusModel', () => { - describe('Ensure setting and getting of variables', () => { - const service = 'test'; - const status = STATUS_TYPES.OK; - const statusModel = new StatusModel(service, status); - - it('should set ang get the service', () => { - expect(statusModel.getService()).toEqual(service); - }); - - it('should set and get the status', () => { - expect(statusModel.getStatus()).toEqual(status); - }); - - it('should throw an error when trying to set an invalid status', () => { - expect(() => statusModel.setStatus('invalid')).toThrow('StatusModel - invalid is not a valid status type'); - }); - }); -}); diff --git a/tests/unit/Service/__snapshots__/BaseConfig.service.test.js.snap b/tests/unit/Service/__snapshots__/BaseConfig.service.test.js.snap deleted file mode 100644 index 37742866..00000000 --- a/tests/unit/Service/__snapshots__/BaseConfig.service.test.js.snap +++ /dev/null @@ -1,35 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Service/BaseConfigService ensureHealthy 400 throws a LambdaTermination 1`] = `"Application is not healthy."`; - -exports[`Service/BaseConfigService ensureHealthy 401 throws a LambdaTermination 1`] = `"Application is not healthy."`; - -exports[`Service/BaseConfigService ensureHealthy 403 throws a LambdaTermination 1`] = `"Application is not healthy."`; - -exports[`Service/BaseConfigService ensureHealthy 404 throws a LambdaTermination 1`] = `"Application is not healthy."`; - -exports[`Service/BaseConfigService ensureHealthy 409 throws a LambdaTermination 1`] = `"Application is not healthy."`; - -exports[`Service/BaseConfigService ensureHealthy 499 throws a LambdaTermination 1`] = `"Application is not healthy."`; - -exports[`Service/BaseConfigService ensureHealthy 500 throws a LambdaTermination 1`] = `"Application is not healthy."`; - -exports[`Service/BaseConfigService ensureHealthy 501 throws a LambdaTermination 1`] = `"Application is not healthy."`; - -exports[`Service/BaseConfigService ensureHealthy 502 throws a LambdaTermination 1`] = `"Application is not healthy."`; - -exports[`Service/BaseConfigService ensureHealthy 503 throws a LambdaTermination 1`] = `"Application is not healthy."`; - -exports[`Service/BaseConfigService ensureHealthy 504 throws a LambdaTermination 1`] = `"Application is not healthy."`; - -exports[`Service/BaseConfigService ensureHealthy Dante Alighieri throws a LambdaTermination 1`] = `"Application is not healthy."`; - -exports[`Service/BaseConfigService get propagates the 404 1`] = `"404"`; - -exports[`Service/BaseConfigService get refuses empty configurations 1`] = `"Configuration file is empty"`; - -exports[`Service/BaseConfigService get refuses invalid configurations 1`] = `"Invalid configuration file"`; - -exports[`Service/BaseConfigService getOrCreate throws any non-404 error 1`] = `"Bad error"`; - -exports[`Service/BaseConfigService patch throws any non-404 error 1`] = `"Bad error"`; diff --git a/tests/unit/Service/__snapshots__/Logger.service.test.js.snap b/tests/unit/Service/__snapshots__/Logger.service.test.js.snap deleted file mode 100644 index 2afbafa3..00000000 --- a/tests/unit/Service/__snapshots__/Logger.service.test.js.snap +++ /dev/null @@ -1,138 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Service/LoggerService error Trims down the axios error: EMPTY 1`] = ` -Object { - "config": Object { - "method": "get", - "url": "http://localhost:9999", - }, - "message": "some-message", - "response": Object { - "data": undefined, - "status": undefined, - }, -} -`; - -exports[`Service/LoggerService error Trims down the axios error: HTTP_417 1`] = ` -Object { - "config": Object { - "method": "get", - "url": "http://localhost:9999", - }, - "message": "some-message", - "response": Object { - "data": Object { - "data": 1, - }, - "status": 417, - }, -} -`; - -exports[`Service/LoggerService error Trims down the axios error: UNDEFINED 1`] = ` -Object { - "config": Object { - "method": "get", - "url": "http://localhost:9999", - }, - "message": "some-message", -} -`; - -exports[`Service/LoggerService info Trims down the axios error: EMPTY 1`] = ` -Object { - "config": Object { - "method": "get", - "url": "http://localhost:9999", - }, - "message": "some-message", - "response": Object { - "data": undefined, - "status": undefined, - }, -} -`; - -exports[`Service/LoggerService info Trims down the axios error: HTTP_417 1`] = ` -Object { - "config": Object { - "method": "get", - "url": "http://localhost:9999", - }, - "message": "some-message", - "response": Object { - "data": Object { - "data": 1, - }, - "status": 417, - }, -} -`; - -exports[`Service/LoggerService info Trims down the axios error: UNDEFINED 1`] = ` -Object { - "config": Object { - "method": "get", - "url": "http://localhost:9999", - }, - "message": "some-message", -} -`; - -exports[`Service/LoggerService object Logs a '"a string"' with level: 'error' 1`] = `"My action: '\\"a string\\"'"`; - -exports[`Service/LoggerService object Logs a '"a string"' with level: 'info' 1`] = `"My action: '\\"a string\\"'"`; - -exports[`Service/LoggerService object Logs a '"a string"' with level: 'warning' 1`] = `"My action: '\\"a string\\"'"`; - -exports[`Service/LoggerService object Logs a '{"a":{"b":null},"c":"a string"}' with level: 'error' 1`] = ` -"My action: '{ - \\"a\\": { - \\"b\\": null - }, - \\"c\\": \\"a string\\" -}'" -`; - -exports[`Service/LoggerService object Logs a '{"a":{"b":null},"c":"a string"}' with level: 'info' 1`] = ` -"My action: '{ - \\"a\\": { - \\"b\\": null - }, - \\"c\\": \\"a string\\" -}'" -`; - -exports[`Service/LoggerService object Logs a '{"a":{"b":null},"c":"a string"}' with level: 'warning' 1`] = ` -"My action: '{ - \\"a\\": { - \\"b\\": null - }, - \\"c\\": \\"a string\\" -}'" -`; - -exports[`Service/LoggerService object Logs a '{"a":1}' with level: 'error' 1`] = ` -"My action: '{ - \\"a\\": 1 -}'" -`; - -exports[`Service/LoggerService object Logs a '{"a":1}' with level: 'info' 1`] = ` -"My action: '{ - \\"a\\": 1 -}'" -`; - -exports[`Service/LoggerService object Logs a '{"a":1}' with level: 'warning' 1`] = ` -"My action: '{ - \\"a\\": 1 -}'" -`; - -exports[`Service/LoggerService object Logs a 'null' with level: 'error' 1`] = `"My action: 'null'"`; - -exports[`Service/LoggerService object Logs a 'null' with level: 'info' 1`] = `"My action: 'null'"`; - -exports[`Service/LoggerService object Logs a 'null' with level: 'warning' 1`] = `"My action: 'null'"`; diff --git a/tests/unit/Service/__snapshots__/SQS.service.test.js.snap b/tests/unit/Service/__snapshots__/SQS.service.test.js.snap deleted file mode 100644 index 82939d44..00000000 --- a/tests/unit/Service/__snapshots__/SQS.service.test.js.snap +++ /dev/null @@ -1,7 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Service/SQS publish failure modes throws an error with the invalid value: 1`] = `"Invalid value for 'failureMode': "`; - -exports[`Service/SQS publish failure modes throws an error with the invalid value: another-value 1`] = `"Invalid value for 'failureMode': another-value"`; - -exports[`Service/SQS publish failure modes throws an error with the invalid value: null 1`] = `"Invalid value for 'failureMode': null"`; diff --git a/tests/unit/Wrapper/LambdaWrapper.test.js b/tests/unit/Wrapper/LambdaWrapper.test.js deleted file mode 100644 index 0a7f4102..00000000 --- a/tests/unit/Wrapper/LambdaWrapper.test.js +++ /dev/null @@ -1,256 +0,0 @@ -/* eslint-disable sonarjs/no-duplicate-string */ -import { DEFINITIONS } from '../../../src/Config/Dependencies'; -import DependencyInjection from '../../../src/DependencyInjection/DependencyInjection.class'; -import ResponseModel from '../../../src/Model/Response.model'; -import RequestService, { REQUEST_TYPES } from '../../../src/Service/Request.service'; -import LambdaTermination from '../../../src/Wrapper/LambdaTermination'; -import LambdaWrapper, { handleError } from '../../../src/Wrapper/LambdaWrapper'; -import { getMockedDi } from '../../lib/mocks'; -import getContext from '../../mocks/aws/context.json'; -import getEvent from '../../mocks/aws/event.json'; - -const handlers = { - SYNC_SUCCESS: () => ResponseModel.generate({ x: 'success' }, 200, 'ok'), - SYNC_THROWING: (di) => { - jest.spyOn(di.dependencies[DEFINITIONS.LOGGER], 'error'); - jest.spyOn(di.dependencies[DEFINITIONS.LOGGER], 'metric'); - - throw new LambdaTermination('SYNC_THROWING', 403, 'external'); - }, - ASYNC_SUCCESS: () => Promise.resolve(ResponseModel.generate({ x: 'success' }, 200, 'ok')), - ASYNC_THROWING: (di) => new Promise(() => { - jest.spyOn(di.dependencies[DEFINITIONS.LOGGER], 'error'); - jest.spyOn(di.dependencies[DEFINITIONS.LOGGER], 'metric'); - - throw new LambdaTermination('ASYNC_THROWING', 403, 'external'); - }), -}; - -describe('Wrapper/LambdaWrapper', () => { - let dependencyInjection = {}; - let requestService = {}; - - const configuration = { - DEFINITIONS: {}, - DEPENDENCIES: {}, - }; - - beforeEach(() => { - // Mute Winston - // eslint-disable-next-line no-underscore-dangle - jest.spyOn(console._stdout, 'write').mockImplementation(() => {}); - }); - - afterEach(() => jest.resetAllMocks()); - - describe('handleError', () => { - [ - [undefined, 400, 0], - [false, 400, 0], - [true, 400, 1], - [undefined, undefined, 1], - [undefined, false, 1], - [undefined, 500, 1], - [true, 500, 1], - ].forEach(([raiseOnEpsagon, code, expected]) => { - it(`error.raiseOnEpsagon = '${raiseOnEpsagon}', code = '${code}' logger.error called ${expected} times`, () => { - const di = getMockedDi(); - const logger = di.get(DEFINITIONS.LOGGER); - const error = { raiseOnEpsagon, code }; - - handleError(di, error); - - expect(logger.error).toHaveBeenCalledTimes(expected); - }); - - [undefined, { data: 1 }].forEach((body) => { - it('Generates a response object', () => { - const di = getMockedDi(); - const error = { raiseOnEpsagon, code, body }; - - const response = handleError(di, error); - - expect(response).toMatchSnapshot(); - }); - }); - }); - }); - - describe('LambdaWrapper', () => { - describe('executes the wrapped function', () => { - it('when it is sync', () => { - const lambda = LambdaWrapper(configuration, handlers.SYNC_SUCCESS); - expect(lambda(getEvent, getContext)).toMatchSnapshot(); - }); - - it('when it is async', async () => { - const lambda = LambdaWrapper(configuration, handlers.ASYNC_SUCCESS); - await expect(lambda(getEvent, getContext)).resolves.toMatchSnapshot(); - }); - }); - - describe('should inject dependency injection into the function', () => { - LambdaWrapper(configuration, (di, request) => { - dependencyInjection = di; - requestService = request; - })(getEvent, getContext); - - it('dependency injection variables should be an instance of the dependency injection class', () => { - expect(dependencyInjection instanceof DependencyInjection).toEqual(true); - }); - - it('dependency injection should output the event that was provided to it', () => { - expect(dependencyInjection.getEvent()).toEqual(getEvent); - }); - - it('dependency injection should output the event that was provided to it', () => { - expect(dependencyInjection.getContext()).toEqual(getContext); - }); - }); - - describe('should inject the request service into the function', () => { - LambdaWrapper(configuration, (di, request) => { - dependencyInjection = di; - requestService = request; - })(getEvent, getContext); - - it('request service variables should be an instance of the dependency injection class', () => { - expect(requestService instanceof RequestService).toEqual(true); - }); - - it('request service should contain variables that were sent to it via the event', () => { - expect(requestService.get('test', null, REQUEST_TYPES.GET)).toEqual(getEvent.queryStringParameters.test); - }); - }); - - describe('should catch exceptions and generate appropriate responses', () => { - it('Logs.error the error without error code', () => { - let infoStub; - let errorStub; - let metricStub; - - const lambda = LambdaWrapper(configuration, (di) => { - infoStub = jest.spyOn(di.dependencies[DEFINITIONS.LOGGER], 'info'); - errorStub = jest.spyOn(di.dependencies[DEFINITIONS.LOGGER], 'error'); - metricStub = jest.spyOn(di.dependencies[DEFINITIONS.LOGGER], 'metric'); - throw new Error('Undefined error'); - }); - - lambda(getEvent, getContext); - - expect(infoStub).not.toHaveBeenCalled(); - expect(errorStub).toHaveBeenCalled(); - expect(metricStub).nthCalledWith(1, 'lambda.statusCode', 500); - }); - - [400, 401, 403, 404, 409, 419, 421, 423, 499].forEach((errorCode) => { - it(`Logs.info the error with code ${errorCode}`, () => { - let infoStub; - let errorStub; - let metricStub; - - const lambda = LambdaWrapper(configuration, (di) => { - infoStub = jest.spyOn(di.dependencies[DEFINITIONS.LOGGER], 'info'); - errorStub = jest.spyOn(di.dependencies[DEFINITIONS.LOGGER], 'error'); - metricStub = jest.spyOn(di.dependencies[DEFINITIONS.LOGGER], 'metric'); - - const error = new Error('4xx error'); - error.code = errorCode; - throw error; - }); - - lambda(getEvent, getContext); - - expect(infoStub).toHaveBeenCalled(); - expect(errorStub).not.toHaveBeenCalled(); - expect(metricStub).nthCalledWith(1, 'lambda.statusCode', errorCode); - }); - }); - - [500, 501, 502, 503].forEach((errorCode) => { - it(`Logs.error the error with code ${errorCode}`, () => { - let infoStub; - let errorStub; - let metricStub; - - const lambda = LambdaWrapper(configuration, (di) => { - infoStub = jest.spyOn(di.dependencies[DEFINITIONS.LOGGER], 'info'); - errorStub = jest.spyOn(di.dependencies[DEFINITIONS.LOGGER], 'error'); - metricStub = jest.spyOn(di.dependencies[DEFINITIONS.LOGGER], 'metric'); - - const error = new Error('5xx error'); - error.code = errorCode; - throw error; - }); - - lambda(getEvent, getContext); - - expect(infoStub).not.toHaveBeenCalled(); - expect(errorStub).toHaveBeenCalled(); - expect(metricStub).nthCalledWith(1, 'lambda.statusCode', errorCode); - }); - }); - - it('Returns 500 exception with a common error', () => { - const lambda = LambdaWrapper(configuration, (di) => { - jest.spyOn(di.dependencies[DEFINITIONS.LOGGER], 'error'); - jest.spyOn(di.dependencies[DEFINITIONS.LOGGER], 'metric'); - throw new Error('Some error'); - }); - - const response = lambda(getEvent, getContext); - const body = JSON.parse(response.body); - - expect(response.statusCode).toEqual(500); - expect(body.message).toEqual('unknown error'); - }); - - it('Returns a response generated by LambdaTermination', () => { - const lambda = LambdaWrapper(configuration, (di) => { - jest.spyOn(di.dependencies[DEFINITIONS.LOGGER], 'error'); - jest.spyOn(di.dependencies[DEFINITIONS.LOGGER], 'metric'); - throw new LambdaTermination('internal', 403, 'external', 'some message'); - }); - - const response = lambda(getEvent, getContext); - const body = JSON.parse(response.body); - - expect(response.statusCode).toEqual(403); - expect(body.data).toEqual('external'); - expect(body.message).toEqual('some message'); - }); - - describe('catches sync errors', () => { - it('returns an error http response with throwError === false', () => { - const lambda = LambdaWrapper(configuration, handlers.SYNC_THROWING, false); - const outcome = lambda(getEvent, getContext); - expect(outcome).toMatchSnapshot(); - }); - - it('returns a raw error with throwError === true', () => { - const lambda = LambdaWrapper(configuration, handlers.SYNC_THROWING, true); - const outcome = lambda(getEvent, getContext); - expect(outcome).toMatchSnapshot(); - - // Be absolutely sure we got an Error object or the lambda will not count as failed - expect(outcome instanceof LambdaTermination).toEqual(true); - expect(outcome instanceof Error).toEqual(true); - }); - }); - - describe('catches async errors', () => { - it('resolves an error http response with throwError === false', async () => { - const lambda = LambdaWrapper(configuration, handlers.ASYNC_THROWING, false); - await expect(lambda(getEvent, getContext)).resolves.toMatchSnapshot(); - }); - - it('rejects the promise with throwError === true', async () => { - const lambda = LambdaWrapper(configuration, handlers.ASYNC_THROWING, true); - - // Be absolutely sure we got a rejection or the lambda will not count as failed - await expect(lambda(getEvent, getContext)).rejects.toThrowErrorMatchingSnapshot(); - }); - }); - }); - }); -}); diff --git a/tests/unit/Wrapper/__snapshots__/LambdaWrapper.test.js.snap b/tests/unit/Wrapper/__snapshots__/LambdaWrapper.test.js.snap deleted file mode 100644 index 7003622e..00000000 --- a/tests/unit/Wrapper/__snapshots__/LambdaWrapper.test.js.snap +++ /dev/null @@ -1,221 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Wrapper/LambdaWrapper LambdaWrapper executes the wrapped function when it is async 1`] = ` -Object { - "body": "{\\"data\\":{\\"x\\":\\"success\\"},\\"message\\":\\"ok\\"}", - "headers": Object { - "Access-Control-Allow-Credentials": true, - "Access-Control-Allow-Origin": "*", - "Content-Type": "application/json", - }, - "statusCode": 200, -} -`; - -exports[`Wrapper/LambdaWrapper LambdaWrapper executes the wrapped function when it is sync 1`] = ` -Object { - "body": "{\\"data\\":{\\"x\\":\\"success\\"},\\"message\\":\\"ok\\"}", - "headers": Object { - "Access-Control-Allow-Credentials": true, - "Access-Control-Allow-Origin": "*", - "Content-Type": "application/json", - }, - "statusCode": 200, -} -`; - -exports[`Wrapper/LambdaWrapper LambdaWrapper should catch exceptions and generate appropriate responses catches async errors rejects the promise with throwError === true 1`] = `"ASYNC_THROWING"`; - -exports[`Wrapper/LambdaWrapper LambdaWrapper should catch exceptions and generate appropriate responses catches async errors resolves an error http response with throwError === false 1`] = ` -Object { - "body": "{\\"data\\":\\"external\\",\\"message\\":\\"unknown error\\"}", - "headers": Object { - "Access-Control-Allow-Credentials": true, - "Access-Control-Allow-Origin": "*", - "Content-Type": "application/json", - }, - "statusCode": 403, -} -`; - -exports[`Wrapper/LambdaWrapper LambdaWrapper should catch exceptions and generate appropriate responses catches sync errors returns a raw error with throwError === true 1`] = `[Error: SYNC_THROWING]`; - -exports[`Wrapper/LambdaWrapper LambdaWrapper should catch exceptions and generate appropriate responses catches sync errors returns an error http response with throwError === false 1`] = ` -Object { - "body": "{\\"data\\":\\"external\\",\\"message\\":\\"unknown error\\"}", - "headers": Object { - "Access-Control-Allow-Credentials": true, - "Access-Control-Allow-Origin": "*", - "Content-Type": "application/json", - }, - "statusCode": 403, -} -`; - -exports[`Wrapper/LambdaWrapper handleError Generates a response object 1`] = ` -Object { - "body": "{\\"data\\":{},\\"message\\":\\"unknown error\\"}", - "headers": Object { - "Access-Control-Allow-Credentials": true, - "Access-Control-Allow-Origin": "*", - "Content-Type": "application/json", - }, - "statusCode": 400, -} -`; - -exports[`Wrapper/LambdaWrapper handleError Generates a response object 2`] = ` -Object { - "body": "{\\"data\\":{\\"data\\":1},\\"message\\":\\"unknown error\\"}", - "headers": Object { - "Access-Control-Allow-Credentials": true, - "Access-Control-Allow-Origin": "*", - "Content-Type": "application/json", - }, - "statusCode": 400, -} -`; - -exports[`Wrapper/LambdaWrapper handleError Generates a response object 3`] = ` -Object { - "body": "{\\"data\\":{},\\"message\\":\\"unknown error\\"}", - "headers": Object { - "Access-Control-Allow-Credentials": true, - "Access-Control-Allow-Origin": "*", - "Content-Type": "application/json", - }, - "statusCode": 400, -} -`; - -exports[`Wrapper/LambdaWrapper handleError Generates a response object 4`] = ` -Object { - "body": "{\\"data\\":{\\"data\\":1},\\"message\\":\\"unknown error\\"}", - "headers": Object { - "Access-Control-Allow-Credentials": true, - "Access-Control-Allow-Origin": "*", - "Content-Type": "application/json", - }, - "statusCode": 400, -} -`; - -exports[`Wrapper/LambdaWrapper handleError Generates a response object 5`] = ` -Object { - "body": "{\\"data\\":{},\\"message\\":\\"unknown error\\"}", - "headers": Object { - "Access-Control-Allow-Credentials": true, - "Access-Control-Allow-Origin": "*", - "Content-Type": "application/json", - }, - "statusCode": 400, -} -`; - -exports[`Wrapper/LambdaWrapper handleError Generates a response object 6`] = ` -Object { - "body": "{\\"data\\":{\\"data\\":1},\\"message\\":\\"unknown error\\"}", - "headers": Object { - "Access-Control-Allow-Credentials": true, - "Access-Control-Allow-Origin": "*", - "Content-Type": "application/json", - }, - "statusCode": 400, -} -`; - -exports[`Wrapper/LambdaWrapper handleError Generates a response object 7`] = ` -Object { - "body": "{\\"data\\":{},\\"message\\":\\"unknown error\\"}", - "headers": Object { - "Access-Control-Allow-Credentials": true, - "Access-Control-Allow-Origin": "*", - "Content-Type": "application/json", - }, - "statusCode": 500, -} -`; - -exports[`Wrapper/LambdaWrapper handleError Generates a response object 8`] = ` -Object { - "body": "{\\"data\\":{\\"data\\":1},\\"message\\":\\"unknown error\\"}", - "headers": Object { - "Access-Control-Allow-Credentials": true, - "Access-Control-Allow-Origin": "*", - "Content-Type": "application/json", - }, - "statusCode": 500, -} -`; - -exports[`Wrapper/LambdaWrapper handleError Generates a response object 9`] = ` -Object { - "body": "{\\"data\\":{},\\"message\\":\\"unknown error\\"}", - "headers": Object { - "Access-Control-Allow-Credentials": true, - "Access-Control-Allow-Origin": "*", - "Content-Type": "application/json", - }, - "statusCode": 500, -} -`; - -exports[`Wrapper/LambdaWrapper handleError Generates a response object 10`] = ` -Object { - "body": "{\\"data\\":{\\"data\\":1},\\"message\\":\\"unknown error\\"}", - "headers": Object { - "Access-Control-Allow-Credentials": true, - "Access-Control-Allow-Origin": "*", - "Content-Type": "application/json", - }, - "statusCode": 500, -} -`; - -exports[`Wrapper/LambdaWrapper handleError Generates a response object 11`] = ` -Object { - "body": "{\\"data\\":{},\\"message\\":\\"unknown error\\"}", - "headers": Object { - "Access-Control-Allow-Credentials": true, - "Access-Control-Allow-Origin": "*", - "Content-Type": "application/json", - }, - "statusCode": 500, -} -`; - -exports[`Wrapper/LambdaWrapper handleError Generates a response object 12`] = ` -Object { - "body": "{\\"data\\":{\\"data\\":1},\\"message\\":\\"unknown error\\"}", - "headers": Object { - "Access-Control-Allow-Credentials": true, - "Access-Control-Allow-Origin": "*", - "Content-Type": "application/json", - }, - "statusCode": 500, -} -`; - -exports[`Wrapper/LambdaWrapper handleError Generates a response object 13`] = ` -Object { - "body": "{\\"data\\":{},\\"message\\":\\"unknown error\\"}", - "headers": Object { - "Access-Control-Allow-Credentials": true, - "Access-Control-Allow-Origin": "*", - "Content-Type": "application/json", - }, - "statusCode": 500, -} -`; - -exports[`Wrapper/LambdaWrapper handleError Generates a response object 14`] = ` -Object { - "body": "{\\"data\\":{\\"data\\":1},\\"message\\":\\"unknown error\\"}", - "headers": Object { - "Access-Control-Allow-Credentials": true, - "Access-Control-Allow-Origin": "*", - "Content-Type": "application/json", - }, - "statusCode": 500, -} -`; diff --git a/tests/unit/core/DependencyAwareClass.spec.ts b/tests/unit/core/DependencyAwareClass.spec.ts new file mode 100644 index 00000000..34fba6df --- /dev/null +++ b/tests/unit/core/DependencyAwareClass.spec.ts @@ -0,0 +1,19 @@ +import { Context, DependencyAwareClass, DependencyInjection } from '@/src'; + +describe('unit.core.DependencyAwareClass', () => { + describe('getContainer', () => { + it('should return the DependencyInjection instance', () => { + const di = new DependencyInjection({ dependencies: {} }, {}, {} as Context); + const dep = new DependencyAwareClass(di); + expect(dep.getContainer()).toBe(di); + }); + }); + + describe('di', () => { + it('should expose the DependencyInjection instance', () => { + const di = new DependencyInjection({ dependencies: {} }, {}, {} as Context); + const dep = new DependencyAwareClass(di); + expect(dep.di).toBe(di); + }); + }); +}); diff --git a/tests/unit/core/DependencyInjection.spec.ts b/tests/unit/core/DependencyInjection.spec.ts new file mode 100644 index 00000000..efdc8fcd --- /dev/null +++ b/tests/unit/core/DependencyInjection.spec.ts @@ -0,0 +1,69 @@ +import { DependencyAwareClass, DependencyInjection } from '@/src'; +import { mockContext, mockEvent } from '@/tests/mocks/aws'; + +class A extends DependencyAwareClass {} + +class B extends DependencyAwareClass {} + +class C extends DependencyAwareClass {} + +describe('unit.core.DependencyInjection', () => { + const mockConfig = { + dependencies: { + A, + B, + }, + }; + + const di = new DependencyInjection(mockConfig, mockEvent, mockContext); + + describe('event', () => { + it('should expose the event', () => { + expect(di.event).toBe(mockEvent); + }); + }); + + describe('context', () => { + it('should expose the Lambda context', () => { + expect(di.context).toBe(mockContext); + }); + }); + + describe('config', () => { + it('should expose the config object', () => { + expect(di.config).toBe(mockConfig); + }); + }); + + describe('get', () => { + it('should return an instance of A, given A', () => { + expect(di.get(A)).toBeInstanceOf(A); + }); + + it('should return an instance of B, given B', () => { + expect(di.get(B)).toBeInstanceOf(B); + }); + + it('should throw, given an unknown dependency', () => { + expect(() => di.get(C)).toThrow('C does not exist in dependency container'); + }); + }); + + describe('getEvent', () => { + it('should return the event', () => { + expect(di.getEvent()).toBe(mockEvent); + }); + }); + + describe('getContext', () => { + it('should return the Lambda context', () => { + expect(di.getContext()).toBe(mockContext); + }); + }); + + describe('getConfiguration', () => { + it('should return the config object', () => { + expect(di.getConfiguration()).toBe(mockConfig); + }); + }); +}); diff --git a/tests/unit/core/LambdaWrapper.spec.ts b/tests/unit/core/LambdaWrapper.spec.ts new file mode 100644 index 00000000..fe4542ea --- /dev/null +++ b/tests/unit/core/LambdaWrapper.spec.ts @@ -0,0 +1,306 @@ +import { RESPONSE_HEADERS } from '@/src/models/ResponseModel'; + +import { + DependencyInjection, + LambdaTermination, + LambdaWrapper, + LambdaWrapperConfig, + LoggerService, + RequestService, +} from '@/src'; +import { mockContext, mockEvent } from '@/tests/mocks/aws'; + +type ErrorWithCode = Error & { code?: number }; + +const config: LambdaWrapperConfig = { + dependencies: { + LoggerService, + RequestService, + }, +}; + +const getDi = () => new DependencyInjection(config, mockEvent, mockContext); + +describe('unit.core.LambdaWrapper', () => { + beforeAll(() => { + // mute log ouptut + const noop = () => { /* do nothing */ }; + jest.spyOn(LoggerService.prototype, 'info').mockImplementation(noop); + jest.spyOn(LoggerService.prototype, 'error').mockImplementation(noop); + jest.spyOn(LoggerService.prototype, 'metric').mockImplementation(noop); + jest.spyOn(LoggerService.prototype, 'label').mockImplementation(noop); + }); + + afterEach(() => jest.resetAllMocks()); + + describe('config', () => { + it('should expose the config object', () => { + const lw = new LambdaWrapper(config); + expect(lw.config).toBe(config); + }); + }); + + describe('configure', () => { + // see tests/unit/core/config.spec.ts for config merge tests + + const base = new LambdaWrapper({ + dependencies: {}, + }); + + const configured = base.configure({ + dependencies: { + LoggerService, + }, + }); + + it('should return a LambdaWrapper with the given config', () => { + expect(configured).toBeInstanceOf(LambdaWrapper); + expect(configured.config).toEqual({ + dependencies: { + LoggerService, + }, + }); + }); + + it('should not modify the original wrapper config', () => { + expect(base.config).toEqual({ + dependencies: {}, + }); + }); + }); + + describe('wrap', () => { + const lambdaWrapper = new LambdaWrapper(config); + + it('should return a wrapped handler function', async () => { + const wrapped = lambdaWrapper.wrap(jest.fn()); + + expect(typeof wrapped).toEqual('function'); + }); + + describe('the wrapped handler', () => { + it('should call the handler', async () => { + const fn = jest.fn(); + const wrapped = lambdaWrapper.wrap(fn); + + await wrapped(mockEvent, mockContext); + + expect(fn).toHaveBeenCalled(); + }); + + it('should forward the return value', async () => { + const result = Math.random(); + const fn = jest.fn().mockResolvedValue(result); + const wrapped = lambdaWrapper.wrap(fn); + + expect(await wrapped(mockEvent, mockContext)).toEqual(result); + }); + + it('should pass dependency injection to the handler', async () => { + const fn = jest.fn(); + const wrapped = lambdaWrapper.wrap(fn); + + await wrapped(mockEvent, mockContext); + + const callArgs: any[] = fn.mock.calls[0]; + expect(callArgs).toHaveLength(1); + expect(callArgs[0]).toBeInstanceOf(DependencyInjection); + }); + + it('should provide the Lambda event via di', async () => { + const fn = jest.fn(); + const wrapped = lambdaWrapper.wrap(fn); + + await wrapped(mockEvent, mockContext); + + const [di]: [DependencyInjection] = fn.mock.calls[0]; + expect(di.event).toEqual(mockEvent); + }); + + it('should provide the Lambda context via di', async () => { + const fn = jest.fn(); + const wrapped = lambdaWrapper.wrap(fn); + + await wrapped(mockEvent, mockContext); + + const [di]: [DependencyInjection] = fn.mock.calls[0]; + expect(di.context).toEqual(mockContext); + }); + }); + + describe('handleUncaughtErrors = true (default)', () => { + describe('when error has no code property', () => { + it('should pass it to logger.error', async () => { + let logger: LoggerService; + + const lambda = lambdaWrapper.wrap((di) => { + logger = di.get(LoggerService); + throw new Error('Undefined error'); + }); + + await lambda(mockEvent, mockContext); + + expect(logger!.info).not.toHaveBeenCalled(); + expect(logger!.error).toHaveBeenCalled(); + expect(logger!.metric).lastCalledWith('lambda.statusCode', 500); + }); + }); + + describe('when error has code 4xx', () => { + [400, 401, 403, 404, 409, 419, 421, 423, 499].forEach((errorCode) => { + it(`should call logger.info with code ${errorCode}`, async () => { + let logger: LoggerService; + + const lambda = lambdaWrapper.wrap((di) => { + logger = di.get(LoggerService); + + const error: ErrorWithCode = new Error('4xx error'); + error.code = errorCode; + throw error; + }); + + await lambda(mockEvent, mockContext); + + expect(logger!.info).toHaveBeenCalled(); + expect(logger!.error).not.toHaveBeenCalled(); + expect(logger!.metric).lastCalledWith('lambda.statusCode', errorCode); + }); + }); + }); + + describe('when error has code 5xx', () => { + [500, 501, 502, 503].forEach((errorCode) => { + it(`should call logger.error with code ${errorCode}`, async () => { + let logger: LoggerService; + + const lambda = lambdaWrapper.wrap((di) => { + logger = di.get(LoggerService); + + const error: ErrorWithCode = new Error('5xx error'); + error.code = errorCode; + throw error; + }); + + await lambda(mockEvent, mockContext); + + expect(logger!.info).not.toHaveBeenCalled(); + expect(logger!.error).toHaveBeenCalled(); + expect(logger!.metric).lastCalledWith('lambda.statusCode', errorCode); + }); + }); + }); + + describe('handler return value', () => { + describe('when a standard Error is thrown', () => { + it('should return status code 500 and message "unknown error"', async () => { + const lambda = lambdaWrapper.wrap(() => { + throw new Error('Some error'); + }); + + const response = await lambda(mockEvent, mockContext); + + expect(response.statusCode).toEqual(500); + expect(JSON.parse(response.body)).toHaveProperty('message', 'unknown error'); + }); + }); + + describe('when a LambdaTermination is thrown', () => { + it('should return status code 403', async () => { + const lambda = lambdaWrapper.wrap(() => { + throw new LambdaTermination('internal', 403, 'external', 'some message'); + }); + + const response = await lambda(mockEvent, mockContext); + + expect(response.statusCode).toEqual(403); + const body = JSON.parse(response.body); + expect(body).toHaveProperty('data', 'external'); + expect(body).toHaveProperty('message', 'some message'); + }); + }); + }); + }); + + describe('handleUncaughtErrors = false', () => { + describe('synchronous error', () => { + it('should return a promise that eventually rejects', async () => { + // note: handler function IS NOT async + const lambda = lambdaWrapper.wrap(() => { + throw new LambdaTermination('sync error message', 403, 'external'); + }, { + handleUncaughtErrors: false, + }); + + const promise = lambda(mockEvent, mockContext); + + // be absolutely sure we got a rejection or the lambda will not count as failed + await expect(promise).rejects.toThrowError(LambdaTermination); + }); + }); + + describe('asynchronous error', () => { + it('should return a promise that eventually rejects', async () => { + // note: handler function IS async + const lambda = lambdaWrapper.wrap(async () => { + throw new LambdaTermination('async error message', 403, 'external'); + }, { + handleUncaughtErrors: false, + }); + + const promise = lambda(mockEvent, mockContext); + + // be absolutely sure we got a rejection or the lambda will not count as failed + await expect(promise).rejects.toThrowError(LambdaTermination); + }); + }); + }); + }); + + describe('handleError', () => { + ([ + [undefined, 400, 0], + [false, 400, 0], + [true, 400, 1], + [undefined, undefined, 1], + [undefined, false, 1], + [undefined, 500, 1], + [true, 500, 1], + ] as const).forEach(([raiseOnEpsagon, code, expected]) => { + it(`should ${expected === 0 ? 'not ' : ''}call logger.error given { raiseOnEpsagon: ${raiseOnEpsagon}, code: ${code} }`, () => { + const di = getDi(); + const logger = di.get(LoggerService); + jest.spyOn(logger, 'error'); + + const error = { + name: 'Error', + message: 'error!', + raiseOnEpsagon, + code, + }; + + LambdaWrapper.handleError(di, error); + + expect(logger.error).toHaveBeenCalledTimes(expected); + }); + + it('should return an HTTP response object', () => { + const di = getDi(); + const error = { + name: 'Error', + message: 'error!', + raiseOnEpsagon, + code, + body: { key: 'value' }, + }; + + const response = LambdaWrapper.handleError(di, error); + + expect(response).toEqual({ + statusCode: code || 500, + body: JSON.stringify({ data: error.body, message: 'unknown error' }), + headers: RESPONSE_HEADERS, + }); + }); + }); + }); +}); diff --git a/tests/unit/core/config.spec.ts b/tests/unit/core/config.spec.ts new file mode 100644 index 00000000..4b302668 --- /dev/null +++ b/tests/unit/core/config.spec.ts @@ -0,0 +1,52 @@ +import DependencyAwareClass from '@/src/core/DependencyAwareClass'; +import { LambdaWrapperConfig, mergeConfig } from '@/src/core/config'; + +class A extends DependencyAwareClass {} + +class B extends DependencyAwareClass {} + +describe('unit.core.config', () => { + describe('mergeConfig', () => { + it('should return the config if no new config is given', () => { + const a: LambdaWrapperConfig = { + dependencies: { A, B }, + }; + const b = {}; + + expect(mergeConfig(a, b)).toEqual(a); + }); + + it('should combine dependencies', () => { + const a: LambdaWrapperConfig = { + dependencies: { A }, + }; + const b: LambdaWrapperConfig = { + dependencies: { B }, + }; + + expect(mergeConfig(a, b)).toEqual({ + dependencies: { A, B }, + }); + }); + + it('should override other keys', () => { + type WithOtherKeys = { test: string; another: string; }; + + const a: LambdaWrapperConfig & WithOtherKeys = { + dependencies: {}, + test: 'initial', + another: 'values', + }; + const b: Partial & WithOtherKeys = { + test: 'overridden', + another: 'here', + }; + + expect(mergeConfig(a, b)).toEqual({ + dependencies: {}, + test: 'overridden', + another: 'here', + }); + }); + }); +}); diff --git a/tests/unit/index.spec.ts b/tests/unit/index.spec.ts new file mode 100644 index 00000000..b2cef2a5 --- /dev/null +++ b/tests/unit/index.spec.ts @@ -0,0 +1,97 @@ +import DependencyAwareClass from '@/src/core/DependencyAwareClass'; +import DependencyInjection from '@/src/core/DependencyInjection'; +import LambdaWrapper from '@/src/core/LambdaWrapper'; +import ResponseModel from '@/src/models/ResponseModel'; +import SQSMessageModel from '@/src/models/SQSMessageModel'; +import StatusModel from '@/src/models/StatusModel'; +import BaseConfigService from '@/src/services/BaseConfigService'; +import HTTPService, { COMICRELIEF_TEST_METADATA_HEADER } from '@/src/services/HTTPService'; +import LoggerService from '@/src/services/LoggerService'; +import RequestService, { REQUEST_TYPES } from '@/src/services/RequestService'; +import SQSService, { SQS_OFFLINE_MODES, SQS_PUBLISH_FAILURE_MODES } from '@/src/services/SQSService'; +import TimerService from '@/src/services/TimerService'; +import LambdaTermination from '@/src/utils/LambdaTermination'; +import PromisifiedDelay from '@/src/utils/PromisifiedDelay'; + +import lambdaWrapper, * as lib from '@/src'; + +describe('unit.index', () => { + describe('default export', () => { + it('should be a LambdaWrapper instance', () => { + expect(lambdaWrapper).toBeInstanceOf(LambdaWrapper); + }); + + it('should be configured with SQSService', () => { + const deps = Object.values(lambdaWrapper.config.dependencies); + expect(deps).toContain(SQSService); + }); + }); + + // these tests prevent accidental removal of exports + + it('should export DependencyAwareClass', () => { + expect(lib.DependencyAwareClass).toBe(DependencyAwareClass); + }); + + it('should export DependencyInjection', () => { + expect(lib.DependencyInjection).toBe(DependencyInjection); + }); + + it('should export LambdaWrapper', () => { + expect(lib.LambdaWrapper).toBe(LambdaWrapper); + }); + + // models + + it('should export ResponseModel', () => { + expect(lib.ResponseModel).toBe(ResponseModel); + }); + + it('should export SQSMessageModel', () => { + expect(lib.SQSMessageModel).toBe(SQSMessageModel); + }); + + it('should export StatusModel', () => { + expect(lib.StatusModel).toBe(StatusModel); + }); + + // services + + it('should export BaseConfigService', () => { + expect(lib.BaseConfigService).toBe(BaseConfigService); + }); + + it('should export HTTPService', () => { + expect(lib.HTTPService).toBe(HTTPService); + expect(lib.COMICRELIEF_TEST_METADATA_HEADER).toBe(COMICRELIEF_TEST_METADATA_HEADER); + }); + + it('should export LoggerService', () => { + expect(lib.LoggerService).toBe(LoggerService); + }); + + it('should export RequestService', () => { + expect(lib.RequestService).toBe(RequestService); + expect(lib.REQUEST_TYPES).toBe(REQUEST_TYPES); + }); + + it('should export SQSService', () => { + expect(lib.SQSService).toBe(SQSService); + expect(lib.SQS_OFFLINE_MODES).toBe(SQS_OFFLINE_MODES); + expect(lib.SQS_PUBLISH_FAILURE_MODES).toBe(SQS_PUBLISH_FAILURE_MODES); + }); + + it('should export TimerService', () => { + expect(lib.TimerService).toBe(TimerService); + }); + + // utils + + it('should export LambdaTermination', () => { + expect(lib.LambdaTermination).toBe(LambdaTermination); + }); + + it('should export PromisifiedDelay', () => { + expect(lib.PromisifiedDelay).toBe(PromisifiedDelay); + }); +}); diff --git a/tests/unit/Model/Response.model.test.js b/tests/unit/models/ResponseModel.spec.ts similarity index 54% rename from tests/unit/Model/Response.model.test.js rename to tests/unit/models/ResponseModel.spec.ts index 2625e013..17442c52 100644 --- a/tests/unit/Model/Response.model.test.js +++ b/tests/unit/models/ResponseModel.spec.ts @@ -1,46 +1,50 @@ -import ResponseModel, { DEFAULT_MESSAGE, RESPONSE_HEADERS } from '../../../src/Model/Response.model'; +import { DEFAULT_MESSAGE, RESPONSE_HEADERS } from '@/src/models/ResponseModel'; -describe('Model/ResponseModel', () => { - it('should return the expected headers', () => { - const response = new ResponseModel({}, 500); - expect(response.generate().headers).toEqual(RESPONSE_HEADERS); +import { ResponseModel } from '@/src'; + +describe('unit.models.ResponseModel', () => { + describe('headers', () => { + it('should include the default response headers', () => { + const response = new ResponseModel({}, 500); + expect(response.generate().headers).toEqual(RESPONSE_HEADERS); + }); }); - describe('ensure body set correctly', () => { + describe('body', () => { it('should set the body data from the constructor', () => { const response = new ResponseModel({ test: 123 }, 500); const responseBody = response.generate(); expect(typeof responseBody.body).toEqual('string'); - expect(responseBody.body.indexOf('123')).not.toEqual(-1); - expect(JSON.parse(responseBody.body).data.test).toEqual(123); + expect(responseBody.body).toContain('123'); + expect(JSON.parse(responseBody.body)).toHaveProperty('data.test', 123); }); - it('should be able to modify the body data', () => { + it('should modify the body data using setData', () => { const response = new ResponseModel({ test: 123 }, 500); response.setData({ test: 234 }); const responseBody = response.generate(); expect(typeof responseBody.body).toEqual('string'); - expect(responseBody.body.indexOf('234')).not.toEqual(-1); - expect(JSON.parse(responseBody.body).data.test).toEqual(234); + expect(responseBody.body).toContain('234'); + expect(JSON.parse(responseBody.body)).toHaveProperty('data.test', 234); }); }); - describe('ensure status codes are set correctly', () => { - it('should return the 200 status code that is supplied to it', () => { + describe('status code', () => { + it('should return a 200 status code that is supplied to it', () => { const response = new ResponseModel({}, 200); expect(response.generate().statusCode).toEqual(200); }); - it('should return the 500 status code that is supplied to it', () => { + it('should return a 500 status code that is supplied to it', () => { const response = new ResponseModel({}, 500); expect(response.generate().statusCode).toEqual(500); }); - it('should allow the status code to be modified once set via the constructor', () => { + it('should modify the status code using setCode', () => { const response = new ResponseModel({}, 200); response.setCode(300); @@ -48,25 +52,25 @@ describe('Model/ResponseModel', () => { }); }); - describe('ensure messages are set correctly', () => { - it('should return a message field when a message is supplied to it', () => { + describe('message', () => { + it('should put a message field in the body', () => { const message = 'test 123'; const response = new ResponseModel({}, 500, message); - expect(JSON.parse(response.generate().body).message).toEqual(message); + expect(JSON.parse(response.generate().body)).toHaveProperty('message', message); }); - it('should be able to get the message using the message getter', () => { + it('should be able to get the message using getMessage', () => { const message = 'test 123'; const response = new ResponseModel({}, 500, message); expect(response.getMessage()).toEqual(message); }); - it('should return success message field when a message is not supplied to it', () => { + it('should return success message if no message is given', () => { const response = new ResponseModel({}, 500); expect(JSON.parse(response.generate().body).message).toEqual(DEFAULT_MESSAGE); }); - it('should allow the message supplied via the constructor to be overridden', () => { + it('should modify the message using setMessage', () => { const response = new ResponseModel({}, 200, 'replace-me'); response.setMessage('test'); @@ -75,7 +79,7 @@ describe('Model/ResponseModel', () => { }); describe('generate', () => { - it('static and instance method produce the same output', () => { + it('should output the same from the static and instance method', () => { const data = { a: 1, b: { c: 2 } }; const code = 201; const message = 'Some message'; diff --git a/tests/unit/models/SQSMessageModel.spec.ts b/tests/unit/models/SQSMessageModel.spec.ts new file mode 100644 index 00000000..3bcd60c0 --- /dev/null +++ b/tests/unit/models/SQSMessageModel.spec.ts @@ -0,0 +1,59 @@ +import { SQSMessageModel as Message } from '@/src'; + +describe('unit.models.SQSMessageModel', () => { + const messageData = { + test: 123, + }; + + const mockedMessage = { + MessageId: 'message-id-123', + ReceiptHandle: 'receipt-handle-123', + Body: JSON.stringify(messageData), + }; + + const messageModel = new Message(mockedMessage); + + describe('getMessageId', () => { + it('should return the message ID', () => { + expect(messageModel.getMessageId()).toEqual(mockedMessage.MessageId); + }); + }); + + describe('getReceiptHandle', () => { + it('should return the receipt handle', () => { + expect(messageModel.getReceiptHandle()).toEqual(mockedMessage.ReceiptHandle); + }); + }); + + describe('getBody', () => { + it('should parse and return the message body', () => { + expect(messageModel.getBody()).toEqual(messageData); + }); + }); + + describe('isForDeletion', () => { + it('should be false initially', () => { + expect(messageModel.isForDeletion()).toBe(false); + }); + }); + + describe('setForDeletion', () => { + it('should set the is-for-deletion status', () => { + messageModel.setForDeletion(true); + expect(messageModel.isForDeletion()).toBe(true); + }); + }); + + describe('getMetaData', () => { + it('should return an empty object initially', () => { + expect(messageModel.getMetaData()).toEqual({}); + }); + }); + + describe('setMetaData', () => { + it('should add a key to metadata', () => { + messageModel.setMetaData('test', 123); + expect(messageModel.getMetaData()).toEqual({ test: 123 }); + }); + }); +}); diff --git a/tests/unit/models/StatusModel.spec.ts b/tests/unit/models/StatusModel.spec.ts new file mode 100644 index 00000000..ad141b56 --- /dev/null +++ b/tests/unit/models/StatusModel.spec.ts @@ -0,0 +1,39 @@ +import StatusModel, { STATUS_TYPES } from '@/src/models/StatusModel'; + +describe('unit.models.StatusModel', () => { + describe('getService', () => { + it('should return the service name', () => { + const statusModel = new StatusModel('test', STATUS_TYPES.OK); + expect(statusModel.getService()).toEqual('test'); + }); + }); + + describe('setService', () => { + it('should set the service name', () => { + const statusModel = new StatusModel('test', STATUS_TYPES.OK); + statusModel.setService('other'); + expect(statusModel.getService()).toEqual('other'); + }); + }); + + describe('getStatus', () => { + it('should return the status', () => { + const statusModel = new StatusModel('test', STATUS_TYPES.OK); + expect(statusModel.getStatus()).toEqual(STATUS_TYPES.OK); + }); + }); + + describe('setStatus', () => { + it('should set the status', () => { + const statusModel = new StatusModel('test', STATUS_TYPES.OK); + statusModel.setStatus(STATUS_TYPES.ACCEPTABLE_FAILURE); + expect(statusModel.getStatus()).toEqual(STATUS_TYPES.ACCEPTABLE_FAILURE); + }); + + it('should throw an error when trying to set an invalid status', () => { + const statusModel = new StatusModel('test', STATUS_TYPES.OK); + expect(() => statusModel.setStatus('invalid')) + .toThrow('StatusModel - invalid is not a valid status type'); + }); + }); +}); diff --git a/tests/unit/Service/BaseConfig.service.test.js b/tests/unit/services/BaseConfigService.spec.ts similarity index 66% rename from tests/unit/Service/BaseConfig.service.test.js rename to tests/unit/services/BaseConfigService.spec.ts index d8b5496c..7a6166a8 100644 --- a/tests/unit/Service/BaseConfig.service.test.js +++ b/tests/unit/services/BaseConfigService.spec.ts @@ -1,9 +1,20 @@ import { S3 } from 'aws-sdk'; -import DependencyInjection from '../../../src/DependencyInjection/DependencyInjection.class'; -import BaseConfigService, { S3_NO_SUCH_KEY_ERROR_CODE, ServiceStates, ServiceStatesHttpCodes } from '../../../src/Service/BaseConfig.service'; +import { + S3_NO_SUCH_KEY_ERROR_CODE, + ServiceStates, + ServiceStatesHttpCodes, +} from '@/src/services/BaseConfigService'; -const createAsyncMock = (returnValue) => { +import { + BaseConfigService, + Context, + DependencyInjection, +} from '@/src'; + +type ErrorWithCode = Error & { code?: any }; + +const createAsyncMock = (returnValue: any) => { const mockedValue = returnValue instanceof Error ? Promise.reject(returnValue) : Promise.resolve(returnValue); @@ -12,40 +23,49 @@ const createAsyncMock = (returnValue) => { }; /** - * Generates a BaseConfigService - * - * @param {*} param0 - * @returns {BaseConfigService} + * Generate a BaseConfigService with mock S3 client. */ -const getService = ({ getObject = null, putObject = null, deleteObject = null } = {}) => { - const di = new DependencyInjection({}, {}, {}); - const service = new BaseConfigService(di); +const getService = ( + { + getObject = null, + putObject = null, + deleteObject = null, + }: any = {}, +): BaseConfigService & { constructor: typeof BaseConfigService; } => { + const di = new DependencyInjection({ + dependencies: { + BaseConfigService, + }, + }, {}, {} as Context); + + const service = di.get(BaseConfigService); + const client = { getObject: createAsyncMock(getObject), putObject: createAsyncMock(putObject), deleteObject: createAsyncMock(deleteObject), - }; + } as unknown as S3; jest.spyOn(service, 'client', 'get').mockReturnValue(client); - return service; + return service as any; }; -const BaseConfigUnitTests = (serviceGenerator: (...args) => BaseConfigService) => { +describe('unit.services.BaseConfigService', () => { afterEach(() => { jest.resetAllMocks(); }); describe('defaultConfig', () => { it('is a valid object', () => { - const service = serviceGenerator(); + const service = getService(); const isValidObject = typeof service.constructor.defaultConfig === 'object' && service.constructor.defaultConfig !== null; expect(isValidObject).toEqual(true); }); it('has state defined', () => { - const service = serviceGenerator(); + const service = getService(); const defaultConfig = service.constructor.defaultConfig; expect('state' in defaultConfig).toEqual(true); @@ -54,14 +74,14 @@ const BaseConfigUnitTests = (serviceGenerator: (...args) => BaseConfigService) = describe('s3config', () => { it('is a valid object', () => { - const service = serviceGenerator(); + const service = getService(); const isValidObject = typeof service.constructor.s3config === 'object' && service.constructor.s3config !== null; expect(isValidObject).toEqual(true); }); it('has Bucket and Key defined', () => { - const service = serviceGenerator(); + const service = getService(); const s3config = service.constructor.s3config; expect('Bucket' in s3config).toEqual(true); @@ -71,7 +91,7 @@ const BaseConfigUnitTests = (serviceGenerator: (...args) => BaseConfigService) = describe('delete', () => { it('calls client.deleteObject', async () => { - const service = serviceGenerator(); + const service = getService(); await service.delete(); expect(service.client.deleteObject).toHaveBeenCalledTimes(1); @@ -81,7 +101,7 @@ const BaseConfigUnitTests = (serviceGenerator: (...args) => BaseConfigService) = describe('put', () => { it('calls client.putObject', async () => { const expected = Symbol('put'); - const service = serviceGenerator(); + const service = getService(); await service.put(expected); expect(service.client.putObject).toHaveBeenCalledTimes(1); @@ -89,7 +109,7 @@ const BaseConfigUnitTests = (serviceGenerator: (...args) => BaseConfigService) = it('returns the provided config unchanged', async () => { const expected = Symbol('put'); - const service = serviceGenerator(); + const service = getService(); const config = await service.put(expected); expect(config).toEqual(expected); @@ -99,29 +119,29 @@ const BaseConfigUnitTests = (serviceGenerator: (...args) => BaseConfigService) = describe('get', () => { it('gets an existing config', async () => { const expected = { a: 1 }; - const service = serviceGenerator({ getObject: { Body: JSON.stringify(expected) } }); + const service = getService({ getObject: { Body: JSON.stringify(expected) } }); const config = await service.get(); expect(config).toEqual(expected); }); it('refuses empty configurations', async () => { - const service = serviceGenerator({ getObject: { Body: '' } }); + const service = getService({ getObject: { Body: '' } }); await expect(service.get()).rejects.toThrowErrorMatchingSnapshot(); }); it('refuses invalid configurations', async () => { - const service = serviceGenerator({ getObject: { Body: '{ "a": 1' } }); + const service = getService({ getObject: { Body: '{ "a": 1' } }); await expect(service.get()).rejects.toThrowErrorMatchingSnapshot(); }); it('propagates the 404', async () => { - const error = new Error('404'); + const error: ErrorWithCode = new Error('404'); error.code = S3_NO_SUCH_KEY_ERROR_CODE; - const service = serviceGenerator({ getObject: error }); + const service = getService({ getObject: error }); await expect(service.get()).rejects.toThrowErrorMatchingSnapshot(); }); @@ -129,20 +149,20 @@ const BaseConfigUnitTests = (serviceGenerator: (...args) => BaseConfigService) = describe('getOrCreate', () => { it('uploads the defaultConfig with a 404 error', async () => { - const error = new Error('404'); + const error: ErrorWithCode = new Error('404'); error.code = S3_NO_SUCH_KEY_ERROR_CODE; - const service = serviceGenerator({ getObject: error }); + const service = getService({ getObject: error }); const config = await service.getOrCreate(); expect(config).toEqual(service.constructor.defaultConfig); }); it('throws any non-404 error', async () => { - const error = new Error('Bad error'); + const error: ErrorWithCode = new Error('Bad error'); error.code = 'another'; - const service = serviceGenerator({ getObject: error }); + const service = getService({ getObject: error }); await expect(service.getOrCreate()).rejects.toThrowErrorMatchingSnapshot(); }); @@ -151,7 +171,7 @@ const BaseConfigUnitTests = (serviceGenerator: (...args) => BaseConfigService) = describe('patch', () => { it('uses the existing config if an existing config is found', async () => { const existing = { a: 1 }; - const service = serviceGenerator({ getObject: { Body: JSON.stringify(existing) } }); + const service = getService({ getObject: { Body: JSON.stringify(existing) } }); const additional = { b: 2 }; const expected = { ...existing, ...additional }; @@ -161,9 +181,9 @@ const BaseConfigUnitTests = (serviceGenerator: (...args) => BaseConfigService) = }); it('uses the base config if no existing config is found', async () => { - const error = new Error('404'); + const error: ErrorWithCode = new Error('404'); error.code = S3_NO_SUCH_KEY_ERROR_CODE; - const service = serviceGenerator({ getObject: error }); + const service = getService({ getObject: error }); const existing = service.constructor.defaultConfig; const additional = { b: 2 }; @@ -174,10 +194,10 @@ const BaseConfigUnitTests = (serviceGenerator: (...args) => BaseConfigService) = }); it('throws any non-404 error', async () => { - const error = new Error('Bad error'); + const error: ErrorWithCode = new Error('Bad error'); error.code = 'another'; - const service = serviceGenerator({ getObject: error }); + const service = getService({ getObject: error }); await expect(service.patch({ b: 1 })).rejects.toThrowErrorMatchingSnapshot(); }); @@ -186,18 +206,18 @@ const BaseConfigUnitTests = (serviceGenerator: (...args) => BaseConfigService) = describe('healthCheck', () => { Object.values(ServiceStates).forEach((state) => { describe(state, () => { - it('Returns the expected HTTP code with the given config', async () => { + it('returns the expected HTTP code with the given config', async () => { const config = { state }; - const service = serviceGenerator(); + const service = getService(); const statusCode = await service.healthCheck(config); const expected = ServiceStatesHttpCodes[state]; expect(statusCode).toEqual(expected); }); - it('Returns the expected HTTP code with the existing config', async () => { + it('returns the expected HTTP code with the existing config', async () => { const config = { state }; - const service = serviceGenerator({ getObject: { Body: JSON.stringify(config) } }); + const service = getService({ getObject: { Body: JSON.stringify(config) } }); const statusCode = await service.healthCheck(); const expected = ServiceStatesHttpCodes[state]; @@ -207,18 +227,18 @@ const BaseConfigUnitTests = (serviceGenerator: (...args) => BaseConfigService) = }); describe('Unknown state', () => { - it('Returns 500 with the given config', async () => { + it('returns 500 with the given config', async () => { const config = { state: 'Unknown' }; - const service = serviceGenerator(); + const service = getService(); const statusCode = await service.healthCheck(config); const expected = 500; expect(statusCode).toEqual(expected); }); - it('Returns 500 with the existing config', async () => { + it('returns 500 with the existing config', async () => { const config = { state: 'Unknown' }; - const service = serviceGenerator({ getObject: { Body: JSON.stringify(config) } }); + const service = getService({ getObject: { Body: JSON.stringify(config) } }); const statusCode = await service.healthCheck(); const expected = 500; @@ -231,7 +251,7 @@ const BaseConfigUnitTests = (serviceGenerator: (...args) => BaseConfigService) = [200, 201, 202, 204, 300, 301, 399].forEach((statusCode) => { describe(statusCode, () => { it('is healthy', async () => { - const service = serviceGenerator(); + const service = getService(); jest.spyOn(service, 'healthCheck').mockImplementation(() => Promise.resolve(statusCode)); await expect(service.ensureHealthy()).resolves.toEqual(statusCode); @@ -242,29 +262,29 @@ const BaseConfigUnitTests = (serviceGenerator: (...args) => BaseConfigService) = [400, 401, 403, 404, 409, 499, 500, 501, 502, 503, 504, 'Dante Alighieri'].forEach((statusCode) => { describe(statusCode, () => { it('throws a LambdaTermination', async () => { - const service = serviceGenerator(); - jest.spyOn(service, 'healthCheck').mockImplementation(() => Promise.resolve(statusCode)); + const service = getService(); + jest.spyOn(service, 'healthCheck').mockImplementation(() => Promise.resolve(statusCode as any)); await expect(service.ensureHealthy()).rejects.toThrowErrorMatchingSnapshot(); }); }); }); }); -}; - -describe('Service/BaseConfigService', () => { - BaseConfigUnitTests(getService); describe('client', () => { - it('Returns an s3 instance (static)', () => { - expect(BaseConfigService.client instanceof S3).toEqual(true); + it('should return an S3 instance (static method)', () => { + expect(BaseConfigService.client).toBeInstanceOf(S3); }); - it('Returns an s3 instance', () => { - const di = new DependencyInjection({}, {}, {}); - const service = new BaseConfigService(di); + it('should return an S3 instance (instance method)', () => { + const di = new DependencyInjection({ + dependencies: { + BaseConfigService, + }, + }, {}, {} as Context); + const service = di.get(BaseConfigService); - expect(service.client instanceof S3).toEqual(true); + expect(service.client).toBeInstanceOf(S3); }); }); }); diff --git a/tests/unit/Service/HTTP.service.test.js b/tests/unit/services/HTTPService.spec.ts similarity index 62% rename from tests/unit/Service/HTTP.service.test.js rename to tests/unit/services/HTTPService.spec.ts index e9ab0101..d50fe2d7 100644 --- a/tests/unit/Service/HTTP.service.test.js +++ b/tests/unit/services/HTTPService.spec.ts @@ -1,25 +1,37 @@ import axios from 'axios'; -import CONFIGURATION from '../../../src/Config/Dependencies'; -import DependencyInjection from '../../../src/DependencyInjection/DependencyInjection.class'; -import HTTPService, { COMICRELIEF_TEST_METADATA_HEADER } from '../../../src/Service/HTTP.service'; -import getEvent from '../../mocks/aws/event.json'; +import { + COMICRELIEF_TEST_METADATA_HEADER, + Context, + DependencyInjection, + HTTPService, + RequestService, +} from '@/src'; +import mockEvent from '@/tests/mocks/aws/event.json'; -const getContext = { invokedFunctionArn: 'my-function' }; +const mockContext = { invokedFunctionArn: 'my-function' } as Context; -const getService = (event = getEvent, context = getContext) => new HTTPService(new DependencyInjection(CONFIGURATION, event, context)); +const getService = (event = mockEvent, context = mockContext) => { + const di = new DependencyInjection({ + dependencies: { + HTTPService, + RequestService, + }, + }, event, context); + return di.get(HTTPService); +}; -describe('Service/HTTPService', () => { +describe('unit.services.HTTPService', () => { afterEach(() => jest.clearAllMocks()); describe('request', () => { const testCases = { - 'GET Request': { method: 'GET', url: '/' }, - 'POST Request': { method: 'POST', url: '/' }, - 'PUT Request': { method: 'PUT', url: '/' }, - 'PATCH Request': { method: 'PATCH', url: '/' }, - 'HEAD Request': { method: 'HEAD', url: '/' }, - 'DELETE Request': { method: 'DELETE', url: '/' }, + 'GET request': { method: 'GET', url: '/' }, + 'POST request': { method: 'POST', url: '/' }, + 'PUT request': { method: 'PUT', url: '/' }, + 'PATCH request': { method: 'PATCH', url: '/' }, + 'HEAD request': { method: 'HEAD', url: '/' }, + 'DELETE request': { method: 'DELETE', url: '/' }, 'with URL': { url: '/some/nested/path' }, 'with baseURL': { baseUrl: 'https://comicrelief.com/test', url: '/additional/url' }, 'overriding timeout': { timeout: 99 }, @@ -42,9 +54,9 @@ describe('Service/HTTPService', () => { it(`adds the test header, ${description}`, async () => { const metadata = JSON.stringify({ user: 'Dante Alighieri' }); const event = { - ...getEvent, + ...mockEvent, headers: { - ...getEvent.headers, + ...mockEvent.headers, [COMICRELIEF_TEST_METADATA_HEADER]: metadata, }, }; diff --git a/tests/unit/Service/Logger.service.test.js b/tests/unit/services/LoggerService.spec.ts similarity index 63% rename from tests/unit/Service/Logger.service.test.js rename to tests/unit/services/LoggerService.spec.ts index 492534ce..22259c49 100644 --- a/tests/unit/Service/Logger.service.test.js +++ b/tests/unit/services/LoggerService.spec.ts @@ -1,16 +1,21 @@ import Winston from 'winston'; -import CONFIGURATION from '../../../src/Config/Dependencies'; -import DependencyInjection from '../../../src/DependencyInjection/DependencyInjection.class'; -import LoggerService from '../../../src/Service/Logger.service'; -import getEvent from '../../mocks/aws/event.json'; +import { + Context, + DependencyInjection, + LoggerService, +} from '@/src'; +import mockEvent from '@/tests/mocks/aws/event.json'; -const getContext = { invokedFunctionArn: 'my-function' }; +const mockContext = { invokedFunctionArn: 'my-function' } as Context; -const getLogger = (event = getEvent, context = getContext) => new LoggerService(new DependencyInjection(CONFIGURATION, event, context)); +const getLogger = (event = mockEvent, context = mockContext) => { + const di = new DependencyInjection({ dependencies: { LoggerService } }, event, context); + return new LoggerService(di); +}; -describe('Service/LoggerService', () => { - const context = { invokedFunctionArn: 'my-function' }; +describe('unit.services.LoggerService', () => { + const context = { invokedFunctionArn: 'my-function' } as Context; const axiosResponses = { UNDEFINED: undefined, @@ -24,39 +29,19 @@ describe('Service/LoggerService', () => { afterEach(() => jest.clearAllMocks()); - describe('constructor', () => { - it('Creates a LoggerService instance', () => { - expect(getLogger()).toBeInstanceOf(LoggerService); - }); - }); - - describe('getLogger', () => { - it('Creates a logger instance', () => { - const logger = getLogger(undefined, context); - const winston = logger.getLogger(); - expect(winston.constructor.name).toEqual('DerivedLogger'); - }); - }); - describe('logger', () => { - it('Starts as null', () => { + it('should return a logger', () => { const logger = getLogger(undefined, context); - expect(logger.winston).toEqual(null); + expect(logger.logger.constructor.name).toEqual('DerivedLogger'); }); - it('Fetches a logger', () => { - const winston = Symbol('winston'); + it('should not call `Winston.createLogger` twice', () => { + const winston = Symbol('winston') as unknown as Winston.Logger; const logger = getLogger(undefined, context); - jest.spyOn(Winston, 'createLogger').mockImplementation(() => winston); - - expect(logger.logger).toEqual(winston); - }); - it("Doesn' call Winston.createLogger twice", () => { - const winston = Symbol('winston'); - const logger = getLogger(undefined, context); jest.spyOn(Winston, 'createLogger').mockImplementation(() => winston); + // use the getter several times expect(logger.logger).toEqual(winston); expect(logger.logger).toEqual(winston); expect(logger.logger).toEqual(winston); @@ -70,8 +55,9 @@ describe('Service/LoggerService', () => { it(`Trims down the axios error: ${key}`, () => { const logger = getLogger(); const log = jest.fn(); + const fakeLogger = { log } as unknown as Winston.Logger; - jest.spyOn(logger, 'logger', 'get').mockReturnValue({ log }); + jest.spyOn(logger, 'logger', 'get').mockReturnValue(fakeLogger); const error = { isAxiosError: true, @@ -104,8 +90,9 @@ describe('Service/LoggerService', () => { it(`Trims down the axios error: ${key}`, () => { const logger = getLogger(); const log = jest.fn(); + const fakeLogger = { log } as unknown as Winston.Logger; - jest.spyOn(logger, 'logger', 'get').mockReturnValue({ log }); + jest.spyOn(logger, 'logger', 'get').mockReturnValue(fakeLogger); const error = { isAxiosError: true, @@ -134,7 +121,7 @@ describe('Service/LoggerService', () => { }); describe('warning', () => { - let LOGGER_SOFT_WARNING; + let LOGGER_SOFT_WARNING: string | undefined; beforeAll(() => { LOGGER_SOFT_WARNING = process.env.LOGGER_SOFT_WARNING; @@ -144,21 +131,19 @@ describe('Service/LoggerService', () => { process.env.LOGGER_SOFT_WARNING = LOGGER_SOFT_WARNING; }); - [ + ([ ['', 'error'], ['some-value', 'error'], - [false, 'error'], ['false', 'error'], ['0', 'error'], ['1', 'info'], - [true, 'info'], ['true', 'info'], - ].forEach(([loggerSoftWarning, func]) => { + ] as [string, 'error' | 'info'][]).forEach(([loggerSoftWarning, func]) => { it(`uses 'this.logger.${func}' in ${loggerSoftWarning}`, () => { process.env.LOGGER_SOFT_WARNING = loggerSoftWarning; const logger = getLogger(); - jest.spyOn(logger, func).mockImplementation(() => {}); + jest.spyOn(logger, func).mockImplementation(() => { /* no-op */ }); logger.warning({}); @@ -168,7 +153,11 @@ describe('Service/LoggerService', () => { }); describe('object', () => { - ['error', 'warning', 'info'].forEach((level) => { + ([ + 'error', + 'warning', + 'info', + ] as const).forEach((level) => { [ null, 'a string', @@ -177,13 +166,15 @@ describe('Service/LoggerService', () => { ].forEach((object) => { it(`Logs a '${JSON.stringify(object)}' with level: '${level}'`, () => { const logger = getLogger(); - let message; + let calledArgs: any[] = []; + const fakeLog = (...args: any[]) => { calledArgs = args; }; - jest.spyOn(logger, level).mockImplementation((arg) => { message = arg; }); + jest.spyOn(logger, level).mockImplementation(fakeLog); logger.object('My action', object, level); + expect(logger[level]).toHaveBeenCalledTimes(1); - expect(message).toMatchSnapshot(); + expect(calledArgs[0]).toMatchSnapshot(); }); }); }); diff --git a/tests/unit/Service/Request.service.test.js b/tests/unit/services/RequestService.spec.ts similarity index 56% rename from tests/unit/Service/Request.service.test.js rename to tests/unit/services/RequestService.spec.ts index ccdbab53..82889252 100644 --- a/tests/unit/Service/Request.service.test.js +++ b/tests/unit/services/RequestService.spec.ts @@ -1,90 +1,112 @@ -/* eslint-disable sonarjs/no-duplicate-string */ import QueryString from 'querystring'; -import CONFIGURATION from '../../../src/Config/Dependencies'; -import DependencyInjection from '../../../src/DependencyInjection/DependencyInjection.class'; -import RequestService, { HTTP_METHODS_WITHOUT_PAYLOADS, HTTP_METHODS_WITH_PAYLOADS } from '../../../src/Service/Request.service'; -import getContext from '../../mocks/aws/context.json'; -import baseEvent from '../../mocks/aws/event.json'; +import { + HTTP_METHODS_WITHOUT_PAYLOADS, + HTTP_METHODS_WITH_PAYLOADS, +} from '@/src/services/RequestService'; + +import { + DependencyInjection, + LoggerService, + RequestService, +} from '@/src'; +import { mockContext, mockEvent } from '@/tests/mocks/aws'; const getEvent = (overrides = {}) => JSON.parse(JSON.stringify(({ - ...baseEvent, + ...mockEvent, ...overrides, }))); -describe('Service/RequestService', () => { +const getRequestService = (event: any) => { + const di = new DependencyInjection({ + dependencies: { + RequestService, + LoggerService, + }, + }, event, mockContext); + return new RequestService(di); +}; + +describe('unit.services.RequestService', () => { + beforeAll(() => { + // mute log ouptut + const noop = () => { /* do nothing */ }; + jest.spyOn(LoggerService.prototype, 'info').mockImplementation(noop); + jest.spyOn(LoggerService.prototype, 'error').mockImplementation(noop); + jest.spyOn(LoggerService.prototype, 'metric').mockImplementation(noop); + jest.spyOn(LoggerService.prototype, 'label').mockImplementation(noop); + }); + afterEach(() => jest.resetAllMocks()); HTTP_METHODS_WITHOUT_PAYLOADS.forEach((httpMethod) => { describe(`HTTP ${httpMethod}`, () => { - describe('.getAll', () => { - it('should return all get parameters as an object', () => { + describe('getAll', () => { + it('should return all query string parameters as an object', () => { const event = getEvent({ httpMethod }); - event.queryStringParameters.test = 123; - const request = new RequestService(new DependencyInjection(CONFIGURATION, event, getContext)); + event.queryStringParameters.test = '123'; + const request = getRequestService(event); const params = request.getAll(); - expect(params.test).toEqual(123); + expect(params.test).toEqual('123'); expect(params['array[]']).toEqual(['one', 'two', 'three']); }); }); - describe('.get', () => { - it('should fetch a query parameter from an AWS event', () => { + describe('get', () => { + it('should fetch a query parameter', () => { const event = getEvent({ httpMethod }); - event.queryStringParameters.test = 123; - const request = new RequestService(new DependencyInjection(CONFIGURATION, event, getContext)); + event.queryStringParameters.test = '123'; + const request = getRequestService(event); - expect(request.get('test')).toEqual(event.queryStringParameters.test); + expect(request.get('test')).toEqual('123'); }); - it('should fetch a query parameter from an AWS event when the request type is set', () => { + it('should fetch a query parameter when the request type is given', () => { const event = getEvent({ httpMethod }); event.queryStringParameters.test = 123; - const request = new RequestService(new DependencyInjection(CONFIGURATION, event, getContext)); + const request = getRequestService(event); - expect(request.get('test', null, httpMethod)).toEqual(event.queryStringParameters.test); + const param = request.get('test', null, httpMethod); + expect(param).toEqual(event.queryStringParameters.test); }); - it(`should return null from a non existent ${httpMethod} parameter from an AWS event`, () => { + it(`should return null from a nonexistent ${httpMethod} parameter`, () => { const event = getEvent({ httpMethod }); - const request = new RequestService(new DependencyInjection(CONFIGURATION, event, getContext)); + const request = getRequestService(event); - expect(request.get('fake')).toEqual(null); + const param = request.get('fake'); + expect(param).toBeNull(); }); - it(`should return null from a non existent ${httpMethod} parameter from an AWS event when the request type is set`, () => { + it(`should return null from a nonexistent ${httpMethod} parameter when the request type is given`, () => { const event = getEvent({ httpMethod }); - const request = new RequestService(new DependencyInjection(CONFIGURATION, event, getContext)); + const request = getRequestService(event); - expect(request.get('fake', null, httpMethod)).toEqual(null); + const param = request.get('fake', null, httpMethod); + expect(param).toBeNull(); }); it('should return an array-type query parameter if its name ends []', () => { const event = getEvent({ httpMethod }); - const request = new RequestService(new DependencyInjection(CONFIGURATION, event, getContext)); + const request = getRequestService(event); - expect(request.get('array[]')).toEqual(['one', 'two', 'three']); + const param = request.get('array[]'); + expect(param).toEqual(['one', 'two', 'three']); }); }); - describe('.validateAgainstConstraints', () => { + describe('validateAgainstConstraints', () => { const constraints = { giftaid: { numericality: true, }, }; - beforeEach(() => { - // Mute Winston - // eslint-disable-next-line no-underscore-dangle - jest.spyOn(console._stdout, 'write').mockImplementation(() => {}); - }); - it('should resolve if there are no validation errors', async () => { const event = getEvent({ httpMethod }); event.queryStringParameters.giftaid = 123; - const request = new RequestService(new DependencyInjection(CONFIGURATION, event, getContext)); + const request = getRequestService(event); await expect(request.validateAgainstConstraints(constraints)).resolves.toEqual(undefined); }); @@ -92,30 +114,31 @@ describe('Service/RequestService', () => { it('should return a response containing validation errors if the data provided is incorrect', async () => { const event = getEvent({ httpMethod }); event.queryStringParameters.giftaid = 'abc'; - const request = new RequestService(new DependencyInjection(CONFIGURATION, event, getContext)); + const request = getRequestService(event); await expect(request.validateAgainstConstraints(constraints)).rejects.toMatchSnapshot(); }); }); - describe('.getUserBrowserAndDevice', () => { + describe('getUserBrowserAndDevice', () => { it('should return null with `headers === undefined`', () => { const event = getEvent({ httpMethod, headers: undefined }); - const request = new RequestService(new DependencyInjection(CONFIGURATION, event, getContext)); + const request = getRequestService(event); expect(request.getUserBrowserAndDevice()).toEqual(null); }); it('should return null with `headers === null`', () => { const event = getEvent({ httpMethod, headers: null }); - const request = new RequestService(new DependencyInjection(CONFIGURATION, event, getContext)); + const request = getRequestService(event); expect(request.getUserBrowserAndDevice()).toEqual(null); }); it('should return a prettified user agent', () => { const event = getEvent({ httpMethod }); - const request = new RequestService(new DependencyInjection(CONFIGURATION, event, getContext)); + const request = getRequestService(event); + expect(request.getUserBrowserAndDevice()).toEqual({ 'browser-type': 'Safari', 'browser-version': '9.1.1', @@ -133,75 +156,68 @@ describe('Service/RequestService', () => { const event = getEvent({ httpMethod }); event.headers['Content-Type'] = 'application/x-www-form-urlencoded'; event.body = 'grant_type=client_credentials&response_type=token&token_format=opaque'; - return { ...event, ...overrides }; }; const queryParameters = QueryString.parse(getPayloadEvent().body); describe(`HTTP ${httpMethod}`, () => { - describe('.getAll', () => { + describe('getAll', () => { it('should return all post parameters as an array', () => { const event = getPayloadEvent(); - const request = new RequestService(new DependencyInjection(CONFIGURATION, event, getContext)); + const request = getRequestService(event); expect(request.getAll()).toEqual(queryParameters); }); }); - describe('.get', () => { + describe('get', () => { it('should fetch a request body parameter from an AWS event', () => { const event = getPayloadEvent(); - const request = new RequestService(new DependencyInjection(CONFIGURATION, event, getContext)); + const request = getRequestService(event); expect(request.get('grant_type')).toEqual(queryParameters.grant_type); }); it('should fetch a request body parameter from an AWS event when the request type is set', () => { const event = getPayloadEvent(); - const request = new RequestService(new DependencyInjection(CONFIGURATION, event, getContext)); + const request = getRequestService(event); expect(request.get('grant_type', null, httpMethod)).toEqual(queryParameters.grant_type); }); it('should return null from a non existent request body parameter from an AWS event', () => { const event = getPayloadEvent(); - const request = new RequestService(new DependencyInjection(CONFIGURATION, event, getContext)); + const request = getRequestService(event); expect(request.get('fake')).toEqual(null); }); it('should return null from a non existent request body parameter from an AWS event when the request type is set', () => { const event = getPayloadEvent(); - const request = new RequestService(new DependencyInjection(CONFIGURATION, event, getContext), getEvent); + const request = getRequestService(event); expect(request.get('fake', null, httpMethod)).toEqual(null); }); }); - describe('.validateAgainstConstraints', () => { + describe('validateAgainstConstraints', () => { const constraints = { giftaid: { numericality: true, }, }; - beforeEach(() => { - // Mute Winston - // eslint-disable-next-line no-underscore-dangle - jest.spyOn(console._stdout, 'write').mockImplementation(() => {}); - }); - it('should resolve if there are no validation errors', async () => { const event = getPayloadEvent({ body: 'giftaid=123' }); - const request = new RequestService(new DependencyInjection(CONFIGURATION, event, getContext)); + const request = getRequestService(event); await expect(request.validateAgainstConstraints(constraints)).resolves.toEqual(undefined); }); it('should return a response containing validation errors if the data provided is incorrect', async () => { const event = getPayloadEvent({ body: 'giftaid=abc' }); - const request = new RequestService(new DependencyInjection(CONFIGURATION, event, getContext)); + const request = getRequestService(event); await expect(request.validateAgainstConstraints(constraints)).rejects.toMatchSnapshot(); }); @@ -209,20 +225,18 @@ describe('Service/RequestService', () => { }); }); - describe('getAllHeaders()', () => { + describe('getAllHeaders', () => { const event = getEvent(); - const di = new DependencyInjection(CONFIGURATION, event, getContext); - const request = new RequestService(di); + const request = getRequestService(event); it('should return all headers from the event', () => { expect(request.getAllHeaders()).toStrictEqual(event.headers); }); }); - describe('getHeader()', () => { + describe('getHeader', () => { const event = getEvent(); - const di = new DependencyInjection(CONFIGURATION, event, getContext); - const request = new RequestService(di); + const request = getRequestService(event); it('should return the specified header', () => { expect(request.getHeader('Accept')).toEqual(event.headers.Accept); diff --git a/tests/unit/Service/SQS.service.test.js b/tests/unit/services/SQSService.spec.ts similarity index 84% rename from tests/unit/Service/SQS.service.test.js rename to tests/unit/services/SQSService.spec.ts index 8ffd210a..7e6838cd 100644 --- a/tests/unit/Service/SQS.service.test.js +++ b/tests/unit/services/SQSService.spec.ts @@ -1,8 +1,33 @@ -import { DEFINITIONS } from '../../../src/Config/Dependencies'; -import DependencyInjection from '../../../src/DependencyInjection/DependencyInjection.class'; -import { SQS_PUBLISH_FAILURE_MODES } from '../../../src/Service/SQS.service'; +import { + Context, + DependencyInjection, + LambdaWrapperConfig, + LoggerService, + SQSService, + SQS_PUBLISH_FAILURE_MODES, + TimerService, + WithSQSServiceConfig, +} from '@/src'; -const createAsyncMock = (returnValue) => { +const TEST_QUEUE = 'TEST_QUEUE'; + +const config: LambdaWrapperConfig & WithSQSServiceConfig = { + dependencies: { + SQSService, + LoggerService, + TimerService, + }, + sqs: { + queues: { + [TEST_QUEUE]: 'QueueName', + }, + queueConsumers: { + [TEST_QUEUE]: 'ConsumerFunctionName', + }, + }, +}; + +const createAsyncMock = (returnValue: any) => { const mockedValue = returnValue instanceof Error ? Promise.reject(returnValue) : Promise.resolve(returnValue); @@ -10,48 +35,45 @@ const createAsyncMock = (returnValue) => { return jest.fn().mockReturnValue({ promise: () => mockedValue }); }; -const TEST_QUEUE = 'TEST_QUEUE'; - /** * Generates a SQSService * - * @param {*} param0 + * @param param0 * @param isOffline - * @returns {SQSService} */ -const getService = ({ sendMessage = null, invoke = null } = {}, isOffline = false) => { - const di = new DependencyInjection({ - QUEUES: { [TEST_QUEUE]: 'QueueName' }, - QUEUE_CONSUMERS: { TEST_QUEUE }, - }, {}, { +const getService = ( + { + sendMessage = null, + invoke = null, + }: any = {}, + isOffline = false, +): SQSService & { sqs: { sendMessage: jest.Mock }; lambda: { invoke: jest.Mock } } => { + const di = new DependencyInjection(config, {}, { invokedFunctionArn: isOffline ? 'offline' : 'arn:aws:lambda:eu-west-1:0123456789:test', - }); - - const logger = di.get(DEFINITIONS.LOGGER); + } as Context); + const logger = di.get(LoggerService); jest.spyOn(logger, 'error').mockImplementation(); - const service = di.get(DEFINITIONS.SQS); + const service = di.get(SQSService); const sqs = { sendMessage: createAsyncMock(sendMessage), - }; - + } as unknown as AWS.SQS; const lambda = { invoke: createAsyncMock(invoke), - }; - + } as unknown as AWS.Lambda; jest.spyOn(service, 'sqs', 'get').mockReturnValue(sqs); jest.spyOn(service, 'lambda', 'get').mockReturnValue(lambda); - return service; + return service as any; }; -describe('Service/SQS', () => { - let envAccountId; - let envOfflineSqsMode; - let envOfflineSqsHost; - let envOfflineSqsPort; - let envRegion; +describe('unit.services.SQSService', () => { + let envAccountId: string | undefined; + let envOfflineSqsMode: string | undefined; + let envOfflineSqsHost: string | undefined; + let envOfflineSqsPort: string | undefined; + let envRegion: string | undefined; beforeAll(() => { envAccountId = process.env.AWS_ACCOUNT_ID; @@ -73,15 +95,23 @@ describe('Service/SQS', () => { jest.resetAllMocks(); }); + it('should load config from the `sqs` key', () => { + const di = new DependencyInjection(config, {}, {} as Context); + const sqs = di.get(SQSService); + + expect(sqs.queues).toEqual(config.sqs?.queues); + expect(sqs.queueConsumers).toEqual(config.sqs?.queueConsumers); + }); + describe('publish', () => { describe('when container.isOffline === false', () => { - [ + ([ ['sends to SQS', undefined], ['sends to SQS, even in "direct" offline mode', 'direct'], ['sends to SQS, even in "local" offline mode', 'local'], ['sends to SQS, even in "aws" offline mode', 'aws'], ['sends to SQS, even in "invalid" offline mode', 'invalid'], - ].forEach(([description, offlineMode]) => { + ] as const).forEach(([description, offlineMode]) => { it(description, async () => { process.env.LAMBDA_WRAPPER_OFFLINE_SQS_MODE = offlineMode; const service = getService({}, false); @@ -255,7 +285,7 @@ describe('Service/SQS', () => { '', null, 'another-value', - ].forEach((invalidValue) => { + ].forEach((invalidValue: any) => { it(`throws an error with the invalid value: ${invalidValue}`, async () => { const service = getService(); diff --git a/tests/unit/services/TimerService.spec.ts b/tests/unit/services/TimerService.spec.ts new file mode 100644 index 00000000..c4dcf82e --- /dev/null +++ b/tests/unit/services/TimerService.spec.ts @@ -0,0 +1,36 @@ +import { + Context, + DependencyInjection, + LoggerService, + TimerService, +} from '@/src'; + +describe('unit.services.TimerService', () => { + beforeAll(() => { + jest.useFakeTimers(); + }); + + afterAll(() => { + jest.useRealTimers(); + }); + + it('should measure time between start and stop', () => { + const di = new DependencyInjection({ + dependencies: { + TimerService, + LoggerService, + }, + }, {}, {} as Context); + const timer = di.get(TimerService); + const logger = di.get(LoggerService); + + let info = 'logger.info not called!'; + jest.spyOn(logger, 'info').mockImplementation((msg: any) => { info = msg; }); + + timer.start('test'); + jest.advanceTimersByTime(12345); + timer.stop('test'); + + expect(info).toContain('test took 12345 ms to complete'); + }); +}); diff --git a/tests/unit/services/__snapshots__/BaseConfigService.spec.ts.snap b/tests/unit/services/__snapshots__/BaseConfigService.spec.ts.snap new file mode 100644 index 00000000..5f6fec23 --- /dev/null +++ b/tests/unit/services/__snapshots__/BaseConfigService.spec.ts.snap @@ -0,0 +1,35 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`unit.services.BaseConfigService ensureHealthy 400 throws a LambdaTermination 1`] = `"Application is not healthy."`; + +exports[`unit.services.BaseConfigService ensureHealthy 401 throws a LambdaTermination 1`] = `"Application is not healthy."`; + +exports[`unit.services.BaseConfigService ensureHealthy 403 throws a LambdaTermination 1`] = `"Application is not healthy."`; + +exports[`unit.services.BaseConfigService ensureHealthy 404 throws a LambdaTermination 1`] = `"Application is not healthy."`; + +exports[`unit.services.BaseConfigService ensureHealthy 409 throws a LambdaTermination 1`] = `"Application is not healthy."`; + +exports[`unit.services.BaseConfigService ensureHealthy 499 throws a LambdaTermination 1`] = `"Application is not healthy."`; + +exports[`unit.services.BaseConfigService ensureHealthy 500 throws a LambdaTermination 1`] = `"Application is not healthy."`; + +exports[`unit.services.BaseConfigService ensureHealthy 501 throws a LambdaTermination 1`] = `"Application is not healthy."`; + +exports[`unit.services.BaseConfigService ensureHealthy 502 throws a LambdaTermination 1`] = `"Application is not healthy."`; + +exports[`unit.services.BaseConfigService ensureHealthy 503 throws a LambdaTermination 1`] = `"Application is not healthy."`; + +exports[`unit.services.BaseConfigService ensureHealthy 504 throws a LambdaTermination 1`] = `"Application is not healthy."`; + +exports[`unit.services.BaseConfigService ensureHealthy Dante Alighieri throws a LambdaTermination 1`] = `"Application is not healthy."`; + +exports[`unit.services.BaseConfigService get propagates the 404 1`] = `"404"`; + +exports[`unit.services.BaseConfigService get refuses empty configurations 1`] = `"Configuration file is empty"`; + +exports[`unit.services.BaseConfigService get refuses invalid configurations 1`] = `"Invalid configuration file"`; + +exports[`unit.services.BaseConfigService getOrCreate throws any non-404 error 1`] = `"Bad error"`; + +exports[`unit.services.BaseConfigService patch throws any non-404 error 1`] = `"Bad error"`; diff --git a/tests/unit/Service/__snapshots__/HTTP.service.test.js.snap b/tests/unit/services/__snapshots__/HTTPService.spec.ts.snap similarity index 68% rename from tests/unit/Service/__snapshots__/HTTP.service.test.js.snap rename to tests/unit/services/__snapshots__/HTTPService.spec.ts.snap index 0b933d4d..2328d61f 100644 --- a/tests/unit/Service/__snapshots__/HTTP.service.test.js.snap +++ b/tests/unit/services/__snapshots__/HTTPService.spec.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Service/HTTPService request DELETE Request: config 1`] = ` +exports[`unit.services.HTTPService request DELETE request: config 1`] = ` Array [ Array [ Object { @@ -13,7 +13,7 @@ Array [ ] `; -exports[`Service/HTTPService request GET Request: config 1`] = ` +exports[`unit.services.HTTPService request GET request: config 1`] = ` Array [ Array [ Object { @@ -26,7 +26,7 @@ Array [ ] `; -exports[`Service/HTTPService request HEAD Request: config 1`] = ` +exports[`unit.services.HTTPService request HEAD request: config 1`] = ` Array [ Array [ Object { @@ -39,7 +39,7 @@ Array [ ] `; -exports[`Service/HTTPService request PATCH Request: config 1`] = ` +exports[`unit.services.HTTPService request PATCH request: config 1`] = ` Array [ Array [ Object { @@ -52,7 +52,7 @@ Array [ ] `; -exports[`Service/HTTPService request POST Request: config 1`] = ` +exports[`unit.services.HTTPService request POST request: config 1`] = ` Array [ Array [ Object { @@ -65,7 +65,7 @@ Array [ ] `; -exports[`Service/HTTPService request PUT Request: config 1`] = ` +exports[`unit.services.HTTPService request PUT request: config 1`] = ` Array [ Array [ Object { @@ -78,7 +78,7 @@ Array [ ] `; -exports[`Service/HTTPService request adds the test header, DELETE Request: config 1`] = ` +exports[`unit.services.HTTPService request adds the test header, DELETE request: config 1`] = ` Array [ Array [ Object { @@ -93,7 +93,7 @@ Array [ ] `; -exports[`Service/HTTPService request adds the test header, GET Request: config 1`] = ` +exports[`unit.services.HTTPService request adds the test header, GET request: config 1`] = ` Array [ Array [ Object { @@ -108,7 +108,7 @@ Array [ ] `; -exports[`Service/HTTPService request adds the test header, HEAD Request: config 1`] = ` +exports[`unit.services.HTTPService request adds the test header, HEAD request: config 1`] = ` Array [ Array [ Object { @@ -123,7 +123,7 @@ Array [ ] `; -exports[`Service/HTTPService request adds the test header, PATCH Request: config 1`] = ` +exports[`unit.services.HTTPService request adds the test header, PATCH request: config 1`] = ` Array [ Array [ Object { @@ -138,7 +138,7 @@ Array [ ] `; -exports[`Service/HTTPService request adds the test header, POST Request: config 1`] = ` +exports[`unit.services.HTTPService request adds the test header, POST request: config 1`] = ` Array [ Array [ Object { @@ -153,7 +153,7 @@ Array [ ] `; -exports[`Service/HTTPService request adds the test header, PUT Request: config 1`] = ` +exports[`unit.services.HTTPService request adds the test header, PUT request: config 1`] = ` Array [ Array [ Object { @@ -168,7 +168,7 @@ Array [ ] `; -exports[`Service/HTTPService request adds the test header, overriding timeout: config 1`] = ` +exports[`unit.services.HTTPService request adds the test header, overriding timeout: config 1`] = ` Array [ Array [ Object { @@ -181,7 +181,7 @@ Array [ ] `; -exports[`Service/HTTPService request adds the test header, with URL: config 1`] = ` +exports[`unit.services.HTTPService request adds the test header, with URL: config 1`] = ` Array [ Array [ Object { @@ -195,7 +195,7 @@ Array [ ] `; -exports[`Service/HTTPService request adds the test header, with baseURL: config 1`] = ` +exports[`unit.services.HTTPService request adds the test header, with baseURL: config 1`] = ` Array [ Array [ Object { @@ -210,7 +210,7 @@ Array [ ] `; -exports[`Service/HTTPService request adds the test header, with headers: config 1`] = ` +exports[`unit.services.HTTPService request adds the test header, with headers: config 1`] = ` Array [ Array [ Object { @@ -224,7 +224,7 @@ Array [ ] `; -exports[`Service/HTTPService request adds the test header, with undefined headers: config 1`] = ` +exports[`unit.services.HTTPService request adds the test header, with undefined headers: config 1`] = ` Array [ Array [ Object { @@ -237,7 +237,7 @@ Array [ ] `; -exports[`Service/HTTPService request overriding timeout: config 1`] = ` +exports[`unit.services.HTTPService request overriding timeout: config 1`] = ` Array [ Array [ Object { @@ -248,7 +248,7 @@ Array [ ] `; -exports[`Service/HTTPService request with URL: config 1`] = ` +exports[`unit.services.HTTPService request with URL: config 1`] = ` Array [ Array [ Object { @@ -260,7 +260,7 @@ Array [ ] `; -exports[`Service/HTTPService request with baseURL: config 1`] = ` +exports[`unit.services.HTTPService request with baseURL: config 1`] = ` Array [ Array [ Object { @@ -273,7 +273,7 @@ Array [ ] `; -exports[`Service/HTTPService request with headers: config 1`] = ` +exports[`unit.services.HTTPService request with headers: config 1`] = ` Array [ Array [ Object { @@ -286,7 +286,7 @@ Array [ ] `; -exports[`Service/HTTPService request with undefined headers: config 1`] = ` +exports[`unit.services.HTTPService request with undefined headers: config 1`] = ` Array [ Array [ Object { diff --git a/tests/unit/services/__snapshots__/LoggerService.spec.ts.snap b/tests/unit/services/__snapshots__/LoggerService.spec.ts.snap new file mode 100644 index 00000000..5033ec6a --- /dev/null +++ b/tests/unit/services/__snapshots__/LoggerService.spec.ts.snap @@ -0,0 +1,138 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`unit.services.LoggerService error Trims down the axios error: EMPTY 1`] = ` +Object { + "config": Object { + "method": "get", + "url": "http://localhost:9999", + }, + "message": "some-message", + "response": Object { + "data": undefined, + "status": undefined, + }, +} +`; + +exports[`unit.services.LoggerService error Trims down the axios error: HTTP_417 1`] = ` +Object { + "config": Object { + "method": "get", + "url": "http://localhost:9999", + }, + "message": "some-message", + "response": Object { + "data": Object { + "data": 1, + }, + "status": 417, + }, +} +`; + +exports[`unit.services.LoggerService error Trims down the axios error: UNDEFINED 1`] = ` +Object { + "config": Object { + "method": "get", + "url": "http://localhost:9999", + }, + "message": "some-message", +} +`; + +exports[`unit.services.LoggerService info Trims down the axios error: EMPTY 1`] = ` +Object { + "config": Object { + "method": "get", + "url": "http://localhost:9999", + }, + "message": "some-message", + "response": Object { + "data": undefined, + "status": undefined, + }, +} +`; + +exports[`unit.services.LoggerService info Trims down the axios error: HTTP_417 1`] = ` +Object { + "config": Object { + "method": "get", + "url": "http://localhost:9999", + }, + "message": "some-message", + "response": Object { + "data": Object { + "data": 1, + }, + "status": 417, + }, +} +`; + +exports[`unit.services.LoggerService info Trims down the axios error: UNDEFINED 1`] = ` +Object { + "config": Object { + "method": "get", + "url": "http://localhost:9999", + }, + "message": "some-message", +} +`; + +exports[`unit.services.LoggerService object Logs a '"a string"' with level: 'error' 1`] = `"My action: '\\"a string\\"'"`; + +exports[`unit.services.LoggerService object Logs a '"a string"' with level: 'info' 1`] = `"My action: '\\"a string\\"'"`; + +exports[`unit.services.LoggerService object Logs a '"a string"' with level: 'warning' 1`] = `"My action: '\\"a string\\"'"`; + +exports[`unit.services.LoggerService object Logs a '{"a":{"b":null},"c":"a string"}' with level: 'error' 1`] = ` +"My action: '{ + \\"a\\": { + \\"b\\": null + }, + \\"c\\": \\"a string\\" +}'" +`; + +exports[`unit.services.LoggerService object Logs a '{"a":{"b":null},"c":"a string"}' with level: 'info' 1`] = ` +"My action: '{ + \\"a\\": { + \\"b\\": null + }, + \\"c\\": \\"a string\\" +}'" +`; + +exports[`unit.services.LoggerService object Logs a '{"a":{"b":null},"c":"a string"}' with level: 'warning' 1`] = ` +"My action: '{ + \\"a\\": { + \\"b\\": null + }, + \\"c\\": \\"a string\\" +}'" +`; + +exports[`unit.services.LoggerService object Logs a '{"a":1}' with level: 'error' 1`] = ` +"My action: '{ + \\"a\\": 1 +}'" +`; + +exports[`unit.services.LoggerService object Logs a '{"a":1}' with level: 'info' 1`] = ` +"My action: '{ + \\"a\\": 1 +}'" +`; + +exports[`unit.services.LoggerService object Logs a '{"a":1}' with level: 'warning' 1`] = ` +"My action: '{ + \\"a\\": 1 +}'" +`; + +exports[`unit.services.LoggerService object Logs a 'null' with level: 'error' 1`] = `"My action: 'null'"`; + +exports[`unit.services.LoggerService object Logs a 'null' with level: 'info' 1`] = `"My action: 'null'"`; + +exports[`unit.services.LoggerService object Logs a 'null' with level: 'warning' 1`] = `"My action: 'null'"`; diff --git a/tests/unit/Service/__snapshots__/Request.service.test.js.snap b/tests/unit/services/__snapshots__/RequestService.spec.ts.snap similarity index 58% rename from tests/unit/Service/__snapshots__/Request.service.test.js.snap rename to tests/unit/services/__snapshots__/RequestService.spec.ts.snap index 868fb53d..5eaa46b7 100644 --- a/tests/unit/Service/__snapshots__/Request.service.test.js.snap +++ b/tests/unit/services/__snapshots__/RequestService.spec.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Service/RequestService HTTP DELETE .validateAgainstConstraints should return a response containing validation errors if the data provided is incorrect 1`] = ` +exports[`unit.services.RequestService HTTP DELETE validateAgainstConstraints should return a response containing validation errors if the data provided is incorrect 1`] = ` ResponseModel { "body": Object { "data": Object {}, @@ -15,7 +15,7 @@ ResponseModel { } `; -exports[`Service/RequestService HTTP GET .validateAgainstConstraints should return a response containing validation errors if the data provided is incorrect 1`] = ` +exports[`unit.services.RequestService HTTP GET validateAgainstConstraints should return a response containing validation errors if the data provided is incorrect 1`] = ` ResponseModel { "body": Object { "data": Object {}, @@ -30,7 +30,7 @@ ResponseModel { } `; -exports[`Service/RequestService HTTP HEAD .validateAgainstConstraints should return a response containing validation errors if the data provided is incorrect 1`] = ` +exports[`unit.services.RequestService HTTP HEAD validateAgainstConstraints should return a response containing validation errors if the data provided is incorrect 1`] = ` ResponseModel { "body": Object { "data": Object {}, @@ -45,7 +45,7 @@ ResponseModel { } `; -exports[`Service/RequestService HTTP OPTIONS .validateAgainstConstraints should return a response containing validation errors if the data provided is incorrect 1`] = ` +exports[`unit.services.RequestService HTTP OPTIONS validateAgainstConstraints should return a response containing validation errors if the data provided is incorrect 1`] = ` ResponseModel { "body": Object { "data": Object {}, @@ -60,7 +60,7 @@ ResponseModel { } `; -exports[`Service/RequestService HTTP PATCH .validateAgainstConstraints should return a response containing validation errors if the data provided is incorrect 1`] = ` +exports[`unit.services.RequestService HTTP PATCH validateAgainstConstraints should return a response containing validation errors if the data provided is incorrect 1`] = ` ResponseModel { "body": Object { "data": Object {}, @@ -75,7 +75,7 @@ ResponseModel { } `; -exports[`Service/RequestService HTTP POST .validateAgainstConstraints should return a response containing validation errors if the data provided is incorrect 1`] = ` +exports[`unit.services.RequestService HTTP POST validateAgainstConstraints should return a response containing validation errors if the data provided is incorrect 1`] = ` ResponseModel { "body": Object { "data": Object {}, @@ -90,7 +90,7 @@ ResponseModel { } `; -exports[`Service/RequestService HTTP PUT .validateAgainstConstraints should return a response containing validation errors if the data provided is incorrect 1`] = ` +exports[`unit.services.RequestService HTTP PUT validateAgainstConstraints should return a response containing validation errors if the data provided is incorrect 1`] = ` ResponseModel { "body": Object { "data": Object {}, diff --git a/tests/unit/services/__snapshots__/SQSService.spec.ts.snap b/tests/unit/services/__snapshots__/SQSService.spec.ts.snap new file mode 100644 index 00000000..be64da51 --- /dev/null +++ b/tests/unit/services/__snapshots__/SQSService.spec.ts.snap @@ -0,0 +1,7 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`unit.services.SQSService publish failure modes throws an error with the invalid value: 1`] = `"Invalid value for 'failureMode': "`; + +exports[`unit.services.SQSService publish failure modes throws an error with the invalid value: another-value 1`] = `"Invalid value for 'failureMode': another-value"`; + +exports[`unit.services.SQSService publish failure modes throws an error with the invalid value: null 1`] = `"Invalid value for 'failureMode': null"`; diff --git a/tests/unit/Wrapper/LambdaTermination.test.js b/tests/unit/utils/LambdaTermination.spec.ts similarity index 55% rename from tests/unit/Wrapper/LambdaTermination.test.js rename to tests/unit/utils/LambdaTermination.spec.ts index dee0d9c6..607ba577 100644 --- a/tests/unit/Wrapper/LambdaTermination.test.js +++ b/tests/unit/utils/LambdaTermination.spec.ts @@ -1,7 +1,7 @@ -import LambdaTermination from '../../../src/Wrapper/LambdaTermination'; +import { LambdaTermination } from '@/src'; -describe('Wrapper/LambdaTermination', () => { - describe('Stores the custom fields', () => { +describe('unit.utils.LambdaTermination', () => { + describe('custom fields', () => { const properties = { internal: 'INTERNAL', code: 401, @@ -11,24 +11,24 @@ describe('Wrapper/LambdaTermination', () => { const lt = new LambdaTermination(properties.internal, properties.code, properties.body); Object.entries(properties).forEach(([key, value]) => { - it(`Exposes '${key}'`, () => { - expect(lt[key]).toEqual(value); + it(`should set and expose '${key}'`, () => { + expect(lt[key as keyof LambdaTermination]).toEqual(value); }); }); }); - it('Generates an error', () => { + it('should create an instance of error', () => { const lt = new LambdaTermination('internal'); - expect(lt instanceof Error).toEqual(true); + expect(lt).toBeInstanceOf(Error); }); - describe('Passes a prop to the superclass that', () => { - it('Becomes Error.message', () => { + describe('error message', () => { + it('should use `internal` param when a string', () => { const lt = new LambdaTermination('abc'); expect(lt.message).toEqual('abc'); }); - it('Is stringified when an object', () => { + it('should use stringified `internal` param when an object', () => { const details = { a: 1 }; const stringified = JSON.stringify(details); const lt = new LambdaTermination(details); diff --git a/tsconfig-base.json b/tsconfig-base.json new file mode 100644 index 00000000..70011e4e --- /dev/null +++ b/tsconfig-base.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + "strict": true, + "target": "es2020", + "module": "commonjs", + "esModuleInterop": true, + "lib": ["es2020"], + "sourceMap": true, + "declaration": true, + "declarationMap": true, + "outDir": "dist", + "baseUrl": ".", + "paths": { + "@/*": ["./*"], + }, + /* Additional Checks */ + "noUnusedLocals": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + }, + "ts-node": { + "files": true, + "require": [ + "tsconfig-paths/register", + ], + }, +} diff --git a/tsconfig-build.json b/tsconfig-build.json new file mode 100644 index 00000000..3b71185b --- /dev/null +++ b/tsconfig-build.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig-base.json", + "compilerOptions": { + "rootDir": "src", + "types": ["node"], + }, + "include": [ + "src/**/*.ts", + "types/*.d.ts", + ], +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..1d67ec3f --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "./tsconfig-base.json", + "compilerOptions": { + "rootDir": ".", + "resolveJsonModule": true, + }, + "include": [ + "src/**/*.ts", + "tests/**/*.ts", + "types/*.d.ts", + ], +} diff --git a/types/alai.d.ts b/types/alai.d.ts new file mode 100644 index 00000000..ab06dcbe --- /dev/null +++ b/types/alai.d.ts @@ -0,0 +1,3 @@ +declare module 'alai' { + export function parse(ctx: import('aws-lambda').Context): string; +} diff --git a/yarn.lock b/yarn.lock index e8b95ca8..75682aad 100644 --- a/yarn.lock +++ b/yarn.lock @@ -649,22 +649,6 @@ "@aws-sdk/util-buffer-from" "3.55.0" tslib "^2.3.1" -"@babel/cli@^7.18.10": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.18.10.tgz#4211adfc45ffa7d4f3cee6b60bb92e9fe68fe56a" - integrity sha512-dLvWH+ZDFAkd2jPBSghrsFBuXrREvFwjpDycXbmUoeochqKYe4zNSLEJYErpLg8dvxvZYe79/MkN461XCwpnGw== - dependencies: - "@jridgewell/trace-mapping" "^0.3.8" - commander "^4.0.1" - convert-source-map "^1.1.0" - fs-readdir-recursive "^1.1.0" - glob "^7.2.0" - make-dir "^2.1.0" - slash "^2.0.0" - optionalDependencies: - "@nicolo-ribaudo/chokidar-2" "2.1.8-no-fsevents.3" - chokidar "^3.4.0" - "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" @@ -672,12 +656,12 @@ dependencies: "@babel/highlight" "^7.18.6" -"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.18.8": +"@babel/compat-data@^7.18.8": version "7.18.8" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.18.8.tgz#2483f565faca607b8535590e84e7de323f27764d" integrity sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ== -"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.18.10", "@babel/core@^7.7.5": +"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.7.5": version "7.18.10" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.10.tgz#39ad504991d77f1f3da91be0b8b949a5bc466fb8" integrity sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw== @@ -698,15 +682,6 @@ json5 "^2.2.1" semver "^6.3.0" -"@babel/eslint-parser@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.18.9.tgz#255a63796819a97b7578751bb08ab9f2a375a031" - integrity sha512-KzSGpMBggz4fKbRbWLNyPVTuQr6cmCcBhOyXTw/fieOVaw5oYAwcAj4a7UKcDYCPxQq+CG1NCDZH9e2JTXquiQ== - dependencies: - eslint-scope "^5.1.1" - eslint-visitor-keys "^2.1.0" - semver "^6.3.0" - "@babel/generator@^7.18.10", "@babel/generator@^7.7.2": version "7.18.12" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.12.tgz#fa58daa303757bd6f5e4bbca91b342040463d9f4" @@ -716,22 +691,7 @@ "@jridgewell/gen-mapping" "^0.3.2" jsesc "^2.5.1" -"@babel/helper-annotate-as-pure@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" - integrity sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-builder-binary-assignment-operator-visitor@^7.18.6": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz#acd4edfd7a566d1d51ea975dff38fd52906981bb" - integrity sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw== - dependencies: - "@babel/helper-explode-assignable-expression" "^7.18.6" - "@babel/types" "^7.18.9" - -"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9": +"@babel/helper-compilation-targets@^7.18.9": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz#69e64f57b524cde3e5ff6cc5a9f4a387ee5563bf" integrity sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg== @@ -741,51 +701,11 @@ browserslist "^4.20.2" semver "^6.3.0" -"@babel/helper-create-class-features-plugin@^7.18.6": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.9.tgz#d802ee16a64a9e824fcbf0a2ffc92f19d58550ce" - integrity sha512-WvypNAYaVh23QcjpMR24CwZY2Nz6hqdOcFdPbNpV56hL5H6KiFheO7Xm1aPdlLQ7d5emYZX7VZwPp9x3z+2opw== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.18.9" - "@babel/helper-member-expression-to-functions" "^7.18.9" - "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-replace-supers" "^7.18.9" - "@babel/helper-split-export-declaration" "^7.18.6" - -"@babel/helper-create-regexp-features-plugin@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.18.6.tgz#3e35f4e04acbbf25f1b3534a657610a000543d3c" - integrity sha512-7LcpH1wnQLGrI+4v+nPp+zUvIkF9x0ddv1Hkdue10tg3gmRnLy97DXh4STiOf1qeIInyD69Qv5kKSZzKD8B/7A== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - regexpu-core "^5.1.0" - -"@babel/helper-define-polyfill-provider@^0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.2.tgz#bd10d0aca18e8ce012755395b05a79f45eca5073" - integrity sha512-r9QJJ+uDWrd+94BSPcP6/de67ygLtvVy6cK4luE6MOuDsZIdoaPBnfSpbO/+LTifjPckbKXRuI9BB/Z2/y3iTg== - dependencies: - "@babel/helper-compilation-targets" "^7.17.7" - "@babel/helper-plugin-utils" "^7.16.7" - debug "^4.1.1" - lodash.debounce "^4.0.8" - resolve "^1.14.2" - semver "^6.1.2" - "@babel/helper-environment-visitor@^7.18.9": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== -"@babel/helper-explode-assignable-expression@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz#41f8228ef0a6f1a036b8dfdfec7ce94f9a6bc096" - integrity sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg== - dependencies: - "@babel/types" "^7.18.6" - "@babel/helper-function-name@^7.18.9": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz#940e6084a55dee867d33b4e487da2676365e86b0" @@ -801,13 +721,6 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-member-expression-to-functions@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz#1531661e8375af843ad37ac692c132841e2fd815" - integrity sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg== - dependencies: - "@babel/types" "^7.18.9" - "@babel/helper-module-imports@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" @@ -815,7 +728,7 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.18.9": +"@babel/helper-module-transforms@^7.18.9": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz#5a1079c005135ed627442df31a42887e80fcb712" integrity sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g== @@ -829,39 +742,11 @@ "@babel/traverse" "^7.18.9" "@babel/types" "^7.18.9" -"@babel/helper-optimise-call-expression@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz#9369aa943ee7da47edab2cb4e838acf09d290ffe" - integrity sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.8.0": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz#4b8aea3b069d8cb8a72cdfe28ddf5ceca695ef2f" integrity sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w== -"@babel/helper-remap-async-to-generator@^7.18.6", "@babel/helper-remap-async-to-generator@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz#997458a0e3357080e54e1d79ec347f8a8cd28519" - integrity sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-wrap-function" "^7.18.9" - "@babel/types" "^7.18.9" - -"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.18.9.tgz#1092e002feca980fbbb0bd4d51b74a65c6a500e6" - integrity sha512-dNsWibVI4lNT6HiuOIBr1oyxo40HvIVmbwPUm3XZ7wMh4k2WxrxTqZwSqw/eEmXDS9np0ey5M2bz9tBmO9c+YQ== - dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-member-expression-to-functions" "^7.18.9" - "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/traverse" "^7.18.9" - "@babel/types" "^7.18.9" - "@babel/helper-simple-access@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz#d6d8f51f4ac2978068df934b569f08f29788c7ea" @@ -869,13 +754,6 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-skip-transparent-expression-wrappers@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz#778d87b3a758d90b471e7b9918f34a9a02eb5818" - integrity sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw== - dependencies: - "@babel/types" "^7.18.9" - "@babel/helper-split-export-declaration@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" @@ -888,7 +766,7 @@ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz#181f22d28ebe1b3857fa575f5c290b1aaf659b56" integrity sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw== -"@babel/helper-validator-identifier@^7.15.7", "@babel/helper-validator-identifier@^7.18.6": +"@babel/helper-validator-identifier@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076" integrity sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g== @@ -898,16 +776,6 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== -"@babel/helper-wrap-function@^7.18.9": - version "7.18.11" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.18.11.tgz#bff23ace436e3f6aefb61f85ffae2291c80ed1fb" - integrity sha512-oBUlbv+rjZLh2Ks9SKi4aL7eKaAXBWleHzU89mP0G6BMUlRxSckk9tSIkgDGydhgFxHuGSlBQZfnaD47oBEB7w== - dependencies: - "@babel/helper-function-name" "^7.18.9" - "@babel/template" "^7.18.10" - "@babel/traverse" "^7.18.11" - "@babel/types" "^7.18.10" - "@babel/helpers@^7.18.9": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.18.9.tgz#4bef3b893f253a1eced04516824ede94dcfe7ff9" @@ -926,168 +794,11 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/node@^7.18.10": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/node/-/node-7.18.10.tgz#ab2be57785346b5bf0721c3d17572402419d9d8a" - integrity sha512-VbqzK6QXfQVi4Bpk6J7XqHXKFNbG2j3rdIdx68+/14GDU7jXDOSyUU/cwqCM1fDwCdxp37pNV/ToSCXsNChcyA== - dependencies: - "@babel/register" "^7.18.9" - commander "^4.0.1" - core-js "^3.22.1" - node-environment-flags "^1.0.5" - regenerator-runtime "^0.13.4" - v8flags "^3.1.1" - "@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.18.10", "@babel/parser@^7.18.11": version "7.18.11" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.11.tgz#68bb07ab3d380affa9a3f96728df07969645d2d9" integrity sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ== -"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz#da5b8f9a580acdfbe53494dba45ea389fb09a4d2" - integrity sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.18.9.tgz#a11af19aa373d68d561f08e0a57242350ed0ec50" - integrity sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg== - dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" - "@babel/plugin-proposal-optional-chaining" "^7.18.9" - -"@babel/plugin-proposal-async-generator-functions@^7.18.10": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.18.10.tgz#85ea478c98b0095c3e4102bff3b67d306ed24952" - integrity sha512-1mFuY2TOsR1hxbjCo4QL+qlIjV07p4H4EUYw2J/WCqsvFV6V9X9z9YhXbWndc/4fw+hYGlDT7egYxliMp5O6Ew== - dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-plugin-utils" "^7.18.9" - "@babel/helper-remap-async-to-generator" "^7.18.9" - "@babel/plugin-syntax-async-generators" "^7.8.4" - -"@babel/plugin-proposal-class-properties@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz#b110f59741895f7ec21a6fff696ec46265c446a3" - integrity sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-proposal-class-static-block@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.6.tgz#8aa81d403ab72d3962fc06c26e222dacfc9b9020" - integrity sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-class-static-block" "^7.14.5" - -"@babel/plugin-proposal-dynamic-import@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz#72bcf8d408799f547d759298c3c27c7e7faa4d94" - integrity sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-dynamic-import" "^7.8.3" - -"@babel/plugin-proposal-export-namespace-from@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz#5f7313ab348cdb19d590145f9247540e94761203" - integrity sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA== - dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - -"@babel/plugin-proposal-json-strings@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz#7e8788c1811c393aff762817e7dbf1ebd0c05f0b" - integrity sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-json-strings" "^7.8.3" - -"@babel/plugin-proposal-logical-assignment-operators@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.9.tgz#8148cbb350483bf6220af06fa6db3690e14b2e23" - integrity sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q== - dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" - -"@babel/plugin-proposal-nullish-coalescing-operator@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz#fdd940a99a740e577d6c753ab6fbb43fdb9467e1" - integrity sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - -"@babel/plugin-proposal-numeric-separator@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz#899b14fbafe87f053d2c5ff05b36029c62e13c75" - integrity sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" - -"@babel/plugin-proposal-object-rest-spread@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.18.9.tgz#f9434f6beb2c8cae9dfcf97d2a5941bbbf9ad4e7" - integrity sha512-kDDHQ5rflIeY5xl69CEqGEZ0KY369ehsCIEbTGb4siHG5BE9sga/T0r0OUwyZNLMmZE79E1kbsqAjwFCW4ds6Q== - dependencies: - "@babel/compat-data" "^7.18.8" - "@babel/helper-compilation-targets" "^7.18.9" - "@babel/helper-plugin-utils" "^7.18.9" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.18.8" - -"@babel/plugin-proposal-optional-catch-binding@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz#f9400d0e6a3ea93ba9ef70b09e72dd6da638a2cb" - integrity sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - -"@babel/plugin-proposal-optional-chaining@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz#e8e8fe0723f2563960e4bf5e9690933691915993" - integrity sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w== - dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - -"@babel/plugin-proposal-private-methods@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz#5209de7d213457548a98436fa2882f52f4be6bea" - integrity sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-proposal-private-property-in-object@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.18.6.tgz#a64137b232f0aca3733a67eb1a144c192389c503" - integrity sha512-9Rysx7FOctvT5ouj5JODjAFAkgGoudQuLPamZb0v1TGLpapdNaftzifU8NTWQm0IRjqoYypdrSmyWgkocDQ8Dw== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-create-class-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-private-property-in-object" "^7.14.5" - -"@babel/plugin-proposal-unicode-property-regex@^7.18.6", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz#af613d2cd5e643643b65cded64207b15c85cb78e" - integrity sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" @@ -1102,48 +813,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-class-properties@^7.12.13", "@babel/plugin-syntax-class-properties@^7.8.3": +"@babel/plugin-syntax-class-properties@^7.8.3": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== dependencies: "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-syntax-class-static-block@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" - integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-dynamic-import@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" - integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-export-namespace-from@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" - integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-syntax-flow@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.18.6.tgz#774d825256f2379d06139be0c723c4dd444f3ca1" - integrity sha512-LUbR+KNTBWCUAqRG9ex5Gnzu2IOkt8jRJbHHXFT9q+L9zm7M/QQbEqXyw1n1pohYvOyWC8CjeyjrSaIwiYjK7A== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-syntax-import-assertions@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.18.6.tgz#cd6190500a4fa2fe31990a963ffab4b63e4505e4" - integrity sha512-/DU3RXad9+bZwrgWJQKbr39gYbJpLJHezqEzRzi/BHRlJ9zsQb4CK2CA/5apllXNomwA1qHwzvHl+AdEmC5krQ== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-import-meta@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" @@ -1158,14 +834,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-jsx@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz#a8feef63b010150abd97f1649ec296e849943ca0" - integrity sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-syntax-logical-assignment-operators@^7.10.4", "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": +"@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== @@ -1179,7 +848,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-numeric-separator@^7.10.4", "@babel/plugin-syntax-numeric-separator@^7.8.3": +"@babel/plugin-syntax-numeric-separator@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== @@ -1202,412 +871,24 @@ "@babel/plugin-syntax-optional-chaining@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" - integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-private-property-in-object@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" - integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-top-level-await@^7.14.5", "@babel/plugin-syntax-top-level-await@^7.8.3": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" - integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-typescript@^7.7.2": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz#1c09cd25795c7c2b8a4ba9ae49394576d4133285" - integrity sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-arrow-functions@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz#19063fcf8771ec7b31d742339dac62433d0611fe" - integrity sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-async-to-generator@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz#ccda3d1ab9d5ced5265fdb13f1882d5476c71615" - integrity sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag== - dependencies: - "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/helper-remap-async-to-generator" "^7.18.6" - -"@babel/plugin-transform-block-scoped-functions@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz#9187bf4ba302635b9d70d986ad70f038726216a8" - integrity sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-block-scoping@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.18.9.tgz#f9b7e018ac3f373c81452d6ada8bd5a18928926d" - integrity sha512-5sDIJRV1KtQVEbt/EIBwGy4T01uYIo4KRB3VUqzkhrAIOGx7AoctL9+Ux88btY0zXdDyPJ9mW+bg+v+XEkGmtw== - dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - -"@babel/plugin-transform-classes@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.18.9.tgz#90818efc5b9746879b869d5ce83eb2aa48bbc3da" - integrity sha512-EkRQxsxoytpTlKJmSPYrsOMjCILacAjtSVkd4gChEe2kXjFCun3yohhW5I7plXJhCemM0gKsaGMcO8tinvCA5g== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.18.9" - "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.9" - "@babel/helper-replace-supers" "^7.18.9" - "@babel/helper-split-export-declaration" "^7.18.6" - globals "^11.1.0" - -"@babel/plugin-transform-computed-properties@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz#2357a8224d402dad623caf6259b611e56aec746e" - integrity sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw== - dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - -"@babel/plugin-transform-destructuring@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.9.tgz#68906549c021cb231bee1db21d3b5b095f8ee292" - integrity sha512-p5VCYNddPLkZTq4XymQIaIfZNJwT9YsjkPOhkVEqt6QIpQFZVM9IltqqYpOEkJoN1DPznmxUDyZ5CTZs/ZCuHA== - dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - -"@babel/plugin-transform-dotall-regex@^7.18.6", "@babel/plugin-transform-dotall-regex@^7.4.4": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz#b286b3e7aae6c7b861e45bed0a2fafd6b1a4fef8" - integrity sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-duplicate-keys@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz#687f15ee3cdad6d85191eb2a372c4528eaa0ae0e" - integrity sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw== - dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - -"@babel/plugin-transform-exponentiation-operator@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz#421c705f4521888c65e91fdd1af951bfefd4dacd" - integrity sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw== - dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-flow-strip-types@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.18.9.tgz#5b4cc521426263b5ce08893a2db41097ceba35bf" - integrity sha512-+G6rp2zRuOAInY5wcggsx4+QVao1qPM0osC9fTUVlAV3zOrzTCnrMAFVnR6+a3T8wz1wFIH7KhYMcMB3u1n80A== - dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - "@babel/plugin-syntax-flow" "^7.18.6" - -"@babel/plugin-transform-for-of@^7.18.8": - version "7.18.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz#6ef8a50b244eb6a0bdbad0c7c61877e4e30097c1" - integrity sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-function-name@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz#cc354f8234e62968946c61a46d6365440fc764e0" - integrity sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ== - dependencies: - "@babel/helper-compilation-targets" "^7.18.9" - "@babel/helper-function-name" "^7.18.9" - "@babel/helper-plugin-utils" "^7.18.9" - -"@babel/plugin-transform-literals@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz#72796fdbef80e56fba3c6a699d54f0de557444bc" - integrity sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg== - dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - -"@babel/plugin-transform-member-expression-literals@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz#ac9fdc1a118620ac49b7e7a5d2dc177a1bfee88e" - integrity sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-modules-amd@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.18.6.tgz#8c91f8c5115d2202f277549848874027d7172d21" - integrity sha512-Pra5aXsmTsOnjM3IajS8rTaLCy++nGM4v3YR4esk5PCsyg9z8NA5oQLwxzMUtDBd8F+UmVza3VxoAaWCbzH1rg== - dependencies: - "@babel/helper-module-transforms" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - babel-plugin-dynamic-import-node "^2.3.3" - -"@babel/plugin-transform-modules-commonjs@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.6.tgz#afd243afba166cca69892e24a8fd8c9f2ca87883" - integrity sha512-Qfv2ZOWikpvmedXQJDSbxNqy7Xr/j2Y8/KfijM0iJyKkBTmWuvCA1yeH1yDM7NJhBW/2aXxeucLj6i80/LAJ/Q== - dependencies: - "@babel/helper-module-transforms" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/helper-simple-access" "^7.18.6" - babel-plugin-dynamic-import-node "^2.3.3" - -"@babel/plugin-transform-modules-systemjs@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.18.9.tgz#545df284a7ac6a05125e3e405e536c5853099a06" - integrity sha512-zY/VSIbbqtoRoJKo2cDTewL364jSlZGvn0LKOf9ntbfxOvjfmyrdtEEOAdswOswhZEb8UH3jDkCKHd1sPgsS0A== - dependencies: - "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-module-transforms" "^7.18.9" - "@babel/helper-plugin-utils" "^7.18.9" - "@babel/helper-validator-identifier" "^7.18.6" - babel-plugin-dynamic-import-node "^2.3.3" - -"@babel/plugin-transform-modules-umd@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz#81d3832d6034b75b54e62821ba58f28ed0aab4b9" - integrity sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ== - dependencies: - "@babel/helper-module-transforms" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-named-capturing-groups-regex@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.18.6.tgz#c89bfbc7cc6805d692f3a49bc5fc1b630007246d" - integrity sha512-UmEOGF8XgaIqD74bC8g7iV3RYj8lMf0Bw7NJzvnS9qQhM4mg+1WHKotUIdjxgD2RGrgFLZZPCFPFj3P/kVDYhg== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-new-target@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz#d128f376ae200477f37c4ddfcc722a8a1b3246a8" - integrity sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-object-super@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz#fb3c6ccdd15939b6ff7939944b51971ddc35912c" - integrity sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/helper-replace-supers" "^7.18.6" - -"@babel/plugin-transform-parameters@^7.18.8": - version "7.18.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.18.8.tgz#ee9f1a0ce6d78af58d0956a9378ea3427cccb48a" - integrity sha512-ivfbE3X2Ss+Fj8nnXvKJS6sjRG4gzwPMsP+taZC+ZzEGjAYlvENixmt1sZ5Ca6tWls+BlKSGKPJ6OOXvXCbkFg== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-property-literals@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz#e22498903a483448e94e032e9bbb9c5ccbfc93a3" - integrity sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-react-jsx@^7.18.10": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.18.10.tgz#ea47b2c4197102c196cbd10db9b3bb20daa820f1" - integrity sha512-gCy7Iikrpu3IZjYZolFE4M1Sm+nrh1/6za2Ewj77Z+XirT4TsbJcvOFOyF+fRPwU6AKKK136CZxx6L8AbSFG6A== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.9" - "@babel/plugin-syntax-jsx" "^7.18.6" - "@babel/types" "^7.18.10" - -"@babel/plugin-transform-regenerator@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz#585c66cb84d4b4bf72519a34cfce761b8676ca73" - integrity sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - regenerator-transform "^0.15.0" - -"@babel/plugin-transform-reserved-words@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz#b1abd8ebf8edaa5f7fe6bbb8d2133d23b6a6f76a" - integrity sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-shorthand-properties@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz#6d6df7983d67b195289be24909e3f12a8f664dc9" - integrity sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-spread@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.18.9.tgz#6ea7a6297740f381c540ac56caf75b05b74fb664" - integrity sha512-39Q814wyoOPtIB/qGopNIL9xDChOE1pNU0ZY5dO0owhiVt/5kFm4li+/bBtwc7QotG0u5EPzqhZdjMtmqBqyQA== - dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" - -"@babel/plugin-transform-sticky-regex@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz#c6706eb2b1524028e317720339583ad0f444adcc" - integrity sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-template-literals@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz#04ec6f10acdaa81846689d63fae117dd9c243a5e" - integrity sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA== - dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - -"@babel/plugin-transform-typeof-symbol@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz#c8cea68263e45addcd6afc9091429f80925762c0" - integrity sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw== - dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - -"@babel/plugin-transform-unicode-escapes@^7.18.10": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz#1ecfb0eda83d09bbcb77c09970c2dd55832aa246" - integrity sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ== - dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - -"@babel/plugin-transform-unicode-regex@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz#194317225d8c201bbae103364ffe9e2cea36cdca" - integrity sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/preset-env@^7.18.10": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.18.10.tgz#83b8dfe70d7eea1aae5a10635ab0a5fe60dfc0f4" - integrity sha512-wVxs1yjFdW3Z/XkNfXKoblxoHgbtUF7/l3PvvP4m02Qz9TZ6uZGxRVYjSQeR87oQmHco9zWitW5J82DJ7sCjvA== - dependencies: - "@babel/compat-data" "^7.18.8" - "@babel/helper-compilation-targets" "^7.18.9" - "@babel/helper-plugin-utils" "^7.18.9" - "@babel/helper-validator-option" "^7.18.6" - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.18.6" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.18.9" - "@babel/plugin-proposal-async-generator-functions" "^7.18.10" - "@babel/plugin-proposal-class-properties" "^7.18.6" - "@babel/plugin-proposal-class-static-block" "^7.18.6" - "@babel/plugin-proposal-dynamic-import" "^7.18.6" - "@babel/plugin-proposal-export-namespace-from" "^7.18.9" - "@babel/plugin-proposal-json-strings" "^7.18.6" - "@babel/plugin-proposal-logical-assignment-operators" "^7.18.9" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.18.6" - "@babel/plugin-proposal-numeric-separator" "^7.18.6" - "@babel/plugin-proposal-object-rest-spread" "^7.18.9" - "@babel/plugin-proposal-optional-catch-binding" "^7.18.6" - "@babel/plugin-proposal-optional-chaining" "^7.18.9" - "@babel/plugin-proposal-private-methods" "^7.18.6" - "@babel/plugin-proposal-private-property-in-object" "^7.18.6" - "@babel/plugin-proposal-unicode-property-regex" "^7.18.6" - "@babel/plugin-syntax-async-generators" "^7.8.4" - "@babel/plugin-syntax-class-properties" "^7.12.13" - "@babel/plugin-syntax-class-static-block" "^7.14.5" - "@babel/plugin-syntax-dynamic-import" "^7.8.3" - "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - "@babel/plugin-syntax-import-assertions" "^7.18.6" - "@babel/plugin-syntax-json-strings" "^7.8.3" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - "@babel/plugin-syntax-private-property-in-object" "^7.14.5" - "@babel/plugin-syntax-top-level-await" "^7.14.5" - "@babel/plugin-transform-arrow-functions" "^7.18.6" - "@babel/plugin-transform-async-to-generator" "^7.18.6" - "@babel/plugin-transform-block-scoped-functions" "^7.18.6" - "@babel/plugin-transform-block-scoping" "^7.18.9" - "@babel/plugin-transform-classes" "^7.18.9" - "@babel/plugin-transform-computed-properties" "^7.18.9" - "@babel/plugin-transform-destructuring" "^7.18.9" - "@babel/plugin-transform-dotall-regex" "^7.18.6" - "@babel/plugin-transform-duplicate-keys" "^7.18.9" - "@babel/plugin-transform-exponentiation-operator" "^7.18.6" - "@babel/plugin-transform-for-of" "^7.18.8" - "@babel/plugin-transform-function-name" "^7.18.9" - "@babel/plugin-transform-literals" "^7.18.9" - "@babel/plugin-transform-member-expression-literals" "^7.18.6" - "@babel/plugin-transform-modules-amd" "^7.18.6" - "@babel/plugin-transform-modules-commonjs" "^7.18.6" - "@babel/plugin-transform-modules-systemjs" "^7.18.9" - "@babel/plugin-transform-modules-umd" "^7.18.6" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.18.6" - "@babel/plugin-transform-new-target" "^7.18.6" - "@babel/plugin-transform-object-super" "^7.18.6" - "@babel/plugin-transform-parameters" "^7.18.8" - "@babel/plugin-transform-property-literals" "^7.18.6" - "@babel/plugin-transform-regenerator" "^7.18.6" - "@babel/plugin-transform-reserved-words" "^7.18.6" - "@babel/plugin-transform-shorthand-properties" "^7.18.6" - "@babel/plugin-transform-spread" "^7.18.9" - "@babel/plugin-transform-sticky-regex" "^7.18.6" - "@babel/plugin-transform-template-literals" "^7.18.9" - "@babel/plugin-transform-typeof-symbol" "^7.18.9" - "@babel/plugin-transform-unicode-escapes" "^7.18.10" - "@babel/plugin-transform-unicode-regex" "^7.18.6" - "@babel/preset-modules" "^0.1.5" - "@babel/types" "^7.18.10" - babel-plugin-polyfill-corejs2 "^0.3.2" - babel-plugin-polyfill-corejs3 "^0.5.3" - babel-plugin-polyfill-regenerator "^0.4.0" - core-js-compat "^3.22.1" - semver "^6.3.0" - -"@babel/preset-modules@^0.1.5": - version "0.1.5" - resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.5.tgz#ef939d6e7f268827e1841638dc6ff95515e115d9" - integrity sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA== + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" - "@babel/plugin-transform-dotall-regex" "^7.4.4" - "@babel/types" "^7.4.4" - esutils "^2.0.2" + "@babel/helper-plugin-utils" "^7.8.0" -"@babel/register@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.18.9.tgz#1888b24bc28d5cc41c412feb015e9ff6b96e439c" - integrity sha512-ZlbnXDcNYHMR25ITwwNKT88JiaukkdVj/nG7r3wnuXkOTHc60Uy05PwMCPre0hSkY68E6zK3xz+vUJSP2jWmcw== +"@babel/plugin-syntax-top-level-await@^7.8.3": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" + integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== dependencies: - clone-deep "^4.0.1" - find-cache-dir "^2.0.0" - make-dir "^2.1.0" - pirates "^4.0.5" - source-map-support "^0.5.16" + "@babel/helper-plugin-utils" "^7.14.5" -"@babel/runtime@^7.8.4": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.9.tgz#b4fcfce55db3d2e5e080d2490f608a3b9f407f4a" - integrity sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw== +"@babel/plugin-syntax-typescript@^7.7.2": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz#1c09cd25795c7c2b8a4ba9ae49394576d4133285" + integrity sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA== dependencies: - regenerator-runtime "^0.13.4" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/template@^7.18.10", "@babel/template@^7.18.6", "@babel/template@^7.3.3": version "7.18.10" @@ -1618,7 +899,7 @@ "@babel/parser" "^7.18.10" "@babel/types" "^7.18.10" -"@babel/traverse@^7.18.10", "@babel/traverse@^7.18.11", "@babel/traverse@^7.18.9", "@babel/traverse@^7.7.2": +"@babel/traverse@^7.18.10", "@babel/traverse@^7.18.9", "@babel/traverse@^7.7.2": version "7.18.11" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.11.tgz#3d51f2afbd83ecf9912bcbb5c4d94e3d2ddaa16f" integrity sha512-TG9PiM2R/cWCAy6BPJKeHzNbu4lPzOSZpeMfeNErskGpTJx6trEvFaVCbDvpcxwy49BKWmEPwiW8mrysNiDvIQ== @@ -1634,7 +915,7 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": +"@babel/types@^7.0.0", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.3.0", "@babel/types@^7.3.3": version "7.18.10" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.10.tgz#4908e81b6b339ca7c6b7a555a5fc29446f26dde6" integrity sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ== @@ -1661,6 +942,13 @@ eslint-config-airbnb "^19.0.4" eslint-config-airbnb-base "^15.0.0" +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== + dependencies: + "@jridgewell/trace-mapping" "0.3.9" + "@dabh/diagnostics@^2.0.2": version "2.0.3" resolved "https://registry.yarnpkg.com/@dabh/diagnostics/-/diagnostics-2.0.3.tgz#7f7e97ee9a725dffc7808d93668cc984e1dc477a" @@ -1734,6 +1022,13 @@ js-yaml "^3.13.1" resolve-from "^5.0.0" +"@istanbuljs/nyc-config-typescript@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@istanbuljs/nyc-config-typescript/-/nyc-config-typescript-1.0.2.tgz#1f5235b28540a07219ae0dd42014912a0b19cf89" + integrity sha512-iKGIyMoyJuFnJRSVTZ78POIRvNnwZaWIf8vG4ZS3rQq58MMDrqEX2nnzx0R28V2X8JvmKYiqY9FP2hlJsm8A0w== + dependencies: + "@istanbuljs/schema" "^0.1.2" + "@istanbuljs/schema@^0.1.2": version "0.1.3" resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" @@ -1964,7 +1259,15 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.13", "@jridgewell/trace-mapping@^0.3.8", "@jridgewell/trace-mapping@^0.3.9": +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.13", "@jridgewell/trace-mapping@^0.3.9": version "0.3.15" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz#aba35c48a38d3fd84b37e66c9c0423f9744f9774" integrity sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g== @@ -1972,11 +1275,6 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3": - version "2.1.8-no-fsevents.3" - resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz#323d72dd25103d0c4fbdce89dadf574a787b1f9b" - integrity sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ== - "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -2430,6 +1728,36 @@ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== +"@tsconfig/node10@^1.0.7": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" + integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA== + +"@tsconfig/node12@^1.0.7": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + +"@tsconfig/node14@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + +"@tsconfig/node16@^1.0.2": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e" + integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== + +"@types/async@^3.2.15": + version "3.2.15" + resolved "https://registry.yarnpkg.com/@types/async/-/async-3.2.15.tgz#26d4768fdda0e466f18d6c9918ca28cc89a4e1fe" + integrity sha512-PAmPfzvFA31mRoqZyTVsgJMsvbynR429UTTxhmfsUCrWGh3/fxOrzqBtaTPJsn4UtzTv4Vb0+/O7CARWb69N4g== + +"@types/aws-lambda@^8.10.102": + version "8.10.102" + resolved "https://registry.yarnpkg.com/@types/aws-lambda/-/aws-lambda-8.10.102.tgz#d2402224ec30cdddfb669005c25b6ee01fd6f5be" + integrity sha512-BT05v46n9KtSHa9SgGuOvm49eSruJ9utD8iNXpdpuUVYk8wOcqmm1LEzpNRkrXxD0CULc38sdLpk6q3Wa2WOwg== + "@types/babel__core@^7.1.14": version "7.1.19" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.19.tgz#7b497495b7d1b4812bdb9d02804d0576f43ee460" @@ -2497,6 +1825,11 @@ jest-matcher-utils "^28.0.0" pretty-format "^28.0.0" +"@types/json-schema@^7.0.9": + version "7.0.11" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" + integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== + "@types/json5@^0.0.29": version "0.0.29" resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" @@ -2512,6 +1845,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.7.3.tgz#432c89796eab539b7a30b7b8801a727b585238a4" integrity sha512-LJgzOEwWuMTBxHzgBR/fhhBOWrvBjvO+zPteUgbbuQi80rYIZHrk1mNbRUqPZqSLP2H7Rwt1EFLL/tNLD1Xx/w== +"@types/node@14": + version "14.18.23" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.23.tgz#70f5f20b0b1b38f696848c1d3647bb95694e615e" + integrity sha512-MhbCWN18R4GhO8ewQWAFK4TGQdBpXWByukz7cWyJmXhvRuCIaM/oWytGPqVmDzgEnnaIc9ss6HbU5mUi+vyZPA== + "@types/normalize-package-data@^2.4.0": version "2.4.1" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" @@ -2537,6 +1875,23 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== +"@types/useragent@^2.3.1": + version "2.3.1" + resolved "https://registry.yarnpkg.com/@types/useragent/-/useragent-2.3.1.tgz#c971243faa04f50df399da35d77538ab5fabae20" + integrity sha512-w70ziElAVDD8lEOQ2Id3YBDE0sn2DTVA1zLB59H4kFngYoOJAIlnMkndiZFrzzHE0jmFDZ9AEWNvmeTm6Rvj9A== + +"@types/uuid@^8.3.4": + version "8.3.4" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc" + integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw== + +"@types/xml2js@^0.4.11": + version "0.4.11" + resolved "https://registry.yarnpkg.com/@types/xml2js/-/xml2js-0.4.11.tgz#bf46a84ecc12c41159a7bd9cf51ae84129af0e79" + integrity sha512-JdigeAKmCyoJUiQljjr7tQG3if9NkqGUgwEUqBvV0N7LM4HyQk7UXCnusRa1lnvXAEYJ8mw8GtZWioagNztOwA== + dependencies: + "@types/node" "*" + "@types/yargs-parser@*": version "21.0.0" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b" @@ -2549,6 +1904,86 @@ dependencies: "@types/yargs-parser" "*" +"@typescript-eslint/eslint-plugin@^5.33.0": + version "5.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.33.0.tgz#059798888720ec52ffa96c5f868e31a8f70fa3ec" + integrity sha512-jHvZNSW2WZ31OPJ3enhLrEKvAZNyAFWZ6rx9tUwaessTc4sx9KmgMNhVcqVAl1ETnT5rU5fpXTLmY9YvC1DCNg== + dependencies: + "@typescript-eslint/scope-manager" "5.33.0" + "@typescript-eslint/type-utils" "5.33.0" + "@typescript-eslint/utils" "5.33.0" + debug "^4.3.4" + functional-red-black-tree "^1.0.1" + ignore "^5.2.0" + regexpp "^3.2.0" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/parser@^5.33.0": + version "5.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.33.0.tgz#26ec3235b74f0667414613727cb98f9b69dc5383" + integrity sha512-cgM5cJrWmrDV2KpvlcSkelTBASAs1mgqq+IUGKJvFxWrapHpaRy5EXPQz9YaKF3nZ8KY18ILTiVpUtbIac86/w== + dependencies: + "@typescript-eslint/scope-manager" "5.33.0" + "@typescript-eslint/types" "5.33.0" + "@typescript-eslint/typescript-estree" "5.33.0" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@5.33.0": + version "5.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.33.0.tgz#509d7fa540a2c58f66bdcfcf278a3fa79002e18d" + integrity sha512-/Jta8yMNpXYpRDl8EwF/M8It2A9sFJTubDo0ATZefGXmOqlaBffEw0ZbkbQ7TNDK6q55NPHFshGBPAZvZkE8Pw== + dependencies: + "@typescript-eslint/types" "5.33.0" + "@typescript-eslint/visitor-keys" "5.33.0" + +"@typescript-eslint/type-utils@5.33.0": + version "5.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.33.0.tgz#92ad1fba973c078d23767ce2d8d5a601baaa9338" + integrity sha512-2zB8uEn7hEH2pBeyk3NpzX1p3lF9dKrEbnXq1F7YkpZ6hlyqb2yZujqgRGqXgRBTHWIUG3NGx/WeZk224UKlIA== + dependencies: + "@typescript-eslint/utils" "5.33.0" + debug "^4.3.4" + tsutils "^3.21.0" + +"@typescript-eslint/types@5.33.0": + version "5.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.33.0.tgz#d41c584831805554b063791338b0220b613a275b" + integrity sha512-nIMt96JngB4MYFYXpZ/3ZNU4GWPNdBbcB5w2rDOCpXOVUkhtNlG2mmm8uXhubhidRZdwMaMBap7Uk8SZMU/ppw== + +"@typescript-eslint/typescript-estree@5.33.0": + version "5.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.33.0.tgz#02d9c9ade6f4897c09e3508c27de53ad6bfa54cf" + integrity sha512-tqq3MRLlggkJKJUrzM6wltk8NckKyyorCSGMq4eVkyL5sDYzJJcMgZATqmF8fLdsWrW7OjjIZ1m9v81vKcaqwQ== + dependencies: + "@typescript-eslint/types" "5.33.0" + "@typescript-eslint/visitor-keys" "5.33.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/utils@5.33.0": + version "5.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.33.0.tgz#46797461ce3146e21c095d79518cc0f8ec574038" + integrity sha512-JxOAnXt9oZjXLIiXb5ZIcZXiwVHCkqZgof0O8KPgz7C7y0HS42gi75PdPlqh1Tf109M0fyUw45Ao6JLo7S5AHw== + dependencies: + "@types/json-schema" "^7.0.9" + "@typescript-eslint/scope-manager" "5.33.0" + "@typescript-eslint/types" "5.33.0" + "@typescript-eslint/typescript-estree" "5.33.0" + eslint-scope "^5.1.1" + eslint-utils "^3.0.0" + +"@typescript-eslint/visitor-keys@5.33.0": + version "5.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.33.0.tgz#fbcbb074e460c11046e067bc3384b5d66b555484" + integrity sha512-/XsqCzD4t+Y9p5wd9HZiptuGKBlaZO5showwqODii5C0nZawxWLF+Q6k5wYHBrQv96h6GYKyqqMHCSTqta8Kiw== + dependencies: + "@typescript-eslint/types" "5.33.0" + eslint-visitor-keys "^3.3.0" + JSONStream@^1.0.4: version "1.3.5" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" @@ -2567,7 +2002,12 @@ acorn-jsx@^5.3.2: resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -acorn@^8.8.0: +acorn-walk@^8.1.1: + version "8.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" + integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== + +acorn@^8.4.1, acorn@^8.8.0: version "8.8.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== @@ -2654,7 +2094,7 @@ ansicolors@~0.3.2: resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.3.2.tgz#665597de86a9ffe3aa9bfbe6cae5c6ea426b4979" integrity sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg== -anymatch@^3.0.3, anymatch@~3.1.2: +anymatch@^3.0.3: version "3.1.2" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== @@ -2687,6 +2127,11 @@ are-we-there-yet@^3.0.0: delegates "^1.0.0" readable-stream "^3.6.0" +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -2735,17 +2180,6 @@ array.prototype.flat@^1.2.5: es-abstract "^1.19.2" es-shim-unscopables "^1.0.0" -array.prototype.reduce@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/array.prototype.reduce/-/array.prototype.reduce-1.0.4.tgz#8167e80089f78bff70a99e20bd4201d4663b0a6f" - integrity sha512-WnM+AjG/DvLRLo4DDl+r+SvCzYtD2Jd9oeBYMcEaI7t3fFrHY9M53/wdLcTvmZNQ70IU6Htj0emFkZ5TS+lrdw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.2" - es-array-method-boxes-properly "^1.0.0" - is-string "^1.0.7" - arrify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" @@ -2815,13 +2249,6 @@ babel-jest@^28.1.3: graceful-fs "^4.2.9" slash "^3.0.0" -babel-plugin-dynamic-import-node@^2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" - integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== - dependencies: - object.assign "^4.1.0" - babel-plugin-istanbul@^6.1.1: version "6.1.1" resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" @@ -2843,30 +2270,6 @@ babel-plugin-jest-hoist@^28.1.3: "@types/babel__core" "^7.1.14" "@types/babel__traverse" "^7.0.6" -babel-plugin-polyfill-corejs2@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.2.tgz#e4c31d4c89b56f3cf85b92558954c66b54bd972d" - integrity sha512-LPnodUl3lS0/4wN3Rb+m+UK8s7lj2jcLRrjho4gLw+OJs+I4bvGXshINesY5xx/apM+biTnQ9reDI8yj+0M5+Q== - dependencies: - "@babel/compat-data" "^7.17.7" - "@babel/helper-define-polyfill-provider" "^0.3.2" - semver "^6.1.1" - -babel-plugin-polyfill-corejs3@^0.5.3: - version "0.5.3" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz#d7e09c9a899079d71a8b670c6181af56ec19c5c7" - integrity sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw== - dependencies: - "@babel/helper-define-polyfill-provider" "^0.3.2" - core-js-compat "^3.21.0" - -babel-plugin-polyfill-regenerator@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.0.tgz#8f51809b6d5883e07e71548d75966ff7635527fe" - integrity sha512-RW1cnryiADFeHmfLS+WW/G431p1PsW5qdRdz0SDRi7TKcUgc7Oh/uXkT7MZ/+tGsT1BkczEAmD5XjUyJ5SWDTw== - dependencies: - "@babel/helper-define-polyfill-provider" "^0.3.2" - babel-preset-current-node-syntax@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" @@ -2920,7 +2323,7 @@ bin-links@^3.0.0: rimraf "^3.0.0" write-file-atomic "^4.0.0" -binary-extensions@^2.0.0, binary-extensions@^2.2.0: +binary-extensions@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== @@ -2950,14 +2353,14 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" -braces@^3.0.2, braces@~3.0.2: +braces@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== dependencies: fill-range "^7.0.1" -browserslist@^4.20.2, browserslist@^4.21.3: +browserslist@^4.20.2: version "4.21.3" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.3.tgz#5df277694eb3c48bc5c4b05af3e8b7e09c5a6d1a" integrity sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ== @@ -2967,6 +2370,13 @@ browserslist@^4.20.2, browserslist@^4.21.3: node-releases "^2.0.6" update-browserslist-db "^1.0.5" +bs-logger@0.x: + version "0.2.6" + resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" + integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== + dependencies: + fast-json-stable-stringify "2.x" + bser@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" @@ -2988,11 +2398,6 @@ buffer@4.9.2: ieee754 "^1.1.4" isarray "^1.0.0" -builtin-modules@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6" - integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw== - builtins@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/builtins/-/builtins-5.0.1.tgz#87f6db9ab0458be728564fa81d876d8d74552fa9" @@ -3111,27 +2516,12 @@ charenc@0.0.2: resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" integrity sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA== -chokidar@^3.4.0: - version "3.5.3" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" - integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== - dependencies: - anymatch "~3.1.2" - braces "~3.0.2" - glob-parent "~5.1.2" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.6.0" - optionalDependencies: - fsevents "~2.3.2" - chownr@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== -ci-info@^3.2.0, ci-info@^3.3.0: +ci-info@^3.2.0: version "3.3.2" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.2.tgz#6d2967ffa407466481c6c90b6e16b3098f080128" integrity sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg== @@ -3148,13 +2538,6 @@ cjs-module-lexer@^1.0.0: resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== -clean-regexp@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/clean-regexp/-/clean-regexp-1.0.0.tgz#8df7c7aae51fd36874e8f8d05b9180bc11a3fed7" - integrity sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw== - dependencies: - escape-string-regexp "^1.0.5" - clean-stack@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" @@ -3195,15 +2578,6 @@ cliui@^7.0.2: strip-ansi "^6.0.0" wrap-ansi "^7.0.0" -clone-deep@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" - integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== - dependencies: - is-plain-object "^2.0.4" - kind-of "^6.0.2" - shallow-clone "^3.0.0" - clone@^1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" @@ -3294,11 +2668,6 @@ combined-stream@^1.0.8: dependencies: delayed-stream "~1.0.0" -commander@^4.0.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" - integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== - comment-parser@1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-1.3.1.tgz#3d7ea3adaf9345594aedee6563f422348f165c1b" @@ -3380,7 +2749,7 @@ conventional-commits-parser@^3.2.3: split2 "^3.0.0" through2 "^4.0.0" -convert-source-map@^1.1.0, convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: +convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.8.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== @@ -3392,19 +2761,6 @@ cookie@^0.4.1: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== -core-js-compat@^3.21.0, core-js-compat@^3.22.1: - version "3.24.1" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.24.1.tgz#d1af84a17e18dfdd401ee39da9996f9a7ba887de" - integrity sha512-XhdNAGeRnTpp8xbD+sR/HFDK9CbeeeqXT6TuofXh3urqEevzkWmLRgrVoykodsw8okqo2pu1BOmuCKrHx63zdw== - dependencies: - browserslist "^4.21.3" - semver "7.0.0" - -core-js@^3.22.1: - version "3.24.1" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.24.1.tgz#cf7724d41724154010a6576b7b57d94c5d66e64f" - integrity sha512-0QTBSYSUZ6Gq21utGzkfITDylE8jWC9Ne1D2MrhvlsZBI1x39OdDIVbzSqtgMndIy6BlHxBXpMGqzZmnztg2rg== - core-util-is@~1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" @@ -3421,6 +2777,11 @@ cosmiconfig@^7.0.0: path-type "^4.0.0" yaml "^1.10.0" +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -3593,6 +2954,11 @@ diff-sequences@^28.1.1: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-28.1.1.tgz#9989dc731266dc2903457a70e996f3a041913ac6" integrity sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw== +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + diff@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/diff/-/diff-5.1.0.tgz#bc52d298c5ea8df9194800224445ed43ffc87e40" @@ -3708,7 +3074,7 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19.5, es-abstract@^1.20.0, es-abstract@^1.20.1: +es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19.5, es-abstract@^1.20.0: version "1.20.1" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.1.tgz#027292cd6ef44bd12b1913b828116f54787d1814" integrity sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA== @@ -3737,11 +3103,6 @@ es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19 string.prototype.trimstart "^1.0.5" unbox-primitive "^1.0.2" -es-array-method-boxes-properly@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" - integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== - es-shim-unscopables@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" @@ -3817,14 +3178,6 @@ eslint-module-utils@^2.7.3: dependencies: debug "^3.2.7" -eslint-plugin-flowtype@^8.0.3: - version "8.0.3" - resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-8.0.3.tgz#e1557e37118f24734aa3122e7536a038d34a4912" - integrity sha512-dX8l6qUL6O+fYPtpNRideCFSpmWOUVx5QcaGLVqe/vlDiBSe4vYljDWDETwnyFzpl7By/WVIu6rcrniCgH9BqQ== - dependencies: - lodash "^4.17.21" - string-natural-compare "^3.0.1" - eslint-plugin-import@^2.25.2: version "2.26.0" resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz#f812dc47be4f2b72b478a021605a59fc6fe8b88b" @@ -3857,31 +3210,6 @@ eslint-plugin-jsdoc@^39.3.2: semver "^7.3.7" spdx-expression-parse "^3.0.1" -eslint-plugin-sonarjs@^0.13.0: - version "0.13.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.13.0.tgz#c34d140cc90abaaed38f5a5201a2ccdebe398862" - integrity sha512-t3m7ta0EspzDxSOZh3cEOJIJVZgN/TlJYaBGnQlK6W/PZNbWep8q4RQskkJkA7/zwNpX0BaoEOSUUrqaADVoqA== - -eslint-plugin-unicorn@^42.0.0: - version "42.0.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-unicorn/-/eslint-plugin-unicorn-42.0.0.tgz#47d60c00c263ad743403b052db689e39acbacff1" - integrity sha512-ixBsbhgWuxVaNlPTT8AyfJMlhyC5flCJFjyK3oKE8TRrwBnaHvUbuIkCM1lqg8ryYrFStL/T557zfKzX4GKSlg== - dependencies: - "@babel/helper-validator-identifier" "^7.15.7" - ci-info "^3.3.0" - clean-regexp "^1.0.0" - eslint-utils "^3.0.0" - esquery "^1.4.0" - indent-string "^4.0.0" - is-builtin-module "^3.1.0" - lodash "^4.17.21" - pluralize "^8.0.0" - read-pkg-up "^7.0.1" - regexp-tree "^0.1.24" - safe-regex "^2.1.1" - semver "^7.3.5" - strip-indent "^3.0.0" - eslint-scope@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" @@ -3905,7 +3233,7 @@ eslint-utils@^3.0.0: dependencies: eslint-visitor-keys "^2.0.0" -eslint-visitor-keys@^2.0.0, eslint-visitor-keys@^2.1.0: +eslint-visitor-keys@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== @@ -4055,7 +3383,7 @@ fast-glob@^3.2.9: merge2 "^1.3.0" micromatch "^4.0.4" -fast-json-stable-stringify@^2.0.0: +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== @@ -4122,15 +3450,6 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" -find-cache-dir@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" - integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== - dependencies: - commondir "^1.0.1" - make-dir "^2.0.0" - pkg-dir "^3.0.0" - find-cache-dir@^3.2.0: version "3.3.2" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" @@ -4147,13 +3466,6 @@ find-up@^2.0.0: dependencies: locate-path "^2.0.0" -find-up@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" - integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== - dependencies: - locate-path "^3.0.0" - find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" @@ -4253,17 +3565,12 @@ fs-minipass@^2.0.0, fs-minipass@^2.1.0: dependencies: minipass "^3.0.0" -fs-readdir-recursive@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz#e32fc030a2ccee44a6b5371308da54be0b397d27" - integrity sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA== - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@^2.3.2, fsevents@~2.3.2: +fsevents@^2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== @@ -4356,7 +3663,7 @@ git-log-parser@^1.2.0: through2 "~2.0.0" traverse "~0.6.6" -glob-parent@^5.1.2, glob-parent@~5.1.2: +glob-parent@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== @@ -4370,7 +3677,7 @@ glob-parent@^6.0.1: dependencies: is-glob "^4.0.3" -glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.2.0: +glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -4503,13 +3810,6 @@ hasha@^5.0.0: is-stream "^2.0.0" type-fest "^0.8.0" -homedir-polyfill@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8" - integrity sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA== - dependencies: - parse-passwd "^1.0.0" - hook-std@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/hook-std/-/hook-std-2.0.0.tgz#ff9aafdebb6a989a354f729bb6445cf4a3a7077c" @@ -4726,13 +4026,6 @@ is-bigint@^1.0.1: dependencies: has-bigints "^1.0.1" -is-binary-path@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" - integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== - dependencies: - binary-extensions "^2.0.0" - is-boolean-object@^1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" @@ -4746,13 +4039,6 @@ is-buffer@~1.1.6: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-builtin-module@^3.1.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-3.2.0.tgz#bb0310dfe881f144ca83f30100ceb10cf58835e0" - integrity sha512-phDA4oSGt7vl1n5tJvTWooWWAsXLY+2xCnxNqvKhGEzujg+A43wPlPOyDg3C8XQHN+6k/JTQWJ/j0dQh/qr+Hw== - dependencies: - builtin-modules "^3.3.0" - is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" @@ -4801,7 +4087,7 @@ is-generator-function@^1.0.7: dependencies: has-tostringtag "^1.0.0" -is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== @@ -4850,13 +4136,6 @@ is-plain-obj@^1.1.0: resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" integrity sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg== -is-plain-object@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" - integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== - dependencies: - isobject "^3.0.1" - is-plain-object@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" @@ -4941,11 +4220,6 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== -isobject@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" - integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== - issue-parser@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/issue-parser/-/issue-parser-6.0.0.tgz#b1edd06315d4f2044a9755daf85fdafde9b4014a" @@ -5334,7 +4608,7 @@ jest-snapshot@^28.1.3: pretty-format "^28.1.3" semver "^7.3.5" -jest-util@^28.1.3: +jest-util@^28.0.0, jest-util@^28.1.3: version "28.1.3" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-28.1.3.tgz#f4f932aa0074f0679943220ff9cbba7e497028b0" integrity sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ== @@ -5426,11 +4700,6 @@ jsesc@^2.5.1: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== -jsesc@~0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" - integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== - json-parse-better-errors@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" @@ -5497,7 +4766,7 @@ just-diff@^5.0.1: resolved "https://registry.yarnpkg.com/just-diff/-/just-diff-5.1.1.tgz#8da6414342a5ed6d02ccd64f5586cbbed3146202" integrity sha512-u8HXJ3HlNrTzY7zrYYKjNEfBlyjqhdBkoyTVdjtn7p02RJD5NvR8rIClzeGA7t+UYP1/7eAkWNLU0+P3QrEqKQ== -kind-of@^6.0.2, kind-of@^6.0.3: +kind-of@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== @@ -5661,14 +4930,6 @@ locate-path@^2.0.0: p-locate "^2.0.0" path-exists "^3.0.0" -locate-path@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" - integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== - dependencies: - p-locate "^3.0.0" - path-exists "^3.0.0" - locate-path@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" @@ -5688,11 +4949,6 @@ lodash.capitalize@^4.2.1: resolved "https://registry.yarnpkg.com/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz#f826c9b4e2a8511d84e3aca29db05e1a4f3b72a9" integrity sha512-kZzYOKspf8XVX5AvmQF94gQW0lejFVgb80G85bU4ZWzoJ6C03PQg3coYAUpSTpQWelrZELd3XWgHzw4Ck5kaIw== -lodash.debounce@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" - integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== - lodash.escaperegexp@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz#64762c48618082518ac3df4ccf5d5886dae20347" @@ -5718,6 +4974,11 @@ lodash.isstring@^4.0.1: resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw== +lodash.memoize@4.x: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== + lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" @@ -5769,14 +5030,6 @@ lru_map@^0.3.3: resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd" integrity sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ== -make-dir@^2.0.0, make-dir@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" - integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== - dependencies: - pify "^4.0.1" - semver "^5.6.0" - make-dir@^3.0.0, make-dir@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" @@ -5784,6 +5037,11 @@ make-dir@^3.0.0, make-dir@^3.0.2: dependencies: semver "^6.0.0" +make-error@1.x, make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + make-fetch-happen@^10.0.3, make-fetch-happen@^10.0.6, make-fetch-happen@^10.2.0: version "10.2.0" resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-10.2.0.tgz#0bde3914f2f82750b5d48c6d2294d2c74f985e5b" @@ -6072,14 +5330,6 @@ node-emoji@^1.11.0: dependencies: lodash "^4.17.21" -node-environment-flags@^1.0.5: - version "1.0.6" - resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.6.tgz#a30ac13621f6f7d674260a54dede048c3982c088" - integrity sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw== - dependencies: - object.getownpropertydescriptors "^2.0.3" - semver "^5.7.0" - node-fetch@^2.6.7: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" @@ -6164,7 +5414,7 @@ normalize-package-data@^4.0.0: semver "^7.3.5" validate-npm-package-license "^3.0.4" -normalize-path@^3.0.0, normalize-path@~3.0.0: +normalize-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== @@ -6393,7 +5643,7 @@ object-keys@^1.1.1: resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== -object.assign@^4.1.0, object.assign@^4.1.2: +object.assign@^4.1.2: version "4.1.3" resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.3.tgz#d36b7700ddf0019abb6b1df1bb13f6445f79051f" integrity sha512-ZFJnX3zltyjcYJL0RoCJuzb+11zWGyaDbjgxZbdV7rFEcHQuYxrZqhow67aA7xpes6LhojyFDaBKAFfogQrikA== @@ -6412,16 +5662,6 @@ object.entries@^1.1.5: define-properties "^1.1.3" es-abstract "^1.19.1" -object.getownpropertydescriptors@^2.0.3: - version "2.1.4" - resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.4.tgz#7965e6437a57278b587383831a9b829455a4bc37" - integrity sha512-sccv3L/pMModT6dJAYF3fzGMVcb38ysQ0tEE6ixv2yXJDtEIPph268OlAdJj5/qZMZDq2g/jqvwppt36uS/uQQ== - dependencies: - array.prototype.reduce "^1.0.4" - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.1" - object.values@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.5.tgz#959f63e3ce9ef108720333082131e4a459b716ac" @@ -6498,7 +5738,7 @@ p-limit@^1.1.0: dependencies: p-try "^1.0.0" -p-limit@^2.0.0, p-limit@^2.2.0: +p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== @@ -6519,13 +5759,6 @@ p-locate@^2.0.0: dependencies: p-limit "^1.1.0" -p-locate@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" - integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== - dependencies: - p-limit "^2.0.0" - p-locate@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" @@ -6653,11 +5886,6 @@ parse-json@^5.0.0, parse-json@^5.2.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" -parse-passwd@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" - integrity sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q== - path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" @@ -6693,7 +5921,7 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== -picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1: +picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== @@ -6703,12 +5931,7 @@ pify@^3.0.0: resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" integrity sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg== -pify@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" - integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== - -pirates@^4.0.4, pirates@^4.0.5: +pirates@^4.0.4: version "4.0.5" resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== @@ -6721,13 +5944,6 @@ pkg-conf@^2.1.0: find-up "^2.0.0" load-json-file "^4.0.0" -pkg-dir@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" - integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== - dependencies: - find-up "^3.0.0" - pkg-dir@^4.1.0, pkg-dir@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" @@ -6735,11 +5951,6 @@ pkg-dir@^4.1.0, pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" -pluralize@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" - integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== - postcss-selector-parser@^6.0.10: version "6.0.10" resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz#79b61e2c0d1bfc2602d549e11d0876256f8df88d" @@ -6954,13 +6165,6 @@ readdir-scoped-modules@^1.1.0: graceful-fs "^4.1.2" once "^1.3.0" -readdirp@~3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" - integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== - dependencies: - picomatch "^2.2.1" - redent@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" @@ -6976,35 +6180,6 @@ redeyed@~2.1.0: dependencies: esprima "~4.0.0" -regenerate-unicode-properties@^10.0.1: - version "10.0.1" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz#7f442732aa7934a3740c779bb9b3340dccc1fb56" - integrity sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw== - dependencies: - regenerate "^1.4.2" - -regenerate@^1.4.2: - version "1.4.2" - resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" - integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== - -regenerator-runtime@^0.13.4: - version "0.13.9" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" - integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== - -regenerator-transform@^0.15.0: - version "0.15.0" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.0.tgz#cbd9ead5d77fae1a48d957cf889ad0586adb6537" - integrity sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg== - dependencies: - "@babel/runtime" "^7.8.4" - -regexp-tree@^0.1.24, regexp-tree@~0.1.1: - version "0.1.24" - resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.24.tgz#3d6fa238450a4d66e5bc9c4c14bb720e2196829d" - integrity sha512-s2aEVuLhvnVJW6s/iPgEGK6R+/xngd2jNQ+xy4bXNDKxZKJH6jpPHY6kVeVv1IeLCHgswRj+Kl3ELaDjG6V1iw== - regexp.prototype.flags@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" @@ -7019,18 +6194,6 @@ regexpp@^3.2.0: resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== -regexpu-core@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.1.0.tgz#2f8504c3fd0ebe11215783a41541e21c79942c6d" - integrity sha512-bb6hk+xWd2PEOkj5It46A16zFMs2mv86Iwpdu94la4S3sJ7C973h2dHpYKwIBGaWSO7cIRJ+UX0IeMaWcO4qwA== - dependencies: - regenerate "^1.4.2" - regenerate-unicode-properties "^10.0.1" - regjsgen "^0.6.0" - regjsparser "^0.8.2" - unicode-match-property-ecmascript "^2.0.0" - unicode-match-property-value-ecmascript "^2.0.0" - registry-auth-token@^4.0.0: version "4.2.2" resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.2.tgz#f02d49c3668884612ca031419491a13539e21fac" @@ -7038,18 +6201,6 @@ registry-auth-token@^4.0.0: dependencies: rc "1.2.8" -regjsgen@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.6.0.tgz#83414c5354afd7d6627b16af5f10f41c4e71808d" - integrity sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA== - -regjsparser@^0.8.2: - version "0.8.4" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.8.4.tgz#8a14285ffcc5de78c5b95d62bbf413b6bc132d5f" - integrity sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA== - dependencies: - jsesc "~0.5.0" - release-zalgo@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/release-zalgo/-/release-zalgo-1.0.0.tgz#09700b7e5074329739330e535c5a90fb67851730" @@ -7098,7 +6249,7 @@ resolve.exports@^1.1.0: resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== -resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.22.0, resolve@^1.22.1: +resolve@^1.10.0, resolve@^1.20.0, resolve@^1.22.0, resolve@^1.22.1: version "1.22.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== @@ -7146,13 +6297,6 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-regex@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-2.1.1.tgz#f7128f00d056e2fe5c11e81a1324dd974aadced2" - integrity sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A== - dependencies: - regexp-tree "~0.1.1" - safe-stable-stringify@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.3.1.tgz#ab67cbe1fe7d40603ca641c5e765cb942d04fc73" @@ -7219,40 +6363,28 @@ semver-regex@^3.1.2: resolved "https://registry.yarnpkg.com/semver-regex/-/semver-regex-3.1.4.tgz#13053c0d4aa11d070a2f2872b6b1e3ae1e1971b4" integrity sha512-6IiqeZNgq01qGf0TId0t3NvKzSvUsjcpdEO3AQNeIjR6A2+ckTnQlDpl4qu1bjRv0RzN3FP9hzFmws3lKqRWkA== -"semver@2 || 3 || 4 || 5", semver@^5.6.0, semver@^5.7.0: +"semver@2 || 3 || 4 || 5": version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" - integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== - -semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - -semver@^7.0.0, semver@^7.1.1, semver@^7.1.2, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7: +semver@7.x, semver@^7.0.0, semver@^7.1.1, semver@^7.1.2, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7: version "7.3.7" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== dependencies: lru-cache "^6.0.0" +semver@^6.0.0, semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== -shallow-clone@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" - integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== - dependencies: - kind-of "^6.0.2" - shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" @@ -7305,11 +6437,6 @@ sisteransi@^1.0.5: resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== -slash@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" - integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== - slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" @@ -7354,14 +6481,6 @@ source-map-support@0.5.13: buffer-from "^1.0.0" source-map "^0.6.0" -source-map-support@^0.5.16: - version "0.5.21" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" - integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - source-map@^0.6.0, source-map@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" @@ -7471,11 +6590,6 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -string-natural-compare@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4" - integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw== - "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" @@ -7724,6 +6838,39 @@ triple-beam@^1.3.0: resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9" integrity sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw== +ts-jest@^28.0.8: + version "28.0.8" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-28.0.8.tgz#cd204b8e7a2f78da32cf6c95c9a6165c5b99cc73" + integrity sha512-5FaG0lXmRPzApix8oFG8RKjAz4ehtm8yMKOTy5HX3fY6W8kmvOrmcY0hKDElW52FJov+clhUbrKAqofnj4mXTg== + dependencies: + bs-logger "0.x" + fast-json-stable-stringify "2.x" + jest-util "^28.0.0" + json5 "^2.2.1" + lodash.memoize "4.x" + make-error "1.x" + semver "7.x" + yargs-parser "^21.0.1" + +ts-node@^10.9.1: + version "10.9.1" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" + integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw== + dependencies: + "@cspotcode/source-map-support" "^0.8.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + v8-compile-cache-lib "^3.0.1" + yn "3.1.1" + tsconfig-paths@^3.14.1: version "3.14.1" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a" @@ -7734,7 +6881,16 @@ tsconfig-paths@^3.14.1: minimist "^1.2.6" strip-bom "^3.0.0" -tslib@^1.11.1, tslib@^1.9.3: +tsconfig-paths@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.1.0.tgz#f8ef7d467f08ae3a695335bf1ece088c5538d2c1" + integrity sha512-AHx4Euop/dXFC+Vx589alFba8QItjF+8hf8LtmuiCwHyI4rHXQtOOENaM8kvYf5fR0dRChy3wzWIZ9WbB7FWow== + dependencies: + json5 "^2.2.1" + minimist "^1.2.6" + strip-bom "^3.0.0" + +tslib@^1.11.1, tslib@^1.8.1, tslib@^1.9.3: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== @@ -7744,6 +6900,13 @@ tslib@^2.3.1: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== +tsutils@^3.21.0: + version "3.21.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" + integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== + dependencies: + tslib "^1.8.1" + type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" @@ -7798,6 +6961,11 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" +typescript@^4.7.4: + version "4.7.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235" + integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ== + uglify-js@^3.1.4: version "3.16.3" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.16.3.tgz#94c7a63337ee31227a18d03b8a3041c210fd1f1d" @@ -7813,29 +6981,6 @@ unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" -unicode-canonical-property-names-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" - integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== - -unicode-match-property-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" - integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== - dependencies: - unicode-canonical-property-names-ecmascript "^2.0.0" - unicode-property-aliases-ecmascript "^2.0.0" - -unicode-match-property-value-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz#1a01aa57247c14c568b89775a54938788189a714" - integrity sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw== - -unicode-property-aliases-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz#0a36cb9a585c4f6abd51ad1deddb285c165297c8" - integrity sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ== - unique-filename@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" @@ -7940,6 +7085,11 @@ uuid@^8.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== +v8-compile-cache-lib@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== + v8-compile-cache@^2.0.3: version "2.3.0" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" @@ -7954,13 +7104,6 @@ v8-to-istanbul@^9.0.1: "@types/istanbul-lib-coverage" "^2.0.1" convert-source-map "^1.6.0" -v8flags@^3.1.1: - version "3.2.0" - resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-3.2.0.tgz#b243e3b4dfd731fa774e7492128109a0fe66d656" - integrity sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg== - dependencies: - homedir-polyfill "^1.0.1" - validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" @@ -8200,7 +7343,7 @@ yargs-parser@^20.2.2, yargs-parser@^20.2.3: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== -yargs-parser@^21.0.0: +yargs-parser@^21.0.0, yargs-parser@^21.0.1: version "21.1.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== @@ -8248,6 +7391,11 @@ yargs@^17.3.1: y18n "^5.0.5" yargs-parser "^21.0.0" +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" From 036e86a98854d19ab9f60d2d8c334be4e8f46ae4 Mon Sep 17 00:00:00 2001 From: Seb Aebischer <49983670+seb-cr@users.noreply.github.com> Date: Wed, 4 Jan 2023 12:24:19 +0000 Subject: [PATCH 02/28] ci: Run publish job on `beta` branch (#1017) Currently config allows publishing from `master` only. --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 06a29475..e84fb5bf 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -53,7 +53,7 @@ jobs: name: Publish runs-on: ubuntu-latest needs: unit-test - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/beta' steps: - name: Checkout uses: actions/checkout@v2 From 3845b0f6170052c60756d93cc4ce0acc5079f8be Mon Sep 17 00:00:00 2001 From: Seb Aebischer <49983670+seb-cr@users.noreply.github.com> Date: Wed, 11 Jan 2023 14:22:01 +0000 Subject: [PATCH 03/28] fix: Make `@types/aws-lambda` a dependency (#1029) Currently this is a dev dependency and won't be installed with Lambda Wrapper in another project. This is a problem because we re-export some of its types, and TypeScript can't find them if the package hasn't been installed. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2a84cf35..ca168001 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,6 @@ "@comicrelief/eslint-config": "^2.0.3", "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/async": "^3.2.15", - "@types/aws-lambda": "^8.10.102", "@types/jest": "^28.1.6", "@types/node": "14", "@types/useragent": "^2.3.1", @@ -49,6 +48,7 @@ }, "dependencies": { "@sentry/node": "^6.0.1", + "@types/aws-lambda": "^8.10.102", "alai": "1.0.3", "async": "^3.2.4", "axios": "^0.27.2", From 2cec559db6b58823e3ee4138c072d256f7bd9664 Mon Sep 17 00:00:00 2001 From: Seb Aebischer <49983670+seb-cr@users.noreply.github.com> Date: Mon, 23 Jan 2023 15:32:25 +0000 Subject: [PATCH 04/28] docs: Fix typo in readme (#1041) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2bd7288a..dabfeac9 100644 --- a/README.md +++ b/README.md @@ -186,7 +186,7 @@ When you go to configure your Lambda Wrapper, you can now include your dependenc ```ts lambdaWrapper.configure({ sqs: { - queues: 42 // Oops! This will be flaggeed as a type error by TypeScript + queues: 42 // Oops! This will be flagged as a type error by TypeScript }, }); ``` From b13a4494b9972e8ffcba2052a23fbdfe8e28eba3 Mon Sep 17 00:00:00 2001 From: Seb Aebischer <49983670+seb-cr@users.noreply.github.com> Date: Mon, 23 Jan 2023 15:39:23 +0000 Subject: [PATCH 05/28] docs: Show how to install the beta version (#1040) So that someone looking at the `beta` branch gets instructions that they can follow to install this version. This will need reverting just before we merge `beta` into `master`. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index dabfeac9..89c37177 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,9 @@ If you're coming from v1 and updating to v2, check out the [v2 migration guide]( Install via npm or Yarn: ```bash -npm i @comicrelief/lambda-wrapper +npm i @comicrelief/lambda-wrapper@beta # or -yarn add @comicrelief/lambda-wrapper +yarn add @comicrelief/lambda-wrapper@beta ``` You can then wrap your Lambda handler functions like this: From d8857d0589e28ffa4437f3e650539402a9fad9fb Mon Sep 17 00:00:00 2001 From: Seb Aebischer <49983670+seb-cr@users.noreply.github.com> Date: Mon, 23 Jan 2023 15:46:05 +0000 Subject: [PATCH 06/28] feat: Export `RESPONSE_HEADERS` and fix its type (#1039) Makes `RESPONSE_HEADERS` available for import from the package index. This is useful if you want to change the default headers in your responses, e.g. to change `Content-Type`. The type is also relaxed to `Record` to allow adding or removing headers. BREAKING CHANGE: the `Access-Control-Allow-Credentials` header value is now the string `'true'` instead of Boolean `true`. This could affect tests that rely on unstringified values. --- src/index.ts | 1 + src/models/ResponseModel.ts | 6 +++--- tests/unit/index.spec.ts | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/index.ts b/src/index.ts index 612c06e2..bac99d92 100644 --- a/src/index.ts +++ b/src/index.ts @@ -31,6 +31,7 @@ export { default as LambdaWrapper, WrapOptions } from './core/LambdaWrapper'; export { default as ResponseModel, + RESPONSE_HEADERS, } from './models/ResponseModel'; export { default as SQSMessageModel, diff --git a/src/models/ResponseModel.ts b/src/models/ResponseModel.ts index c6697c8e..971c5222 100644 --- a/src/models/ResponseModel.ts +++ b/src/models/ResponseModel.ts @@ -1,12 +1,12 @@ /** - * HTTP headers to be included in all responses. + * HTTP headers to be included in `ResponseModel`. */ -export const RESPONSE_HEADERS = { +export const RESPONSE_HEADERS: Record = { 'Content-Type': 'application/json', /** Required for CORS support to work */ 'Access-Control-Allow-Origin': '*', /** Required for cookies, authorization headers with HTTPS */ - 'Access-Control-Allow-Credentials': true, + 'Access-Control-Allow-Credentials': 'true', }; /** diff --git a/tests/unit/index.spec.ts b/tests/unit/index.spec.ts index b2cef2a5..d08c3113 100644 --- a/tests/unit/index.spec.ts +++ b/tests/unit/index.spec.ts @@ -1,7 +1,7 @@ import DependencyAwareClass from '@/src/core/DependencyAwareClass'; import DependencyInjection from '@/src/core/DependencyInjection'; import LambdaWrapper from '@/src/core/LambdaWrapper'; -import ResponseModel from '@/src/models/ResponseModel'; +import ResponseModel, { RESPONSE_HEADERS } from '@/src/models/ResponseModel'; import SQSMessageModel from '@/src/models/SQSMessageModel'; import StatusModel from '@/src/models/StatusModel'; import BaseConfigService from '@/src/services/BaseConfigService'; @@ -45,6 +45,7 @@ describe('unit.index', () => { it('should export ResponseModel', () => { expect(lib.ResponseModel).toBe(ResponseModel); + expect(lib.RESPONSE_HEADERS).toBe(RESPONSE_HEADERS); }); it('should export SQSMessageModel', () => { From cb4863e4d5a367ca843943d31759ff971e302047 Mon Sep 17 00:00:00 2001 From: Seb Aebischer Date: Wed, 9 Aug 2023 13:50:12 +0100 Subject: [PATCH 07/28] docs: Add note about disabling minification --- README.md | 15 +++++++++++++++ docs/migration/v2.md | 2 ++ 2 files changed, 17 insertions(+) diff --git a/README.md b/README.md index 89c37177..b7036fe6 100644 --- a/README.md +++ b/README.md @@ -204,6 +204,21 @@ lambdaWrapper.configure({ }); ``` +## Notes + +Lambda Wrapper's dependency injection relies on class names being preserved. If your build process includes minifying or uglifying your code, you'll need to disable these transformations. + +In many of our projects we use `serverless-webpack` to bundle service code prior to deployment. To disable name mangling, set `optimization.minimize` to `false` in your webpack config: + +```js +// webpack.config.js +module.exports = { + // ... + optimization: { + minimize: false, + }, +``` + ## Development ### Testing diff --git a/docs/migration/v2.md b/docs/migration/v2.md index 2690540c..1f15ceae 100644 --- a/docs/migration/v2.md +++ b/docs/migration/v2.md @@ -7,6 +7,8 @@ This doc summarises the breaking changes introduced in v2 and what you need to d - [Dependency injection](#dependency-injection) - [Models](#models) +The new version of dependency injection does not work if code is minified. See [Notes](../../README.md#notes) in the main readme for how to turn this off in webpack. + ## Configuration v1 required several consts with shouty names. In v2 these are replaced with a single config object with camel-case keys. You pass this to the new `configure` method to get a configured instance of `LambdaWrapper`. From b73313bbf653954afdbfc624c7dcd7e085460c63 Mon Sep 17 00:00:00 2001 From: Seb Aebischer <49983670+seb-cr@users.noreply.github.com> Date: Thu, 14 Sep 2023 12:55:59 +0100 Subject: [PATCH 08/28] feat: Propagate config type to dependencies (#1112) Dependency-aware classes can now access the type of the Lambda Wrapper config. This opens the door to using information from the config to better type some methods. As an example, our `SQSService` has many methods that take a queue name. Previously these had to be type `string` and we relied on runtime checks to ensure valid queue names were passed in. With access to the config type, we can infer queue names from the keys of `config.sqs.queues`, so this check can now be done at compile-time, _and_ we get IntelliSense suggestions for queue names! This is still a work in progress, although potentially useful and usable enough in its current state to release to beta. See the updated documentation for known issues. There should be no breaking changes. All new type parameters are optional. Unrecognised SQS queue names will raise compiler errors now, however these should not exist in a working application. --- docs/services/SQSService.md | 56 ++++++++++++- src/core/DependencyAwareClass.ts | 5 +- src/core/DependencyInjection.ts | 8 +- src/core/LambdaWrapper.ts | 2 +- src/index.ts | 1 + src/services/SQSService.ts | 108 +++++++++++++++++++++---- tests/unit/services/SQSService.spec.ts | 15 +++- 7 files changed, 169 insertions(+), 26 deletions(-) diff --git a/docs/services/SQSService.md b/docs/services/SQSService.md index de10c1fa..73c697be 100644 --- a/docs/services/SQSService.md +++ b/docs/services/SQSService.md @@ -13,7 +13,7 @@ export default lambdaWrapper.configure({ sqs: { queues: { // add an entry for each queue mapping to its AWS name - submissions: process.env.SQS_QUEUE_SUBMISSIONS, + submissions: process.env.SQS_QUEUE_SUBMISSIONS as string, }, }, }); @@ -35,6 +35,60 @@ export default lambdaWrapper.wrap(async (di) => { }); ``` +## In TypeScript + +When using TypeScript, queue names are inferred from your Lambda Wrapper config so that IntelliSense can provide hints and TypeScript will tell you at compile-time if you try to publish to an undefined queue. + +```ts +// ok +await sqs.publish('submissions', message); + +// error: Argument of type '"submission"' is not assignable to parameter of +// type '"submissions"'. +await sqs.publish('submission', message); +``` + +Note that if you're passing the queue name in as a variable, you'll need to ensure the variable type is specific enough and not simply `string`. If you have a list of queue names you will need to declare it `as const`. Otherwise, use string literal types, or the `QueueName` generic type which extracts the type of all queue names from your Lambda Wrapper config. + +```ts +const myQueues = ['queue1', 'queue2']; +for (const queue of myQueues) { + // won't compile because `queue` is of type `string` + await sqs.publish(queue, message); +} + +const myQueues = ['queue1', 'queue2'] as const; +for (const queue of myQueues) { + // ok now because `queue` is of type `"queue1" | "queue2"` + await sqs.publish(queue, message); +} + +// you can also simply use string literal types +let queue: "queue1" | "queue2"; + +// or accept any queue defined in the config using `QueueName` +let queue: QueueName; +``` + +This is all pretty cool, but the current implementation has a caveat: the `WithSQSServiceConfig` type has to be a little vague about `sqs.queues` in order to get TypeScript to infer its keys. The following config will not raise any errors itself, but is invalid and will make the `QueueName` type `never`. + +```ts +lambdaWrapper.configure({ + sqs: { + queues: { + good: 'good-queue', + bad: 0, // oops, not a string, but no errors here! + }, + }, +}); + +// even though this is queue has valid config, the invalid one breaks it: +// Argument of type 'string' is not assignable to parameter of type 'never'. +await sqs.publish('good', message); +``` + +If you start getting _not assignable to parameter of type 'never'_ errors on all your `SQSService` method calls, double-check that your config is correct. Be particularly careful with environment variables – by default they have type `string | undefined`. In the first example at the top of this page, a type assertion was used to coerce this to `string`. + ## Serverless Offline & SQS Emulation Serverless Offline only emulates API Gateway and Lambda, so sending an SQS message would use the real SQS queue and trigger the consumer function (if any) in AWS. When working with offline code, you often want the local functions to be invoked instead. diff --git a/src/core/DependencyAwareClass.ts b/src/core/DependencyAwareClass.ts index 8957cf93..63b5b2df 100644 --- a/src/core/DependencyAwareClass.ts +++ b/src/core/DependencyAwareClass.ts @@ -1,10 +1,11 @@ import DependencyInjection from './DependencyInjection'; +import { LambdaWrapperConfig } from './config'; /** * Base class for dependencies. */ -export default class DependencyAwareClass { - constructor(readonly di: DependencyInjection) {} +export default class DependencyAwareClass { + constructor(readonly di: DependencyInjection) {} /** * Get dependency injection container. diff --git a/src/core/DependencyInjection.ts b/src/core/DependencyInjection.ts index 81ff20dc..0a464ed5 100644 --- a/src/core/DependencyInjection.ts +++ b/src/core/DependencyInjection.ts @@ -4,7 +4,7 @@ import DependencyAwareClass from './DependencyAwareClass'; import { LambdaWrapperConfig } from './config'; // eslint-disable-next-line no-use-before-define -type Class = new (di: DependencyInjection) => T; +type Class = new (di: DependencyInjection) => TInstance; /** * Dependency injection container. @@ -12,7 +12,7 @@ type Class = new (di: DependencyInjection) => T; * Dependencies (singleton instances of dependency-aware classes) are provided * to the main Lambda handler and other dependencies via this class. */ -export default class DependencyInjection { +export default class DependencyInjection { /** * Instantiated dependencies. */ @@ -24,7 +24,7 @@ export default class DependencyInjection { private isConstructing = true; constructor( - readonly config: LambdaWrapperConfig, + readonly config: TConfig, readonly event: any, readonly context: Context, ) { @@ -41,7 +41,7 @@ export default class DependencyInjection { * * @param dependency */ - get(dependency: Class): T { + get(dependency: Class): T { if (this.isConstructing) { throw new Error( 'Dependencies are not available in dependency class constructors.\n\n' diff --git a/src/core/LambdaWrapper.ts b/src/core/LambdaWrapper.ts index 59085d62..397a058b 100644 --- a/src/core/LambdaWrapper.ts +++ b/src/core/LambdaWrapper.ts @@ -34,7 +34,7 @@ export default class LambdaWrapper(handler: (di: DependencyInjection) => Promise, options?: WrapOptions) { + wrap(handler: (di: DependencyInjection) => Promise, options?: WrapOptions) { const { handleUncaughtErrors = true, } = options || {}; diff --git a/src/index.ts b/src/index.ts index bac99d92..2a61e4a0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -58,6 +58,7 @@ export { } from './services/RequestService'; export { default as SQSService, + QueueName, SQS_OFFLINE_MODES, SQS_PUBLISH_FAILURE_MODES, SQSServiceConfig, diff --git a/src/services/SQSService.ts b/src/services/SQSService.ts index 1144f016..b40385b4 100644 --- a/src/services/SQSService.ts +++ b/src/services/SQSService.ts @@ -5,6 +5,7 @@ import { v4 as uuid } from 'uuid'; import DependencyAwareClass from '../core/DependencyAwareClass'; import DependencyInjection from '../core/DependencyInjection'; +import { LambdaWrapperConfig } from '../core/config'; import SQSMessageModel from '../models/SQSMessageModel'; import StatusModel, { STATUS_TYPES } from '../models/StatusModel'; import LoggerService from './LoggerService'; @@ -25,7 +26,7 @@ export interface SQSServiceConfig { * } * ``` */ - queues?: Record; + queues?: object; /** * Maps short friendly queue names to the queue consumer function name, for * use with offline SQS emulation. Example: @@ -48,6 +49,17 @@ export interface WithSQSServiceConfig { sqs?: SQSServiceConfig; } +/** + * Type of a queue name taken from the Lambda Wrapper config type. + * + * If the `sqs` config key is absent, the resulting type is `never` (since no + * queues are defined). + */ +export type QueueName = + TConfig extends { sqs: { queues: Record } } + ? string & Key + : never; + /** * Allowed values for `process.env.LAMBDA_WRAPPER_OFFLINE_SQS_MODE`. */ @@ -103,7 +115,7 @@ export const SQS_PUBLISH_FAILURE_MODES = { * sqs: { * queues: { * // add an entry for each queue mapping to its AWS name - * submissions: process.env.SQS_QUEUE_SUBMISSIONS, + * submissions: process.env.SQS_QUEUE_SUBMISSIONS as string, * }, * }, * }); @@ -119,22 +131,90 @@ export const SQS_PUBLISH_FAILURE_MODES = { * await sqs.publish('submissions', message); * }); * ``` + * + * When using TypeScript, queue names are inferred from your Lambda Wrapper + * config so that IntelliSense can provide hints and TypeScript will tell you + * at compile-time if you try to publish to an undefined queue. + * + * ```ts + * // ok + * await sqs.publish('submissions', message); + * + * // error: Argument of type '"submission"' is not assignable to parameter of + * // type '"submissions"'. + * await sqs.publish('submission', message); + * ``` + * + * Note that if you're passing the queue name in as a variable, you'll need to + * ensure the variable type is specific enough and not simply `string`. If you + * have a list of queue names you will need to declare it `as const`. Otherwise, + * use string literal types, or the `QueueName` generic type which extracts the + * type of all queue names from your Lambda Wrapper config. + * + * ```ts + * const myQueues = ['queue1', 'queue2']; + * for (const queue of myQueues) { + * // won't compile because `queue` is of type `string` + * await sqs.publish(queue, message); + * } + * + * const myQueues = ['queue1', 'queue2'] as const; + * for (const queue of myQueues) { + * // ok now because `queue` is of type `"queue1" | "queue2"` + * await sqs.publish(queue, message); + * } + * + * // you can also simply use string literal types + * let queue: "queue1" | "queue2"; + * + * // or accept any queue defined in the config using `QueueName` + * let queue: QueueName; + * ``` + * + * This is all pretty cool, but the current implementation has a caveat: the + * `WithSQSServiceConfig` type has to be a little vague about `sqs.queues` in + * order to get TypeScript to infer its keys. The following config will not + * raise any errors itself, but is invalid and will make the `QueueName` type + * `never`. + * + * ```ts + * lambdaWrapper.configure({ + * sqs: { + * queues: { + * good: 'good-queue', + * bad: 0, // oops, not a string, but no errors here! + * }, + * }, + * }); + * + * // even though this is queue has valid config, the invalid one breaks it: + * // Argument of type 'string' is not assignable to parameter of type 'never'. + * await sqs.publish('good', message); + * ``` + * + * If you start getting _not assignable to parameter of type 'never'_ errors on + * all your `SQSService` method calls, double-check that your config is correct. + * Be particularly careful with environment variables – by default they have + * type `string | undefined`. In the first example at the top of this page, a + * type assertion was used to coerce this to `string`. */ -export default class SQSService extends DependencyAwareClass { - readonly queues: Record; +export default class SQSService< + TConfig extends LambdaWrapperConfig & WithSQSServiceConfig = any, +> extends DependencyAwareClass { + readonly queues: Record, string>; - readonly queueConsumers: Record; + readonly queueConsumers: Record, string>; - readonly queueUrls: Record; + readonly queueUrls: Record, string>; private $sqs?: AWS.SQS; private $lambda?: AWS.Lambda; - constructor(di: DependencyInjection) { + constructor(di: DependencyInjection) { super(di); - const config = (this.di.config as WithSQSServiceConfig).sqs; + const config = this.di.config.sqs; this.queues = config?.queues || {}; this.queueConsumers = config?.queueConsumers || {}; @@ -161,7 +241,7 @@ export default class SQSService extends DependencyAwareClass { ? `http://${offlineHost}:${offlinePort}/queue/${queueName}` : `https://sqs.${REGION}.amazonaws.com/${accountId}/${queueName}`] )), - ); + ) as Record, string>; } /** @@ -220,7 +300,7 @@ export default class SQSService extends DependencyAwareClass { * @param queue * @param messageModels */ - batchDelete(queue: string, messageModels: SQSMessageModel[]): Promise { + batchDelete(queue: QueueName, messageModels: SQSMessageModel[]): Promise { const queueUrl = this.queueUrls[queue]; const logger = this.di.get(LoggerService); const timer = this.di.get(TimerService); @@ -303,7 +383,7 @@ export default class SQSService extends DependencyAwareClass { * * @param queue */ - getMessageCount(queue: string): Promise { + getMessageCount(queue: QueueName): Promise { const queueUrl = this.queueUrls[queue]; const logger = this.di.get(LoggerService); const timer = this.di.get(TimerService); @@ -346,7 +426,7 @@ export default class SQSService extends DependencyAwareClass { * - `catch`: errors will be caught and logged. This is the default. * - `throw`: errors will be thrown, causing promise to reject. */ - async publish(queue: string, messageObject: object, messageGroupId = null, failureMode: 'catch' | 'throw' = SQS_PUBLISH_FAILURE_MODES.CATCH) { + async publish(queue: QueueName, messageObject: object, messageGroupId = null, failureMode: 'catch' | 'throw' = SQS_PUBLISH_FAILURE_MODES.CATCH) { if (!Object.values(SQS_PUBLISH_FAILURE_MODES).includes(failureMode)) { throw new Error(`Invalid value for 'failureMode': ${failureMode}`); } @@ -394,7 +474,7 @@ export default class SQSService extends DependencyAwareClass { * @param queue * @param messageParameters */ - async publishOffline(queue: string, messageParameters: AWS.SQS.SendMessageRequest) { + async publishOffline(queue: QueueName, messageParameters: AWS.SQS.SendMessageRequest) { if (!this.di.isOffline) { throw new Error('Can only publishOffline while running serverless offline.'); } @@ -429,7 +509,7 @@ export default class SQSService extends DependencyAwareClass { * @param queue string * @param timeout number */ - receive(queue: string, timeout = 15): Promise { + receive(queue: QueueName, timeout = 15): Promise { const queueUrl = this.queueUrls[queue]; const logger = this.di.get(LoggerService); const timer = this.di.get(TimerService); diff --git a/tests/unit/services/SQSService.spec.ts b/tests/unit/services/SQSService.spec.ts index 7e6838cd..2baed1a2 100644 --- a/tests/unit/services/SQSService.spec.ts +++ b/tests/unit/services/SQSService.spec.ts @@ -1,17 +1,15 @@ import { Context, DependencyInjection, - LambdaWrapperConfig, LoggerService, SQSService, SQS_PUBLISH_FAILURE_MODES, TimerService, - WithSQSServiceConfig, } from '@/src'; const TEST_QUEUE = 'TEST_QUEUE'; -const config: LambdaWrapperConfig & WithSQSServiceConfig = { +const config = { dependencies: { SQSService, LoggerService, @@ -35,6 +33,15 @@ const createAsyncMock = (returnValue: any) => { return jest.fn().mockReturnValue({ promise: () => mockedValue }); }; +type MockSQSService = SQSService & { + sqs: { + sendMessage: jest.Mock; + }; + lambda: { + invoke: jest.Mock; + } +}; + /** * Generates a SQSService * @@ -47,7 +54,7 @@ const getService = ( invoke = null, }: any = {}, isOffline = false, -): SQSService & { sqs: { sendMessage: jest.Mock }; lambda: { invoke: jest.Mock } } => { +): MockSQSService => { const di = new DependencyInjection(config, {}, { invokedFunctionArn: isOffline ? 'offline' : 'arn:aws:lambda:eu-west-1:0123456789:test', } as Context); From 82a5aa8c7d9806642798a6302575669d19af0d8b Mon Sep 17 00:00:00 2001 From: Seb Aebischer <49983670+seb-cr@users.noreply.github.com> Date: Mon, 18 Sep 2023 15:29:08 +0100 Subject: [PATCH 09/28] fix: Add missing space in DI error message (#1137) Hidden because it was on a line join. --- src/core/DependencyInjection.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/DependencyInjection.ts b/src/core/DependencyInjection.ts index 0a464ed5..89bbe8a4 100644 --- a/src/core/DependencyInjection.ts +++ b/src/core/DependencyInjection.ts @@ -45,7 +45,7 @@ export default class DependencyInjection Date: Mon, 18 Sep 2023 15:33:24 +0100 Subject: [PATCH 10/28] fix: Dependency updates (#1138) General housekeeping, and required to fix a vulnerability in `xml2js`. I've done major updates for `jest` (which required regenerating snapsnots in their new format) and `uuid` (no breaking changes relevant to us). Other major updates require newer versions of Node. Corresponds to #1097 which updated our v1.x dependencies. --- package.json | 32 +- .../__snapshots__/HTTPService.spec.ts.snap | 196 +- .../__snapshots__/LoggerService.spec.ts.snap | 66 +- .../__snapshots__/RequestService.spec.ts.snap | 56 +- yarn.lock | 2242 ++++++++++------- 5 files changed, 1533 insertions(+), 1059 deletions(-) diff --git a/package.json b/package.json index ca168001..c8fd6e3c 100644 --- a/package.json +++ b/package.json @@ -23,24 +23,24 @@ "devDependencies": { "@comicrelief/eslint-config": "^2.0.3", "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@types/async": "^3.2.15", - "@types/jest": "^28.1.6", + "@types/async": "^3.2.20", + "@types/jest": "^29.5.4", "@types/node": "14", - "@types/useragent": "^2.3.1", - "@types/uuid": "^8.3.4", - "@types/xml2js": "^0.4.11", + "@types/useragent": "^2.3.2", + "@types/uuid": "^9.0.3", + "@types/xml2js": "^0.4.12", "@typescript-eslint/eslint-plugin": "^5.33.0", "@typescript-eslint/parser": "^5.33.0", - "aws-sdk": "^2.1194.0", - "eslint": "^8.22.0", + "aws-sdk": "^2.1456.0", + "eslint": "^8.49.0", "eslint-plugin-import": "^2.25.2", "eslint-plugin-jsdoc": "^39.3.2", - "jest": "^28.1.3", + "jest": "^29.7.0", "nyc": "^15.1.0", - "semantic-release": "^19.0.3", - "ts-jest": "^28.0.8", + "semantic-release": "^19.0.5", + "ts-jest": "^29.1.1", "ts-node": "^10.9.1", - "tsconfig-paths": "^4.1.0", + "tsconfig-paths": "^4.2.0", "typescript": "^4.7.4" }, "peerDependencies": { @@ -48,15 +48,15 @@ }, "dependencies": { "@sentry/node": "^6.0.1", - "@types/aws-lambda": "^8.10.102", + "@types/aws-lambda": "^8.10.120", "alai": "1.0.3", "async": "^3.2.4", "axios": "^0.27.2", - "epsagon": "^1.123.2", + "epsagon": "^1.123.3", "useragent": "2.3.0", - "uuid": "^8.3.2", + "uuid": "^9.0.1", "validate.js": "0.13.1", - "winston": "^3.8.1", - "xml2js": "^0.4.23" + "winston": "^3.10.0", + "xml2js": "^0.6.2" } } diff --git a/tests/unit/services/__snapshots__/HTTPService.spec.ts.snap b/tests/unit/services/__snapshots__/HTTPService.spec.ts.snap index 2328d61f..059f6f48 100644 --- a/tests/unit/services/__snapshots__/HTTPService.spec.ts.snap +++ b/tests/unit/services/__snapshots__/HTTPService.spec.ts.snap @@ -1,10 +1,10 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`unit.services.HTTPService request DELETE request: config 1`] = ` -Array [ - Array [ - Object { - "headers": Object {}, +[ + [ + { + "headers": {}, "method": "DELETE", "timeout": 10000, "url": "/", @@ -14,10 +14,10 @@ Array [ `; exports[`unit.services.HTTPService request GET request: config 1`] = ` -Array [ - Array [ - Object { - "headers": Object {}, +[ + [ + { + "headers": {}, "method": "GET", "timeout": 10000, "url": "/", @@ -27,10 +27,10 @@ Array [ `; exports[`unit.services.HTTPService request HEAD request: config 1`] = ` -Array [ - Array [ - Object { - "headers": Object {}, +[ + [ + { + "headers": {}, "method": "HEAD", "timeout": 10000, "url": "/", @@ -40,10 +40,10 @@ Array [ `; exports[`unit.services.HTTPService request PATCH request: config 1`] = ` -Array [ - Array [ - Object { - "headers": Object {}, +[ + [ + { + "headers": {}, "method": "PATCH", "timeout": 10000, "url": "/", @@ -53,10 +53,10 @@ Array [ `; exports[`unit.services.HTTPService request POST request: config 1`] = ` -Array [ - Array [ - Object { - "headers": Object {}, +[ + [ + { + "headers": {}, "method": "POST", "timeout": 10000, "url": "/", @@ -66,10 +66,10 @@ Array [ `; exports[`unit.services.HTTPService request PUT request: config 1`] = ` -Array [ - Array [ - Object { - "headers": Object {}, +[ + [ + { + "headers": {}, "method": "PUT", "timeout": 10000, "url": "/", @@ -79,11 +79,11 @@ Array [ `; exports[`unit.services.HTTPService request adds the test header, DELETE request: config 1`] = ` -Array [ - Array [ - Object { - "headers": Object { - "x-comicrelief-test-metadata": "{\\"user\\":\\"Dante Alighieri\\"}", +[ + [ + { + "headers": { + "x-comicrelief-test-metadata": "{"user":"Dante Alighieri"}", }, "method": "DELETE", "timeout": 10000, @@ -94,11 +94,11 @@ Array [ `; exports[`unit.services.HTTPService request adds the test header, GET request: config 1`] = ` -Array [ - Array [ - Object { - "headers": Object { - "x-comicrelief-test-metadata": "{\\"user\\":\\"Dante Alighieri\\"}", +[ + [ + { + "headers": { + "x-comicrelief-test-metadata": "{"user":"Dante Alighieri"}", }, "method": "GET", "timeout": 10000, @@ -109,11 +109,11 @@ Array [ `; exports[`unit.services.HTTPService request adds the test header, HEAD request: config 1`] = ` -Array [ - Array [ - Object { - "headers": Object { - "x-comicrelief-test-metadata": "{\\"user\\":\\"Dante Alighieri\\"}", +[ + [ + { + "headers": { + "x-comicrelief-test-metadata": "{"user":"Dante Alighieri"}", }, "method": "HEAD", "timeout": 10000, @@ -124,11 +124,11 @@ Array [ `; exports[`unit.services.HTTPService request adds the test header, PATCH request: config 1`] = ` -Array [ - Array [ - Object { - "headers": Object { - "x-comicrelief-test-metadata": "{\\"user\\":\\"Dante Alighieri\\"}", +[ + [ + { + "headers": { + "x-comicrelief-test-metadata": "{"user":"Dante Alighieri"}", }, "method": "PATCH", "timeout": 10000, @@ -139,11 +139,11 @@ Array [ `; exports[`unit.services.HTTPService request adds the test header, POST request: config 1`] = ` -Array [ - Array [ - Object { - "headers": Object { - "x-comicrelief-test-metadata": "{\\"user\\":\\"Dante Alighieri\\"}", +[ + [ + { + "headers": { + "x-comicrelief-test-metadata": "{"user":"Dante Alighieri"}", }, "method": "POST", "timeout": 10000, @@ -154,11 +154,11 @@ Array [ `; exports[`unit.services.HTTPService request adds the test header, PUT request: config 1`] = ` -Array [ - Array [ - Object { - "headers": Object { - "x-comicrelief-test-metadata": "{\\"user\\":\\"Dante Alighieri\\"}", +[ + [ + { + "headers": { + "x-comicrelief-test-metadata": "{"user":"Dante Alighieri"}", }, "method": "PUT", "timeout": 10000, @@ -169,11 +169,11 @@ Array [ `; exports[`unit.services.HTTPService request adds the test header, overriding timeout: config 1`] = ` -Array [ - Array [ - Object { - "headers": Object { - "x-comicrelief-test-metadata": "{\\"user\\":\\"Dante Alighieri\\"}", +[ + [ + { + "headers": { + "x-comicrelief-test-metadata": "{"user":"Dante Alighieri"}", }, "timeout": 99, }, @@ -182,11 +182,11 @@ Array [ `; exports[`unit.services.HTTPService request adds the test header, with URL: config 1`] = ` -Array [ - Array [ - Object { - "headers": Object { - "x-comicrelief-test-metadata": "{\\"user\\":\\"Dante Alighieri\\"}", +[ + [ + { + "headers": { + "x-comicrelief-test-metadata": "{"user":"Dante Alighieri"}", }, "timeout": 10000, "url": "/some/nested/path", @@ -196,12 +196,12 @@ Array [ `; exports[`unit.services.HTTPService request adds the test header, with baseURL: config 1`] = ` -Array [ - Array [ - Object { +[ + [ + { "baseUrl": "https://comicrelief.com/test", - "headers": Object { - "x-comicrelief-test-metadata": "{\\"user\\":\\"Dante Alighieri\\"}", + "headers": { + "x-comicrelief-test-metadata": "{"user":"Dante Alighieri"}", }, "timeout": 10000, "url": "/additional/url", @@ -211,12 +211,12 @@ Array [ `; exports[`unit.services.HTTPService request adds the test header, with headers: config 1`] = ` -Array [ - Array [ - Object { - "headers": Object { +[ + [ + { + "headers": { "Authorization": "Bearer test", - "x-comicrelief-test-metadata": "{\\"user\\":\\"Dante Alighieri\\"}", + "x-comicrelief-test-metadata": "{"user":"Dante Alighieri"}", }, "timeout": 10000, }, @@ -225,11 +225,11 @@ Array [ `; exports[`unit.services.HTTPService request adds the test header, with undefined headers: config 1`] = ` -Array [ - Array [ - Object { - "headers": Object { - "x-comicrelief-test-metadata": "{\\"user\\":\\"Dante Alighieri\\"}", +[ + [ + { + "headers": { + "x-comicrelief-test-metadata": "{"user":"Dante Alighieri"}", }, "timeout": 10000, }, @@ -238,10 +238,10 @@ Array [ `; exports[`unit.services.HTTPService request overriding timeout: config 1`] = ` -Array [ - Array [ - Object { - "headers": Object {}, +[ + [ + { + "headers": {}, "timeout": 99, }, ], @@ -249,10 +249,10 @@ Array [ `; exports[`unit.services.HTTPService request with URL: config 1`] = ` -Array [ - Array [ - Object { - "headers": Object {}, +[ + [ + { + "headers": {}, "timeout": 10000, "url": "/some/nested/path", }, @@ -261,11 +261,11 @@ Array [ `; exports[`unit.services.HTTPService request with baseURL: config 1`] = ` -Array [ - Array [ - Object { +[ + [ + { "baseUrl": "https://comicrelief.com/test", - "headers": Object {}, + "headers": {}, "timeout": 10000, "url": "/additional/url", }, @@ -274,10 +274,10 @@ Array [ `; exports[`unit.services.HTTPService request with headers: config 1`] = ` -Array [ - Array [ - Object { - "headers": Object { +[ + [ + { + "headers": { "Authorization": "Bearer test", }, "timeout": 10000, @@ -287,9 +287,9 @@ Array [ `; exports[`unit.services.HTTPService request with undefined headers: config 1`] = ` -Array [ - Array [ - Object { +[ + [ + { "headers": undefined, "timeout": 10000, }, diff --git a/tests/unit/services/__snapshots__/LoggerService.spec.ts.snap b/tests/unit/services/__snapshots__/LoggerService.spec.ts.snap index 5033ec6a..a3db2be1 100644 --- a/tests/unit/services/__snapshots__/LoggerService.spec.ts.snap +++ b/tests/unit/services/__snapshots__/LoggerService.spec.ts.snap @@ -1,13 +1,13 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`unit.services.LoggerService error Trims down the axios error: EMPTY 1`] = ` -Object { - "config": Object { +{ + "config": { "method": "get", "url": "http://localhost:9999", }, "message": "some-message", - "response": Object { + "response": { "data": undefined, "status": undefined, }, @@ -15,14 +15,14 @@ Object { `; exports[`unit.services.LoggerService error Trims down the axios error: HTTP_417 1`] = ` -Object { - "config": Object { +{ + "config": { "method": "get", "url": "http://localhost:9999", }, "message": "some-message", - "response": Object { - "data": Object { + "response": { + "data": { "data": 1, }, "status": 417, @@ -31,8 +31,8 @@ Object { `; exports[`unit.services.LoggerService error Trims down the axios error: UNDEFINED 1`] = ` -Object { - "config": Object { +{ + "config": { "method": "get", "url": "http://localhost:9999", }, @@ -41,13 +41,13 @@ Object { `; exports[`unit.services.LoggerService info Trims down the axios error: EMPTY 1`] = ` -Object { - "config": Object { +{ + "config": { "method": "get", "url": "http://localhost:9999", }, "message": "some-message", - "response": Object { + "response": { "data": undefined, "status": undefined, }, @@ -55,14 +55,14 @@ Object { `; exports[`unit.services.LoggerService info Trims down the axios error: HTTP_417 1`] = ` -Object { - "config": Object { +{ + "config": { "method": "get", "url": "http://localhost:9999", }, "message": "some-message", - "response": Object { - "data": Object { + "response": { + "data": { "data": 1, }, "status": 417, @@ -71,8 +71,8 @@ Object { `; exports[`unit.services.LoggerService info Trims down the axios error: UNDEFINED 1`] = ` -Object { - "config": Object { +{ + "config": { "method": "get", "url": "http://localhost:9999", }, @@ -80,54 +80,54 @@ Object { } `; -exports[`unit.services.LoggerService object Logs a '"a string"' with level: 'error' 1`] = `"My action: '\\"a string\\"'"`; +exports[`unit.services.LoggerService object Logs a '"a string"' with level: 'error' 1`] = `"My action: '"a string"'"`; -exports[`unit.services.LoggerService object Logs a '"a string"' with level: 'info' 1`] = `"My action: '\\"a string\\"'"`; +exports[`unit.services.LoggerService object Logs a '"a string"' with level: 'info' 1`] = `"My action: '"a string"'"`; -exports[`unit.services.LoggerService object Logs a '"a string"' with level: 'warning' 1`] = `"My action: '\\"a string\\"'"`; +exports[`unit.services.LoggerService object Logs a '"a string"' with level: 'warning' 1`] = `"My action: '"a string"'"`; exports[`unit.services.LoggerService object Logs a '{"a":{"b":null},"c":"a string"}' with level: 'error' 1`] = ` "My action: '{ - \\"a\\": { - \\"b\\": null + "a": { + "b": null }, - \\"c\\": \\"a string\\" + "c": "a string" }'" `; exports[`unit.services.LoggerService object Logs a '{"a":{"b":null},"c":"a string"}' with level: 'info' 1`] = ` "My action: '{ - \\"a\\": { - \\"b\\": null + "a": { + "b": null }, - \\"c\\": \\"a string\\" + "c": "a string" }'" `; exports[`unit.services.LoggerService object Logs a '{"a":{"b":null},"c":"a string"}' with level: 'warning' 1`] = ` "My action: '{ - \\"a\\": { - \\"b\\": null + "a": { + "b": null }, - \\"c\\": \\"a string\\" + "c": "a string" }'" `; exports[`unit.services.LoggerService object Logs a '{"a":1}' with level: 'error' 1`] = ` "My action: '{ - \\"a\\": 1 + "a": 1 }'" `; exports[`unit.services.LoggerService object Logs a '{"a":1}' with level: 'info' 1`] = ` "My action: '{ - \\"a\\": 1 + "a": 1 }'" `; exports[`unit.services.LoggerService object Logs a '{"a":1}' with level: 'warning' 1`] = ` "My action: '{ - \\"a\\": 1 + "a": 1 }'" `; diff --git a/tests/unit/services/__snapshots__/RequestService.spec.ts.snap b/tests/unit/services/__snapshots__/RequestService.spec.ts.snap index 5eaa46b7..3acb76a4 100644 --- a/tests/unit/services/__snapshots__/RequestService.spec.ts.snap +++ b/tests/unit/services/__snapshots__/RequestService.spec.ts.snap @@ -2,11 +2,11 @@ exports[`unit.services.RequestService HTTP DELETE validateAgainstConstraints should return a response containing validation errors if the data provided is incorrect 1`] = ` ResponseModel { - "body": Object { - "data": Object {}, + "body": { + "data": {}, "message": "required fields are missing", - "validation_errors": Object { - "giftaid": Array [ + "validation_errors": { + "giftaid": [ "Giftaid is not a number", ], }, @@ -17,11 +17,11 @@ ResponseModel { exports[`unit.services.RequestService HTTP GET validateAgainstConstraints should return a response containing validation errors if the data provided is incorrect 1`] = ` ResponseModel { - "body": Object { - "data": Object {}, + "body": { + "data": {}, "message": "required fields are missing", - "validation_errors": Object { - "giftaid": Array [ + "validation_errors": { + "giftaid": [ "Giftaid is not a number", ], }, @@ -32,11 +32,11 @@ ResponseModel { exports[`unit.services.RequestService HTTP HEAD validateAgainstConstraints should return a response containing validation errors if the data provided is incorrect 1`] = ` ResponseModel { - "body": Object { - "data": Object {}, + "body": { + "data": {}, "message": "required fields are missing", - "validation_errors": Object { - "giftaid": Array [ + "validation_errors": { + "giftaid": [ "Giftaid is not a number", ], }, @@ -47,11 +47,11 @@ ResponseModel { exports[`unit.services.RequestService HTTP OPTIONS validateAgainstConstraints should return a response containing validation errors if the data provided is incorrect 1`] = ` ResponseModel { - "body": Object { - "data": Object {}, + "body": { + "data": {}, "message": "required fields are missing", - "validation_errors": Object { - "giftaid": Array [ + "validation_errors": { + "giftaid": [ "Giftaid is not a number", ], }, @@ -62,11 +62,11 @@ ResponseModel { exports[`unit.services.RequestService HTTP PATCH validateAgainstConstraints should return a response containing validation errors if the data provided is incorrect 1`] = ` ResponseModel { - "body": Object { - "data": Object {}, + "body": { + "data": {}, "message": "required fields are missing", - "validation_errors": Object { - "giftaid": Array [ + "validation_errors": { + "giftaid": [ "Giftaid is not a number", ], }, @@ -77,11 +77,11 @@ ResponseModel { exports[`unit.services.RequestService HTTP POST validateAgainstConstraints should return a response containing validation errors if the data provided is incorrect 1`] = ` ResponseModel { - "body": Object { - "data": Object {}, + "body": { + "data": {}, "message": "required fields are missing", - "validation_errors": Object { - "giftaid": Array [ + "validation_errors": { + "giftaid": [ "Giftaid is not a number", ], }, @@ -92,11 +92,11 @@ ResponseModel { exports[`unit.services.RequestService HTTP PUT validateAgainstConstraints should return a response containing validation errors if the data provided is incorrect 1`] = ` ResponseModel { - "body": Object { - "data": Object {}, + "body": { + "data": {}, "message": "required fields are missing", - "validation_errors": Object { - "giftaid": Array [ + "validation_errors": { + "giftaid": [ "Giftaid is not a number", ], }, diff --git a/yarn.lock b/yarn.lock index 75682aad..18af5b98 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,11 @@ # yarn lockfile v1 +"@aashutoshrathi/word-wrap@^1.2.3": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" + integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== + "@ampproject/remapping@^2.1.0": version "2.2.0" resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" @@ -747,6 +752,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz#4b8aea3b069d8cb8a72cdfe28ddf5ceca695ef2f" integrity sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w== +"@babel/helper-plugin-utils@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295" + integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== + "@babel/helper-simple-access@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz#d6d8f51f4ac2978068df934b569f08f29788c7ea" @@ -834,6 +844,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" +"@babel/plugin-syntax-jsx@^7.7.2": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz#a6b68e84fb76e759fc3b93e901876ffabbe1d918" + integrity sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" @@ -899,7 +916,7 @@ "@babel/parser" "^7.18.10" "@babel/types" "^7.18.10" -"@babel/traverse@^7.18.10", "@babel/traverse@^7.18.9", "@babel/traverse@^7.7.2": +"@babel/traverse@^7.18.10", "@babel/traverse@^7.18.9": version "7.18.11" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.11.tgz#3d51f2afbd83ecf9912bcbb5c4d94e3d2ddaa16f" integrity sha512-TG9PiM2R/cWCAy6BPJKeHzNbu4lPzOSZpeMfeNErskGpTJx6trEvFaVCbDvpcxwy49BKWmEPwiW8mrysNiDvIQ== @@ -967,45 +984,74 @@ esquery "^1.4.0" jsdoc-type-pratt-parser "~3.1.0" -"@eslint/eslintrc@^1.3.0": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.0.tgz#29f92c30bb3e771e4a2048c95fa6855392dfac4f" - integrity sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw== +"@eslint-community/eslint-utils@^4.2.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.6.1": + version "4.8.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.8.1.tgz#8c4bb756cc2aa7eaf13cfa5e69c83afb3260c20c" + integrity sha512-PWiOzLIUAjN/w5K17PoF4n6sKBw0gqLHPhywmYHP4t1VFQQVYeb1yWsJwnMVEMl3tUHME7X/SJPZLmtG7XBDxQ== + +"@eslint/eslintrc@^2.1.2": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.2.tgz#c6936b4b328c64496692f76944e755738be62396" + integrity sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g== dependencies: ajv "^6.12.4" debug "^4.3.2" - espree "^9.3.2" - globals "^13.15.0" + espree "^9.6.0" + globals "^13.19.0" ignore "^5.2.0" import-fresh "^3.2.1" js-yaml "^4.1.0" minimatch "^3.1.2" strip-json-comments "^3.1.1" +"@eslint/js@8.49.0": + version "8.49.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.49.0.tgz#86f79756004a97fa4df866835093f1df3d03c333" + integrity sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w== + "@gar/promisify@^1.1.3": version "1.1.3" resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== -"@humanwhocodes/config-array@^0.10.4": - version "0.10.4" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.10.4.tgz#01e7366e57d2ad104feea63e72248f22015c520c" - integrity sha512-mXAIHxZT3Vcpg83opl1wGlVZ9xydbfZO3r5YfRSH6Gpp2J/PfdBP0wbDa2sO6/qRbcalpoevVyW6A/fI6LfeMw== +"@humanwhocodes/config-array@^0.11.11": + version "0.11.11" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.11.tgz#88a04c570dbbc7dd943e4712429c3df09bc32844" + integrity sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA== dependencies: "@humanwhocodes/object-schema" "^1.2.1" debug "^4.1.1" - minimatch "^3.0.4" + minimatch "^3.0.5" -"@humanwhocodes/gitignore-to-minimatch@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz#316b0a63b91c10e53f242efb4ace5c3b34e8728d" - integrity sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA== +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== "@humanwhocodes/object-schema@^1.2.1": version "1.2.1" resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + "@isaacs/string-locale-compare@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@isaacs/string-locale-compare/-/string-locale-compare-1.1.0.tgz#291c227e93fd407a96ecd59879a35809120e432b" @@ -1034,110 +1080,110 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== -"@jest/console@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-28.1.3.tgz#2030606ec03a18c31803b8a36382762e447655df" - integrity sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw== +"@jest/console@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.7.0.tgz#cd4822dbdb84529265c5a2bdb529a3c9cc950ffc" + integrity sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg== dependencies: - "@jest/types" "^28.1.3" + "@jest/types" "^29.6.3" "@types/node" "*" chalk "^4.0.0" - jest-message-util "^28.1.3" - jest-util "^28.1.3" + jest-message-util "^29.7.0" + jest-util "^29.7.0" slash "^3.0.0" -"@jest/core@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-28.1.3.tgz#0ebf2bd39840f1233cd5f2d1e6fc8b71bd5a1ac7" - integrity sha512-CIKBrlaKOzA7YG19BEqCw3SLIsEwjZkeJzf5bdooVnW4bH5cktqe3JX+G2YV1aK5vP8N9na1IGWFzYaTp6k6NA== +"@jest/core@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.7.0.tgz#b6cccc239f30ff36609658c5a5e2291757ce448f" + integrity sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg== dependencies: - "@jest/console" "^28.1.3" - "@jest/reporters" "^28.1.3" - "@jest/test-result" "^28.1.3" - "@jest/transform" "^28.1.3" - "@jest/types" "^28.1.3" + "@jest/console" "^29.7.0" + "@jest/reporters" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" ci-info "^3.2.0" exit "^0.1.2" graceful-fs "^4.2.9" - jest-changed-files "^28.1.3" - jest-config "^28.1.3" - jest-haste-map "^28.1.3" - jest-message-util "^28.1.3" - jest-regex-util "^28.0.2" - jest-resolve "^28.1.3" - jest-resolve-dependencies "^28.1.3" - jest-runner "^28.1.3" - jest-runtime "^28.1.3" - jest-snapshot "^28.1.3" - jest-util "^28.1.3" - jest-validate "^28.1.3" - jest-watcher "^28.1.3" + jest-changed-files "^29.7.0" + jest-config "^29.7.0" + jest-haste-map "^29.7.0" + jest-message-util "^29.7.0" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-resolve-dependencies "^29.7.0" + jest-runner "^29.7.0" + jest-runtime "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" + jest-watcher "^29.7.0" micromatch "^4.0.4" - pretty-format "^28.1.3" - rimraf "^3.0.0" + pretty-format "^29.7.0" slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/environment@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-28.1.3.tgz#abed43a6b040a4c24fdcb69eab1f97589b2d663e" - integrity sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA== +"@jest/environment@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.7.0.tgz#24d61f54ff1f786f3cd4073b4b94416383baf2a7" + integrity sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw== dependencies: - "@jest/fake-timers" "^28.1.3" - "@jest/types" "^28.1.3" + "@jest/fake-timers" "^29.7.0" + "@jest/types" "^29.6.3" "@types/node" "*" - jest-mock "^28.1.3" + jest-mock "^29.7.0" -"@jest/expect-utils@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-28.1.3.tgz#58561ce5db7cd253a7edddbc051fb39dda50f525" - integrity sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA== +"@jest/expect-utils@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6" + integrity sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA== dependencies: - jest-get-type "^28.0.2" + jest-get-type "^29.6.3" -"@jest/expect@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-28.1.3.tgz#9ac57e1d4491baca550f6bdbd232487177ad6a72" - integrity sha512-lzc8CpUbSoE4dqT0U+g1qODQjBRHPpCPXissXD4mS9+sWQdmmpeJ9zSH1rS1HEkrsMN0fb7nKrJ9giAR1d3wBw== +"@jest/expect@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.7.0.tgz#76a3edb0cb753b70dfbfe23283510d3d45432bf2" + integrity sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ== dependencies: - expect "^28.1.3" - jest-snapshot "^28.1.3" + expect "^29.7.0" + jest-snapshot "^29.7.0" -"@jest/fake-timers@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-28.1.3.tgz#230255b3ad0a3d4978f1d06f70685baea91c640e" - integrity sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw== +"@jest/fake-timers@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.7.0.tgz#fd91bf1fffb16d7d0d24a426ab1a47a49881a565" + integrity sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ== dependencies: - "@jest/types" "^28.1.3" - "@sinonjs/fake-timers" "^9.1.2" + "@jest/types" "^29.6.3" + "@sinonjs/fake-timers" "^10.0.2" "@types/node" "*" - jest-message-util "^28.1.3" - jest-mock "^28.1.3" - jest-util "^28.1.3" + jest-message-util "^29.7.0" + jest-mock "^29.7.0" + jest-util "^29.7.0" -"@jest/globals@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-28.1.3.tgz#a601d78ddc5fdef542728309894895b4a42dc333" - integrity sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA== +"@jest/globals@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.7.0.tgz#8d9290f9ec47ff772607fa864ca1d5a2efae1d4d" + integrity sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ== dependencies: - "@jest/environment" "^28.1.3" - "@jest/expect" "^28.1.3" - "@jest/types" "^28.1.3" + "@jest/environment" "^29.7.0" + "@jest/expect" "^29.7.0" + "@jest/types" "^29.6.3" + jest-mock "^29.7.0" -"@jest/reporters@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-28.1.3.tgz#9adf6d265edafc5fc4a434cfb31e2df5a67a369a" - integrity sha512-JuAy7wkxQZVNU/V6g9xKzCGC5LVXx9FDcABKsSXp5MiKPEE2144a/vXTEDoyzjUpZKfVwp08Wqg5A4WfTMAzjg== +"@jest/reporters@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.7.0.tgz#04b262ecb3b8faa83b0b3d321623972393e8f4c7" + integrity sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^28.1.3" - "@jest/test-result" "^28.1.3" - "@jest/transform" "^28.1.3" - "@jest/types" "^28.1.3" - "@jridgewell/trace-mapping" "^0.3.13" + "@jest/console" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@jridgewell/trace-mapping" "^0.3.18" "@types/node" "*" chalk "^4.0.0" collect-v8-coverage "^1.0.0" @@ -1145,82 +1191,81 @@ glob "^7.1.3" graceful-fs "^4.2.9" istanbul-lib-coverage "^3.0.0" - istanbul-lib-instrument "^5.1.0" + istanbul-lib-instrument "^6.0.0" istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^4.0.0" istanbul-reports "^3.1.3" - jest-message-util "^28.1.3" - jest-util "^28.1.3" - jest-worker "^28.1.3" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + jest-worker "^29.7.0" slash "^3.0.0" string-length "^4.0.1" strip-ansi "^6.0.0" - terminal-link "^2.0.0" v8-to-istanbul "^9.0.1" -"@jest/schemas@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-28.1.3.tgz#ad8b86a66f11f33619e3d7e1dcddd7f2d40ff905" - integrity sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg== +"@jest/schemas@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" + integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== dependencies: - "@sinclair/typebox" "^0.24.1" + "@sinclair/typebox" "^0.27.8" -"@jest/source-map@^28.1.2": - version "28.1.2" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-28.1.2.tgz#7fe832b172b497d6663cdff6c13b0a920e139e24" - integrity sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww== +"@jest/source-map@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.6.3.tgz#d90ba772095cf37a34a5eb9413f1b562a08554c4" + integrity sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw== dependencies: - "@jridgewell/trace-mapping" "^0.3.13" + "@jridgewell/trace-mapping" "^0.3.18" callsites "^3.0.0" graceful-fs "^4.2.9" -"@jest/test-result@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-28.1.3.tgz#5eae945fd9f4b8fcfce74d239e6f725b6bf076c5" - integrity sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg== +"@jest/test-result@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.7.0.tgz#8db9a80aa1a097bb2262572686734baed9b1657c" + integrity sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA== dependencies: - "@jest/console" "^28.1.3" - "@jest/types" "^28.1.3" + "@jest/console" "^29.7.0" + "@jest/types" "^29.6.3" "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-sequencer@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-28.1.3.tgz#9d0c283d906ac599c74bde464bc0d7e6a82886c3" - integrity sha512-NIMPEqqa59MWnDi1kvXXpYbqsfQmSJsIbnd85mdVGkiDfQ9WQQTXOLsvISUfonmnBT+w85WEgneCigEEdHDFxw== +"@jest/test-sequencer@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz#6cef977ce1d39834a3aea887a1726628a6f072ce" + integrity sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw== dependencies: - "@jest/test-result" "^28.1.3" + "@jest/test-result" "^29.7.0" graceful-fs "^4.2.9" - jest-haste-map "^28.1.3" + jest-haste-map "^29.7.0" slash "^3.0.0" -"@jest/transform@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-28.1.3.tgz#59d8098e50ab07950e0f2fc0fc7ec462371281b0" - integrity sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA== +"@jest/transform@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.7.0.tgz#df2dd9c346c7d7768b8a06639994640c642e284c" + integrity sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw== dependencies: "@babel/core" "^7.11.6" - "@jest/types" "^28.1.3" - "@jridgewell/trace-mapping" "^0.3.13" + "@jest/types" "^29.6.3" + "@jridgewell/trace-mapping" "^0.3.18" babel-plugin-istanbul "^6.1.1" chalk "^4.0.0" - convert-source-map "^1.4.0" - fast-json-stable-stringify "^2.0.0" + convert-source-map "^2.0.0" + fast-json-stable-stringify "^2.1.0" graceful-fs "^4.2.9" - jest-haste-map "^28.1.3" - jest-regex-util "^28.0.2" - jest-util "^28.1.3" + jest-haste-map "^29.7.0" + jest-regex-util "^29.6.3" + jest-util "^29.7.0" micromatch "^4.0.4" pirates "^4.0.4" slash "^3.0.0" - write-file-atomic "^4.0.1" + write-file-atomic "^4.0.2" -"@jest/types@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-28.1.3.tgz#b05de80996ff12512bc5ceb1d208285a7d11748b" - integrity sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ== +"@jest/types@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" + integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== dependencies: - "@jest/schemas" "^28.1.3" + "@jest/schemas" "^29.6.3" "@types/istanbul-lib-coverage" "^2.0.0" "@types/istanbul-reports" "^3.0.0" "@types/node" "*" @@ -1249,6 +1294,11 @@ resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== + "@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": version "1.1.2" resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" @@ -1259,6 +1309,11 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== +"@jridgewell/sourcemap-codec@^1.4.14": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + "@jridgewell/trace-mapping@0.3.9": version "0.3.9" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" @@ -1267,7 +1322,7 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.13", "@jridgewell/trace-mapping@^0.3.9": +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.9": version "0.3.15" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz#aba35c48a38d3fd84b37e66c9c0423f9744f9774" integrity sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g== @@ -1275,6 +1330,14 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" +"@jridgewell/trace-mapping@^0.3.18": + version "0.3.19" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz#f8a3249862f91be48d3127c3cfe992f79b4b8811" + integrity sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -1288,7 +1351,7 @@ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== -"@nodelib/fs.walk@^1.2.3": +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": version "1.2.8" resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== @@ -1296,10 +1359,10 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@npmcli/arborist@^5.0.0", "@npmcli/arborist@^5.0.4": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@npmcli/arborist/-/arborist-5.5.0.tgz#e40f5379a900ef80c4a97c4e4ca0e9796b702861" - integrity sha512-+Qg3h+J0o4NQMpiC4JgllnrvwBJ1utSBWOTa2xsWqbbVAcPTdEwPV3V0BWEhhrx5zt3hRfIwv9TWmYBvyxd4aw== +"@npmcli/arborist@^5.6.3": + version "5.6.3" + resolved "https://registry.yarnpkg.com/@npmcli/arborist/-/arborist-5.6.3.tgz#40810080272e097b4a7a4f56108f4a31638a9874" + integrity sha512-/7hbqEM6YuRjwTcQXkK1+xKslEblY5kFQe0tZ7jKyMlIR6x4iOmhLErIkBBGtTKvYxRKdpcxnFXjCobg3UqmsA== dependencies: "@isaacs/string-locale-compare" "^1.1.0" "@npmcli/installed-package-contents" "^1.0.7" @@ -1309,11 +1372,12 @@ "@npmcli/name-from-folder" "^1.0.1" "@npmcli/node-gyp" "^2.0.0" "@npmcli/package-json" "^2.0.0" - "@npmcli/query" "^1.1.1" + "@npmcli/query" "^1.2.0" "@npmcli/run-script" "^4.1.3" - bin-links "^3.0.0" - cacache "^16.0.6" + bin-links "^3.0.3" + cacache "^16.1.3" common-ancestor-path "^1.0.1" + hosted-git-info "^5.2.1" json-parse-even-better-errors "^2.3.1" json-stringify-nice "^1.1.4" minimatch "^5.1.0" @@ -1322,7 +1386,7 @@ nopt "^6.0.0" npm-install-checks "^5.0.0" npm-package-arg "^9.0.0" - npm-pick-manifest "^7.0.0" + npm-pick-manifest "^7.0.2" npm-registry-fetch "^13.0.0" npmlog "^6.0.2" pacote "^13.6.1" @@ -1344,9 +1408,9 @@ integrity sha512-8yQtQ9ArHh/TzdUDKQwEvwCgpDuhSWTDAbiKMl3854PcT+Dk4UmWaiawuFTLy9n5twzXOBXVflWe+90/ffXQrA== "@npmcli/config@^4.2.1": - version "4.2.1" - resolved "https://registry.yarnpkg.com/@npmcli/config/-/config-4.2.1.tgz#7a4b46f4a315fe369a3f8543c67fae0b89549a40" - integrity sha512-iJEnXNAGGr7sGUcoKmeJNrc943vFiWrDWq6DNK/t+SuqoObmozMb3tN3G5T9yo3uBf5Cw4h+SWgoqSaiwczl0Q== + version "4.2.2" + resolved "https://registry.yarnpkg.com/@npmcli/config/-/config-4.2.2.tgz#2e3334dda84f48d059309c53d152e66b05ca24b7" + integrity sha512-5GNcLd+0c4bYBnFop53+26CO5GQP0R9YcxlernohpHDWdIgzUg9I0+GEMk3sNHnLntATVU39d283A4OO+W402w== dependencies: "@npmcli/map-workspaces" "^2.0.2" ini "^3.0.0" @@ -1364,7 +1428,7 @@ dependencies: ansi-styles "^4.3.0" -"@npmcli/fs@^2.1.0", "@npmcli/fs@^2.1.1": +"@npmcli/fs@^2.1.0": version "2.1.1" resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-2.1.1.tgz#c0c480b03450d8b9fc086816a50cb682668a48bf" integrity sha512-1Q0uzx6c/NVNGszePbr5Gc2riSU1zLpNlo/1YWntH+eaPmMgBssAW0qXofCVkpdj3ce4swZtlDYQu+NKiYcptg== @@ -1372,10 +1436,25 @@ "@gar/promisify" "^1.1.3" semver "^7.3.5" +"@npmcli/fs@^2.1.1": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-2.1.2.tgz#a9e2541a4a2fec2e69c29b35e6060973da79b865" + integrity sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ== + dependencies: + "@gar/promisify" "^1.1.3" + semver "^7.3.5" + +"@npmcli/fs@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-3.1.0.tgz#233d43a25a91d68c3a863ba0da6a3f00924a173e" + integrity sha512-7kZUAaLscfgbwBQRbvdMYaZOWyMEcPTH/tJjnyAWJ/dvvs9Ef+CERx/qJb9GExJpl1qipaDGn7KqHnFGGixd0w== + dependencies: + semver "^7.3.5" + "@npmcli/git@^3.0.0": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@npmcli/git/-/git-3.0.1.tgz#049b99b1381a2ddf7dc56ba3e91eaf76ca803a8d" - integrity sha512-UU85F/T+F1oVn3IsB/L6k9zXIMpXBuUBE25QDH0SsURwT6IOBqkC7M16uqo2vVZIyji3X1K4XH9luip7YekH1A== + version "3.0.2" + resolved "https://registry.yarnpkg.com/@npmcli/git/-/git-3.0.2.tgz#5c5de6b4d70474cf2d09af149ce42e4e1dacb931" + integrity sha512-CAcd08y3DWBJqJDpfuVL0uijlq5oaXaOJEKHKc4wqrjd00gkvTZB+nFuLn+doOOKddaQS9JfqtNoFCO2LCvA3w== dependencies: "@npmcli/promise-spawn" "^3.0.0" lru-cache "^7.4.4" @@ -1447,10 +1526,10 @@ dependencies: infer-owner "^1.0.4" -"@npmcli/query@^1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@npmcli/query/-/query-1.1.1.tgz#462c4268473ae39e89d5fefbad94d9af7e1217c4" - integrity sha512-UF3I0fD94wzQ84vojMO2jDB8ibjRSTqhi8oz2mzVKiJ9gZHbeGlu9kzPvgHuGDK0Hf2cARhWtTfCDHNEwlL9hg== +"@npmcli/query@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@npmcli/query/-/query-1.2.0.tgz#46468d583cf013aa92102970700f9555314aabe4" + integrity sha512-uWglsUM3PjBLgTSmZ3/vygeGdvWEIZ3wTUnzGFbprC/RtvQSaT+GAXu1DXmSFj2bD3oOZdcRm1xdzsV2z1YWdw== dependencies: npm-package-arg "^9.1.0" postcss-selector-parser "^6.0.10" @@ -1468,105 +1547,128 @@ which "^2.0.2" "@octokit/auth-token@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-3.0.0.tgz#6f22c5fc56445c496628488ba6810131558fa4a9" - integrity sha512-MDNFUBcJIptB9At7HiV7VCvU3NcL4GnfCQaP8C5lrxWrRPMJBnemYtehaKSOlaM7AYxeRyj9etenu8LVpSpVaQ== - dependencies: - "@octokit/types" "^6.0.3" + version "3.0.4" + resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-3.0.4.tgz#70e941ba742bdd2b49bdb7393e821dea8520a3db" + integrity sha512-TWFX7cZF2LXoCvdmJWY7XVPi74aSY0+FfBZNSXEXFkMpjcqsQwDSYVv5FhRFaI0V1ECnwbz4j59T/G+rXNWaIQ== -"@octokit/core@^4.0.0": - version "4.0.4" - resolved "https://registry.yarnpkg.com/@octokit/core/-/core-4.0.4.tgz#335d9b377691e3264ce57a9e5a1f6cda783e5838" - integrity sha512-sUpR/hc4Gc7K34o60bWC7WUH6Q7T6ftZ2dUmepSyJr9PRF76/qqkWjE2SOEzCqLA5W83SaISymwKtxks+96hPQ== +"@octokit/core@^4.2.1": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@octokit/core/-/core-4.2.4.tgz#d8769ec2b43ff37cc3ea89ec4681a20ba58ef907" + integrity sha512-rYKilwgzQ7/imScn3M9/pFfUf4I1AZEH3KhyJmtPdE2zfaXAn2mFfUy4FbKewzc2We5y/LlKLj36fWJLKC2SIQ== dependencies: "@octokit/auth-token" "^3.0.0" "@octokit/graphql" "^5.0.0" "@octokit/request" "^6.0.0" "@octokit/request-error" "^3.0.0" - "@octokit/types" "^6.0.3" + "@octokit/types" "^9.0.0" before-after-hook "^2.2.0" universal-user-agent "^6.0.0" "@octokit/endpoint@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-7.0.0.tgz#be758a1236d68d6bbb505e686dd50881c327a519" - integrity sha512-Kz/mIkOTjs9rV50hf/JK9pIDl4aGwAtT8pry6Rpy+hVXkAPhXanNQRxMoq6AeRgDCZR6t/A1zKniY2V1YhrzlQ== + version "7.0.6" + resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-7.0.6.tgz#791f65d3937555141fb6c08f91d618a7d645f1e2" + integrity sha512-5L4fseVRUsDFGR00tMWD/Trdeeihn999rTMGRMC1G/Ldi1uWlWJzI98H4Iak5DB/RVvQuyMYKqSK/R6mbSOQyg== dependencies: - "@octokit/types" "^6.0.3" + "@octokit/types" "^9.0.0" is-plain-object "^5.0.0" universal-user-agent "^6.0.0" "@octokit/graphql@^5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-5.0.0.tgz#2cc6eb3bf8e0278656df1a7d0ca0d7591599e3b3" - integrity sha512-1ZZ8tX4lUEcLPvHagfIVu5S2xpHYXAmgN0+95eAOPoaVPzCfUXJtA5vASafcpWcO86ze0Pzn30TAx72aB2aguQ== + version "5.0.6" + resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-5.0.6.tgz#9eac411ac4353ccc5d3fca7d76736e6888c5d248" + integrity sha512-Fxyxdy/JH0MnIB5h+UQ3yCoh1FG4kWXfFKkpWqjZHw/p+Kc8Y44Hu/kCgNBT6nU1shNumEchmW/sUO1JuQnPcw== dependencies: "@octokit/request" "^6.0.0" - "@octokit/types" "^6.0.3" + "@octokit/types" "^9.0.0" universal-user-agent "^6.0.0" -"@octokit/openapi-types@^12.11.0": - version "12.11.0" - resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-12.11.0.tgz#da5638d64f2b919bca89ce6602d059f1b52d3ef0" - integrity sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ== +"@octokit/openapi-types@^18.0.0": + version "18.0.0" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-18.0.0.tgz#f43d765b3c7533fd6fb88f3f25df079c24fccf69" + integrity sha512-V8GImKs3TeQRxRtXFpG2wl19V7444NIOTDF24AWuIbmNaNYOQMWRbjcGDXV5B+0n887fgDcuMNOmlul+k+oJtw== -"@octokit/plugin-paginate-rest@^3.0.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-3.1.0.tgz#86f8be759ce2d6d7c879a31490fd2f7410b731f0" - integrity sha512-+cfc40pMzWcLkoDcLb1KXqjX0jTGYXjKuQdFQDc6UAknISJHnZTiBqld6HDwRJvD4DsouDKrWXNbNV0lE/3AXA== +"@octokit/plugin-paginate-rest@^6.1.2": + version "6.1.2" + resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-6.1.2.tgz#f86456a7a1fe9e58fec6385a85cf1b34072341f8" + integrity sha512-qhrmtQeHU/IivxucOV1bbI/xZyC/iOBhclokv7Sut5vnejAIAEXVcGQeRpQlU39E0WwK9lNvJHphHri/DB6lbQ== dependencies: - "@octokit/types" "^6.41.0" + "@octokit/tsconfig" "^1.0.2" + "@octokit/types" "^9.2.3" -"@octokit/plugin-request-log@^1.0.4": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz#5e50ed7083a613816b1e4a28aeec5fb7f1462e85" - integrity sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA== +"@octokit/plugin-retry@^4.1.3": + version "4.1.6" + resolved "https://registry.yarnpkg.com/@octokit/plugin-retry/-/plugin-retry-4.1.6.tgz#e33b1e520f0bd24d515c9901676b55df64dfc795" + integrity sha512-obkYzIgEC75r8+9Pnfiiqy3y/x1bc3QLE5B7qvv9wi9Kj0R5tGQFC6QMBg1154WQ9lAVypuQDGyp3hNpp15gQQ== + dependencies: + "@octokit/types" "^9.0.0" + bottleneck "^2.15.3" -"@octokit/plugin-rest-endpoint-methods@^6.0.0": - version "6.2.0" - resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-6.2.0.tgz#c06359d2f94436f8c67d345093cb02dedd31d974" - integrity sha512-PZ+yfkbZAuRUtqu6Y191/V3eM0KBPx+Yq7nh+ONPdpm3EX4pd5UnK2y2XgO/0AtNum5a4aJCDjqsDuUZ2hWRXw== +"@octokit/plugin-throttling@^5.2.3": + version "5.2.3" + resolved "https://registry.yarnpkg.com/@octokit/plugin-throttling/-/plugin-throttling-5.2.3.tgz#9f552a14dcee5c7326dd9dee64a71ea76b108814" + integrity sha512-C9CFg9mrf6cugneKiaI841iG8DOv6P5XXkjmiNNut+swePxQ7RWEdAZRp5rJoE1hjsIqiYcKa/ZkOQ+ujPI39Q== dependencies: - "@octokit/types" "^6.41.0" - deprecation "^2.3.1" + "@octokit/types" "^9.0.0" + bottleneck "^2.15.3" "@octokit/request-error@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-3.0.0.tgz#f527d178f115a3b62d76ce4804dd5bdbc0270a81" - integrity sha512-WBtpzm9lR8z4IHIMtOqr6XwfkGvMOOILNLxsWvDwtzm/n7f5AWuqJTXQXdDtOvPfTDrH4TPhEvW2qMlR4JFA2w== + version "3.0.3" + resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-3.0.3.tgz#ef3dd08b8e964e53e55d471acfe00baa892b9c69" + integrity sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ== dependencies: - "@octokit/types" "^6.0.3" + "@octokit/types" "^9.0.0" deprecation "^2.0.0" once "^1.4.0" "@octokit/request@^6.0.0": - version "6.2.0" - resolved "https://registry.yarnpkg.com/@octokit/request/-/request-6.2.0.tgz#9c25606df84e6f2ccbcc2c58e1d35438e20b688b" - integrity sha512-7IAmHnaezZrgUqtRShMlByJK33MT9ZDnMRgZjnRrRV9a/jzzFwKGz0vxhFU6i7VMLraYcQ1qmcAOin37Kryq+Q== + version "6.2.8" + resolved "https://registry.yarnpkg.com/@octokit/request/-/request-6.2.8.tgz#aaf480b32ab2b210e9dadd8271d187c93171d8eb" + integrity sha512-ow4+pkVQ+6XVVsekSYBzJC0VTVvh/FCTUUgTsboGq+DTeWdyIFV8WSCdo0RIxk6wSkBTHqIK1mYuY7nOBXOchw== dependencies: "@octokit/endpoint" "^7.0.0" "@octokit/request-error" "^3.0.0" - "@octokit/types" "^6.16.1" + "@octokit/types" "^9.0.0" is-plain-object "^5.0.0" node-fetch "^2.6.7" universal-user-agent "^6.0.0" -"@octokit/rest@^19.0.0": - version "19.0.3" - resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-19.0.3.tgz#b9a4e8dc8d53e030d611c053153ee6045f080f02" - integrity sha512-5arkTsnnRT7/sbI4fqgSJ35KiFaN7zQm0uQiQtivNQLI8RQx8EHwJCajcTUwmaCMNDg7tdCvqAnc7uvHHPxrtQ== +"@octokit/tsconfig@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@octokit/tsconfig/-/tsconfig-1.0.2.tgz#59b024d6f3c0ed82f00d08ead5b3750469125af7" + integrity sha512-I0vDR0rdtP8p2lGMzvsJzbhdOWy405HcGovrspJ8RRibHnyRgggUSNO5AIox5LmqiwmatHKYsvj6VGFHkqS7lA== + +"@octokit/types@^9.0.0", "@octokit/types@^9.2.3": + version "9.3.2" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-9.3.2.tgz#3f5f89903b69f6a2d196d78ec35f888c0013cac5" + integrity sha512-D4iHGTdAnEEVsB8fl95m1hiz7D5YiRdQ9b/OEb3BYRVwbLsGHcRVPz+u+BgRLNk0Q0/4iZCBqDN96j2XNxfXrA== + dependencies: + "@octokit/openapi-types" "^18.0.0" + +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + +"@pnpm/config.env-replace@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz#ab29da53df41e8948a00f2433f085f54de8b3a4c" + integrity sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w== + +"@pnpm/network.ca-file@^1.0.1": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz#2ab05e09c1af0cdf2fcf5035bea1484e222f7983" + integrity sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA== dependencies: - "@octokit/core" "^4.0.0" - "@octokit/plugin-paginate-rest" "^3.0.0" - "@octokit/plugin-request-log" "^1.0.4" - "@octokit/plugin-rest-endpoint-methods" "^6.0.0" + graceful-fs "4.2.10" -"@octokit/types@^6.0.3", "@octokit/types@^6.16.1", "@octokit/types@^6.41.0": - version "6.41.0" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.41.0.tgz#e58ef78d78596d2fb7df9c6259802464b5f84a04" - integrity sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg== +"@pnpm/npm-conf@^2.1.0": + version "2.2.2" + resolved "https://registry.yarnpkg.com/@pnpm/npm-conf/-/npm-conf-2.2.2.tgz#0058baf1c26cbb63a828f0193795401684ac86f0" + integrity sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA== dependencies: - "@octokit/openapi-types" "^12.11.0" + "@pnpm/config.env-replace" "^1.1.0" + "@pnpm/network.ca-file" "^1.0.1" + config-chain "^1.1.11" "@semantic-release/commit-analyzer@^9.0.2": version "9.0.2" @@ -1581,54 +1683,50 @@ lodash "^4.17.4" micromatch "^4.0.2" -"@semantic-release/error@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@semantic-release/error/-/error-2.2.0.tgz#ee9d5a09c9969eade1ec864776aeda5c5cddbbf0" - integrity sha512-9Tj/qn+y2j+sjCI3Jd+qseGtHjOAeg7dU2/lVcqIQ9TV3QDaDXDYXcoOHU+7o2Hwh8L8ymL4gfuO7KxDs3q2zg== - "@semantic-release/error@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@semantic-release/error/-/error-3.0.0.tgz#30a3b97bbb5844d695eb22f9d3aa40f6a92770c2" integrity sha512-5hiM4Un+tpl4cKw3lV4UgzJj+SmfNIDCLLw0TepzQxz9ZGV5ixnqkzIVF+3tp0ZHgcMKE+VNGHJjEeyFG2dcSw== "@semantic-release/github@^8.0.0": - version "8.0.5" - resolved "https://registry.yarnpkg.com/@semantic-release/github/-/github-8.0.5.tgz#73a128b7989bf3b4f8968c6cf2fa802dda27dfd2" - integrity sha512-9pGxRM3gv1hgoZ/muyd4pWnykdIUVfCiev6MXE9lOyGQof4FQy95GFE26nDcifs9ZG7bBzV8ue87bo/y1zVf0g== - dependencies: - "@octokit/rest" "^19.0.0" - "@semantic-release/error" "^2.2.0" + version "8.1.0" + resolved "https://registry.yarnpkg.com/@semantic-release/github/-/github-8.1.0.tgz#c31fc5852d32975648445804d1984cd96e72c4d0" + integrity sha512-erR9E5rpdsz0dW1I7785JtndQuMWN/iDcemcptf67tBNOmBUN0b2YNOgcjYUnBpgRpZ5ozfBHrK7Bz+2ets/Dg== + dependencies: + "@octokit/core" "^4.2.1" + "@octokit/plugin-paginate-rest" "^6.1.2" + "@octokit/plugin-retry" "^4.1.3" + "@octokit/plugin-throttling" "^5.2.3" + "@semantic-release/error" "^3.0.0" aggregate-error "^3.0.0" - bottleneck "^2.18.1" debug "^4.0.0" dir-glob "^3.0.0" - fs-extra "^10.0.0" + fs-extra "^11.0.0" globby "^11.0.0" - http-proxy-agent "^5.0.0" - https-proxy-agent "^5.0.0" + http-proxy-agent "^7.0.0" + https-proxy-agent "^7.0.0" issue-parser "^6.0.0" lodash "^4.17.4" mime "^3.0.0" p-filter "^2.0.0" - p-retry "^4.0.0" url-join "^4.0.0" "@semantic-release/npm@^9.0.0": - version "9.0.1" - resolved "https://registry.yarnpkg.com/@semantic-release/npm/-/npm-9.0.1.tgz#d81828eb1fb771e2767b3a8ee989915e1af27075" - integrity sha512-I5nVZklxBzfMFwemhRNbSrkiN/dsH3c7K9+KSk6jUnq0rdLFUuJt7EBsysq4Ir3moajQgFkfEryEHPqiKJj20g== + version "9.0.2" + resolved "https://registry.yarnpkg.com/@semantic-release/npm/-/npm-9.0.2.tgz#0f0903b4df6e93ef237372146bc376087fed4e1d" + integrity sha512-zgsynF6McdzxPnFet+a4iO9HpAlARXOM5adz7VGVCvj0ne8wtL2ZOQoDV2wZPDmdEotDIbVeJjafhelZjs9j6g== dependencies: "@semantic-release/error" "^3.0.0" aggregate-error "^3.0.0" execa "^5.0.0" - fs-extra "^10.0.0" + fs-extra "^11.0.0" lodash "^4.17.15" nerf-dart "^1.0.0" normalize-url "^6.0.0" npm "^8.3.0" rc "^1.2.8" read-pkg "^5.0.0" - registry-auth-token "^4.0.0" + registry-auth-token "^5.0.0" semver "^7.1.2" tempy "^1.0.0" @@ -1704,24 +1802,24 @@ "@sentry/types" "6.19.7" tslib "^1.9.3" -"@sinclair/typebox@^0.24.1": - version "0.24.28" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.28.tgz#15aa0b416f82c268b1573ab653e4413c965fe794" - integrity sha512-dgJd3HLOkLmz4Bw50eZx/zJwtBq65nms3N9VBYu5LTjJ883oBFkTyXRlCB/ZGGwqYpJJHA5zW2Ibhl5ngITfow== +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== -"@sinonjs/commons@^1.7.0": - version "1.8.3" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" - integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ== +"@sinonjs/commons@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.0.tgz#beb434fe875d965265e04722ccfc21df7f755d72" + integrity sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA== dependencies: type-detect "4.0.8" -"@sinonjs/fake-timers@^9.1.2": - version "9.1.2" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz#4eaab737fab77332ab132d396a3c0d364bd0ea8c" - integrity sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw== +"@sinonjs/fake-timers@^10.0.2": + version "10.3.0" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66" + integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA== dependencies: - "@sinonjs/commons" "^1.7.0" + "@sinonjs/commons" "^3.0.0" "@tootallnate/once@2": version "2.0.0" @@ -1748,15 +1846,15 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e" integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== -"@types/async@^3.2.15": - version "3.2.15" - resolved "https://registry.yarnpkg.com/@types/async/-/async-3.2.15.tgz#26d4768fdda0e466f18d6c9918ca28cc89a4e1fe" - integrity sha512-PAmPfzvFA31mRoqZyTVsgJMsvbynR429UTTxhmfsUCrWGh3/fxOrzqBtaTPJsn4UtzTv4Vb0+/O7CARWb69N4g== +"@types/async@^3.2.20": + version "3.2.20" + resolved "https://registry.yarnpkg.com/@types/async/-/async-3.2.20.tgz#53517caaa68c94f99da1c4e986cf7f2954981515" + integrity sha512-6jSBQQugzyX1aWto0CbvOnmxrU9tMoXfA9gc4IrLEtvr3dTwSg5GLGoWiZnGLI6UG/kqpB3JOQKQrqnhUWGKQA== -"@types/aws-lambda@^8.10.102": - version "8.10.102" - resolved "https://registry.yarnpkg.com/@types/aws-lambda/-/aws-lambda-8.10.102.tgz#d2402224ec30cdddfb669005c25b6ee01fd6f5be" - integrity sha512-BT05v46n9KtSHa9SgGuOvm49eSruJ9utD8iNXpdpuUVYk8wOcqmm1LEzpNRkrXxD0CULc38sdLpk6q3Wa2WOwg== +"@types/aws-lambda@^8.10.120": + version "8.10.120" + resolved "https://registry.yarnpkg.com/@types/aws-lambda/-/aws-lambda-8.10.120.tgz#d654644a4c03372622ef00f94acc9f5653a8ee62" + integrity sha512-ReLUWj/2EYNx7qs17bkBkgSmr0jB1GGTRyorhHJmq8nqlWxEyWyto1GFsGTGdbLI0cAj1AcbBAA1oZYUQEqZng== "@types/babel__core@^7.1.14": version "7.1.19" @@ -1817,13 +1915,13 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@^28.1.6": - version "28.1.6" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-28.1.6.tgz#d6a9cdd38967d2d746861fb5be6b120e38284dd4" - integrity sha512-0RbGAFMfcBJKOmqRazM8L98uokwuwD5F8rHrv/ZMbrZBwVOWZUyPG6VFNscjYr/vjM3Vu4fRrCPbOs42AfemaQ== +"@types/jest@^29.5.4": + version "29.5.4" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.4.tgz#9d0a16edaa009a71e6a71a999acd582514dab566" + integrity sha512-PhglGmhWeD46FYOVLt3X7TiWjzwuVGW9wG/4qocPevXMjCmrIc5b6db9WjeGE4QYVpUAWMDv3v0IiBwObY289A== dependencies: - jest-matcher-utils "^28.0.0" - pretty-format "^28.0.0" + expect "^29.0.0" + pretty-format "^29.0.0" "@types/json-schema@^7.0.9": version "7.0.11" @@ -1860,35 +1958,25 @@ resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== -"@types/prettier@^2.1.5": - version "2.7.0" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.0.tgz#ea03e9f0376a4446f44797ca19d9c46c36e352dc" - integrity sha512-RI1L7N4JnW5gQw2spvL7Sllfuf1SaHdrZpCHiBlCXjIlufi1SMNnbu2teze3/QE67Fg2tBlH7W+mi4hVNk4p0A== - -"@types/retry@0.12.0": - version "0.12.0" - resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" - integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== - "@types/stack-utils@^2.0.0": version "2.0.1" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== -"@types/useragent@^2.3.1": - version "2.3.1" - resolved "https://registry.yarnpkg.com/@types/useragent/-/useragent-2.3.1.tgz#c971243faa04f50df399da35d77538ab5fabae20" - integrity sha512-w70ziElAVDD8lEOQ2Id3YBDE0sn2DTVA1zLB59H4kFngYoOJAIlnMkndiZFrzzHE0jmFDZ9AEWNvmeTm6Rvj9A== +"@types/useragent@^2.3.2": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@types/useragent/-/useragent-2.3.2.tgz#d8572cf7dfc486e80e164cbd49546749877bddf2" + integrity sha512-68udf5WhUcw0F5ueJ3rVF7LTHqT05DfwxhVubXIyDhqEtCy3KWLd28K76b4WWwVrZfJonY37Ru0sU0MJzUuJ9Q== -"@types/uuid@^8.3.4": - version "8.3.4" - resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc" - integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw== +"@types/uuid@^9.0.3": + version "9.0.3" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.3.tgz#6cdd939b4316b4f81625de9f06028d848c4a1533" + integrity sha512-taHQQH/3ZyI3zP8M/puluDEIEvtQHVYcC6y3N8ijFtAd28+Ey/G4sg1u2gB01S8MwybLOKAp9/yCMu/uR5l3Ug== -"@types/xml2js@^0.4.11": - version "0.4.11" - resolved "https://registry.yarnpkg.com/@types/xml2js/-/xml2js-0.4.11.tgz#bf46a84ecc12c41159a7bd9cf51ae84129af0e79" - integrity sha512-JdigeAKmCyoJUiQljjr7tQG3if9NkqGUgwEUqBvV0N7LM4HyQk7UXCnusRa1lnvXAEYJ8mw8GtZWioagNztOwA== +"@types/xml2js@^0.4.12": + version "0.4.12" + resolved "https://registry.yarnpkg.com/@types/xml2js/-/xml2js-0.4.12.tgz#d9aae03295476fd5cbc74e0b572816208dbec6d1" + integrity sha512-CZPpQKBZ8db66EP5hCjwvYrLThgZvnyZrPXK2W+UI1oOaWezGt34iOaUCX4Jah2X8+rQqjvl9VKEIT8TR1I0rA== dependencies: "@types/node" "*" @@ -2007,11 +2095,16 @@ acorn-walk@^8.1.1: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== -acorn@^8.4.1, acorn@^8.8.0: +acorn@^8.4.1: version "8.8.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== +acorn@^8.9.0: + version "8.10.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" + integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== + agent-base@6, agent-base@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" @@ -2019,6 +2112,13 @@ agent-base@6, agent-base@^6.0.2: dependencies: debug "4" +agent-base@^7.0.2, agent-base@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.0.tgz#536802b76bc0b34aa50195eb2442276d613e3434" + integrity sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg== + dependencies: + debug "^4.3.4" + agentkeepalive@^4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.2.1.tgz#a7975cbb9f83b367f06c90cc51ff28fe7d499717" @@ -2036,7 +2136,7 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^4.0.0" -ajv@^6.10.0, ajv@^6.12.4: +ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -2058,18 +2158,23 @@ ansi-escapes@^4.2.1: dependencies: type-fest "^0.21.3" -ansi-escapes@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-5.0.0.tgz#b6a0caf0eef0c41af190e9a749e0c00ec04bb2a6" - integrity sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA== +ansi-escapes@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-6.2.0.tgz#8a13ce75286f417f1963487d86ba9f90dccf9947" + integrity sha512-kzRaCqXnpzWs+3z5ABPQiVke+iq0KXkHo8xiWV4RPTi5Yli0l97BEQuhXV1s7+aSU/fu1kUuxgS4MsQ0fRuygw== dependencies: - type-fest "^1.0.2" + type-fest "^3.0.0" ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== +ansi-regex@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" + integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== + ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -2089,6 +2194,11 @@ ansi-styles@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== +ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + ansicolors@~0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.3.2.tgz#665597de86a9ffe3aa9bfbe6cae5c6ea426b4979" @@ -2205,10 +2315,10 @@ available-typed-arrays@^1.0.5: resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== -aws-sdk@^2.1194.0: - version "2.1194.0" - resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1194.0.tgz#6a820684fa3f58ea40caf90d302414a23df7c308" - integrity sha512-wbgib7r7sHPkZIhqSMduueKYqe+DrFyxsKnUKHj6hdNcRKqEeqzvKp4olWmFs/3z3qU8+g78kBXr9rujvko1ug== +aws-sdk@^2.1456.0: + version "2.1456.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1456.0.tgz#0c89368eac5556f4959a8876386edd79f96808b8" + integrity sha512-0fzxx55Skc44i4q6iMuNjYSHEEaCZIbjvl4jaT3Jki6GNYQAdWb0/+BPaTalkb8UgrY9wSZ0h86DH2w+nf6e7g== dependencies: buffer "4.9.2" events "1.1.1" @@ -2219,7 +2329,7 @@ aws-sdk@^2.1194.0: url "0.10.3" util "^0.12.4" uuid "8.0.0" - xml2js "0.4.19" + xml2js "0.5.0" axios-minified@^1.0.7: version "1.0.7" @@ -2236,15 +2346,15 @@ axios@^0.27.2: follow-redirects "^1.14.9" form-data "^4.0.0" -babel-jest@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-28.1.3.tgz#c1187258197c099072156a0a121c11ee1e3917d5" - integrity sha512-epUaPOEWMk3cWX0M/sPvCHHCe9fMFAa/9hXEgKP8nFfNl/jlGkE9ucq9NqkZGXLDduCJYS0UvSlPUwC0S+rH6Q== +babel-jest@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5" + integrity sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg== dependencies: - "@jest/transform" "^28.1.3" + "@jest/transform" "^29.7.0" "@types/babel__core" "^7.1.14" babel-plugin-istanbul "^6.1.1" - babel-preset-jest "^28.1.3" + babel-preset-jest "^29.6.3" chalk "^4.0.0" graceful-fs "^4.2.9" slash "^3.0.0" @@ -2260,10 +2370,10 @@ babel-plugin-istanbul@^6.1.1: istanbul-lib-instrument "^5.0.4" test-exclude "^6.0.0" -babel-plugin-jest-hoist@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-28.1.3.tgz#1952c4d0ea50f2d6d794353762278d1d8cca3fbe" - integrity sha512-Ys3tUKAmfnkRUpPdpa98eYrAR0nV+sSFUZZEGuQ2EbFd1y4SOLtD5QDNHAq+bb9a+bbXvYQC4b+ID/THIMcU6Q== +babel-plugin-jest-hoist@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz#aadbe943464182a8922c3c927c3067ff40d24626" + integrity sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg== dependencies: "@babel/template" "^7.3.3" "@babel/types" "^7.3.3" @@ -2288,12 +2398,12 @@ babel-preset-current-node-syntax@^1.0.0: "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-syntax-top-level-await" "^7.8.3" -babel-preset-jest@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-28.1.3.tgz#5dfc20b99abed5db994406c2b9ab94c73aaa419d" - integrity sha512-L+fupJvlWAHbQfn74coNX3zf60LXMJsezNvvx8eIh7iOR1luJ1poxYgQk1F8PYtNq/6QODDHCqsSnTFSWC491A== +babel-preset-jest@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz#fa05fa510e7d493896d7b0dd2033601c840f171c" + integrity sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA== dependencies: - babel-plugin-jest-hoist "^28.1.3" + babel-plugin-jest-hoist "^29.6.3" babel-preset-current-node-syntax "^1.0.0" balanced-match@^1.0.0: @@ -2311,14 +2421,14 @@ before-after-hook@^2.2.0: resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.2.tgz#a6e8ca41028d90ee2c24222f201c90956091613e" integrity sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ== -bin-links@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/bin-links/-/bin-links-3.0.2.tgz#5c40f14b0742faa2ae952caa76b4a29090befcbb" - integrity sha512-+oSWBdbCUK6X4LOCSrU36fWRzZNaK7/evX7GozR9xwl2dyiVi3UOUwTyyOVYI1FstgugfsM9QESRrWo7gjCYbg== +bin-links@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/bin-links/-/bin-links-3.0.3.tgz#3842711ef3db2cd9f16a5f404a996a12db355a6e" + integrity sha512-zKdnMPWEdh4F5INR07/eBrodC7QrF5JKvqskjz/ZZRXg5YSAZIbn8zGhbhUrElzHBZ2fvEQdOU59RHcTG3GiwA== dependencies: cmd-shim "^5.0.0" mkdirp-infer-owner "^2.0.0" - npm-normalize-package-bin "^1.0.0" + npm-normalize-package-bin "^2.0.0" read-cmd-shim "^3.0.0" rimraf "^3.0.0" write-file-atomic "^4.0.0" @@ -2328,7 +2438,7 @@ binary-extensions@^2.2.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== -bottleneck@^2.18.1: +bottleneck@^2.15.3: version "2.19.5" resolved "https://registry.yarnpkg.com/bottleneck/-/bottleneck-2.19.5.tgz#5df0b90f59fd47656ebe63c78a98419205cadd91" integrity sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw== @@ -2405,7 +2515,31 @@ builtins@^5.0.0: dependencies: semver "^7.0.0" -cacache@^16.0.0, cacache@^16.0.6, cacache@^16.1.0, cacache@^16.1.1: +cacache@^16.0.0, cacache@^16.1.3: + version "16.1.3" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-16.1.3.tgz#a02b9f34ecfaf9a78c9f4bc16fceb94d5d67a38e" + integrity sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ== + dependencies: + "@npmcli/fs" "^2.1.0" + "@npmcli/move-file" "^2.0.0" + chownr "^2.0.0" + fs-minipass "^2.1.0" + glob "^8.0.1" + infer-owner "^1.0.4" + lru-cache "^7.7.1" + minipass "^3.1.6" + minipass-collect "^1.0.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + mkdirp "^1.0.4" + p-map "^4.0.0" + promise-inflight "^1.0.1" + rimraf "^3.0.2" + ssri "^9.0.0" + tar "^6.1.11" + unique-filename "^2.0.0" + +cacache@^16.1.0: version "16.1.1" resolved "https://registry.yarnpkg.com/cacache/-/cacache-16.1.1.tgz#4e79fb91d3efffe0630d5ad32db55cc1b870669c" integrity sha512-VDKN+LHyCQXaaYZ7rA/qtkURU+/yYhviUdvqEv2LT6QPZU8jpyzEkEVAcKlKLt5dJ5BRp11ym8lo3NKLluEPLg== @@ -2429,6 +2563,24 @@ cacache@^16.0.0, cacache@^16.0.6, cacache@^16.1.0, cacache@^16.1.1: tar "^6.1.11" unique-filename "^1.1.1" +cacache@^17.0.0: + version "17.1.4" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-17.1.4.tgz#b3ff381580b47e85c6e64f801101508e26604b35" + integrity sha512-/aJwG2l3ZMJ1xNAnqbMpA40of9dj/pIH3QfiuQSqjfPJF747VR0J/bHn+/KdNnHKc6XQcWt/AfRSBft82W1d2A== + dependencies: + "@npmcli/fs" "^3.1.0" + fs-minipass "^3.0.0" + glob "^10.2.2" + lru-cache "^7.7.1" + minipass "^7.0.3" + minipass-collect "^1.0.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + p-map "^4.0.0" + ssri "^10.0.0" + tar "^6.1.11" + unique-filename "^3.0.0" + caching-transform@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/caching-transform/-/caching-transform-4.0.0.tgz#00d297a4206d71e2163c39eaffa8157ac0651f0f" @@ -2501,10 +2653,10 @@ chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.0.1.tgz#ca57d71e82bb534a296df63bbacc4a1c22b2a4b6" - integrity sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w== +chalk@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385" + integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w== char-regex@^1.0.2: version "1.0.2" @@ -2551,10 +2703,10 @@ cli-columns@^4.0.0: string-width "^4.2.3" strip-ansi "^6.0.1" -cli-table3@^0.6.1, cli-table3@^0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.2.tgz#aaf5df9d8b5bf12634dc8b3040806a0c07120d2a" - integrity sha512-QyavHCaIC80cMivimWu4aWHilIpiDpfm3hGmqAmXVL1UsnbLuBSMd21hTX6VY4ZSDSM73ESLeF8TOYId3rBTbw== +cli-table3@^0.6.2, cli-table3@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.3.tgz#61ab765aac156b52f222954ffc607a6f01dbeeb2" + integrity sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg== dependencies: string-width "^4.2.0" optionalDependencies: @@ -2696,6 +2848,14 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== +config-chain@^1.1.11: + version "1.1.13" + resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.13.tgz#fad0795aa6a6cdaff9ed1b68e9dff94372c232f4" + integrity sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ== + dependencies: + ini "^1.3.4" + proto-list "~1.2.1" + confusing-browser-globals@^1.0.10: version "1.0.11" resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz#ae40e9b57cdd3915408a2805ebd3a5585608dc81" @@ -2749,13 +2909,18 @@ conventional-commits-parser@^3.2.3: split2 "^3.0.0" through2 "^4.0.0" -convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: +convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.8.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== dependencies: safe-buffer "~5.1.1" +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + cookie@^0.4.1: version "0.4.2" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" @@ -2767,9 +2932,9 @@ core-util-is@~1.0.0: integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== cosmiconfig@^7.0.0: - version "7.0.1" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.1.tgz#714d756522cace867867ccb4474c5d01bbae5d6d" - integrity sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ== + version "7.1.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6" + integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA== dependencies: "@types/parse-json" "^4.0.0" import-fresh "^3.2.1" @@ -2777,6 +2942,19 @@ cosmiconfig@^7.0.0: path-type "^4.0.0" yaml "^1.10.0" +create-jest@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/create-jest/-/create-jest-29.7.0.tgz#a355c5b3cb1e1af02ba177fe7afd7feee49a5320" + integrity sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q== + dependencies: + "@jest/types" "^29.6.3" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + jest-config "^29.7.0" + jest-util "^29.7.0" + prompts "^2.0.1" + create-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" @@ -2850,10 +3028,10 @@ decamelize@^1.1.0, decamelize@^1.2.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== -dedent@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" - integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== +dedent@^1.0.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.1.tgz#4f3fc94c8b711e9bb2800d185cd6ad20f2a90aff" + integrity sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg== deep-extend@^0.6.0: version "0.6.0" @@ -2921,7 +3099,7 @@ depd@^1.1.2: resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== -deprecation@^2.0.0, deprecation@^2.3.1: +deprecation@^2.0.0: version "2.3.1" resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== @@ -2949,17 +3127,17 @@ dezalgo@^1.0.0: asap "^2.0.0" wrappy "1" -diff-sequences@^28.1.1: - version "28.1.1" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-28.1.1.tgz#9989dc731266dc2903457a70e996f3a041913ac6" - integrity sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw== +diff-sequences@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" + integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== diff@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== -diff@^5.0.0: +diff@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/diff/-/diff-5.1.0.tgz#bc52d298c5ea8df9194800224445ed43ffc87e40" integrity sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw== @@ -2999,21 +3177,31 @@ duplexer2@~0.1.0: dependencies: readable-stream "^2.0.2" +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + electron-to-chromium@^1.4.202: version "1.4.219" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.219.tgz#a7a672304b6aa4f376918d3f63a47f2c3906009a" integrity sha512-zoQJsXOUw0ZA0YxbjkmzBumAJRtr6je5JySuL/bAoFs0DuLiLJ+5FzRF7/ZayihxR2QcewlRZVm5QZdUhwjOgA== -emittery@^0.10.2: - version "0.10.2" - resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.10.2.tgz#902eec8aedb8c41938c46e9385e9db7e03182933" - integrity sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw== +emittery@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" + integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + enabled@2.0.x: version "2.0.0" resolved "https://registry.yarnpkg.com/enabled/-/enabled-2.0.0.tgz#f9dd92ec2d6f4bbc0d5d1e64e21d61cd4665e7c2" @@ -3045,10 +3233,10 @@ env-paths@^2.2.0: resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== -epsagon@^1.123.2: - version "1.123.2" - resolved "https://registry.yarnpkg.com/epsagon/-/epsagon-1.123.2.tgz#d0b9151491c4bc2f5b590d7e716db12862ae7a23" - integrity sha512-FrnqFEyMzjNHUkjLFsRuURyhFmLP1rEzzDhZD7kPrCKgE1eeZbUEmSSpLkhBOV1spLfOu5AlSQuoboBW3z0EMw== +epsagon@^1.123.3: + version "1.123.3" + resolved "https://registry.yarnpkg.com/epsagon/-/epsagon-1.123.3.tgz#7b86c5962348aaf9be14b8d970cc4fb80a835737" + integrity sha512-fb0zuBG2FDSlNG6PV98MY9/BPZicPEBIYgyMGuiWmk4vN5RVFyKc9Lo/VwzEm7jNmKBpIAJKJxaI9v6AoJX+uw== dependencies: "@aws-sdk/client-sns" "^3.41.0" "@aws-sdk/util-dynamodb" "^3.49.0" @@ -3218,10 +3406,10 @@ eslint-scope@^5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" -eslint-scope@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642" - integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== +eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== dependencies: esrecurse "^4.3.0" estraverse "^5.2.0" @@ -3243,59 +3431,62 @@ eslint-visitor-keys@^3.3.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== -eslint@^8.22.0: - version "8.22.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.22.0.tgz#78fcb044196dfa7eef30a9d65944f6f980402c48" - integrity sha512-ci4t0sz6vSRKdmkOGmprBo6fmI4PrphDFMy5JEq/fNS0gQkJM3rLmrqcp8ipMcdobH3KtUP40KniAE9W19S4wA== - dependencies: - "@eslint/eslintrc" "^1.3.0" - "@humanwhocodes/config-array" "^0.10.4" - "@humanwhocodes/gitignore-to-minimatch" "^1.0.2" - ajv "^6.10.0" +eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint@^8.49.0: + version "8.49.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.49.0.tgz#09d80a89bdb4edee2efcf6964623af1054bf6d42" + integrity sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^2.1.2" + "@eslint/js" "8.49.0" + "@humanwhocodes/config-array" "^0.11.11" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + ajv "^6.12.4" chalk "^4.0.0" cross-spawn "^7.0.2" debug "^4.3.2" doctrine "^3.0.0" escape-string-regexp "^4.0.0" - eslint-scope "^7.1.1" - eslint-utils "^3.0.0" - eslint-visitor-keys "^3.3.0" - espree "^9.3.3" - esquery "^1.4.0" + eslint-scope "^7.2.2" + eslint-visitor-keys "^3.4.3" + espree "^9.6.1" + esquery "^1.4.2" esutils "^2.0.2" fast-deep-equal "^3.1.3" file-entry-cache "^6.0.1" find-up "^5.0.0" - functional-red-black-tree "^1.0.1" - glob-parent "^6.0.1" - globals "^13.15.0" - globby "^11.1.0" - grapheme-splitter "^1.0.4" + glob-parent "^6.0.2" + globals "^13.19.0" + graphemer "^1.4.0" ignore "^5.2.0" - import-fresh "^3.0.0" imurmurhash "^0.1.4" is-glob "^4.0.0" + is-path-inside "^3.0.3" js-yaml "^4.1.0" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" lodash.merge "^4.6.2" minimatch "^3.1.2" natural-compare "^1.4.0" - optionator "^0.9.1" - regexpp "^3.2.0" + optionator "^0.9.3" strip-ansi "^6.0.1" - strip-json-comments "^3.1.0" text-table "^0.2.0" - v8-compile-cache "^2.0.3" -espree@^9.3.2, espree@^9.3.3: - version "9.3.3" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.3.tgz#2dd37c4162bb05f433ad3c1a52ddf8a49dc08e9d" - integrity sha512-ORs1Rt/uQTqUKjDdGCyrtYxbazf5umATSf/K4qxjmZHORR6HJk+2s/2Pqe+Kk49HHINC/xNIrGfgh8sZcll0ng== +espree@^9.6.0, espree@^9.6.1: + version "9.6.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== dependencies: - acorn "^8.8.0" + acorn "^8.9.0" acorn-jsx "^5.3.2" - eslint-visitor-keys "^3.3.0" + eslint-visitor-keys "^3.4.1" esprima@^4.0.0, esprima@~4.0.0: version "4.0.1" @@ -3309,6 +3500,13 @@ esquery@^1.4.0: dependencies: estraverse "^5.1.0" +esquery@^1.4.2: + version "1.5.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" + integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== + dependencies: + estraverse "^5.1.0" + esrecurse@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" @@ -3356,16 +3554,21 @@ exit@^0.1.2: resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== -expect@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/expect/-/expect-28.1.3.tgz#90a7c1a124f1824133dd4533cce2d2bdcb6603ec" - integrity sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g== +expect@^29.0.0, expect@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" + integrity sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw== dependencies: - "@jest/expect-utils" "^28.1.3" - jest-get-type "^28.0.2" - jest-matcher-utils "^28.1.3" - jest-message-util "^28.1.3" - jest-util "^28.1.3" + "@jest/expect-utils" "^29.7.0" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + +exponential-backoff@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6" + integrity sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw== fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" @@ -3383,7 +3586,7 @@ fast-glob@^3.2.9: merge2 "^1.3.0" micromatch "^4.0.4" -fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== @@ -3527,6 +3730,14 @@ foreground-child@^2.0.0: cross-spawn "^7.0.0" signal-exit "^3.0.2" +foreground-child@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.1.1.tgz#1d173e776d75d2772fed08efe4a0de1ea1b12d0d" + integrity sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" + form-data@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" @@ -3549,10 +3760,10 @@ fromentries@^1.2.0, fromentries@^1.3.2: resolved "https://registry.yarnpkg.com/fromentries/-/fromentries-1.3.2.tgz#e4bca6808816bf8f93b52750f1127f5a6fd86e3a" integrity sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg== -fs-extra@^10.0.0: - version "10.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" - integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== +fs-extra@^11.0.0: + version "11.1.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.1.1.tgz#da69f7c39f3b002378b0954bb6ae7efdc0876e2d" + integrity sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ== dependencies: graceful-fs "^4.2.0" jsonfile "^6.0.1" @@ -3565,6 +3776,13 @@ fs-minipass@^2.0.0, fs-minipass@^2.1.0: dependencies: minipass "^3.0.0" +fs-minipass@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-3.0.3.tgz#79a85981c4dc120065e96f62086bf6f9dc26cc54" + integrity sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw== + dependencies: + minipass "^7.0.3" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -3670,13 +3888,24 @@ glob-parent@^5.1.2: dependencies: is-glob "^4.0.1" -glob-parent@^6.0.1: +glob-parent@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== dependencies: is-glob "^4.0.3" +glob@^10.2.2: + version "10.3.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.4.tgz#c85c9c7ab98669102b6defda76d35c5b1ef9766f" + integrity sha512-6LFElP3A+i/Q8XQKEvZjkEWEOTgAIALR9AO2rwT8bgPhDd1anmqDJDZ6lLddI4ehxxxR1S5RIqKe1uapMQfYaQ== + dependencies: + foreground-child "^3.1.0" + jackspeak "^2.0.3" + minimatch "^9.0.1" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + path-scurry "^1.10.1" + glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" @@ -3705,10 +3934,10 @@ globals@^11.1.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -globals@^13.15.0: - version "13.17.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.17.0.tgz#902eb1e680a41da93945adbdcb5a9f361ba69bd4" - integrity sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw== +globals@^13.19.0: + version "13.21.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.21.0.tgz#163aae12f34ef502f5153cfbdd3600f36c63c571" + integrity sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg== dependencies: type-fest "^0.20.2" @@ -3729,15 +3958,20 @@ google-protobuf-minified@^1.0.8: resolved "https://registry.yarnpkg.com/google-protobuf-minified/-/google-protobuf-minified-1.0.8.tgz#c1bf97efddf4f0ba5a2ba74dc7b8576ea9cc3fa1" integrity sha512-FejfI4d9HOtBijz7Ph0jtjNUIBMqMdLRyzzG7fgQgi0MnyuCmTcXEe2Fo30fgKv9d6kqZALoQgNYrHfREwLZvA== -graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.10, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: +graceful-fs@4.2.10, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.6, graceful-fs@^4.2.9: version "4.2.10" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== -grapheme-splitter@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" - integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== +graceful-fs@^4.2.10, graceful-fs@^4.2.4: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== handlebars@^4.7.7: version "4.7.7" @@ -3827,10 +4061,10 @@ hosted-git-info@^4.0.0, hosted-git-info@^4.0.1: dependencies: lru-cache "^6.0.0" -hosted-git-info@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-5.1.0.tgz#9786123f92ef3627f24abc3f15c20d98ec4a6594" - integrity sha512-Ek+QmMEqZF8XrbFdwoDjSbm7rT23pCgEMOJmz6GPk/s4yH//RQfNPArhIxbguNxROq/+5lNBwCDHMhA903Kx1Q== +hosted-git-info@^5.0.0, hosted-git-info@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-5.2.1.tgz#0ba1c97178ef91f3ab30842ae63d6a272341156f" + integrity sha512-xIcQYMnhcx2Nr4JTjsFmwwnr9vldugPy9uVm0o87bjqqWMv9GaqsTeT+i99wTl0mk1uLxJtHxLb8kymqTENQsw== dependencies: lru-cache "^7.5.1" @@ -3844,6 +4078,11 @@ http-cache-semantics@^4.1.0: resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== +http-cache-semantics@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" + integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== + http-proxy-agent@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" @@ -3853,6 +4092,14 @@ http-proxy-agent@^5.0.0: agent-base "6" debug "4" +http-proxy-agent@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz#e9096c5afd071a3fce56e6252bb321583c124673" + integrity sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ== + dependencies: + agent-base "^7.1.0" + debug "^4.3.4" + https-proxy-agent@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" @@ -3861,6 +4108,14 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" +https-proxy-agent@^7.0.0: + version "7.0.2" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz#e2645b846b90e96c6e6f347fb5b2e41f1590b09b" + integrity sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA== + dependencies: + agent-base "^7.0.2" + debug "4" + human-signals@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" @@ -3902,7 +4157,7 @@ ignore@^5.2.0: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== -import-fresh@^3.0.0, import-fresh@^3.2.1: +import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== @@ -3951,16 +4206,16 @@ inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -ini@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ini/-/ini-3.0.0.tgz#2f6de95006923aa75feed8894f5686165adc08f1" - integrity sha512-TxYQaeNW/N8ymDvwAxPyRbhMBtnEwuvaTYpOQkFx1nSeusgezHniEc/l35Vo4iCq/mMiTJbpD7oYxN98hFlfmw== - -ini@~1.3.0: +ini@^1.3.4, ini@~1.3.0: version "1.3.8" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== +ini@^3.0.0, ini@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ini/-/ini-3.0.1.tgz#c76ec81007875bc44d544ff7a11a55d12294102d" + integrity sha512-it4HyVAUTKBc6m8e1iXWvXSTdndF7HbdN713+kvLrymxTaU4AUBWrJ4vEooP+V7fexnVD3LKcBshjGGPefSMUQ== + init-package-json@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/init-package-json/-/init-package-json-3.0.2.tgz#f5bc9bac93f2bdc005778bc2271be642fecfcd69" @@ -4126,7 +4381,7 @@ is-path-cwd@^2.2.0: resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== -is-path-inside@^3.0.2: +is-path-inside@^3.0.2, is-path-inside@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== @@ -4253,7 +4508,7 @@ istanbul-lib-instrument@^4.0.0: istanbul-lib-coverage "^3.0.0" semver "^6.3.0" -istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: +istanbul-lib-instrument@^5.0.4: version "5.2.0" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.0.tgz#31d18bdd127f825dd02ea7bfdfd906f8ab840e9f" integrity sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A== @@ -4264,6 +4519,17 @@ istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: istanbul-lib-coverage "^3.2.0" semver "^6.3.0" +istanbul-lib-instrument@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.0.tgz#7a8af094cbfff1d5bb280f62ce043695ae8dd5b8" + integrity sha512-x58orMzEVfzPUKqlbLd1hXCnySCxKdDKa6Rjg97CwuLLRI4g3FHTdnExu1OqffVFay6zeMW+T6/DowFLndWnIw== + dependencies: + "@babel/core" "^7.12.3" + "@babel/parser" "^7.14.7" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.2.0" + semver "^7.5.4" + istanbul-lib-processinfo@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz#366d454cd0dcb7eb6e0e419378e60072c8626169" @@ -4302,368 +4568,377 @@ istanbul-reports@^3.0.2, istanbul-reports@^3.1.3: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" +jackspeak@^2.0.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.3.3.tgz#95e4cbcc03b3eb357bf6bcce14a903fb3d1151e1" + integrity sha512-R2bUw+kVZFS/h1AZqBKrSgDmdmjApzgY0AlCPumopFiAlbUxE2gf+SCuBzQ0cP5hHmUmFYF5yw55T97Th5Kstg== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + java-properties@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/java-properties/-/java-properties-1.0.2.tgz#ccd1fa73907438a5b5c38982269d0e771fe78211" integrity sha512-qjdpeo2yKlYTH7nFdK0vbZWuTCesk4o63v5iVOlhMQPfuIZQfW/HI35SjfhA+4qpg36rnFSvUK5b1m+ckIblQQ== -jest-changed-files@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-28.1.3.tgz#d9aeee6792be3686c47cb988a8eaf82ff4238831" - integrity sha512-esaOfUWJXk2nfZt9SPyC8gA1kNfdKLkQWyzsMlqq8msYSlNKfmZxfRgZn4Cd4MGVUF+7v6dBs0d5TOAKa7iIiA== +jest-changed-files@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a" + integrity sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w== dependencies: execa "^5.0.0" + jest-util "^29.7.0" p-limit "^3.1.0" -jest-circus@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-28.1.3.tgz#d14bd11cf8ee1a03d69902dc47b6bd4634ee00e4" - integrity sha512-cZ+eS5zc79MBwt+IhQhiEp0OeBddpc1n8MBo1nMB8A7oPMKEO+Sre+wHaLJexQUj9Ya/8NOBY0RESUgYjB6fow== +jest-circus@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.7.0.tgz#b6817a45fcc835d8b16d5962d0c026473ee3668a" + integrity sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw== dependencies: - "@jest/environment" "^28.1.3" - "@jest/expect" "^28.1.3" - "@jest/test-result" "^28.1.3" - "@jest/types" "^28.1.3" + "@jest/environment" "^29.7.0" + "@jest/expect" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" "@types/node" "*" chalk "^4.0.0" co "^4.6.0" - dedent "^0.7.0" + dedent "^1.0.0" is-generator-fn "^2.0.0" - jest-each "^28.1.3" - jest-matcher-utils "^28.1.3" - jest-message-util "^28.1.3" - jest-runtime "^28.1.3" - jest-snapshot "^28.1.3" - jest-util "^28.1.3" + jest-each "^29.7.0" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-runtime "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" p-limit "^3.1.0" - pretty-format "^28.1.3" + pretty-format "^29.7.0" + pure-rand "^6.0.0" slash "^3.0.0" stack-utils "^2.0.3" -jest-cli@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-28.1.3.tgz#558b33c577d06de55087b8448d373b9f654e46b2" - integrity sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ== +jest-cli@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.7.0.tgz#5592c940798e0cae677eec169264f2d839a37995" + integrity sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg== dependencies: - "@jest/core" "^28.1.3" - "@jest/test-result" "^28.1.3" - "@jest/types" "^28.1.3" + "@jest/core" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" chalk "^4.0.0" + create-jest "^29.7.0" exit "^0.1.2" - graceful-fs "^4.2.9" import-local "^3.0.2" - jest-config "^28.1.3" - jest-util "^28.1.3" - jest-validate "^28.1.3" - prompts "^2.0.1" + jest-config "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" yargs "^17.3.1" -jest-config@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-28.1.3.tgz#e315e1f73df3cac31447eed8b8740a477392ec60" - integrity sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ== +jest-config@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.7.0.tgz#bcbda8806dbcc01b1e316a46bb74085a84b0245f" + integrity sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ== dependencies: "@babel/core" "^7.11.6" - "@jest/test-sequencer" "^28.1.3" - "@jest/types" "^28.1.3" - babel-jest "^28.1.3" + "@jest/test-sequencer" "^29.7.0" + "@jest/types" "^29.6.3" + babel-jest "^29.7.0" chalk "^4.0.0" ci-info "^3.2.0" deepmerge "^4.2.2" glob "^7.1.3" graceful-fs "^4.2.9" - jest-circus "^28.1.3" - jest-environment-node "^28.1.3" - jest-get-type "^28.0.2" - jest-regex-util "^28.0.2" - jest-resolve "^28.1.3" - jest-runner "^28.1.3" - jest-util "^28.1.3" - jest-validate "^28.1.3" + jest-circus "^29.7.0" + jest-environment-node "^29.7.0" + jest-get-type "^29.6.3" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-runner "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" micromatch "^4.0.4" parse-json "^5.2.0" - pretty-format "^28.1.3" + pretty-format "^29.7.0" slash "^3.0.0" strip-json-comments "^3.1.1" -jest-diff@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-28.1.3.tgz#948a192d86f4e7a64c5264ad4da4877133d8792f" - integrity sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw== +jest-diff@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" + integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw== dependencies: chalk "^4.0.0" - diff-sequences "^28.1.1" - jest-get-type "^28.0.2" - pretty-format "^28.1.3" + diff-sequences "^29.6.3" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" -jest-docblock@^28.1.1: - version "28.1.1" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-28.1.1.tgz#6f515c3bf841516d82ecd57a62eed9204c2f42a8" - integrity sha512-3wayBVNiOYx0cwAbl9rwm5kKFP8yHH3d/fkEaL02NPTkDojPtheGB7HZSFY4wzX+DxyrvhXz0KSCVksmCknCuA== +jest-docblock@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.7.0.tgz#8fddb6adc3cdc955c93e2a87f61cfd350d5d119a" + integrity sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g== dependencies: detect-newline "^3.0.0" -jest-each@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-28.1.3.tgz#bdd1516edbe2b1f3569cfdad9acd543040028f81" - integrity sha512-arT1z4sg2yABU5uogObVPvSlSMQlDA48owx07BDPAiasW0yYpYHYOo4HHLz9q0BVzDVU4hILFjzJw0So9aCL/g== +jest-each@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.7.0.tgz#162a9b3f2328bdd991beaabffbb74745e56577d1" + integrity sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ== dependencies: - "@jest/types" "^28.1.3" + "@jest/types" "^29.6.3" chalk "^4.0.0" - jest-get-type "^28.0.2" - jest-util "^28.1.3" - pretty-format "^28.1.3" - -jest-environment-node@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-28.1.3.tgz#7e74fe40eb645b9d56c0c4b70ca4357faa349be5" - integrity sha512-ugP6XOhEpjAEhGYvp5Xj989ns5cB1K6ZdjBYuS30umT4CQEETaxSiPcZ/E1kFktX4GkrcM4qu07IIlDYX1gp+A== - dependencies: - "@jest/environment" "^28.1.3" - "@jest/fake-timers" "^28.1.3" - "@jest/types" "^28.1.3" + jest-get-type "^29.6.3" + jest-util "^29.7.0" + pretty-format "^29.7.0" + +jest-environment-node@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376" + integrity sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/fake-timers" "^29.7.0" + "@jest/types" "^29.6.3" "@types/node" "*" - jest-mock "^28.1.3" - jest-util "^28.1.3" + jest-mock "^29.7.0" + jest-util "^29.7.0" -jest-get-type@^28.0.2: - version "28.0.2" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-28.0.2.tgz#34622e628e4fdcd793d46db8a242227901fcf203" - integrity sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA== +jest-get-type@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" + integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== -jest-haste-map@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-28.1.3.tgz#abd5451129a38d9841049644f34b034308944e2b" - integrity sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA== +jest-haste-map@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.7.0.tgz#3c2396524482f5a0506376e6c858c3bbcc17b104" + integrity sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA== dependencies: - "@jest/types" "^28.1.3" + "@jest/types" "^29.6.3" "@types/graceful-fs" "^4.1.3" "@types/node" "*" anymatch "^3.0.3" fb-watchman "^2.0.0" graceful-fs "^4.2.9" - jest-regex-util "^28.0.2" - jest-util "^28.1.3" - jest-worker "^28.1.3" + jest-regex-util "^29.6.3" + jest-util "^29.7.0" + jest-worker "^29.7.0" micromatch "^4.0.4" walker "^1.0.8" optionalDependencies: fsevents "^2.3.2" -jest-leak-detector@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-28.1.3.tgz#a6685d9b074be99e3adee816ce84fd30795e654d" - integrity sha512-WFVJhnQsiKtDEo5lG2mM0v40QWnBM+zMdHHyJs8AWZ7J0QZJS59MsyKeJHWhpBZBH32S48FOVvGyOFT1h0DlqA== +jest-leak-detector@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz#5b7ec0dadfdfec0ca383dc9aa016d36b5ea4c728" + integrity sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw== dependencies: - jest-get-type "^28.0.2" - pretty-format "^28.1.3" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" -jest-matcher-utils@^28.0.0, jest-matcher-utils@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz#5a77f1c129dd5ba3b4d7fc20728806c78893146e" - integrity sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw== +jest-matcher-utils@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" + integrity sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g== dependencies: chalk "^4.0.0" - jest-diff "^28.1.3" - jest-get-type "^28.0.2" - pretty-format "^28.1.3" + jest-diff "^29.7.0" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" -jest-message-util@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-28.1.3.tgz#232def7f2e333f1eecc90649b5b94b0055e7c43d" - integrity sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g== +jest-message-util@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" + integrity sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w== dependencies: "@babel/code-frame" "^7.12.13" - "@jest/types" "^28.1.3" + "@jest/types" "^29.6.3" "@types/stack-utils" "^2.0.0" chalk "^4.0.0" graceful-fs "^4.2.9" micromatch "^4.0.4" - pretty-format "^28.1.3" + pretty-format "^29.7.0" slash "^3.0.0" stack-utils "^2.0.3" -jest-mock@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-28.1.3.tgz#d4e9b1fc838bea595c77ab73672ebf513ab249da" - integrity sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA== +jest-mock@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347" + integrity sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw== dependencies: - "@jest/types" "^28.1.3" + "@jest/types" "^29.6.3" "@types/node" "*" + jest-util "^29.7.0" jest-pnp-resolver@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== -jest-regex-util@^28.0.2: - version "28.0.2" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-28.0.2.tgz#afdc377a3b25fb6e80825adcf76c854e5bf47ead" - integrity sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw== +jest-regex-util@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52" + integrity sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg== -jest-resolve-dependencies@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-28.1.3.tgz#8c65d7583460df7275c6ea2791901fa975c1fe66" - integrity sha512-qa0QO2Q0XzQoNPouMbCc7Bvtsem8eQgVPNkwn9LnS+R2n8DaVDPL/U1gngC0LTl1RYXJU0uJa2BMC2DbTfFrHA== +jest-resolve-dependencies@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz#1b04f2c095f37fc776ff40803dc92921b1e88428" + integrity sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA== dependencies: - jest-regex-util "^28.0.2" - jest-snapshot "^28.1.3" + jest-regex-util "^29.6.3" + jest-snapshot "^29.7.0" -jest-resolve@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-28.1.3.tgz#cfb36100341ddbb061ec781426b3c31eb51aa0a8" - integrity sha512-Z1W3tTjE6QaNI90qo/BJpfnvpxtaFTFw5CDgwpyE/Kz8U/06N1Hjf4ia9quUhCh39qIGWF1ZuxFiBiJQwSEYKQ== +jest-resolve@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.7.0.tgz#64d6a8992dd26f635ab0c01e5eef4399c6bcbc30" + integrity sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA== dependencies: chalk "^4.0.0" graceful-fs "^4.2.9" - jest-haste-map "^28.1.3" + jest-haste-map "^29.7.0" jest-pnp-resolver "^1.2.2" - jest-util "^28.1.3" - jest-validate "^28.1.3" + jest-util "^29.7.0" + jest-validate "^29.7.0" resolve "^1.20.0" - resolve.exports "^1.1.0" + resolve.exports "^2.0.0" slash "^3.0.0" -jest-runner@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-28.1.3.tgz#5eee25febd730b4713a2cdfd76bdd5557840f9a1" - integrity sha512-GkMw4D/0USd62OVO0oEgjn23TM+YJa2U2Wu5zz9xsQB1MxWKDOlrnykPxnMsN0tnJllfLPinHTka61u0QhaxBA== +jest-runner@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.7.0.tgz#809af072d408a53dcfd2e849a4c976d3132f718e" + integrity sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ== dependencies: - "@jest/console" "^28.1.3" - "@jest/environment" "^28.1.3" - "@jest/test-result" "^28.1.3" - "@jest/transform" "^28.1.3" - "@jest/types" "^28.1.3" + "@jest/console" "^29.7.0" + "@jest/environment" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" "@types/node" "*" chalk "^4.0.0" - emittery "^0.10.2" + emittery "^0.13.1" graceful-fs "^4.2.9" - jest-docblock "^28.1.1" - jest-environment-node "^28.1.3" - jest-haste-map "^28.1.3" - jest-leak-detector "^28.1.3" - jest-message-util "^28.1.3" - jest-resolve "^28.1.3" - jest-runtime "^28.1.3" - jest-util "^28.1.3" - jest-watcher "^28.1.3" - jest-worker "^28.1.3" + jest-docblock "^29.7.0" + jest-environment-node "^29.7.0" + jest-haste-map "^29.7.0" + jest-leak-detector "^29.7.0" + jest-message-util "^29.7.0" + jest-resolve "^29.7.0" + jest-runtime "^29.7.0" + jest-util "^29.7.0" + jest-watcher "^29.7.0" + jest-worker "^29.7.0" p-limit "^3.1.0" source-map-support "0.5.13" -jest-runtime@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-28.1.3.tgz#a57643458235aa53e8ec7821949e728960d0605f" - integrity sha512-NU+881ScBQQLc1JHG5eJGU7Ui3kLKrmwCPPtYsJtBykixrM2OhVQlpMmFWJjMyDfdkGgBMNjXCGB/ebzsgNGQw== - dependencies: - "@jest/environment" "^28.1.3" - "@jest/fake-timers" "^28.1.3" - "@jest/globals" "^28.1.3" - "@jest/source-map" "^28.1.2" - "@jest/test-result" "^28.1.3" - "@jest/transform" "^28.1.3" - "@jest/types" "^28.1.3" +jest-runtime@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.7.0.tgz#efecb3141cf7d3767a3a0cc8f7c9990587d3d817" + integrity sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/fake-timers" "^29.7.0" + "@jest/globals" "^29.7.0" + "@jest/source-map" "^29.6.3" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" chalk "^4.0.0" cjs-module-lexer "^1.0.0" collect-v8-coverage "^1.0.0" - execa "^5.0.0" glob "^7.1.3" graceful-fs "^4.2.9" - jest-haste-map "^28.1.3" - jest-message-util "^28.1.3" - jest-mock "^28.1.3" - jest-regex-util "^28.0.2" - jest-resolve "^28.1.3" - jest-snapshot "^28.1.3" - jest-util "^28.1.3" + jest-haste-map "^29.7.0" + jest-message-util "^29.7.0" + jest-mock "^29.7.0" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" slash "^3.0.0" strip-bom "^4.0.0" -jest-snapshot@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-28.1.3.tgz#17467b3ab8ddb81e2f605db05583d69388fc0668" - integrity sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg== +jest-snapshot@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.7.0.tgz#c2c574c3f51865da1bb329036778a69bf88a6be5" + integrity sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw== dependencies: "@babel/core" "^7.11.6" "@babel/generator" "^7.7.2" + "@babel/plugin-syntax-jsx" "^7.7.2" "@babel/plugin-syntax-typescript" "^7.7.2" - "@babel/traverse" "^7.7.2" "@babel/types" "^7.3.3" - "@jest/expect-utils" "^28.1.3" - "@jest/transform" "^28.1.3" - "@jest/types" "^28.1.3" - "@types/babel__traverse" "^7.0.6" - "@types/prettier" "^2.1.5" + "@jest/expect-utils" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" babel-preset-current-node-syntax "^1.0.0" chalk "^4.0.0" - expect "^28.1.3" + expect "^29.7.0" graceful-fs "^4.2.9" - jest-diff "^28.1.3" - jest-get-type "^28.0.2" - jest-haste-map "^28.1.3" - jest-matcher-utils "^28.1.3" - jest-message-util "^28.1.3" - jest-util "^28.1.3" + jest-diff "^29.7.0" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" natural-compare "^1.4.0" - pretty-format "^28.1.3" - semver "^7.3.5" + pretty-format "^29.7.0" + semver "^7.5.3" -jest-util@^28.0.0, jest-util@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-28.1.3.tgz#f4f932aa0074f0679943220ff9cbba7e497028b0" - integrity sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ== +jest-util@^29.0.0, jest-util@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" + integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== dependencies: - "@jest/types" "^28.1.3" + "@jest/types" "^29.6.3" "@types/node" "*" chalk "^4.0.0" ci-info "^3.2.0" graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-validate@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-28.1.3.tgz#e322267fd5e7c64cea4629612c357bbda96229df" - integrity sha512-SZbOGBWEsaTxBGCOpsRWlXlvNkvTkY0XxRfh7zYmvd8uL5Qzyg0CHAXiXKROflh801quA6+/DsT4ODDthOC/OA== +jest-validate@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.7.0.tgz#7bf705511c64da591d46b15fce41400d52147d9c" + integrity sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw== dependencies: - "@jest/types" "^28.1.3" + "@jest/types" "^29.6.3" camelcase "^6.2.0" chalk "^4.0.0" - jest-get-type "^28.0.2" + jest-get-type "^29.6.3" leven "^3.1.0" - pretty-format "^28.1.3" + pretty-format "^29.7.0" -jest-watcher@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-28.1.3.tgz#c6023a59ba2255e3b4c57179fc94164b3e73abd4" - integrity sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g== +jest-watcher@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.7.0.tgz#7810d30d619c3a62093223ce6bb359ca1b28a2f2" + integrity sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g== dependencies: - "@jest/test-result" "^28.1.3" - "@jest/types" "^28.1.3" + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" - emittery "^0.10.2" - jest-util "^28.1.3" + emittery "^0.13.1" + jest-util "^29.7.0" string-length "^4.0.1" -jest-worker@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-28.1.3.tgz#7e3c4ce3fa23d1bb6accb169e7f396f98ed4bb98" - integrity sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g== +jest-worker@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a" + integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== dependencies: "@types/node" "*" + jest-util "^29.7.0" merge-stream "^2.0.0" supports-color "^8.0.0" -jest@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest/-/jest-28.1.3.tgz#e9c6a7eecdebe3548ca2b18894a50f45b36dfc6b" - integrity sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA== +jest@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613" + integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw== dependencies: - "@jest/core" "^28.1.3" - "@jest/types" "^28.1.3" + "@jest/core" "^29.7.0" + "@jest/types" "^29.6.3" import-local "^3.0.2" - jest-cli "^28.1.3" + jest-cli "^29.7.0" jmespath@0.16.0: version "0.16.0" @@ -4742,6 +5017,11 @@ json5@^2.2.1: resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== +json5@^2.2.2, json5@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + jsonfile@^6.0.1: version "6.1.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" @@ -4762,9 +5042,9 @@ just-diff-apply@^5.2.0: integrity sha512-AAV5Jw7tsniWwih8Ly3fXxEZ06y+6p5TwQMsw0dzZ/wPKilzyDgdAnL0Ug4NNIquPUOh1vfFWEHbmXUqM5+o8g== just-diff@^5.0.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/just-diff/-/just-diff-5.1.1.tgz#8da6414342a5ed6d02ccd64f5586cbbed3146202" - integrity sha512-u8HXJ3HlNrTzY7zrYYKjNEfBlyjqhdBkoyTVdjtn7p02RJD5NvR8rIClzeGA7t+UYP1/7eAkWNLU0+P3QrEqKQ== + version "5.2.0" + resolved "https://registry.yarnpkg.com/just-diff/-/just-diff-5.2.0.tgz#60dca55891cf24cd4a094e33504660692348a241" + integrity sha512-6ufhP9SHjb7jibNFrNxyFZ6od3g+An6Ai9mhGRvcYe8UJlH0prseN64M+6ZBBUoKYHZsitDP42gAJ8+eVWr3lw== kind-of@^6.0.3: version "6.0.3" @@ -4794,36 +5074,36 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" -libnpmaccess@^6.0.2: - version "6.0.3" - resolved "https://registry.yarnpkg.com/libnpmaccess/-/libnpmaccess-6.0.3.tgz#473cc3e4aadb2bc713419d92e45d23b070d8cded" - integrity sha512-4tkfUZprwvih2VUZYMozL7EMKgQ5q9VW2NtRyxWtQWlkLTAWHRklcAvBN49CVqEkhUw7vTX2fNgB5LzgUucgYg== +libnpmaccess@^6.0.4: + version "6.0.4" + resolved "https://registry.yarnpkg.com/libnpmaccess/-/libnpmaccess-6.0.4.tgz#2dd158bd8a071817e2207d3b201d37cf1ad6ae6b" + integrity sha512-qZ3wcfIyUoW0+qSFkMBovcTrSGJ3ZeyvpR7d5N9pEYv/kXs8sHP2wiqEIXBKLFrZlmM0kR0RJD7mtfLngtlLag== dependencies: aproba "^2.0.0" minipass "^3.1.1" npm-package-arg "^9.0.1" npm-registry-fetch "^13.0.0" -libnpmdiff@^4.0.2: - version "4.0.4" - resolved "https://registry.yarnpkg.com/libnpmdiff/-/libnpmdiff-4.0.4.tgz#487ccb609dacd7f558f089feef3153933e157d02" - integrity sha512-bUz12309DdkeFL/K0sKhW1mbg8DARMbNI0vQKrJp1J8lxhxqkAjzSQ3eQCacFjSwCz4xaf630ogwuOkSt61ZEQ== +libnpmdiff@^4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/libnpmdiff/-/libnpmdiff-4.0.5.tgz#ffaf93fa9440ea759444b8830fdb5c661b09a7c0" + integrity sha512-9fICQIzmH892UwHHPmb+Seup50UIBWcMIK2FdxvlXm9b4kc1nSH0b/BuY1mORJQtB6ydPMnn+BLzOTmd/SKJmw== dependencies: "@npmcli/disparity-colors" "^2.0.0" "@npmcli/installed-package-contents" "^1.0.7" binary-extensions "^2.2.0" - diff "^5.0.0" + diff "^5.1.0" minimatch "^5.0.1" npm-package-arg "^9.0.1" pacote "^13.6.1" tar "^6.1.0" -libnpmexec@^4.0.2: - version "4.0.10" - resolved "https://registry.yarnpkg.com/libnpmexec/-/libnpmexec-4.0.10.tgz#854909123af9655026b42c6dd9ae18a7cf92785c" - integrity sha512-f2bMoQTlvAJaIVYt+iBEvZP7WTX+8jBLApxQKBkMfg6RTKhEtmW2gZ7C0jc4pEzboZzIwqHeUHcSekIyoE1N3A== +libnpmexec@^4.0.14: + version "4.0.14" + resolved "https://registry.yarnpkg.com/libnpmexec/-/libnpmexec-4.0.14.tgz#9ad44232434b374e477eb2c2e4548baaf698f773" + integrity sha512-dwmzv2K29SdoAHBOa7QR6CfQbFG/PiZDRF6HZrlI6C4DLt2hNgOHTFaUGOpqE2C+YGu0ZwYTDywxRe0eOnf0ZA== dependencies: - "@npmcli/arborist" "^5.0.0" + "@npmcli/arborist" "^5.6.3" "@npmcli/ci-detect" "^2.0.0" "@npmcli/fs" "^2.1.1" "@npmcli/run-script" "^4.2.0" @@ -4838,42 +5118,42 @@ libnpmexec@^4.0.2: semver "^7.3.7" walk-up-path "^1.0.0" -libnpmfund@^3.0.1: - version "3.0.2" - resolved "https://registry.yarnpkg.com/libnpmfund/-/libnpmfund-3.0.2.tgz#7da0827950f0db2cce0acb0dc7652d1834a8b239" - integrity sha512-wmFMP/93Wjy+jDg5LaSldDgAhSgCyA64JUUmp806Kae7y3YP9Qv5m1vUhPxT4yebxgB2v/I6G1/RUcNb1y0kVg== +libnpmfund@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/libnpmfund/-/libnpmfund-3.0.5.tgz#817f9e2120889beb483d9ba8eda142bb84293e4e" + integrity sha512-KdeRoG/dem8H3PcEU2/0SKi3ip7AWwczgS72y/3PE+PBrz/s/G52FNIA9jeLnBirkLC0sOyQHfeM3b7e24ZM+g== dependencies: - "@npmcli/arborist" "^5.0.0" + "@npmcli/arborist" "^5.6.3" -libnpmhook@^8.0.2: - version "8.0.3" - resolved "https://registry.yarnpkg.com/libnpmhook/-/libnpmhook-8.0.3.tgz#9628518a63455d21dafda312ee46175275707ff5" - integrity sha512-TEdNI1mC5zS+w/juCgxrwwQnpbq9lY76NDOS0N37pn6pWIUxB1Yq8mwy6MUEXR1TgH4HurSQyKT6I6Kp9Wjm4A== +libnpmhook@^8.0.4: + version "8.0.4" + resolved "https://registry.yarnpkg.com/libnpmhook/-/libnpmhook-8.0.4.tgz#6c58e5fe763ff5d600ae9c20457ea9a69d1f7d87" + integrity sha512-nuD6e+Nx0OprjEi0wOeqASMl6QIH235th/Du2/8upK3evByFhzIgdfOeP1OhstavW4xtsl0hk5Vw4fAWWuSUgA== dependencies: aproba "^2.0.0" npm-registry-fetch "^13.0.0" -libnpmorg@^4.0.2: - version "4.0.3" - resolved "https://registry.yarnpkg.com/libnpmorg/-/libnpmorg-4.0.3.tgz#a85cbdb3665ad4f7c7279d239a4581ec2eeef5a6" - integrity sha512-r4CpmCEF+e5PbFMBi64xSXmqn0uGgV4T7NWpGL4/A6KT/DTtIxALILQZq+l0ZdN1xm4RjOvqSDR22oT4il8rAQ== +libnpmorg@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/libnpmorg/-/libnpmorg-4.0.4.tgz#2a01d49372cf0df90d79a61e69bddaf2ed704311" + integrity sha512-1bTpD7iub1rDCsgiBguhJhiDufLQuc8DEti20euqsXz9O0ncXVpCYqf2SMmHR4GEdmAvAj2r7FMiyA9zGdaTpA== dependencies: aproba "^2.0.0" npm-registry-fetch "^13.0.0" -libnpmpack@^4.0.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/libnpmpack/-/libnpmpack-4.1.2.tgz#9234a3b1ae433f922c19e97cd3a8a0b135b5f4cc" - integrity sha512-megSAPeZGv9jnDM4KovKbczjyuy/EcPxCIU/iaWsDU1IEAVtBJ0qHqNUm5yN2AgN501Tb3CL6KeFGYdG4E31rQ== +libnpmpack@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/libnpmpack/-/libnpmpack-4.1.3.tgz#025cfe39829acd8260662bf259e3a9331fc1e4b2" + integrity sha512-rYP4X++ME3ZiFO+2iN3YnXJ4LB4Gsd0z5cgszWJZxaEpDN4lRIXirSyynGNsN/hn4taqnlxD+3DPlFDShvRM8w== dependencies: "@npmcli/run-script" "^4.1.3" npm-package-arg "^9.0.1" pacote "^13.6.1" -libnpmpublish@^6.0.2: - version "6.0.4" - resolved "https://registry.yarnpkg.com/libnpmpublish/-/libnpmpublish-6.0.4.tgz#adb41ec6b0c307d6f603746a4d929dcefb8f1a0b" - integrity sha512-lvAEYW8mB8QblL6Q/PI/wMzKNvIrF7Kpujf/4fGS/32a2i3jzUXi04TNyIBcK6dQJ34IgywfaKGh+Jq4HYPFmg== +libnpmpublish@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/libnpmpublish/-/libnpmpublish-6.0.5.tgz#5a894f3de2e267d62f86be2a508e362599b5a4b1" + integrity sha512-LUR08JKSviZiqrYTDfywvtnsnxr+tOvBU0BF8H+9frt7HMvc6Qn6F8Ubm72g5hDTHbq8qupKfDvDAln2TVPvFg== dependencies: normalize-package-data "^4.0.0" npm-package-arg "^9.0.1" @@ -4881,25 +5161,25 @@ libnpmpublish@^6.0.2: semver "^7.3.7" ssri "^9.0.0" -libnpmsearch@^5.0.2: - version "5.0.3" - resolved "https://registry.yarnpkg.com/libnpmsearch/-/libnpmsearch-5.0.3.tgz#ed502a4c2c70ea36723180455fae1357546b2184" - integrity sha512-Ofq76qKAPhxbiyzPf/5LPjJln26VTKwU9hIU0ACxQ6tNtBJ1CHmI7iITrdp7vNezhZc0FlkXwrIpqXjhBJZgLQ== +libnpmsearch@^5.0.4: + version "5.0.4" + resolved "https://registry.yarnpkg.com/libnpmsearch/-/libnpmsearch-5.0.4.tgz#b32aa2b23051c00cdcc0912274d0d416e6655d81" + integrity sha512-XHDmsvpN5+pufvGnfLRqpy218gcGGbbbXR6wPrDJyd1em6agKdYByzU5ccskDHH9iVm2UeLydpDsW1ksYuU0cg== dependencies: npm-registry-fetch "^13.0.0" -libnpmteam@^4.0.2: - version "4.0.3" - resolved "https://registry.yarnpkg.com/libnpmteam/-/libnpmteam-4.0.3.tgz#9335fbbd032b3770f5c9b7ffc6203f47d1ed144a" - integrity sha512-LsYYLz4TlTpcqkusInY5MhKjiHFaCx1GV0LmydXJ/QMh+3IWBJpUhes4ynTZuFoJKkDIFjxyMU09ul+RZixgdg== +libnpmteam@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/libnpmteam/-/libnpmteam-4.0.4.tgz#ac26068808d93b1051d926457db14e4b3ff669ef" + integrity sha512-rzKSwi6MLzwwevbM/vl+BBQTErgn24tCfgPUdzBlszrw3j5necOu7WnTzgvZMDv6maGUwec6Ut1rxszOgH0l+Q== dependencies: aproba "^2.0.0" npm-registry-fetch "^13.0.0" -libnpmversion@^3.0.1: - version "3.0.6" - resolved "https://registry.yarnpkg.com/libnpmversion/-/libnpmversion-3.0.6.tgz#a4a476d38a44d38db9ac424a5e7334479e7fb8b9" - integrity sha512-+lI+AO7cZwDxyAeWCIR8+n9XEfgSDAqmNbv4zy+H6onGthsk/+E3aa+5zIeBpyG5g268zjpc0qrBch0Q3w0nBA== +libnpmversion@^3.0.7: + version "3.0.7" + resolved "https://registry.yarnpkg.com/libnpmversion/-/libnpmversion-3.0.7.tgz#e4c6c07ee28cf351ce1e2293a5ac9922b09ea94d" + integrity sha512-O0L4eNMUIMQ+effi1HsZPKp2N6wecwqGqB8PvkvmLPWN7EsdabdzAVG48nv0p/OjlbIai5KQg/L+qMMfCA4ZjA== dependencies: "@npmcli/git" "^3.0.0" "@npmcli/run-script" "^4.1.3" @@ -5025,6 +5305,11 @@ lru-cache@^7.4.4, lru-cache@^7.5.1, lru-cache@^7.7.1: resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.13.2.tgz#bb5d3f1deea3f3a7a35c1c44345566a612e09cd0" integrity sha512-VJL3nIpA79TodY/ctmZEfhASgqekbT574/c4j3jn4bKXbSCnTTCH/KltZyvL2GlV+tGSMtsWyem8DCX7qKTMBA== +"lru-cache@^9.1.1 || ^10.0.0": + version "10.0.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.0.1.tgz#0a3be479df549cca0e5d693ac402ff19537a6b7a" + integrity sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g== + lru_map@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd" @@ -5042,7 +5327,7 @@ make-error@1.x, make-error@^1.1.1: resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== -make-fetch-happen@^10.0.3, make-fetch-happen@^10.0.6, make-fetch-happen@^10.2.0: +make-fetch-happen@^10.0.3: version "10.2.0" resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-10.2.0.tgz#0bde3914f2f82750b5d48c6d2294d2c74f985e5b" integrity sha512-OnEfCLofQVJ5zgKwGk55GaqosqKjaR6khQlJY3dBAA+hM25Bc5CmX5rKUfVut+rYA3uidA7zb7AvcglU87rPRg== @@ -5064,6 +5349,49 @@ make-fetch-happen@^10.0.3, make-fetch-happen@^10.0.6, make-fetch-happen@^10.2.0: socks-proxy-agent "^7.0.0" ssri "^9.0.0" +make-fetch-happen@^10.0.6, make-fetch-happen@^10.2.0: + version "10.2.1" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz#f5e3835c5e9817b617f2770870d9492d28678164" + integrity sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w== + dependencies: + agentkeepalive "^4.2.1" + cacache "^16.1.0" + http-cache-semantics "^4.1.0" + http-proxy-agent "^5.0.0" + https-proxy-agent "^5.0.0" + is-lambda "^1.0.1" + lru-cache "^7.7.1" + minipass "^3.1.6" + minipass-collect "^1.0.2" + minipass-fetch "^2.0.3" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + negotiator "^0.6.3" + promise-retry "^2.0.1" + socks-proxy-agent "^7.0.0" + ssri "^9.0.0" + +make-fetch-happen@^11.0.3: + version "11.1.1" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-11.1.1.tgz#85ceb98079584a9523d4bf71d32996e7e208549f" + integrity sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w== + dependencies: + agentkeepalive "^4.2.1" + cacache "^17.0.0" + http-cache-semantics "^4.1.1" + http-proxy-agent "^5.0.0" + https-proxy-agent "^5.0.0" + is-lambda "^1.0.1" + lru-cache "^7.7.1" + minipass "^5.0.0" + minipass-fetch "^3.0.0" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + negotiator "^0.6.3" + promise-retry "^2.0.1" + socks-proxy-agent "^7.0.0" + ssri "^10.0.0" + makeerror@1.0.12: version "1.0.12" resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" @@ -5082,21 +5410,21 @@ map-obj@^4.0.0: integrity sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ== marked-terminal@^5.0.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/marked-terminal/-/marked-terminal-5.1.1.tgz#d2edc2991841d893ee943b44b40b2ee9518b4d9f" - integrity sha512-+cKTOx9P4l7HwINYhzbrBSyzgxO2HaHKGZGuB1orZsMIgXYaJyfidT81VXRdpelW/PcHEWxywscePVgI/oUF6g== + version "5.2.0" + resolved "https://registry.yarnpkg.com/marked-terminal/-/marked-terminal-5.2.0.tgz#c5370ec2bae24fb2b34e147b731c94fa933559d3" + integrity sha512-Piv6yNwAQXGFjZSaiNljyNFw7jKDdGrw70FSbtxEyldLsyeuV5ZHm/1wW++kWbrOF1VPnUgYOhB2oLL0ZpnekA== dependencies: - ansi-escapes "^5.0.0" + ansi-escapes "^6.2.0" cardinal "^2.1.1" - chalk "^5.0.0" - cli-table3 "^0.6.1" + chalk "^5.2.0" + cli-table3 "^0.6.3" node-emoji "^1.11.0" - supports-hyperlinks "^2.2.0" + supports-hyperlinks "^2.3.0" marked@^4.0.10: - version "4.0.18" - resolved "https://registry.yarnpkg.com/marked/-/marked-4.0.18.tgz#cd0ac54b2e5610cfb90e8fd46ccaa8292c9ed569" - integrity sha512-wbLDJ7Zh0sqA0Vdg6aqlbT+yPxqLblpAZh1mK2+AO2twQkPywvvqQNfEPVwSSRjZ7dZcdeVBIAgiO7MMp3Dszw== + version "4.3.0" + resolved "https://registry.yarnpkg.com/marked/-/marked-4.3.0.tgz#796362821b019f734054582038b116481b456cf3" + integrity sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A== md5@^2.2.1: version "2.3.0" @@ -5169,20 +5497,34 @@ min-indent@^1.0.0: resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== -minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: +minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: brace-expansion "^1.1.7" -minimatch@^5.0.1, minimatch@^5.1.0: +minimatch@^5.0.1: version "5.1.0" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.0.tgz#1717b464f4971b144f6aabe8f2d0b8e4511e09c7" integrity sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg== dependencies: brace-expansion "^2.0.1" +minimatch@^5.1.0: + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^9.0.1: + version "9.0.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" + integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== + dependencies: + brace-expansion "^2.0.1" + minimist-options@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" @@ -5215,6 +5557,17 @@ minipass-fetch@^2.0.3: optionalDependencies: encoding "^0.1.13" +minipass-fetch@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-3.0.4.tgz#4d4d9b9f34053af6c6e597a64be8e66e42bf45b7" + integrity sha512-jHAqnA728uUpIaFm7NWsCnqKT6UqZz7GcI/bDpPATuwYyKwJwW0remxSCxUlKiEty+eopHGa3oc8WxgQ1FFJqg== + dependencies: + minipass "^7.0.3" + minipass-sized "^1.0.3" + minizlib "^2.1.2" + optionalDependencies: + encoding "^0.1.13" + minipass-flush@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" @@ -5251,6 +5604,16 @@ minipass@^3.0.0, minipass@^3.1.1, minipass@^3.1.6: dependencies: yallist "^4.0.0" +minipass@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" + integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== + +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.3.tgz#05ea638da44e475037ed94d1c7efcc76a25e1974" + integrity sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg== + minizlib@^2.1.1, minizlib@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" @@ -5331,13 +5694,13 @@ node-emoji@^1.11.0: lodash "^4.17.21" node-fetch@^2.6.7: - version "2.6.7" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" - integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== + version "2.7.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== dependencies: whatwg-url "^5.0.0" -node-gyp@^9.0.0, node-gyp@^9.1.0: +node-gyp@^9.0.0: version "9.1.0" resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-9.1.0.tgz#c8d8e590678ea1f7b8097511dedf41fc126648f8" integrity sha512-HkmN0ZpQJU7FLbJauJTHkHlSVAXlNGDAzH/VYFZGDOnFyn/Na3GlNJfkudmufOdS6/jNFhy88ObzL7ERz9es1g== @@ -5353,6 +5716,23 @@ node-gyp@^9.0.0, node-gyp@^9.1.0: tar "^6.1.2" which "^2.0.2" +node-gyp@^9.1.0: + version "9.4.0" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-9.4.0.tgz#2a7a91c7cba4eccfd95e949369f27c9ba704f369" + integrity sha512-dMXsYP6gc9rRbejLXmTbVRYjAHw7ppswsKyMxuxJxxOHzluIO1rGp9TOQgjFJ+2MCqcOcQTOPB/8Xwhr+7s4Eg== + dependencies: + env-paths "^2.2.0" + exponential-backoff "^3.1.1" + glob "^7.1.4" + graceful-fs "^4.2.6" + make-fetch-happen "^11.0.3" + nopt "^6.0.0" + npmlog "^6.0.0" + rimraf "^3.0.2" + semver "^7.3.5" + tar "^6.1.2" + which "^2.0.2" + node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" @@ -5405,9 +5785,9 @@ normalize-package-data@^3.0.0: validate-npm-package-license "^3.0.1" normalize-package-data@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-4.0.0.tgz#1122d5359af21d4cd08718b92b058a658594177c" - integrity sha512-m+GL22VXJKkKbw62ZaBBjv8u6IE3UI4Mh5QakIqs3fWiKe0Xyi6L97hakwZK41/LD4R/2ly71Bayx0NLMwLA/g== + version "4.0.1" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-4.0.1.tgz#b46b24e0616d06cadf9d5718b29b6d445a82a62c" + integrity sha512-EBk5QKKuocMJhB3BILuKhmaPjI8vNRSpIfO9woLC6NyHVkKKdVEdAO1mrT0ZfxNR1lKwCcTkuZfmGIFdizZ8Pg== dependencies: hosted-git-info "^5.0.0" is-core-module "^2.8.1" @@ -5431,13 +5811,20 @@ npm-audit-report@^3.0.0: dependencies: chalk "^4.0.0" -npm-bundled@^1.1.1, npm-bundled@^1.1.2: +npm-bundled@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.2.tgz#944c78789bd739035b70baa2ca5cc32b8d860bc1" integrity sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ== dependencies: npm-normalize-package-bin "^1.0.1" +npm-bundled@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-2.0.1.tgz#94113f7eb342cd7a67de1e789f896b04d2c600f4" + integrity sha512-gZLxXdjEzE/+mOstGDqR6b0EkhJ+kM6fxM6vUuckuctuVPh80Q6pw/rSZj9s4Gex9GxWtIicO1pc8DB9KZWudw== + dependencies: + npm-normalize-package-bin "^2.0.0" + npm-install-checks@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/npm-install-checks/-/npm-install-checks-5.0.0.tgz#5ff27d209a4e3542b8ac6b0c1db6063506248234" @@ -5445,15 +5832,20 @@ npm-install-checks@^5.0.0: dependencies: semver "^7.1.1" -npm-normalize-package-bin@^1.0.0, npm-normalize-package-bin@^1.0.1: +npm-normalize-package-bin@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== +npm-normalize-package-bin@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz#9447a1adaaf89d8ad0abe24c6c84ad614a675fff" + integrity sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ== + npm-package-arg@^9.0.0, npm-package-arg@^9.0.1, npm-package-arg@^9.1.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-9.1.0.tgz#a60e9f1e7c03e4e3e4e994ea87fff8b90b522987" - integrity sha512-4J0GL+u2Nh6OnhvUKXRr2ZMG4lR8qtLp+kv7UiV00Y+nGiSxtttCyIRHCt5L5BNkXQld/RceYItau3MDOoGiBw== + version "9.1.2" + resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-9.1.2.tgz#fc8acecb00235f42270dda446f36926ddd9ac2bc" + integrity sha512-pzd9rLEx4TfNJkovvlBSLGhq31gGu2QDexFPWT19yCDh0JgnRhlBLNo5759N0AJmBk+kQ9Y/hXoLnlgFD+ukmg== dependencies: hosted-git-info "^5.0.0" proc-log "^2.0.1" @@ -5461,22 +5853,22 @@ npm-package-arg@^9.0.0, npm-package-arg@^9.0.1, npm-package-arg@^9.1.0: validate-npm-package-name "^4.0.0" npm-packlist@^5.1.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-5.1.1.tgz#79bcaf22a26b6c30aa4dd66b976d69cc286800e0" - integrity sha512-UfpSvQ5YKwctmodvPPkK6Fwk603aoVsf8AEbmVKAEECrfvL8SSe1A2YIwrJ6xmTHAITKPwwZsWo7WwEbNk0kxw== + version "5.1.3" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-5.1.3.tgz#69d253e6fd664b9058b85005905012e00e69274b" + integrity sha512-263/0NGrn32YFYi4J533qzrQ/krmmrWwhKkzwTuM4f/07ug51odoaNjUexxO4vxlzURHcmYMH1QjvHjsNDKLVg== dependencies: glob "^8.0.1" ignore-walk "^5.0.1" - npm-bundled "^1.1.2" - npm-normalize-package-bin "^1.0.1" + npm-bundled "^2.0.0" + npm-normalize-package-bin "^2.0.0" -npm-pick-manifest@^7.0.0, npm-pick-manifest@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-7.0.1.tgz#76dda30a7cd6b99be822217a935c2f5eacdaca4c" - integrity sha512-IA8+tuv8KujbsbLQvselW2XQgmXWS47t3CB0ZrzsRZ82DbDfkcFunOaPm4X7qNuhMfq+FmV7hQT4iFVpHqV7mg== +npm-pick-manifest@^7.0.0, npm-pick-manifest@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-7.0.2.tgz#1d372b4e7ea7c6712316c0e99388a73ed3496e84" + integrity sha512-gk37SyRmlIjvTfcYl6RzDbSmS9Y4TOBXfsPnoYqTHARNgWbyDiCSMLUpmALDj4jjcTZpURiEfsSHJj9k7EV4Rw== dependencies: npm-install-checks "^5.0.0" - npm-normalize-package-bin "^1.0.1" + npm-normalize-package-bin "^2.0.0" npm-package-arg "^9.0.0" semver "^7.3.5" @@ -5488,10 +5880,10 @@ npm-profile@^6.2.0: npm-registry-fetch "^13.0.1" proc-log "^2.0.0" -npm-registry-fetch@^13.0.0, npm-registry-fetch@^13.0.1, npm-registry-fetch@^13.3.0: - version "13.3.0" - resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-13.3.0.tgz#0ce10fa4a699a1e70685ecf41bbfb4150d74231b" - integrity sha512-10LJQ/1+VhKrZjIuY9I/+gQTvumqqlgnsCufoXETHAPFTS3+M+Z5CFhZRDHGavmJ6rOye3UvNga88vl8n1r6gg== +npm-registry-fetch@^13.0.0, npm-registry-fetch@^13.0.1, npm-registry-fetch@^13.3.1: + version "13.3.1" + resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-13.3.1.tgz#bb078b5fa6c52774116ae501ba1af2a33166af7e" + integrity sha512-eukJPi++DKRTjSBRcDZSDDsGqRK3ehbxfFUcgaRd0Yp6kRwOwh2WVn0r+8rMB4nnuzvAk6rQVzl6K5CkYOmnvw== dependencies: make-fetch-happen "^10.0.6" minipass "^3.1.6" @@ -5514,12 +5906,12 @@ npm-user-validate@^1.0.1: integrity sha512-uQwcd/tY+h1jnEaze6cdX/LrhWhoBxfSknxentoqmIuStxUExxjWd3ULMLFPiFUrZKbOVMowH6Jq2FRWfmhcEw== npm@^8.3.0: - version "8.17.0" - resolved "https://registry.yarnpkg.com/npm/-/npm-8.17.0.tgz#05c77fb2794daa3d9b2cd0460859f1f9dc596676" - integrity sha512-tIcfZd541v86Sqrf+t/GW6ivqiT8b/2b3EAjNw3vRe+eVnL4mlkVwu17hjCOrsPVntLb5C6tQG4jPUE5Oveeyw== + version "8.19.4" + resolved "https://registry.yarnpkg.com/npm/-/npm-8.19.4.tgz#65ad6a2dfdd157a4ef4467fb86e8dcd35a43493f" + integrity sha512-3HANl8i9DKnUA89P4KEgVNN28EjSeDCmvEqbzOAuxCFDzdBZzjUl99zgnGpOUumvW5lvJo2HKcjrsc+tfyv1Hw== dependencies: "@isaacs/string-locale-compare" "^1.1.0" - "@npmcli/arborist" "^5.0.4" + "@npmcli/arborist" "^5.6.3" "@npmcli/ci-detect" "^2.0.0" "@npmcli/config" "^4.2.1" "@npmcli/fs" "^2.1.0" @@ -5528,32 +5920,34 @@ npm@^8.3.0: "@npmcli/run-script" "^4.2.1" abbrev "~1.1.1" archy "~1.0.0" - cacache "^16.1.1" + cacache "^16.1.3" chalk "^4.1.2" chownr "^2.0.0" cli-columns "^4.0.0" cli-table3 "^0.6.2" columnify "^1.6.0" fastest-levenshtein "^1.0.12" + fs-minipass "^2.1.0" glob "^8.0.1" graceful-fs "^4.2.10" - hosted-git-info "^5.0.0" - ini "^3.0.0" + hosted-git-info "^5.2.1" + ini "^3.0.1" init-package-json "^3.0.2" is-cidr "^4.0.2" json-parse-even-better-errors "^2.3.1" - libnpmaccess "^6.0.2" - libnpmdiff "^4.0.2" - libnpmexec "^4.0.2" - libnpmfund "^3.0.1" - libnpmhook "^8.0.2" - libnpmorg "^4.0.2" - libnpmpack "^4.0.2" - libnpmpublish "^6.0.2" - libnpmsearch "^5.0.2" - libnpmteam "^4.0.2" - libnpmversion "^3.0.1" + libnpmaccess "^6.0.4" + libnpmdiff "^4.0.5" + libnpmexec "^4.0.14" + libnpmfund "^3.0.5" + libnpmhook "^8.0.4" + libnpmorg "^4.0.4" + libnpmpack "^4.1.3" + libnpmpublish "^6.0.5" + libnpmsearch "^5.0.4" + libnpmteam "^4.0.4" + libnpmversion "^3.0.7" make-fetch-happen "^10.2.0" + minimatch "^5.1.0" minipass "^3.1.6" minipass-pipeline "^1.2.4" mkdirp "^1.0.4" @@ -5564,19 +5958,19 @@ npm@^8.3.0: npm-audit-report "^3.0.0" npm-install-checks "^5.0.0" npm-package-arg "^9.1.0" - npm-pick-manifest "^7.0.1" + npm-pick-manifest "^7.0.2" npm-profile "^6.2.0" - npm-registry-fetch "^13.3.0" + npm-registry-fetch "^13.3.1" npm-user-validate "^1.0.1" npmlog "^6.0.2" opener "^1.5.2" p-map "^4.0.0" - pacote "^13.6.1" + pacote "^13.6.2" parse-conflict-json "^2.0.2" proc-log "^2.0.1" qrcode-terminal "^0.12.0" read "~1.0.7" - read-package-json "^5.0.1" + read-package-json "^5.0.2" read-package-json-fast "^2.0.3" readdir-scoped-modules "^1.1.0" rimraf "^3.0.2" @@ -5697,17 +6091,17 @@ opener@^1.5.2: resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A== -optionator@^0.9.1: - version "0.9.1" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" - integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== +optionator@^0.9.3: + version "0.9.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" + integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== dependencies: + "@aashutoshrathi/word-wrap" "^1.2.3" deep-is "^0.1.3" fast-levenshtein "^2.0.6" levn "^0.4.1" prelude-ls "^1.2.1" type-check "^0.4.0" - word-wrap "^1.2.3" os-tmpdir@~1.0.2: version "1.0.2" @@ -5797,14 +6191,6 @@ p-reduce@^2.0.0: resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-2.1.0.tgz#09408da49507c6c274faa31f28df334bc712b64a" integrity sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw== -p-retry@^4.0.0: - version "4.6.2" - resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.6.2.tgz#9baae7184057edd4e17231cee04264106e092a16" - integrity sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ== - dependencies: - "@types/retry" "0.12.0" - retry "^0.13.1" - p-try@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" @@ -5825,10 +6211,10 @@ package-hash@^4.0.0: lodash.flattendeep "^4.4.0" release-zalgo "^1.0.0" -pacote@^13.0.3, pacote@^13.6.1: - version "13.6.1" - resolved "https://registry.yarnpkg.com/pacote/-/pacote-13.6.1.tgz#ac6cbd9032b4c16e5c1e0c60138dfe44e4cc589d" - integrity sha512-L+2BI1ougAPsFjXRyBhcKmfT016NscRFLv6Pz5EiNf1CCFJFU0pSKKQwsZTyAQB+sTuUL4TyFyp6J1Ork3dOqw== +pacote@^13.0.3, pacote@^13.6.1, pacote@^13.6.2: + version "13.6.2" + resolved "https://registry.yarnpkg.com/pacote/-/pacote-13.6.2.tgz#0d444ba3618ab3e5cd330b451c22967bbd0ca48a" + integrity sha512-Gu8fU3GsvOPkak2CkbojR7vjs3k3P9cA6uazKTHdsdV0gpCEQq2opelnEv30KRQWgVzP5Vd/5umjcedma3MKtg== dependencies: "@npmcli/git" "^3.0.0" "@npmcli/installed-package-contents" "^1.0.7" @@ -5911,6 +6297,14 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +path-scurry@^1.10.1: + version "1.10.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.10.1.tgz#9ba6bf5aa8500fe9fd67df4f0d9483b2b0bfc698" + integrity sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ== + dependencies: + lru-cache "^9.1.1 || ^10.0.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + path-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" @@ -5964,13 +6358,12 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -pretty-format@^28.0.0, pretty-format@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-28.1.3.tgz#c9fba8cedf99ce50963a11b27d982a9ae90970d5" - integrity sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q== +pretty-format@^29.0.0, pretty-format@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" + integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== dependencies: - "@jest/schemas" "^28.1.3" - ansi-regex "^5.0.1" + "@jest/schemas" "^29.6.3" ansi-styles "^5.0.0" react-is "^18.0.0" @@ -5997,9 +6390,9 @@ promise-all-reject-late@^1.0.0: integrity sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw== promise-call-limit@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/promise-call-limit/-/promise-call-limit-1.0.1.tgz#4bdee03aeb85674385ca934da7114e9bcd3c6e24" - integrity sha512-3+hgaa19jzCGLuSCbieeRsu5C2joKfYn8pY6JAuXFRVfF4IO+L7UPpFWNTeWT9pM7uhskvbPPd/oEOktCn317Q== + version "1.0.2" + resolved "https://registry.yarnpkg.com/promise-call-limit/-/promise-call-limit-1.0.2.tgz#f64b8dd9ef7693c9c7613e7dfe8d6d24de3031ea" + integrity sha512-1vTUnfI2hzui8AEIixbdAJlFY4LFDXqQswy/2eOlThAscXCY4It8FdVuI0fMJGAB2aWGbdQf/gv0skKYXmdrHA== promise-inflight@^1.0.1: version "1.0.1" @@ -6029,6 +6422,11 @@ promzard@^0.3.0: dependencies: read "1" +proto-list@~1.2.1: + version "1.2.4" + resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" + integrity sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA== + pseudomap@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" @@ -6044,6 +6442,11 @@ punycode@^2.1.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== +pure-rand@^6.0.0: + version "6.0.3" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.3.tgz#3c9e6b53c09e52ac3cedffc85ab7c1c7094b38cb" + integrity sha512-KddyFewCsO0j3+np81IQ+SweXLDnDQTs5s67BOnrYmYe/yNmUhttQyGsYzy8yUnoljGAQ9sl38YB4vH8ur7Y+w== + q@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" @@ -6069,7 +6472,7 @@ quick-lru@^4.0.1: resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== -rc@1.2.8, rc@^1.2.8: +rc@^1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== @@ -6085,9 +6488,9 @@ react-is@^18.0.0: integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== read-cmd-shim@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-3.0.0.tgz#62b8c638225c61e6cc607f8f4b779f3b8238f155" - integrity sha512-KQDVjGqhZk92PPNRj9ZEXEuqg8bUobSKRw+q0YQ3TKI5xkce7bUJobL4Z/OtiEbAAv70yEpYIXp4iQ9L8oPVog== + version "3.0.1" + resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-3.0.1.tgz#868c235ec59d1de2db69e11aec885bc095aea087" + integrity sha512-kEmDUoYf/CDy8yZbLTmhB1X9kkjf9Q80PCNsDMb7ufrGd6zZSQA1+UyjrO+pZm5K/S4OXCWJeiIt1JA8kAsa6g== read-package-json-fast@^2.0.2, read-package-json-fast@^2.0.3: version "2.0.3" @@ -6097,15 +6500,15 @@ read-package-json-fast@^2.0.2, read-package-json-fast@^2.0.3: json-parse-even-better-errors "^2.3.0" npm-normalize-package-bin "^1.0.1" -read-package-json@^5.0.0, read-package-json@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-5.0.1.tgz#1ed685d95ce258954596b13e2e0e76c7d0ab4c26" - integrity sha512-MALHuNgYWdGW3gKzuNMuYtcSSZbGQm94fAp16xt8VsYTLBjUSc55bLMKe6gzpWue0Tfi6CBgwCSdDAqutGDhMg== +read-package-json@^5.0.0, read-package-json@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-5.0.2.tgz#b8779ccfd169f523b67208a89cc912e3f663f3fa" + integrity sha512-BSzugrt4kQ/Z0krro8zhTwV1Kd79ue25IhNN/VtHFy1mG/6Tluyi+msc0UpwaoQzxSHa28mntAjIZY6kEgfR9Q== dependencies: glob "^8.0.1" json-parse-even-better-errors "^2.3.1" normalize-package-data "^4.0.0" - npm-normalize-package-bin "^1.0.1" + npm-normalize-package-bin "^2.0.0" read-pkg-up@^7.0.0, read-pkg-up@^7.0.1: version "7.0.1" @@ -6133,10 +6536,10 @@ read@1, read@^1.0.7, read@~1.0.7: dependencies: mute-stream "~0.0.4" -readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.4.0, readable-stream@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== +readable-stream@3, readable-stream@^3.0.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== dependencies: inherits "^2.0.3" string_decoder "^1.1.1" @@ -6155,6 +6558,15 @@ readable-stream@^2.0.0, readable-stream@^2.0.2, readable-stream@~2.3.6: string_decoder "~1.1.1" util-deprecate "~1.0.1" +readable-stream@^3.4.0, readable-stream@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + readdir-scoped-modules@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz#8d45407b4f870a0dcaebc0e28670d18e74514309" @@ -6194,12 +6606,12 @@ regexpp@^3.2.0: resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== -registry-auth-token@^4.0.0: - version "4.2.2" - resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.2.tgz#f02d49c3668884612ca031419491a13539e21fac" - integrity sha512-PC5ZysNb42zpFME6D/XlIgtNGdTl8bBOCw90xQLVMpzuuubJKYDWFAEuUNc+Cn8Z8724tg2SDhDRrkVEsqfDMg== +registry-auth-token@^5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-5.0.2.tgz#8b026cc507c8552ebbe06724136267e63302f756" + integrity sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ== dependencies: - rc "1.2.8" + "@pnpm/npm-conf" "^2.1.0" release-zalgo@^1.0.0: version "1.0.0" @@ -6244,10 +6656,10 @@ resolve-from@^5.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== -resolve.exports@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" - integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== +resolve.exports@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" + integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== resolve@^1.10.0, resolve@^1.20.0, resolve@^1.22.0, resolve@^1.22.1: version "1.22.1" @@ -6263,11 +6675,6 @@ retry@^0.12.0: resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow== -retry@^0.13.1: - version "0.13.1" - resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" - integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== - reusify@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" @@ -6317,10 +6724,10 @@ sax@>=0.6.0: resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== -semantic-release@^19.0.3: - version "19.0.3" - resolved "https://registry.yarnpkg.com/semantic-release/-/semantic-release-19.0.3.tgz#9291053ad9890052f28e7c5921d4741530d516fd" - integrity sha512-HaFbydST1cDKZHuFZxB8DTrBLJVK/AnDExpK0s3EqLIAAUAHUgnd+VSJCUtTYQKkAkauL8G9CucODrVCc7BuAA== +semantic-release@^19.0.5: + version "19.0.5" + resolved "https://registry.yarnpkg.com/semantic-release/-/semantic-release-19.0.5.tgz#d7fab4b33fc20f1288eafd6c441e5d0938e5e174" + integrity sha512-NMPKdfpXTnPn49FDogMBi36SiBfXkSOJqCkk0E4iWOY1tusvvgBwqUmxTX1kmlT6kIYed9YwNKD1sfPpqa5yaA== dependencies: "@semantic-release/commit-analyzer" "^9.0.2" "@semantic-release/error" "^3.0.0" @@ -6368,17 +6775,24 @@ semver-regex@^3.1.2: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@7.x, semver@^7.0.0, semver@^7.1.1, semver@^7.1.2, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7: +semver@^6.0.0, semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +semver@^7.0.0, semver@^7.1.1, semver@^7.1.2, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7: version "7.3.7" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== dependencies: lru-cache "^6.0.0" -semver@^6.0.0, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +semver@^7.5.3, semver@^7.5.4: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" set-blocking@^2.0.0: version "2.0.0" @@ -6416,6 +6830,11 @@ signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + signale@^1.2.1: version "1.4.0" resolved "https://registry.yarnpkg.com/signale/-/signale-1.4.0.tgz#c4be58302fb0262ac00fc3d886a7c113759042f1" @@ -6555,6 +6974,13 @@ sprintf-js@~1.0.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== +ssri@^10.0.0: + version "10.0.5" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-10.0.5.tgz#e49efcd6e36385196cb515d3a2ad6c3f0265ef8c" + integrity sha512-bSf16tAFkGeRlUNDjXu8FzaMQt6g2HZJrun7mtMbIPOddxt3GLMSz5VWUWcqTJUPfLEaDIepGxv+bYQW49596A== + dependencies: + minipass "^7.0.3" + ssri@^9.0.0, ssri@^9.0.1: version "9.0.1" resolved "https://registry.yarnpkg.com/ssri/-/ssri-9.0.1.tgz#544d4c357a8d7b71a19700074b6883fcb4eae057" @@ -6590,7 +7016,7 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -6599,6 +7025,15 @@ string-length@^4.0.1: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + string.prototype.trimend@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz#914a65baaab25fbdd4ee291ca7dde57e869cb8d0" @@ -6631,13 +7066,20 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: ansi-regex "^5.0.1" +strip-ansi@^7.0.1: + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" @@ -6660,7 +7102,7 @@ strip-indent@^3.0.0: dependencies: min-indent "^1.0.0" -strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: +strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== @@ -6691,10 +7133,10 @@ supports-color@^8.0.0: dependencies: has-flag "^4.0.0" -supports-hyperlinks@^2.0.0, supports-hyperlinks@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" - integrity sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ== +supports-hyperlinks@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz#3943544347c1ff90b15effb03fc14ae45ec10624" + integrity sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA== dependencies: has-flag "^4.0.0" supports-color "^7.0.0" @@ -6704,7 +7146,19 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -tar@^6.1.0, tar@^6.1.11, tar@^6.1.2: +tar@^6.1.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.0.tgz#b14ce49a79cb1cd23bc9b016302dea5474493f73" + integrity sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^5.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + +tar@^6.1.11, tar@^6.1.2: version "6.1.11" resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA== @@ -6732,14 +7186,6 @@ tempy@^1.0.0: type-fest "^0.16.0" unique-string "^2.0.0" -terminal-link@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" - integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== - dependencies: - ansi-escapes "^4.2.1" - supports-hyperlinks "^2.0.0" - test-exclude@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" @@ -6838,18 +7284,18 @@ triple-beam@^1.3.0: resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9" integrity sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw== -ts-jest@^28.0.8: - version "28.0.8" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-28.0.8.tgz#cd204b8e7a2f78da32cf6c95c9a6165c5b99cc73" - integrity sha512-5FaG0lXmRPzApix8oFG8RKjAz4ehtm8yMKOTy5HX3fY6W8kmvOrmcY0hKDElW52FJov+clhUbrKAqofnj4mXTg== +ts-jest@^29.1.1: + version "29.1.1" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.1.1.tgz#f58fe62c63caf7bfcc5cc6472082f79180f0815b" + integrity sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA== dependencies: bs-logger "0.x" fast-json-stable-stringify "2.x" - jest-util "^28.0.0" - json5 "^2.2.1" + jest-util "^29.0.0" + json5 "^2.2.3" lodash.memoize "4.x" make-error "1.x" - semver "7.x" + semver "^7.5.3" yargs-parser "^21.0.1" ts-node@^10.9.1: @@ -6881,12 +7327,12 @@ tsconfig-paths@^3.14.1: minimist "^1.2.6" strip-bom "^3.0.0" -tsconfig-paths@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.1.0.tgz#f8ef7d467f08ae3a695335bf1ece088c5538d2c1" - integrity sha512-AHx4Euop/dXFC+Vx589alFba8QItjF+8hf8LtmuiCwHyI4rHXQtOOENaM8kvYf5fR0dRChy3wzWIZ9WbB7FWow== +tsconfig-paths@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz#ef78e19039133446d244beac0fd6a1632e2d107c" + integrity sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg== dependencies: - json5 "^2.2.1" + json5 "^2.2.2" minimist "^1.2.6" strip-bom "^3.0.0" @@ -6949,10 +7395,10 @@ type-fest@^0.8.0, type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== -type-fest@^1.0.2: - version "1.4.0" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-1.4.0.tgz#e9fb813fe3bf1744ec359d55d1affefa76f14be1" - integrity sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA== +type-fest@^3.0.0: + version "3.13.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-3.13.1.tgz#bb744c1f0678bea7543a2d1ec24e83e68e8c8706" + integrity sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g== typedarray-to-buffer@^3.1.5: version "3.1.5" @@ -6988,6 +7434,20 @@ unique-filename@^1.1.1: dependencies: unique-slug "^2.0.0" +unique-filename@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-2.0.1.tgz#e785f8675a9a7589e0ac77e0b5c34d2eaeac6da2" + integrity sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A== + dependencies: + unique-slug "^3.0.0" + +unique-filename@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-3.0.0.tgz#48ba7a5a16849f5080d26c760c86cf5cf05770ea" + integrity sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g== + dependencies: + unique-slug "^4.0.0" + unique-slug@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c" @@ -6995,6 +7455,20 @@ unique-slug@^2.0.0: dependencies: imurmurhash "^0.1.4" +unique-slug@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-3.0.0.tgz#6d347cf57c8a7a7a6044aabd0e2d74e4d76dc7c9" + integrity sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w== + dependencies: + imurmurhash "^0.1.4" + +unique-slug@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-4.0.0.tgz#6bae6bb16be91351badd24cdce741f892a6532e3" + integrity sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ== + dependencies: + imurmurhash "^0.1.4" + unique-string@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" @@ -7085,16 +7559,16 @@ uuid@^8.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== +uuid@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" + integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== + v8-compile-cache-lib@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== -v8-compile-cache@^2.0.3: - version "2.3.0" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" - integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== - v8-to-istanbul@^9.0.1: version "9.0.1" resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz#b6f994b0b5d4ef255e17a0d17dc444a9f5132fa4" @@ -7207,11 +7681,12 @@ winston-transport@^4.5.0: readable-stream "^3.6.0" triple-beam "^1.3.0" -winston@^3.8.1: - version "3.8.1" - resolved "https://registry.yarnpkg.com/winston/-/winston-3.8.1.tgz#76f15b3478cde170b780234e0c4cf805c5a7fb57" - integrity sha512-r+6YAiCR4uI3N8eQNOg8k3P3PqwAm20cLKlzVD9E66Ch39+LZC+VH1UKf9JemQj2B3QoUHfKD7Poewn0Pr3Y1w== +winston@^3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/winston/-/winston-3.10.0.tgz#d033cb7bd3ced026fed13bf9d92c55b903116803" + integrity sha512-nT6SIDaE9B7ZRO0u3UvdrimG0HkB7dSTAgInQnNR2SOPJ4bvq5q79+pXLftKmP52lJGW15+H5MCK0nM9D3KB/g== dependencies: + "@colors/colors" "1.5.0" "@dabh/diagnostics" "^2.0.2" async "^3.2.3" is-stream "^2.0.0" @@ -7223,16 +7698,20 @@ winston@^3.8.1: triple-beam "^1.3.0" winston-transport "^4.5.0" -word-wrap@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" - integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== - wordwrap@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" @@ -7242,14 +7721,14 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" wrappy@1: version "1.0.2" @@ -7266,26 +7745,26 @@ write-file-atomic@^3.0.0: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" -write-file-atomic@^4.0.0, write-file-atomic@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.1.tgz#9faa33a964c1c85ff6f849b80b42a88c2c537c8f" - integrity sha512-nSKUxgAbyioruk6hU87QzVbY279oYT6uiwgDoujth2ju4mJ+TZau7SQBhtbTmUyuNYTuXnSyRn66FV0+eCgcrQ== +write-file-atomic@^4.0.0, write-file-atomic@^4.0.1, write-file-atomic@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" + integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== dependencies: imurmurhash "^0.1.4" signal-exit "^3.0.7" -xml2js@0.4.19: - version "0.4.19" - resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7" - integrity sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q== +xml2js@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.5.0.tgz#d9440631fbb2ed800203fad106f2724f62c493b7" + integrity sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA== dependencies: sax ">=0.6.0" - xmlbuilder "~9.0.1" + xmlbuilder "~11.0.0" -xml2js@^0.4.23: - version "0.4.23" - resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66" - integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug== +xml2js@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.6.2.tgz#dd0b630083aa09c161e25a4d0901e2b2a929b499" + integrity sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA== dependencies: sax ">=0.6.0" xmlbuilder "~11.0.0" @@ -7295,11 +7774,6 @@ xmlbuilder@~11.0.0: resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== -xmlbuilder@~9.0.1: - version "9.0.7" - resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" - integrity sha512-7YXTQc3P2l9+0rjaUbLwMKRhtmwg1M1eDf6nag7urC7pIPYLD9W/jmzQ4ptRSUbodw5S0jfoGTflLemQibSpeQ== - xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" From df570b01e662d5a31cc7afa95eed2b0bc05eeb55 Mon Sep 17 00:00:00 2001 From: Seb Aebischer Date: Tue, 19 Sep 2023 11:35:10 +0100 Subject: [PATCH 11/28] docs: Clarify some things in v2 migration guide --- docs/migration/v2.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/migration/v2.md b/docs/migration/v2.md index 1f15ceae..0e875d0b 100644 --- a/docs/migration/v2.md +++ b/docs/migration/v2.md @@ -95,6 +95,7 @@ import lambdaWrapper from '@/src/config/LambdaWrapper'; export default lambdaWrapper.wrap(async (di) => { const request = di.get(RequestService); // ... + return response; }); ``` @@ -110,7 +111,7 @@ export default lambdaWrapper.wrap(async (di) => { ## Dependency injection -As you'll have seen in the above examples, dependencies are no longer identified by a `DEFINITIONS` string. `get` now takes the dependency class directly. +As you'll have seen in the above examples, dependencies are no longer identified by a `DEFINITIONS` string. The `get` method now takes the dependency class directly. Instead of this: @@ -144,9 +145,9 @@ export default lambdaWrapper.wrap(async (di) => { `get` will also always throw an error when used in a constructor to avoid surprises where other dependencies may be `undefined`. Instead of storing references to dependencies in class members, `get` them just before use. -`definitions` has been removed. +The `definitions` property has been removed. -`getEvent`, `getContext` and `getConfiguration` have been deprecated and will be removed in a future major release. Use the `event`, `context` and `config` properties directly. +The `getEvent`, `getContext` and `getConfiguration` methods have been deprecated and will be removed in a future major release. Use the `event`, `context` and `config` properties directly. ## Models From fa71459ebe8d913ad00f5b2bf45f67602f08b69f Mon Sep 17 00:00:00 2001 From: Seb Aebischer Date: Tue, 19 Sep 2023 11:39:43 +0100 Subject: [PATCH 12/28] docs: Consistently coerce queue names to string Related to #1112 --- docs/migration/v2.md | 2 +- docs/services/SQSService.md | 5 +++-- src/services/SQSService.ts | 7 +++++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/docs/migration/v2.md b/docs/migration/v2.md index 0e875d0b..7db43774 100644 --- a/docs/migration/v2.md +++ b/docs/migration/v2.md @@ -60,7 +60,7 @@ export const lambdaWrapper = lw.configure({ }, sqs: { queues: { - myQueue: process.env.SQS_MY_QUEUE, + myQueue: process.env.SQS_MY_QUEUE as string, }, }, }); diff --git a/docs/services/SQSService.md b/docs/services/SQSService.md index 73c697be..02063934 100644 --- a/docs/services/SQSService.md +++ b/docs/services/SQSService.md @@ -119,8 +119,9 @@ To take advantage of SQS emulation, you will need to do the following in your pr queues: { // Add an entry for each queue with its AWS name. // Usually we define queue names in our serverless.yml and provide them - // to the application via environment variables. - submissions: process.env.SQS_QUEUE_SUBMISSIONS, + // to the application via environment variables. If you haven't defined + // types for your env vars, you'll need to coerce them to `string`. + submissions: process.env.SQS_QUEUE_SUBMISSIONS as string, }, queueConsumers: { // See section below about offline SQS emulation. diff --git a/src/services/SQSService.ts b/src/services/SQSService.ts index b40385b4..3e9d7c91 100644 --- a/src/services/SQSService.ts +++ b/src/services/SQSService.ts @@ -16,12 +16,15 @@ export interface SQSServiceConfig { * Maps short friendly queue names to the full SQS queue name. * * Usually we define queue names in our `serverless.yml` and provide them to - * the application via environment variables. Example: + * the application via environment variables. If you haven't defined types + * for your env vars, you'll need to coerce them to `string`. + * + * Example: * * ```ts * { * queues: { - * submissions: process.env.SQS_QUEUE_SUBMISSIONS, + * submissions: process.env.SQS_QUEUE_SUBMISSIONS as string, * } * } * ``` From 15742f41b109a90a2293310c267df7e3b957bc9a Mon Sep 17 00:00:00 2001 From: Seb Aebischer <49983670+seb-cr@users.noreply.github.com> Date: Thu, 28 Sep 2023 16:26:35 +0100 Subject: [PATCH 13/28] fix: Allow synchronous handlers (#1159) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current type of `LambdaWrapper#wrap` requires handlers to return a `Promise`. We should support synchronous handlers too. No implementation change is required – they're already supported, just disallowed by the type system. Jira: [ENG-2732] --- src/core/LambdaWrapper.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core/LambdaWrapper.ts b/src/core/LambdaWrapper.ts index 397a058b..e7dece59 100644 --- a/src/core/LambdaWrapper.ts +++ b/src/core/LambdaWrapper.ts @@ -34,7 +34,10 @@ export default class LambdaWrapper(handler: (di: DependencyInjection) => Promise, options?: WrapOptions) { + wrap( + handler: (di: DependencyInjection) => T | Promise, + options?: WrapOptions, + ) { const { handleUncaughtErrors = true, } = options || {}; From fcdff3ef6b3b63e9860840748fb7f283260488fa Mon Sep 17 00:00:00 2001 From: Seb Aebischer <49983670+seb-cr@users.noreply.github.com> Date: Thu, 19 Oct 2023 16:07:19 +0100 Subject: [PATCH 14/28] feat: Replace Epsagon with Lumigo (#1168) Epsagon is no longer operating. They took down their website on 3 October and backend services have been gradually removed, and since 10 October any service still making requests to Espagon servers faces delays and Lambda timeouts. We have now migrated to [Lumigo]. Their platform supports [auto-tracing], allowing us to begin monitoring without deploying updates to Lambda Wrapper, however we are missing our metrics and labels which correspond to [Execution Tags] in Lumigo. This PR makes the change to wrap handlers with Lumigo's tracer, and updates our logger to use Execution Tags for metrics and labels. To enable Lumigo tracing and tags, set `LUMIGO_TRACER_TOKEN` in your Lambda environment to your [Lumigo token]. Note that if you have enabled auto-tracing, this will be set automatically and tracing should "just work". There are a couple of other little things that need doing (e.g. removing the `raiseOnEpsagon` flag) that I'll cover in separate PRs. Jira: [ENG-2764] [Lumigo]: https://lumigo.io/ [auto-tracing]: https://docs.lumigo.io/docs/serverless-applications#automatic-instrumentation [Execution Tags]: https://docs.lumigo.io/docs/execution-tags [Lumigo token]: https://docs.lumigo.io/docs/lumigo-tokens BREAKING CHANGE: We no longer use Epsagon for monitoring. Lambda Wrapper now supports [Lumigo](https://lumigo.io/) instead. --- README.md | 8 + package.json | 2 +- src/core/LambdaWrapper.ts | 47 +- src/services/LoggerService.ts | 31 +- tests/unit/core/LambdaWrapper.spec.ts | 89 +++ yarn.lock | 802 ++------------------------ 6 files changed, 187 insertions(+), 792 deletions(-) diff --git a/README.md b/README.md index b7036fe6..35b9d704 100644 --- a/README.md +++ b/README.md @@ -204,6 +204,14 @@ lambdaWrapper.configure({ }); ``` +## Monitoring + +At Comic Relief we use [Lumigo](https://lumigo.io/) for monitoring and observability of our deployed services. Lambda Wrapper includes the Lumigo tracer to allow us to tag traces with custom labels and metrics ([execution tags](https://docs.lumigo.io/docs/execution-tags)). + +Lumigo integration works out-of-the-box with Lumigo's [auto-trace feature](https://docs.lumigo.io/docs/serverless-applications#automatic-instrumentation). If you prefer manual tracing, enable it by setting `LUMIGO_TRACER_TOKEN` in your Lambda environment variables. + +And if you don't use Lumigo, don't worry, their tracer will not be instantiated in your functions and no calls will be made to their servers unless `LUMIGO_TRACER_TOKEN` is set. + ## Notes Lambda Wrapper's dependency injection relies on class names being preserved. If your build process includes minifying or uglifying your code, you'll need to disable these transformations. diff --git a/package.json b/package.json index c8fd6e3c..8ebc2c59 100644 --- a/package.json +++ b/package.json @@ -47,12 +47,12 @@ "aws-sdk": "^2.831.0" }, "dependencies": { + "@lumigo/tracer": "^1.87.0", "@sentry/node": "^6.0.1", "@types/aws-lambda": "^8.10.120", "alai": "1.0.3", "async": "^3.2.4", "axios": "^0.27.2", - "epsagon": "^1.123.3", "useragent": "2.3.0", "uuid": "^9.0.1", "validate.js": "0.13.1", diff --git a/src/core/LambdaWrapper.ts b/src/core/LambdaWrapper.ts index e7dece59..15c2238d 100644 --- a/src/core/LambdaWrapper.ts +++ b/src/core/LambdaWrapper.ts @@ -1,4 +1,4 @@ -import Epsagon from 'epsagon'; +import * as lumigo from '@lumigo/tracer'; import { Context } from '../index'; import ResponseModel from '../models/ResponseModel'; @@ -86,19 +86,45 @@ export default class LambdaWrapper Promise; } return wrapper; } + /** + * `true` if we will send traces to Lumigo. + * + * The `LUMIGO_TRACER_TOKEN` env var is present in both manually traced and + * auto-traced functions. + */ + static get isLumigoEnabled(): boolean { + return !!process.env.LUMIGO_TRACER_TOKEN; + } + + /** + * `true` if the Lambda function is already being traced by a higher-level + * Lumigo wrapper, in which case we don't need to manually wrap our handlers. + * + * There are two ways that this can be done, based on the documentation + * [here](https://docs.lumigo.io/docs/lambda-layers): using a Lambda runtime + * wrapper, or handler redirection. Each method can be detected via its + * environment variables. Auto-trace uses the runtime wrapper. + */ + static get isLumigoWrappingUs(): boolean { + return this.isLumigoEnabled && ( + process.env.AWS_LAMBDA_EXEC_WRAPPER === '/opt/lumigo_wrapper' + || !!process.env.LUMIGO_ORIGINAL_HANDLER + ); + } + /** * Process the result once we have one. * @@ -115,11 +141,10 @@ export default class LambdaWrapper { }); }); + describe('isLumigoEnabled', () => { + describe('when a Lumigo token is present', () => { + beforeAll(() => { + process.env.LUMIGO_TRACER_TOKEN = 'test'; + }); + + afterAll(() => { + delete process.env.LUMIGO_TRACER_TOKEN; + }); + + it('should return true', () => { + expect(LambdaWrapper.isLumigoEnabled).toBe(true); + }); + }); + + describe('when there is no Lumigo token', () => { + beforeAll(() => { + delete process.env.LUMIGO_TRACER_TOKEN; + }); + + it('should return false', () => { + expect(LambdaWrapper.isLumigoEnabled).toBe(false); + }); + }); + }); + + describe('isLumigoWrappingUs', () => { + describe('when using the runtime wrapper (e.g. auto-trace)', () => { + beforeAll(() => { + process.env.AWS_LAMBDA_EXEC_WRAPPER = '/opt/lumigo_wrapper'; + delete process.env.LUMIGO_ORIGINAL_HANDLER; + process.env.LUMIGO_TRACER_TOKEN = 'test'; + }); + + afterAll(() => { + delete process.env.AWS_LAMBDA_EXEC_WRAPPER; + delete process.env.LUMIGO_TRACER_TOKEN; + }); + + it('should return true', () => { + expect(LambdaWrapper.isLumigoWrappingUs).toBe(true); + }); + }); + + describe('when using handler redirection', () => { + beforeAll(() => { + delete process.env.AWS_LAMBDA_EXEC_WRAPPER; + process.env.LUMIGO_ORIGINAL_HANDLER = 'handler.js'; + process.env.LUMIGO_TRACER_TOKEN = 'test'; + }); + + afterAll(() => { + delete process.env.LUMIGO_ORIGINAL_HANDLER; + delete process.env.LUMIGO_TRACER_TOKEN; + }); + + it('should return true', () => { + expect(LambdaWrapper.isLumigoWrappingUs).toBe(true); + }); + }); + + describe('when there is only a Lumigo token', () => { + beforeAll(() => { + delete process.env.AWS_LAMBDA_EXEC_WRAPPER; + delete process.env.LUMIGO_ORIGINAL_HANDLER; + process.env.LUMIGO_TRACER_TOKEN = 'test'; + }); + + afterAll(() => { + delete process.env.LUMIGO_TRACER_TOKEN; + }); + + it('should return false', () => { + expect(LambdaWrapper.isLumigoWrappingUs).toBe(false); + }); + }); + + describe('when there is no Lumigo token', () => { + beforeAll(() => { + delete process.env.AWS_LAMBDA_EXEC_WRAPPER; + delete process.env.LUMIGO_TRACER_TOKEN; + }); + + it('should return false', () => { + expect(LambdaWrapper.isLumigoWrappingUs).toBe(false); + }); + }); + }); + describe('handleError', () => { ([ [undefined, 400, 0], diff --git a/yarn.lock b/yarn.lock index 18af5b98..232f257b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15,645 +15,6 @@ "@jridgewell/gen-mapping" "^0.1.0" "@jridgewell/trace-mapping" "^0.3.9" -"@aws-crypto/ie11-detection@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@aws-crypto/ie11-detection/-/ie11-detection-2.0.0.tgz#bb6c2facf8f03457e949dcf0921477397ffa4c6e" - integrity sha512-pkVXf/dq6PITJ0jzYZ69VhL8VFOFoPZLZqtU/12SGnzYuJOOGNfF41q9GxdI1yqC8R13Rq3jOLKDFpUJFT5eTA== - dependencies: - tslib "^1.11.1" - -"@aws-crypto/sha256-browser@2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-browser/-/sha256-browser-2.0.0.tgz#741c9024df55ec59b51e5b1f5d806a4852699fb5" - integrity sha512-rYXOQ8BFOaqMEHJrLHul/25ckWH6GTJtdLSajhlqGMx0PmSueAuvboCuZCTqEKlxR8CQOwRarxYMZZSYlhRA1A== - dependencies: - "@aws-crypto/ie11-detection" "^2.0.0" - "@aws-crypto/sha256-js" "^2.0.0" - "@aws-crypto/supports-web-crypto" "^2.0.0" - "@aws-crypto/util" "^2.0.0" - "@aws-sdk/types" "^3.1.0" - "@aws-sdk/util-locate-window" "^3.0.0" - "@aws-sdk/util-utf8-browser" "^3.0.0" - tslib "^1.11.1" - -"@aws-crypto/sha256-js@2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-js/-/sha256-js-2.0.0.tgz#f1f936039bdebd0b9e2dd834d65afdc2aac4efcb" - integrity sha512-VZY+mCY4Nmrs5WGfitmNqXzaE873fcIZDu54cbaDaaamsaTOP1DBImV9F4pICc3EHjQXujyE8jig+PFCaew9ig== - dependencies: - "@aws-crypto/util" "^2.0.0" - "@aws-sdk/types" "^3.1.0" - tslib "^1.11.1" - -"@aws-crypto/sha256-js@^2.0.0": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-js/-/sha256-js-2.0.1.tgz#79e1e6cf61f652ef2089c08d471c722ecf1626a9" - integrity sha512-mbHTBSPBvg6o/mN/c18Z/zifM05eJrapj5ggoOIeHIWckvkv5VgGi7r/wYpt+QAO2ySKXLNvH2d8L7bne4xrMQ== - dependencies: - "@aws-crypto/util" "^2.0.1" - "@aws-sdk/types" "^3.1.0" - tslib "^1.11.1" - -"@aws-crypto/supports-web-crypto@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@aws-crypto/supports-web-crypto/-/supports-web-crypto-2.0.0.tgz#fd6cde30b88f77d5a4f57b2c37c560d918014f9e" - integrity sha512-Ge7WQ3E0OC7FHYprsZV3h0QIcpdyJLvIeg+uTuHqRYm8D6qCFJoiC+edSzSyFiHtZf+NOQDJ1q46qxjtzIY2nA== - dependencies: - tslib "^1.11.1" - -"@aws-crypto/util@^2.0.0", "@aws-crypto/util@^2.0.1": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@aws-crypto/util/-/util-2.0.1.tgz#976cf619cf85084ca85ec5eb947a6ac6b8b5c98c" - integrity sha512-JJmFFwvbm08lULw4Nm5QOLg8+lAQeC8aCXK5xrtxntYzYXCGfHwUJ4Is3770Q7HmICsXthGQ+ZsDL7C2uH3yBQ== - dependencies: - "@aws-sdk/types" "^3.1.0" - "@aws-sdk/util-utf8-browser" "^3.0.0" - tslib "^1.11.1" - -"@aws-sdk/abort-controller@3.127.0": - version "3.127.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/abort-controller/-/abort-controller-3.127.0.tgz#60c98bffdb185d8eb5d3e43f30f57a32cc8687d6" - integrity sha512-G77FLYcl9egUoD3ZmR6TX94NMqBMeT53hBGrEE3uVUJV1CwfGKfaF007mPpRZnIB3avnJBQGEK6MrwlCfv2qAw== - dependencies: - "@aws-sdk/types" "3.127.0" - tslib "^2.3.1" - -"@aws-sdk/client-sns@^3.41.0": - version "3.145.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-sns/-/client-sns-3.145.0.tgz#78ce204b28f36899f888e62ca3e6d40d36fdcd0b" - integrity sha512-fz0h7vLLZKeOkaawT4wERAk9XNLRuTXpUc5ymGz3pk7nk0giz9apbyGv+apz+J2WXC7+muZL4jiNe+e8+4+Zfg== - dependencies: - "@aws-crypto/sha256-browser" "2.0.0" - "@aws-crypto/sha256-js" "2.0.0" - "@aws-sdk/client-sts" "3.145.0" - "@aws-sdk/config-resolver" "3.130.0" - "@aws-sdk/credential-provider-node" "3.145.0" - "@aws-sdk/fetch-http-handler" "3.131.0" - "@aws-sdk/hash-node" "3.127.0" - "@aws-sdk/invalid-dependency" "3.127.0" - "@aws-sdk/middleware-content-length" "3.127.0" - "@aws-sdk/middleware-host-header" "3.127.0" - "@aws-sdk/middleware-logger" "3.127.0" - "@aws-sdk/middleware-recursion-detection" "3.127.0" - "@aws-sdk/middleware-retry" "3.127.0" - "@aws-sdk/middleware-serde" "3.127.0" - "@aws-sdk/middleware-signing" "3.130.0" - "@aws-sdk/middleware-stack" "3.127.0" - "@aws-sdk/middleware-user-agent" "3.127.0" - "@aws-sdk/node-config-provider" "3.127.0" - "@aws-sdk/node-http-handler" "3.127.0" - "@aws-sdk/protocol-http" "3.127.0" - "@aws-sdk/smithy-client" "3.142.0" - "@aws-sdk/types" "3.127.0" - "@aws-sdk/url-parser" "3.127.0" - "@aws-sdk/util-base64-browser" "3.109.0" - "@aws-sdk/util-base64-node" "3.55.0" - "@aws-sdk/util-body-length-browser" "3.55.0" - "@aws-sdk/util-body-length-node" "3.55.0" - "@aws-sdk/util-defaults-mode-browser" "3.142.0" - "@aws-sdk/util-defaults-mode-node" "3.142.0" - "@aws-sdk/util-user-agent-browser" "3.127.0" - "@aws-sdk/util-user-agent-node" "3.127.0" - "@aws-sdk/util-utf8-browser" "3.109.0" - "@aws-sdk/util-utf8-node" "3.109.0" - entities "2.2.0" - fast-xml-parser "3.19.0" - tslib "^2.3.1" - -"@aws-sdk/client-sso@3.145.0": - version "3.145.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.145.0.tgz#039943d3277817ae8fa20f615017e2f25f3068ef" - integrity sha512-Z5mbzXB3V0JJzga/MSjTpr+Hq0htxiHO2DNg/q1IeNrKUKDBwEO7MrcGURS/tCPZgyeyNZY08hkXN9ixtoE1HA== - dependencies: - "@aws-crypto/sha256-browser" "2.0.0" - "@aws-crypto/sha256-js" "2.0.0" - "@aws-sdk/config-resolver" "3.130.0" - "@aws-sdk/fetch-http-handler" "3.131.0" - "@aws-sdk/hash-node" "3.127.0" - "@aws-sdk/invalid-dependency" "3.127.0" - "@aws-sdk/middleware-content-length" "3.127.0" - "@aws-sdk/middleware-host-header" "3.127.0" - "@aws-sdk/middleware-logger" "3.127.0" - "@aws-sdk/middleware-recursion-detection" "3.127.0" - "@aws-sdk/middleware-retry" "3.127.0" - "@aws-sdk/middleware-serde" "3.127.0" - "@aws-sdk/middleware-stack" "3.127.0" - "@aws-sdk/middleware-user-agent" "3.127.0" - "@aws-sdk/node-config-provider" "3.127.0" - "@aws-sdk/node-http-handler" "3.127.0" - "@aws-sdk/protocol-http" "3.127.0" - "@aws-sdk/smithy-client" "3.142.0" - "@aws-sdk/types" "3.127.0" - "@aws-sdk/url-parser" "3.127.0" - "@aws-sdk/util-base64-browser" "3.109.0" - "@aws-sdk/util-base64-node" "3.55.0" - "@aws-sdk/util-body-length-browser" "3.55.0" - "@aws-sdk/util-body-length-node" "3.55.0" - "@aws-sdk/util-defaults-mode-browser" "3.142.0" - "@aws-sdk/util-defaults-mode-node" "3.142.0" - "@aws-sdk/util-user-agent-browser" "3.127.0" - "@aws-sdk/util-user-agent-node" "3.127.0" - "@aws-sdk/util-utf8-browser" "3.109.0" - "@aws-sdk/util-utf8-node" "3.109.0" - tslib "^2.3.1" - -"@aws-sdk/client-sts@3.145.0": - version "3.145.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-sts/-/client-sts-3.145.0.tgz#bd8c9afdb79bffd6fe23b87ea62ef382625c9c32" - integrity sha512-6mKLV/0CYkUokFyVDyAw3QyIzzNvYg2u7l8HrsqIKrhLGKtYJn7Mph4P50UHExY8kMTk5IcQDF27JZBTKIw5FQ== - dependencies: - "@aws-crypto/sha256-browser" "2.0.0" - "@aws-crypto/sha256-js" "2.0.0" - "@aws-sdk/config-resolver" "3.130.0" - "@aws-sdk/credential-provider-node" "3.145.0" - "@aws-sdk/fetch-http-handler" "3.131.0" - "@aws-sdk/hash-node" "3.127.0" - "@aws-sdk/invalid-dependency" "3.127.0" - "@aws-sdk/middleware-content-length" "3.127.0" - "@aws-sdk/middleware-host-header" "3.127.0" - "@aws-sdk/middleware-logger" "3.127.0" - "@aws-sdk/middleware-recursion-detection" "3.127.0" - "@aws-sdk/middleware-retry" "3.127.0" - "@aws-sdk/middleware-sdk-sts" "3.130.0" - "@aws-sdk/middleware-serde" "3.127.0" - "@aws-sdk/middleware-signing" "3.130.0" - "@aws-sdk/middleware-stack" "3.127.0" - "@aws-sdk/middleware-user-agent" "3.127.0" - "@aws-sdk/node-config-provider" "3.127.0" - "@aws-sdk/node-http-handler" "3.127.0" - "@aws-sdk/protocol-http" "3.127.0" - "@aws-sdk/smithy-client" "3.142.0" - "@aws-sdk/types" "3.127.0" - "@aws-sdk/url-parser" "3.127.0" - "@aws-sdk/util-base64-browser" "3.109.0" - "@aws-sdk/util-base64-node" "3.55.0" - "@aws-sdk/util-body-length-browser" "3.55.0" - "@aws-sdk/util-body-length-node" "3.55.0" - "@aws-sdk/util-defaults-mode-browser" "3.142.0" - "@aws-sdk/util-defaults-mode-node" "3.142.0" - "@aws-sdk/util-user-agent-browser" "3.127.0" - "@aws-sdk/util-user-agent-node" "3.127.0" - "@aws-sdk/util-utf8-browser" "3.109.0" - "@aws-sdk/util-utf8-node" "3.109.0" - entities "2.2.0" - fast-xml-parser "3.19.0" - tslib "^2.3.1" - -"@aws-sdk/config-resolver@3.130.0": - version "3.130.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/config-resolver/-/config-resolver-3.130.0.tgz#ba0fa915fa5613e87051a9826531e59cab4387b1" - integrity sha512-7dkCHHI9kRcHW6YNr9/2Ub6XkvU9Fu6H/BnlKbaKlDR8jq7QpaFhPhctOVi5D/NDpxJgALifexFne0dvo3piTw== - dependencies: - "@aws-sdk/signature-v4" "3.130.0" - "@aws-sdk/types" "3.127.0" - "@aws-sdk/util-config-provider" "3.109.0" - "@aws-sdk/util-middleware" "3.127.0" - tslib "^2.3.1" - -"@aws-sdk/credential-provider-env@3.127.0": - version "3.127.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.127.0.tgz#06eb67461f7df8feb14abd3b459f682544d78e43" - integrity sha512-Ig7XhUikRBlnRTYT5JBGzWfYZp68X5vkFVIFCmsHHt/qVy0Nz9raZpmDHicdS1u67yxDkWgCPn/bNevWnM0GFg== - dependencies: - "@aws-sdk/property-provider" "3.127.0" - "@aws-sdk/types" "3.127.0" - tslib "^2.3.1" - -"@aws-sdk/credential-provider-imds@3.127.0": - version "3.127.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-imds/-/credential-provider-imds-3.127.0.tgz#1fc7b40bf21adcc2a897e47b72796bd8ebcc7d86" - integrity sha512-I6KlIBBzmJn/U1KikiC50PK3SspT9G5lkVLBaW5a6YfOcijqVTXfAN3kYzqhfeS0j4IgfJEwKVsjsZfmprJO5A== - dependencies: - "@aws-sdk/node-config-provider" "3.127.0" - "@aws-sdk/property-provider" "3.127.0" - "@aws-sdk/types" "3.127.0" - "@aws-sdk/url-parser" "3.127.0" - tslib "^2.3.1" - -"@aws-sdk/credential-provider-ini@3.145.0": - version "3.145.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.145.0.tgz#dda74a080e3536b41c0d1df7852745939c30d12a" - integrity sha512-i4cMYI18sj9T8peXP8EsOv86mR6exDl2O2bYO84ej53Ln78HRuJunyipGdF29vjea6SRTA8odUaA/TbsdxGouA== - dependencies: - "@aws-sdk/credential-provider-env" "3.127.0" - "@aws-sdk/credential-provider-imds" "3.127.0" - "@aws-sdk/credential-provider-sso" "3.145.0" - "@aws-sdk/credential-provider-web-identity" "3.127.0" - "@aws-sdk/property-provider" "3.127.0" - "@aws-sdk/shared-ini-file-loader" "3.127.0" - "@aws-sdk/types" "3.127.0" - tslib "^2.3.1" - -"@aws-sdk/credential-provider-node@3.145.0": - version "3.145.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.145.0.tgz#cc6ec178906677131cee05fdc992e1ca786d875b" - integrity sha512-wtIeCPuFjoBOZUOHD2u68wLZTcrXDF64JsufDgUYdXiONXG7QKwYoFkHm8VldmgrqysH0dND4eHf8bPUuxzzXg== - dependencies: - "@aws-sdk/credential-provider-env" "3.127.0" - "@aws-sdk/credential-provider-imds" "3.127.0" - "@aws-sdk/credential-provider-ini" "3.145.0" - "@aws-sdk/credential-provider-process" "3.127.0" - "@aws-sdk/credential-provider-sso" "3.145.0" - "@aws-sdk/credential-provider-web-identity" "3.127.0" - "@aws-sdk/property-provider" "3.127.0" - "@aws-sdk/shared-ini-file-loader" "3.127.0" - "@aws-sdk/types" "3.127.0" - tslib "^2.3.1" - -"@aws-sdk/credential-provider-process@3.127.0": - version "3.127.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.127.0.tgz#6046a20013a3edd58b631668ed1d73dfd63a931c" - integrity sha512-6v0m2lqkO9J5fNlTl+HjriQNIdfg8mjVST544+5y9EnC/FVmTnIz64vfHveWdNkP/fehFx7wTimNENtoSqCn3A== - dependencies: - "@aws-sdk/property-provider" "3.127.0" - "@aws-sdk/shared-ini-file-loader" "3.127.0" - "@aws-sdk/types" "3.127.0" - tslib "^2.3.1" - -"@aws-sdk/credential-provider-sso@3.145.0": - version "3.145.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.145.0.tgz#217621bd736553c29279cc52932e4e4662bd9442" - integrity sha512-F08vQYsTOm4B9PqLIzER2fjp/89Owy4ZedB88UA+kLNGwNZX/6L6CAVOCZlefyaQB9t9x4YpWim5XWh8hheceQ== - dependencies: - "@aws-sdk/client-sso" "3.145.0" - "@aws-sdk/property-provider" "3.127.0" - "@aws-sdk/shared-ini-file-loader" "3.127.0" - "@aws-sdk/types" "3.127.0" - tslib "^2.3.1" - -"@aws-sdk/credential-provider-web-identity@3.127.0": - version "3.127.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.127.0.tgz#a56c390bf0148f20573abd022930b28df345043a" - integrity sha512-85ahDZnLYB3dqkW+cQ0bWt+NVqOoxomTrJoq3IC2q6muebeFrJ0pyf0JEW/RNRzBiUvvsZujzGdWifzWyQKfVg== - dependencies: - "@aws-sdk/property-provider" "3.127.0" - "@aws-sdk/types" "3.127.0" - tslib "^2.3.1" - -"@aws-sdk/fetch-http-handler@3.131.0": - version "3.131.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/fetch-http-handler/-/fetch-http-handler-3.131.0.tgz#426721ba3c4e7687a6c12ce10bdc661900325815" - integrity sha512-eNxmPZQX2IUeBGWHNC7eNTekWn9VIPLYEMKJbKYUBJryxuTJ7TtLeyEK5oakUjMwP1AUvWT+CV7C+8L7uG1omQ== - dependencies: - "@aws-sdk/protocol-http" "3.127.0" - "@aws-sdk/querystring-builder" "3.127.0" - "@aws-sdk/types" "3.127.0" - "@aws-sdk/util-base64-browser" "3.109.0" - tslib "^2.3.1" - -"@aws-sdk/hash-node@3.127.0": - version "3.127.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/hash-node/-/hash-node-3.127.0.tgz#2fbbeb509a515e6a5cfd6846c02cc1967961a40b" - integrity sha512-wx7DKlXdKebH4JcMsOevdsm2oDNMVm36kuMm0XWRIrFWQ/oq7OquDpEMJzWvGqWF/IfFUpb7FhAWZZpALwlcwA== - dependencies: - "@aws-sdk/types" "3.127.0" - "@aws-sdk/util-buffer-from" "3.55.0" - tslib "^2.3.1" - -"@aws-sdk/invalid-dependency@3.127.0": - version "3.127.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/invalid-dependency/-/invalid-dependency-3.127.0.tgz#3a99603e1969f67278495b827243e9a391b8cfc4" - integrity sha512-bxvmtmJ6gIRfOHvh1jAPZBH2mzppEblPjEOFo4mOzXz4U3qPIxeuukCjboMnGK9QEpV2wObWcYYld0vxoRrfiA== - dependencies: - "@aws-sdk/types" "3.127.0" - tslib "^2.3.1" - -"@aws-sdk/is-array-buffer@3.55.0": - version "3.55.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/is-array-buffer/-/is-array-buffer-3.55.0.tgz#c46122c5636f01d5895e5256a587768c3425ea7a" - integrity sha512-NbiPHVYuPxdqdFd6FxzzN3H1BQn/iWA3ri3Ry7AyLeP/tGs1yzEWMwf8BN8TSMALI0GXT6Sh0GDWy3Ok5xB6DA== - dependencies: - tslib "^2.3.1" - -"@aws-sdk/middleware-content-length@3.127.0": - version "3.127.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-content-length/-/middleware-content-length-3.127.0.tgz#662c1971fdb2dd7d34a9945ebd8da52578900434" - integrity sha512-AFmMaIEW3Rzg0TaKB9l/RENLowd7ZEEOpm0trYw1CgUUORWW/ydCsDT7pekPlC25CPbhUmWXCSA4xPFSYOVnDw== - dependencies: - "@aws-sdk/protocol-http" "3.127.0" - "@aws-sdk/types" "3.127.0" - tslib "^2.3.1" - -"@aws-sdk/middleware-host-header@3.127.0": - version "3.127.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-host-header/-/middleware-host-header-3.127.0.tgz#679f685bd8b4f221ed2c11e90b381d6904034ef9" - integrity sha512-e2gTLJb5lYP9lRV7hN3rKY2l4jv8OygOoHElZJ3Z8KPZskjHelYPcQ8XbdfhSXXxC3vc/0QqN0ResFt3W3Pplg== - dependencies: - "@aws-sdk/protocol-http" "3.127.0" - "@aws-sdk/types" "3.127.0" - tslib "^2.3.1" - -"@aws-sdk/middleware-logger@3.127.0": - version "3.127.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-logger/-/middleware-logger-3.127.0.tgz#b62fd148888f418bd74b0c9d76b80588224ee98f" - integrity sha512-jMNLcZB/ECA7OfkNBLNeAlrLRehyfnUeNQJHW3kcxs9h1+6VxaF6wY+WKozszLI7/3OBzQrFHBQCfRZV7ykSLg== - dependencies: - "@aws-sdk/types" "3.127.0" - tslib "^2.3.1" - -"@aws-sdk/middleware-recursion-detection@3.127.0": - version "3.127.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.127.0.tgz#84949efd4a05a4d00da3e9242825e3c9d715f800" - integrity sha512-tB6WX+Z1kUKTnn5h38XFrTCzoqPKjUZLUjN4Wb27/cbeSiTSKGAZcCXHOJm36Ukorl5arlybQTqGe689EU00Hw== - dependencies: - "@aws-sdk/protocol-http" "3.127.0" - "@aws-sdk/types" "3.127.0" - tslib "^2.3.1" - -"@aws-sdk/middleware-retry@3.127.0": - version "3.127.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-retry/-/middleware-retry-3.127.0.tgz#bcd0741ed676588101739083c6bd141d5c1911e1" - integrity sha512-ZSvg/AyGUacWnf3i8ZbyImtiCH+NyafF8uV7bITP7JkwPrG+VdNocJZOr88GRM0c1A0jfkOf7+oq+fInPwwiNA== - dependencies: - "@aws-sdk/protocol-http" "3.127.0" - "@aws-sdk/service-error-classification" "3.127.0" - "@aws-sdk/types" "3.127.0" - "@aws-sdk/util-middleware" "3.127.0" - tslib "^2.3.1" - uuid "^8.3.2" - -"@aws-sdk/middleware-sdk-sts@3.130.0": - version "3.130.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.130.0.tgz#b8dc87c25db048ae8b91962459dfaec5d5b48a8f" - integrity sha512-FDfs7+ohbhEK3eH3Dshr6JDiL8P72bp3ffeNpPBXuURFqwt4pCmjHuX3SqQR0JIJ2cl3aIdxc17rKaZJfOjtPw== - dependencies: - "@aws-sdk/middleware-signing" "3.130.0" - "@aws-sdk/property-provider" "3.127.0" - "@aws-sdk/protocol-http" "3.127.0" - "@aws-sdk/signature-v4" "3.130.0" - "@aws-sdk/types" "3.127.0" - tslib "^2.3.1" - -"@aws-sdk/middleware-serde@3.127.0": - version "3.127.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-serde/-/middleware-serde-3.127.0.tgz#8732d71ed0d28c43e609fcc156b1a1ac307c0d5f" - integrity sha512-xmWMYV/t9M+b9yHjqaD1noDNJJViI2QwOH7TQZ9VbbrvdVtDrFuS9Sf9He80TBCJqeHShwQN9783W1I3Pu/8kw== - dependencies: - "@aws-sdk/types" "3.127.0" - tslib "^2.3.1" - -"@aws-sdk/middleware-signing@3.130.0": - version "3.130.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-signing/-/middleware-signing-3.130.0.tgz#10c5606cf6cd32cf9afa857b0ff32659460902a7" - integrity sha512-JePq5XLR9TfRN3RQ0d7Za/bEW5D3xgtD1FNAwHeenWALeozMuQgRPjM5RroCnL/5jY3wuvCZI7cSXeqhawWqmA== - dependencies: - "@aws-sdk/property-provider" "3.127.0" - "@aws-sdk/protocol-http" "3.127.0" - "@aws-sdk/signature-v4" "3.130.0" - "@aws-sdk/types" "3.127.0" - tslib "^2.3.1" - -"@aws-sdk/middleware-stack@3.127.0": - version "3.127.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-stack/-/middleware-stack-3.127.0.tgz#d569d964256cdd4a5afd149de325296cf19762f6" - integrity sha512-S1IoUE5o1vCmjsF5nIE8zlItNOM1UE+lhmZeigF7knXJ9+a6ewMB6POAj/s4eoi0wcn0eSnAGsqJCWMSUjOPLA== - dependencies: - tslib "^2.3.1" - -"@aws-sdk/middleware-user-agent@3.127.0": - version "3.127.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.127.0.tgz#f676aac4ddaba64bb12b6d69b0ed7328479cf798" - integrity sha512-CHxgswoOzdkOEoIq7Oyob3Sx/4FYUv6BhUesAX7MNshaDDsTQPbSWjw5bqZDiL/gO+X/34fvqCVVpVD2GvxW/g== - dependencies: - "@aws-sdk/protocol-http" "3.127.0" - "@aws-sdk/types" "3.127.0" - tslib "^2.3.1" - -"@aws-sdk/node-config-provider@3.127.0": - version "3.127.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/node-config-provider/-/node-config-provider-3.127.0.tgz#43a460526f0c24a661264189712e0ff5475e9b45" - integrity sha512-bAHkASMhLZHT1yv2TX6OJGFV9Lc3t1gKfTMEKdXM2O2YhGfSx9A/qLeJm79oDfnILWQtSS2NicxlRDI2lYGf4g== - dependencies: - "@aws-sdk/property-provider" "3.127.0" - "@aws-sdk/shared-ini-file-loader" "3.127.0" - "@aws-sdk/types" "3.127.0" - tslib "^2.3.1" - -"@aws-sdk/node-http-handler@3.127.0": - version "3.127.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/node-http-handler/-/node-http-handler-3.127.0.tgz#81c0a34061b233027bc673f3359c36555c0688d7" - integrity sha512-pyMKvheK8eDwWLgYIRsWy8wiyhsbYYcqkZQs3Eh6upI4E8iCY7eMmhWvHYCibvsO+UjsOwa4cAMOfwnv/Z9s8A== - dependencies: - "@aws-sdk/abort-controller" "3.127.0" - "@aws-sdk/protocol-http" "3.127.0" - "@aws-sdk/querystring-builder" "3.127.0" - "@aws-sdk/types" "3.127.0" - tslib "^2.3.1" - -"@aws-sdk/property-provider@3.127.0": - version "3.127.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/property-provider/-/property-provider-3.127.0.tgz#3b70d23354c35ea04c29c97f05cc4108c2e194ba" - integrity sha512-JxenxlTEkWfLrtJqIjaXaJzAVQbbscoCb5bNjmdud07ESLVfWRKJx2nAJdecHKYp2M5NQyqBuFhQ1ELSFYQKCA== - dependencies: - "@aws-sdk/types" "3.127.0" - tslib "^2.3.1" - -"@aws-sdk/protocol-http@3.127.0": - version "3.127.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/protocol-http/-/protocol-http-3.127.0.tgz#c1d7bb20f09f9e86fd885d3effb33850b618e549" - integrity sha512-UG83PVuKX40wilG2uRU0Fvz4OY8Bt+bSPOG776DFjwIXYzK7BwpJm9H2XI2HLhS5WxrJHhwrLBRgW6UiykMnFw== - dependencies: - "@aws-sdk/types" "3.127.0" - tslib "^2.3.1" - -"@aws-sdk/querystring-builder@3.127.0": - version "3.127.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/querystring-builder/-/querystring-builder-3.127.0.tgz#50a100d13bd13bb06ee92dcd9568e21a37fb9c49" - integrity sha512-tsoyp4lLPsASPDYWsezGAHD8VJsZbjUNATNAzTCFdH6p+4SKBK83Q5kfXCzxt13M+l3oKbxxIWLvS0kVQFyltQ== - dependencies: - "@aws-sdk/types" "3.127.0" - "@aws-sdk/util-uri-escape" "3.55.0" - tslib "^2.3.1" - -"@aws-sdk/querystring-parser@3.127.0": - version "3.127.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/querystring-parser/-/querystring-parser-3.127.0.tgz#d485db0d24005e95bb4c9c478691cd805e5fc0f4" - integrity sha512-Vn/Dv+PqUSepp/DzLqq0LJJD8HdPefJCnLbO5WcHCARHSGlyGlZUFEM45k/oEHpTvgMXj/ORaP3A+tLwLu0AmA== - dependencies: - "@aws-sdk/types" "3.127.0" - tslib "^2.3.1" - -"@aws-sdk/service-error-classification@3.127.0": - version "3.127.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/service-error-classification/-/service-error-classification-3.127.0.tgz#64b69215b2525e3b6806856187ef54b00c0f85d1" - integrity sha512-wjZY9rnlA8SPrICUumTYicEKtK4/yKB62iadUk66hxe8MrH8JhuHH2NqIad0Pt/bK/YtNVhd3yb4pRapOeY5qQ== - -"@aws-sdk/shared-ini-file-loader@3.127.0": - version "3.127.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.127.0.tgz#019c5512bf92f954f6aca6f6811e38fe048aadf6" - integrity sha512-S3Nn4KRTqoJsB/TbRZSWBBUrkckNMR0Juqz7bOB+wupVvddKP6IcpspSC/GX9zgJjVMV8iGisZ6AUsYsC5r+cA== - dependencies: - tslib "^2.3.1" - -"@aws-sdk/signature-v4@3.130.0": - version "3.130.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/signature-v4/-/signature-v4-3.130.0.tgz#152085234311610a350fdcd9a7f877a83aa44cf1" - integrity sha512-g5G1a1NHL2uOoFfC2zQdZcj+wbjgBQPkx6xGdtqNKf9v2kS0n6ap5JUGEaqWE02lUlmWHsoMsS73hXtzwXaBRQ== - dependencies: - "@aws-sdk/is-array-buffer" "3.55.0" - "@aws-sdk/types" "3.127.0" - "@aws-sdk/util-hex-encoding" "3.109.0" - "@aws-sdk/util-middleware" "3.127.0" - "@aws-sdk/util-uri-escape" "3.55.0" - tslib "^2.3.1" - -"@aws-sdk/smithy-client@3.142.0": - version "3.142.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/smithy-client/-/smithy-client-3.142.0.tgz#d27abff1892de644ac25fc07305fbc0050d7d512" - integrity sha512-G38YWTfSFZb5cOH6IwLct530Uy8pnmJvJFeC1pd1nkKD4PRZb+bI2w4xXSX+znYdLA71RYK620OtVKJlB44PtA== - dependencies: - "@aws-sdk/middleware-stack" "3.127.0" - "@aws-sdk/types" "3.127.0" - tslib "^2.3.1" - -"@aws-sdk/types@3.127.0", "@aws-sdk/types@^3.1.0": - version "3.127.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.127.0.tgz#a7bafc47ee2328eee2453087521e6c3a39e7278d" - integrity sha512-e0wtx2IkOl7rwfKfLH5pPTzQ+d45V7b1WrjeL0WDI8kOu6w+sXmhNxI6uM2kf0k4NiTLN84lW290AEWupey9Og== - -"@aws-sdk/url-parser@3.127.0": - version "3.127.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/url-parser/-/url-parser-3.127.0.tgz#7a5c6186e83dc6f823c989c0575aebe384e676b0" - integrity sha512-njZ7zn41JHRpNfr3BCesVXCLZE0zcWSfEdtRV0ICw0cU1FgYcKELSuY9+gLUB4ci6uc7gq7mPE8+w30FcM4QeA== - dependencies: - "@aws-sdk/querystring-parser" "3.127.0" - "@aws-sdk/types" "3.127.0" - tslib "^2.3.1" - -"@aws-sdk/util-base64-browser@3.109.0": - version "3.109.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-base64-browser/-/util-base64-browser-3.109.0.tgz#e7faf5c4cbb88bc39b9c1c5a1a79e4c869e9f645" - integrity sha512-lAZ6fyDGiRLaIsKT9qh7P9FGuNyZ4gAbr1YOSQk/5mHtaTuUvxlPptZuInNM/0MPQm6lpcot00D8IWTucn4PbA== - dependencies: - tslib "^2.3.1" - -"@aws-sdk/util-base64-node@3.55.0": - version "3.55.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-base64-node/-/util-base64-node-3.55.0.tgz#da9a3fd6752be49163572144793e6b23d0186ff4" - integrity sha512-UQ/ZuNoAc8CFMpSiRYmevaTsuRKzLwulZTnM8LNlIt9Wx1tpNvqp80cfvVj7yySKROtEi20wq29h31dZf1eYNQ== - dependencies: - "@aws-sdk/util-buffer-from" "3.55.0" - tslib "^2.3.1" - -"@aws-sdk/util-body-length-browser@3.55.0": - version "3.55.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.55.0.tgz#9c2637097501032f6a1afddb76687415fe9b44b6" - integrity sha512-Ei2OCzXQw5N6ZkTMZbamUzc1z+z1R1Ja5tMEagz5BxuX4vWdBObT+uGlSzL8yvTbjoPjnxWA2aXyEqaUP3JS8Q== - dependencies: - tslib "^2.3.1" - -"@aws-sdk/util-body-length-node@3.55.0": - version "3.55.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-body-length-node/-/util-body-length-node-3.55.0.tgz#67049bbb6c62d794a1bb5a13b9a678988c925489" - integrity sha512-lU1d4I+9wJwydduXs0SxSfd+mHKjxeyd39VwOv6i2KSwWkPbji9UQqpflKLKw+r45jL7+xU/zfeTUg5Tt/3Gew== - dependencies: - tslib "^2.3.1" - -"@aws-sdk/util-buffer-from@3.55.0": - version "3.55.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-buffer-from/-/util-buffer-from-3.55.0.tgz#e7c927974b07a29502aa1ad58509b91d0d7cf0f7" - integrity sha512-uVzKG1UgvnV7XX2FPTylBujYMKBPBaq/qFBxfl0LVNfrty7YjpfieQxAe6yRLD+T0Kir/WDQwGvYC+tOYG3IGA== - dependencies: - "@aws-sdk/is-array-buffer" "3.55.0" - tslib "^2.3.1" - -"@aws-sdk/util-config-provider@3.109.0": - version "3.109.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-config-provider/-/util-config-provider-3.109.0.tgz#7828b8894b2b23c289ffc5c106cbced7a5d6ee86" - integrity sha512-GrAZl/aBv0A28LkyNyq8SPJ5fmViCwz80fWLMeWx/6q5AbivuILogjlWwEZSvZ9zrlHOcFC0+AnCa5pQrjaslw== - dependencies: - tslib "^2.3.1" - -"@aws-sdk/util-defaults-mode-browser@3.142.0": - version "3.142.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.142.0.tgz#808e136ba0b371a68d9d3a4aff7671ee39b68d88" - integrity sha512-vVB/CrodMmIfv4v54MyBlKO0sQSI/+Mvs4g5gMyVjmT4a+1gnktJQ9R6ZHQ2/ErGewcra6eH9MU5T0r1kYe0+w== - dependencies: - "@aws-sdk/property-provider" "3.127.0" - "@aws-sdk/types" "3.127.0" - bowser "^2.11.0" - tslib "^2.3.1" - -"@aws-sdk/util-defaults-mode-node@3.142.0": - version "3.142.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.142.0.tgz#d2a8cec87a5295b81ec4315ff0a31bad799a2ac0" - integrity sha512-13d5RZLO13EDwll3COUq3D4KVsqM63kdf+YjG5mzXR1eXo6GVjghfQfiy0MYM6YbAjTfJxZQkc0nFgWLU8jdyg== - dependencies: - "@aws-sdk/config-resolver" "3.130.0" - "@aws-sdk/credential-provider-imds" "3.127.0" - "@aws-sdk/node-config-provider" "3.127.0" - "@aws-sdk/property-provider" "3.127.0" - "@aws-sdk/types" "3.127.0" - tslib "^2.3.1" - -"@aws-sdk/util-dynamodb@^3.49.0": - version "3.145.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-dynamodb/-/util-dynamodb-3.145.0.tgz#337fcafd938411d2cbf3769a1d71ff5cebc580d2" - integrity sha512-Vj+hJ2s9AsKTZHs0s4v3SmtQdK46C/nUn6XpWXMjXvCcZI5yNRam689+9FspGNGyLosWOMskPQFE0YmjpjIYWQ== - dependencies: - tslib "^2.3.1" - -"@aws-sdk/util-hex-encoding@3.109.0": - version "3.109.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.109.0.tgz#93b20acc27c0a1d7d80f653bf19d3dd01c2ccc65" - integrity sha512-s8CgTNrn3cLkrdiohfxLuOYPCanzvHn/aH5RW6DaMoeQiG5Hl9QUiP/WtdQ9QQx3xvpQFpmvxIaSBwSgFNLQxA== - dependencies: - tslib "^2.3.1" - -"@aws-sdk/util-locate-window@^3.0.0": - version "3.55.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-locate-window/-/util-locate-window-3.55.0.tgz#a4136a20ee1bfcb73967a6614caf769ef79db070" - integrity sha512-0sPmK2JaJE2BbTcnvybzob/VrFKCXKfN4CUKcvn0yGg/me7Bz+vtzQRB3Xp+YSx+7OtWxzv63wsvHoAnXvgxgg== - dependencies: - tslib "^2.3.1" - -"@aws-sdk/util-middleware@3.127.0": - version "3.127.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-middleware/-/util-middleware-3.127.0.tgz#266d6160886f272cb3e3c3eb5266abbac0c033bc" - integrity sha512-EwAPPed9TNqh+Wov2VStLn2NuJ/Wyt7IkZCbCsBuSNp3BFZ1V4gfwTjqtKCtB2LQgQ48MTgWgNCvrH0zjCSPGg== - dependencies: - tslib "^2.3.1" - -"@aws-sdk/util-uri-escape@3.55.0": - version "3.55.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-uri-escape/-/util-uri-escape-3.55.0.tgz#ee57743c628a1c9f942dfe73205ce890ec011916" - integrity sha512-mmdDLUpFCN2nkfwlLdOM54lTD528GiGSPN1qb8XtGLgZsJUmg3uJSFIN2lPeSbEwJB3NFjVas/rnQC48i7mV8w== - dependencies: - tslib "^2.3.1" - -"@aws-sdk/util-user-agent-browser@3.127.0": - version "3.127.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.127.0.tgz#dc6c4c9049ebf238c321883593b2cd3d82b5e755" - integrity sha512-uO2oHmJswuYKJS+GiMdYI8izhpC9M7/jFFvnAmLlTEVwpEi1VX9KePAOF+u5AaBC2kzITo/7dg141XfRHZloIQ== - dependencies: - "@aws-sdk/types" "3.127.0" - bowser "^2.11.0" - tslib "^2.3.1" - -"@aws-sdk/util-user-agent-node@3.127.0": - version "3.127.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.127.0.tgz#368dc0c0e1160e8ca9e5ca21f3857004509aa06e" - integrity sha512-3P/M4ZDD2qMeeoCk7TE/Mw7cG5IjB87F6BP8nI8/oHuaz7j6fsI7D49SNpyjl8JApRynZ122Ad6hwQwRj3isYw== - dependencies: - "@aws-sdk/node-config-provider" "3.127.0" - "@aws-sdk/types" "3.127.0" - tslib "^2.3.1" - -"@aws-sdk/util-utf8-browser@3.109.0", "@aws-sdk/util-utf8-browser@^3.0.0": - version "3.109.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.109.0.tgz#d013272e1981b23a4c84ac06f154db686c0cf84e" - integrity sha512-FmcGSz0v7Bqpl1SE8G1Gc0CtDpug+rvqNCG/szn86JApD/f5x8oByjbEiAyTU2ZH2VevUntx6EW68ulHyH+x+w== - dependencies: - tslib "^2.3.1" - -"@aws-sdk/util-utf8-node@3.109.0": - version "3.109.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-utf8-node/-/util-utf8-node-3.109.0.tgz#89e06d916f5b246c7265f59bac742973ac0767ac" - integrity sha512-Ti/ZBdvz2eSTElsucjzNmzpyg2MwfD1rXmxD0hZuIF8bPON/0+sZYnWd5CbDw9kgmhy28dmKue086tbZ1G0iLQ== - dependencies: - "@aws-sdk/util-buffer-from" "3.55.0" - tslib "^2.3.1" - "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" @@ -1338,6 +699,25 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" +"@lumigo/node-core@1.13.0": + version "1.13.0" + resolved "https://registry.yarnpkg.com/@lumigo/node-core/-/node-core-1.13.0.tgz#3f67992fc8674f48aa34d4acc732c839ec416cc0" + integrity sha512-RO8q9ZBE3ZkGpN1K5liPE1EZMxfooLgRR6kb++Vuvxm9f9296+PcMQs+io0kEsTaP34KfdaLrr8Wzz49w637FA== + dependencies: + shimmer "^1.2.1" + utf8 "^3.0.0" + +"@lumigo/tracer@^1.87.0": + version "1.87.0" + resolved "https://registry.yarnpkg.com/@lumigo/tracer/-/tracer-1.87.0.tgz#8891fefa59c7c117b58eb8fbc49f8b07b1bd64e1" + integrity sha512-4PGeuVelkQHOXpFhKT4KVJiluZS0mK2AYTEqhGZYklpBecqijh6CWaHE6HqrpFqadJF8pCfMGALA1ceQpJjvZg== + dependencies: + "@lumigo/node-core" "1.13.0" + agentkeepalive "^4.1.4" + axios "0.24.0" + shimmer "1.2.1" + utf8 "^3.0.0" + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -2119,6 +1499,13 @@ agent-base@^7.0.2, agent-base@^7.1.0: dependencies: debug "^4.3.4" +agentkeepalive@^4.1.4: + version "4.5.0" + resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923" + integrity sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew== + dependencies: + humanize-ms "^1.2.1" + agentkeepalive@^4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.2.1.tgz#a7975cbb9f83b367f06c90cc51ff28fe7d499717" @@ -2331,12 +1718,12 @@ aws-sdk@^2.1456.0: uuid "8.0.0" xml2js "0.5.0" -axios-minified@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/axios-minified/-/axios-minified-1.0.7.tgz#bccf7383c1bc8f0d0bbe2e93c60d11101d0c0e75" - integrity sha512-AOQFwQcyMgawzm1qHNLxD6z95cVRsLUZIydFfAp+fUs9MDhW3PkJgUtXPNQg2ncGBre3k4wNcH7QFf/PgTBoGw== +axios@0.24.0: + version "0.24.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.24.0.tgz#804e6fa1e4b9c5288501dd9dff56a7a0940d20d6" + integrity sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA== dependencies: - follow-redirects "^1.10.0" + follow-redirects "^1.14.4" axios@^0.27.2: version "0.27.2" @@ -2443,11 +1830,6 @@ bottleneck@^2.15.3: resolved "https://registry.yarnpkg.com/bottleneck/-/bottleneck-2.19.5.tgz#5df0b90f59fd47656ebe63c78a98419205cadd91" integrity sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw== -bowser@^2.11.0: - version "2.11.0" - resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.11.0.tgz#5ca3c35757a7aa5771500c70a73a9f91ef420a8f" - integrity sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA== - brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -2663,11 +2045,6 @@ char-regex@^1.0.2: resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== -charenc@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" - integrity sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA== - chownr@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" @@ -2969,11 +2346,6 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" -crypt@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" - integrity sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow== - crypto-random-string@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" @@ -3104,16 +2476,6 @@ deprecation@^2.0.0: resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== -detect-indent@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" - integrity sha512-rlpvsxUtM0PQvy9iZe640/IWwWYyBsTApREbA1pHOpmOUIl9MkP/U4z7vTtg4Oaojvqhxt7sdufnT0EzGaR31g== - -detect-newline@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" - integrity sha512-CwffZFvlJffUg9zZA0uqrjQayUTC8ob94pnr5sFwaVv3IOmkfUHcWH+jXaQK3askE51Cqe8/9Ql/0uXNwqZ8Zg== - detect-newline@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" @@ -3214,11 +2576,6 @@ encoding@^0.1.13: dependencies: iconv-lite "^0.6.2" -entities@2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" - integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== - env-ci@^5.0.0: version "5.5.0" resolved "https://registry.yarnpkg.com/env-ci/-/env-ci-5.5.0.tgz#43364e3554d261a586dec707bc32be81112b545f" @@ -3233,23 +2590,6 @@ env-paths@^2.2.0: resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== -epsagon@^1.123.3: - version "1.123.3" - resolved "https://registry.yarnpkg.com/epsagon/-/epsagon-1.123.3.tgz#7b86c5962348aaf9be14b8d970cc4fb80a835737" - integrity sha512-fb0zuBG2FDSlNG6PV98MY9/BPZicPEBIYgyMGuiWmk4vN5RVFyKc9Lo/VwzEm7jNmKBpIAJKJxaI9v6AoJX+uw== - dependencies: - "@aws-sdk/client-sns" "^3.41.0" - "@aws-sdk/util-dynamodb" "^3.49.0" - axios-minified "^1.0.7" - google-protobuf-minified "^1.0.8" - json-stringify-safe "^5.0.1" - md5 "^2.2.1" - require-in-the-middle "^5.0.3" - shimmer "^1.2.1" - sort-json "^2.0.0" - uuid-parse "^1.1.0" - uuid4 "^1.0.0" - err-code@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" @@ -3596,11 +2936,6 @@ fast-levenshtein@^2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== -fast-xml-parser@3.19.0: - version "3.19.0" - resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-3.19.0.tgz#cb637ec3f3999f51406dd8ff0e6fc4d83e520d01" - integrity sha512-4pXwmBplsCPv8FOY1WRakF970TjNGnGnfbOnLqjlYvMiF1SR3yOHyxMR/YCXpPTOspNF5gwudqktIP4VsWkvBg== - fastest-levenshtein@^1.0.12: version "1.0.16" resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" @@ -3710,7 +3045,12 @@ fn.name@1.x.x: resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== -follow-redirects@^1.10.0, follow-redirects@^1.14.9: +follow-redirects@^1.14.4: + version "1.15.3" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a" + integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q== + +follow-redirects@^1.14.9: version "1.15.1" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5" integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA== @@ -3953,11 +3293,6 @@ globby@^11.0.0, globby@^11.0.1, globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" -google-protobuf-minified@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/google-protobuf-minified/-/google-protobuf-minified-1.0.8.tgz#c1bf97efddf4f0ba5a2ba74dc7b8576ea9cc3fa1" - integrity sha512-FejfI4d9HOtBijz7Ph0jtjNUIBMqMdLRyzzG7fgQgi0MnyuCmTcXEe2Fo30fgKv9d6kqZALoQgNYrHfREwLZvA== - graceful-fs@4.2.10, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.6, graceful-fs@^4.2.9: version "4.2.10" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" @@ -4289,11 +3624,6 @@ is-boolean-object@^1.1.0: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-buffer@~1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" - integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== - is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" @@ -5426,15 +4756,6 @@ marked@^4.0.10: resolved "https://registry.yarnpkg.com/marked/-/marked-4.3.0.tgz#796362821b019f734054582038b116481b456cf3" integrity sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A== -md5@^2.2.1: - version "2.3.0" - resolved "https://registry.yarnpkg.com/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f" - integrity sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g== - dependencies: - charenc "0.0.2" - crypt "0.0.2" - is-buffer "~1.1.6" - meow@^8.0.0: version "8.1.2" resolved "https://registry.yarnpkg.com/meow/-/meow-8.1.2.tgz#bcbe45bda0ee1729d350c03cffc8395a36c4e897" @@ -5641,11 +4962,6 @@ modify-values@^1.0.0: resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== -module-details-from-path@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/module-details-from-path/-/module-details-from-path-1.0.3.tgz#114c949673e2a8a35e9d35788527aa37b679da2b" - integrity sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A== - ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -6625,15 +5941,6 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== -require-in-the-middle@^5.0.3: - version "5.2.0" - resolved "https://registry.yarnpkg.com/require-in-the-middle/-/require-in-the-middle-5.2.0.tgz#4b71e3cc7f59977100af9beb76bf2d056a5a6de2" - integrity sha512-efCx3b+0Z69/LGJmm9Yvi4cqEdxnoGnxYxGxBghkkTTFeXRtTCmmhO0AnAfHz59k957uTSuy8WaHqOs8wbYUWg== - dependencies: - debug "^4.1.1" - module-details-from-path "^1.0.3" - resolve "^1.22.1" - require-main-filename@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" @@ -6661,7 +5968,7 @@ resolve.exports@^2.0.0: resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== -resolve@^1.10.0, resolve@^1.20.0, resolve@^1.22.0, resolve@^1.22.1: +resolve@^1.10.0, resolve@^1.20.0, resolve@^1.22.0: version "1.22.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== @@ -6811,7 +6118,7 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -shimmer@^1.2.1: +shimmer@1.2.1, shimmer@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.2.1.tgz#610859f7de327b587efebf501fb43117f9aff337" integrity sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw== @@ -6883,15 +6190,6 @@ socks@^2.6.2: ip "^2.0.0" smart-buffer "^4.2.0" -sort-json@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/sort-json/-/sort-json-2.0.1.tgz#7338783bef807185dc37d5b02e3afd905d537cfb" - integrity sha512-s8cs2bcsQCzo/P2T/uoU6Js4dS/jnX8+4xunziNoq9qmSpZNCrRIAIvp4avsz0ST18HycV4z/7myJ7jsHWB2XQ== - dependencies: - detect-indent "^5.0.0" - detect-newline "^2.1.0" - minimist "^1.2.0" - source-map-support@0.5.13: version "0.5.13" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" @@ -7336,16 +6634,11 @@ tsconfig-paths@^4.2.0: minimist "^1.2.6" strip-bom "^3.0.0" -tslib@^1.11.1, tslib@^1.8.1, tslib@^1.9.3: +tslib@^1.8.1, tslib@^1.9.3: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.3.1: - version "2.4.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" - integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== - tsutils@^3.21.0: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" @@ -7522,6 +6815,11 @@ useragent@2.3.0: lru-cache "4.1.x" tmp "0.0.x" +utf8@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1" + integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ== + util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -7539,16 +6837,6 @@ util@^0.12.4: safe-buffer "^5.1.2" which-typed-array "^1.1.2" -uuid-parse@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/uuid-parse/-/uuid-parse-1.1.0.tgz#7061c5a1384ae0e1f943c538094597e1b5f3a65b" - integrity sha512-OdmXxA8rDsQ7YpNVbKSJkNzTw2I+S5WsbMDnCtIWSQaosNAcWtFuI/YK1TjzUI6nbkgiqEyh8gWngfcv8Asd9A== - -uuid4@^1.0.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/uuid4/-/uuid4-1.1.4.tgz#80fa0618749110bdb8aa47cc2cc2167c6331f4eb" - integrity sha512-Gr1q2k40LpF8CokcnQFjPDsdslzJbTCTBG5xQIEflUov431gFkY5KduiGIeKYAamkQnNn4IfdHJbLnl9Bib8TQ== - uuid@8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.0.0.tgz#bc6ccf91b5ff0ac07bbcdbf1c7c4e150db4dbb6c" From beea2ea291ac0ffe1ba904ece0b13608fe5a6f76 Mon Sep 17 00:00:00 2001 From: Seb Aebischer <49983670+seb-cr@users.noreply.github.com> Date: Fri, 3 Nov 2023 11:10:55 +0000 Subject: [PATCH 15/28] feat: Throw if there are conflicting dependency names (#1155) Adds validation to check that all dependencies have a unique name. This follows on from [a discussion] with @zhibek about why we need to turn off webpack's minification optimisation. TL;DR two dependency classes can be minified to the same one-letter name, and since dependencies are keyed by class name, this causes nasty runtime errors. Lambda Wrapper will now be able to detect this situation and suggest turning off minification, linking to the [readme note] of how to do this for webpack. As part of this, dependency classes will be deduplicated before being instantiated. If a dependency is specified several times (unlikely but possible) it will now be instantiated only once. Noting for completeness in case we discover unexpected side effects down the line. Jira: [ENG-2728] [a discussion]: https://github.com/comicrelief/serverless-payments/pull/614#discussion_r1332757352 [readme note]: https://github.com/comicrelief/lambda-wrapper/tree/beta#notes --- src/core/DependencyInjection.ts | 33 ++++++++++- tests/mocks/dependencies.ts | 9 +++ tests/mocks/dependencies2.ts | 8 +++ tests/unit/core/DependencyInjection.spec.ts | 61 ++++++++++++++++++--- 4 files changed, 103 insertions(+), 8 deletions(-) create mode 100644 tests/mocks/dependencies.ts create mode 100644 tests/mocks/dependencies2.ts diff --git a/src/core/DependencyInjection.ts b/src/core/DependencyInjection.ts index 89bbe8a4..af48d795 100644 --- a/src/core/DependencyInjection.ts +++ b/src/core/DependencyInjection.ts @@ -28,7 +28,38 @@ export default class DependencyInjection Constructor.name) + .reduce( + (counts, name) => ({ ...counts, [name]: (counts[name] || 0) + 1 }), + {} as Record, + ); + if (Object.values(countByName).some((count) => count > 1)) { + const duplicateNames = Object.entries(countByName) + .filter(([, count]) => count > 1) + .map(([name]) => name); + + // if all class names are single-letter, they're probably minified -- in + // this case, give a hint about how to fix it + const action = duplicateNames.every((it) => it.length === 1) + ? "If you don't recognise the single-letter names listed above, your " + + "bundler may be minifying your code. You'll need to disable this " + + 'for Lambda Wrapper to work correctly. Please refer to the Notes ' + + 'section of the Lambda Wrapper readme:\n\n' + + ' https://github.com/comicrelief/lambda-wrapper/tree/beta#notes' + : 'Please ensure that all dependency classes have a unique name.'; + + throw new Error( + `Dependency names are not unique: ${duplicateNames.join(', ')}\n\n${action}`, + ); + } + + // instantiate all dependencies this.dependencies = Object.fromEntries( classes.map((Constructor) => [Constructor.name, new Constructor(this)]), ); diff --git a/tests/mocks/dependencies.ts b/tests/mocks/dependencies.ts new file mode 100644 index 00000000..b02fef55 --- /dev/null +++ b/tests/mocks/dependencies.ts @@ -0,0 +1,9 @@ +import { DependencyAwareClass } from '@/src'; + +export class A extends DependencyAwareClass {} + +export class B extends DependencyAwareClass {} + +export class C extends DependencyAwareClass {} + +export class ServiceA extends DependencyAwareClass {} diff --git a/tests/mocks/dependencies2.ts b/tests/mocks/dependencies2.ts new file mode 100644 index 00000000..3fdd0eec --- /dev/null +++ b/tests/mocks/dependencies2.ts @@ -0,0 +1,8 @@ +// Some more dependency classes, with the same name as those in dependencies.ts +// so that we can test name conflicts. + +import { DependencyAwareClass } from '@/src'; + +export class A extends DependencyAwareClass {} + +export class ServiceA extends DependencyAwareClass {} diff --git a/tests/unit/core/DependencyInjection.spec.ts b/tests/unit/core/DependencyInjection.spec.ts index efdc8fcd..0256a6dd 100644 --- a/tests/unit/core/DependencyInjection.spec.ts +++ b/tests/unit/core/DependencyInjection.spec.ts @@ -1,11 +1,15 @@ -import { DependencyAwareClass, DependencyInjection } from '@/src'; +import { DependencyInjection } from '@/src'; import { mockContext, mockEvent } from '@/tests/mocks/aws'; - -class A extends DependencyAwareClass {} - -class B extends DependencyAwareClass {} - -class C extends DependencyAwareClass {} +import { + A, + B, + C, + ServiceA, +} from '@/tests/mocks/dependencies'; +import { + A as AClash, + ServiceA as ServiceAClash, +} from '@/tests/mocks/dependencies2'; describe('unit.core.DependencyInjection', () => { const mockConfig = { @@ -17,6 +21,49 @@ describe('unit.core.DependencyInjection', () => { const di = new DependencyInjection(mockConfig, mockEvent, mockContext); + describe('constructor', () => { + describe('dependency conflicts', () => { + it('should throw if dependencies have conflicting names', () => { + const clashConfig = { + dependencies: { + ServiceA, + ServiceAClash, + }, + }; + + expect( + () => new DependencyInjection(clashConfig, mockEvent, mockContext), + ).toThrowError('ensure that all dependency classes have a unique name'); + }); + + it('should suggest turning off minification if names are single-letter', () => { + const clashConfig = { + dependencies: { + A, + AClash, + }, + }; + + expect( + () => new DependencyInjection(clashConfig, mockEvent, mockContext), + ).toThrowError('your bundler may be minifying your code'); + }); + + it('should not throw if the same dependency is included twice', () => { + const okayConfig = { + dependencies: { + A, + again: A, + }, + }; + + expect( + () => new DependencyInjection(okayConfig, mockEvent, mockContext), + ).not.toThrow(); + }); + }); + }); + describe('event', () => { it('should expose the event', () => { expect(di.event).toBe(mockEvent); From 481d430b3d200506ae188726d0cfe85b8fd9e502 Mon Sep 17 00:00:00 2001 From: Seb Aebischer <49983670+seb-cr@users.noreply.github.com> Date: Fri, 3 Nov 2023 13:18:56 +0000 Subject: [PATCH 16/28] feat: Replace `StatusModel` with types (#1172) First step to replace our model classes with types. Some advantages include: - No boilerplate data class with getters and setters (and all the corresponding tests) - Less to maintain and smaller bundles - IntelliSense for status values, even if Lambda Wrapper is used in non-TypeScript projects There are no longer run-time checks on the `status` value, but this is not a regression. There was nothing to stop you bypassing those before, e.g. by doing `statusModel.status = 'random string'`. Now, if you use TypeScript, that is an error. Jira: [ENG-2734] BREAKING CHANGE: `StatusModel` and `STATUS_TYPES` are replaced with the `ServiceStatus` and `Status` types --- docs/migration/v2.md | 47 +++++++++++++++++++- src/index.ts | 9 ++-- src/models/StatusModel.ts | 61 -------------------------- src/services/SQSService.ts | 15 ++++--- src/types/Status.ts | 9 ++++ tests/unit/index.spec.ts | 5 --- tests/unit/models/StatusModel.spec.ts | 39 ---------------- tests/unit/services/SQSService.spec.ts | 60 +++++++++++++++++++++++++ 8 files changed, 129 insertions(+), 116 deletions(-) delete mode 100644 src/models/StatusModel.ts create mode 100644 src/types/Status.ts delete mode 100644 tests/unit/models/StatusModel.spec.ts diff --git a/docs/migration/v2.md b/docs/migration/v2.md index 7db43774..734f4199 100644 --- a/docs/migration/v2.md +++ b/docs/migration/v2.md @@ -6,6 +6,7 @@ This doc summarises the breaking changes introduced in v2 and what you need to d - [Wrapping a function](#wrapping-a-function) - [Dependency injection](#dependency-injection) - [Models](#models) + - [StatusModel](#statusmodel) The new version of dependency injection does not work if code is minified. See [Notes](../../README.md#notes) in the main readme for how to turn this off in webpack. @@ -155,4 +156,48 @@ The `Model` base class has been removed. It's hard to make it type-safe (it trie The `MarketingPreference` model is removed, as this is application-specific and again is replaced by our [data-models](https://github.com/comicrelief/data-models) repo. -Other models (`ResponseModel`, `SQSMessageModel`, `StatusModel`) are unaffected except that they no longer inherit from a common `Model` class. +The `StatusModel` model has been replaced by a simple object type. See the [StatusModel](#statusmodel) section below. + +Other models (`ResponseModel`, `SQSMessageModel`) are unaffected except that they no longer inherit from a common `Model` class. + +### `StatusModel` + +Service statuses are now simple objects with the `ServiceStatus` type. The `STATUS_TYPES` object has also been removed in favour of a string union type. + +Instead of this: + +```js +import { StatusModel, STATUS_TYPES } from '@comicrelief/lambda-wrapper'; + +async function checkStatus() { + const result = new StatusModel('My service', STATUS_TYPES.OK); + try { + await testMyService(); // some function that throws if there's a failure + } catch (error) { + result.setStatus(STATUS_TYPES.APPLICATION_FAILURE); + } + return result; +} +``` + +do this: + +```ts +import { ServiceStatus, StatusValue } from '@comicrelief/lambda-wrapper'; + +async function checkStatus(): Promise { + let status: StatusValue; + try { + await testMyService(); // some function that throws if there's a failure + status = 'OK'; + } catch (error) { + status = 'APPLICATION_FAILURE'; + } + return { + service: 'My service', + status, + }; +} +``` + +Note that we can keep `status` unset initially, and TypeScript will complain if you forget to set it before `checkStatus` returns. diff --git a/src/index.ts b/src/index.ts index 2a61e4a0..c2991abb 100644 --- a/src/index.ts +++ b/src/index.ts @@ -36,10 +36,6 @@ export { export { default as SQSMessageModel, } from './models/SQSMessageModel'; -export { - default as StatusModel, - STATUS_TYPES, -} from './models/StatusModel'; export { default as BaseConfigService, @@ -68,5 +64,10 @@ export { default as TimerService, } from './services/TimerService'; +export { + ServiceStatus, + Status, +} from './types/Status'; + export { default as LambdaTermination } from './utils/LambdaTermination'; export { default as PromisifiedDelay } from './utils/PromisifiedDelay'; diff --git a/src/models/StatusModel.ts b/src/models/StatusModel.ts deleted file mode 100644 index 942bc19d..00000000 --- a/src/models/StatusModel.ts +++ /dev/null @@ -1,61 +0,0 @@ -export const STATUS_TYPES = { - OK: 'OK', - ACCEPTABLE_FAILURE: 'ACCEPTABLE_FAILURE', - APPLICATION_FAILURE: 'APPLICATION_FAILURE', -}; - -/** - * Model for our status check endpoints. - */ -export default class StatusModel { - /** - * Service name. - */ - service: string; - - /** - * One of the `STATUS_TYPES` values. - */ - status: string; - - constructor(service: string, status: string) { - this.service = service; - this.status = status; - } - - /** - * Get the service name. - */ - getService(): string { - return this.service; - } - - /** - * Set the service name. - * - * @param service - */ - setService(service: string) { - this.service = service; - } - - /** - * Set the status. - * - * @param status - */ - setStatus(status: string) { - if (!(status in STATUS_TYPES)) { - throw new TypeError(`${StatusModel.name} - ${status} is not a valid status type`); - } - - this.status = status; - } - - /** - * Get the status. - */ - getStatus(): string { - return this.status; - } -} diff --git a/src/services/SQSService.ts b/src/services/SQSService.ts index 3e9d7c91..ff86d048 100644 --- a/src/services/SQSService.ts +++ b/src/services/SQSService.ts @@ -7,7 +7,7 @@ import DependencyAwareClass from '../core/DependencyAwareClass'; import DependencyInjection from '../core/DependencyInjection'; import { LambdaWrapperConfig } from '../core/config'; import SQSMessageModel from '../models/SQSMessageModel'; -import StatusModel, { STATUS_TYPES } from '../models/StatusModel'; +import { ServiceStatus, Status } from '../types/Status'; import LoggerService from './LoggerService'; import TimerService from './TimerService'; @@ -354,7 +354,7 @@ export default class SQSService< /** * Check SQS status. */ - checkStatus() { + checkStatus(): Promise { const logger = this.di.get(LoggerService); const timer = this.di.get(TimerService); const timerId = `sqs-list-queues-${uuid()}`; @@ -365,18 +365,21 @@ export default class SQSService< this.sqs.listQueues({}, (error, data) => { timer.stop(timerId); - const statusModel = new StatusModel('SQS', STATUS_TYPES.OK); + let status: Status = 'OK'; if (error) { logger.error(error); - statusModel.setStatus(STATUS_TYPES.APPLICATION_FAILURE); + status = 'APPLICATION_FAILURE'; } if (typeof data.QueueUrls === 'undefined' || data.QueueUrls.length === 0) { - statusModel.setStatus(STATUS_TYPES.APPLICATION_FAILURE); + status = 'APPLICATION_FAILURE'; } - resolve(statusModel); + resolve({ + service: 'SQS', + status, + }); }); }); } diff --git a/src/types/Status.ts b/src/types/Status.ts new file mode 100644 index 00000000..27d3ccf3 --- /dev/null +++ b/src/types/Status.ts @@ -0,0 +1,9 @@ +export type Status = + | 'OK' + | 'ACCEPTABLE_FAILURE' + | 'APPLICATION_FAILURE'; + +export type ServiceStatus = { + service: string; + status: Status; +}; diff --git a/tests/unit/index.spec.ts b/tests/unit/index.spec.ts index d08c3113..ae226666 100644 --- a/tests/unit/index.spec.ts +++ b/tests/unit/index.spec.ts @@ -3,7 +3,6 @@ import DependencyInjection from '@/src/core/DependencyInjection'; import LambdaWrapper from '@/src/core/LambdaWrapper'; import ResponseModel, { RESPONSE_HEADERS } from '@/src/models/ResponseModel'; import SQSMessageModel from '@/src/models/SQSMessageModel'; -import StatusModel from '@/src/models/StatusModel'; import BaseConfigService from '@/src/services/BaseConfigService'; import HTTPService, { COMICRELIEF_TEST_METADATA_HEADER } from '@/src/services/HTTPService'; import LoggerService from '@/src/services/LoggerService'; @@ -52,10 +51,6 @@ describe('unit.index', () => { expect(lib.SQSMessageModel).toBe(SQSMessageModel); }); - it('should export StatusModel', () => { - expect(lib.StatusModel).toBe(StatusModel); - }); - // services it('should export BaseConfigService', () => { diff --git a/tests/unit/models/StatusModel.spec.ts b/tests/unit/models/StatusModel.spec.ts deleted file mode 100644 index ad141b56..00000000 --- a/tests/unit/models/StatusModel.spec.ts +++ /dev/null @@ -1,39 +0,0 @@ -import StatusModel, { STATUS_TYPES } from '@/src/models/StatusModel'; - -describe('unit.models.StatusModel', () => { - describe('getService', () => { - it('should return the service name', () => { - const statusModel = new StatusModel('test', STATUS_TYPES.OK); - expect(statusModel.getService()).toEqual('test'); - }); - }); - - describe('setService', () => { - it('should set the service name', () => { - const statusModel = new StatusModel('test', STATUS_TYPES.OK); - statusModel.setService('other'); - expect(statusModel.getService()).toEqual('other'); - }); - }); - - describe('getStatus', () => { - it('should return the status', () => { - const statusModel = new StatusModel('test', STATUS_TYPES.OK); - expect(statusModel.getStatus()).toEqual(STATUS_TYPES.OK); - }); - }); - - describe('setStatus', () => { - it('should set the status', () => { - const statusModel = new StatusModel('test', STATUS_TYPES.OK); - statusModel.setStatus(STATUS_TYPES.ACCEPTABLE_FAILURE); - expect(statusModel.getStatus()).toEqual(STATUS_TYPES.ACCEPTABLE_FAILURE); - }); - - it('should throw an error when trying to set an invalid status', () => { - const statusModel = new StatusModel('test', STATUS_TYPES.OK); - expect(() => statusModel.setStatus('invalid')) - .toThrow('StatusModel - invalid is not a valid status type'); - }); - }); -}); diff --git a/tests/unit/services/SQSService.spec.ts b/tests/unit/services/SQSService.spec.ts index 2baed1a2..29af39a1 100644 --- a/tests/unit/services/SQSService.spec.ts +++ b/tests/unit/services/SQSService.spec.ts @@ -33,6 +33,16 @@ const createAsyncMock = (returnValue: any) => { return jest.fn().mockReturnValue({ promise: () => mockedValue }); }; +const createCallbackMock = (returnValue: any) => jest.fn() + .mockImplementation((...args: any[]) => { + const callback = args.pop(); + if (returnValue instanceof Error) { + callback(returnValue, {}); + } else { + callback(null, returnValue); + } + }); + type MockSQSService = SQSService & { sqs: { sendMessage: jest.Mock; @@ -50,6 +60,7 @@ type MockSQSService = SQSService & { */ const getService = ( { + listQueues = null, sendMessage = null, invoke = null, }: any = {}, @@ -64,6 +75,7 @@ const getService = ( const service = di.get(SQSService); const sqs = { + listQueues: createCallbackMock(listQueues), sendMessage: createAsyncMock(sendMessage), } as unknown as AWS.SQS; const lambda = { @@ -110,6 +122,54 @@ describe('unit.services.SQSService', () => { expect(sqs.queueConsumers).toEqual(config.sqs?.queueConsumers); }); + describe('checkStatus', () => { + describe('when SQS is available', () => { + it('should return status "OK"', async () => { + const service = getService({ + listQueues: { + QueueUrls: [config.sqs.queues[TEST_QUEUE]], + }, + }, false); + + const result = await service.checkStatus(); + expect(result).toEqual({ + service: 'SQS', + status: 'OK', + }); + }); + }); + + describe('when SQS is unavailable', () => { + it('should return status "APPLICATION_FAILURE"', async () => { + const service = getService({ + listQueues: new Error('service unavailable'), + }, false); + + const result = await service.checkStatus(); + expect(result).toEqual({ + service: 'SQS', + status: 'APPLICATION_FAILURE', + }); + }); + }); + + describe('when `listQueues` returns no queues', () => { + it('should return status "APPLICATION_FAILURE"', async () => { + const service = getService({ + listQueues: { + QueueUrls: [], + }, + }, false); + + const result = await service.checkStatus(); + expect(result).toEqual({ + service: 'SQS', + status: 'APPLICATION_FAILURE', + }); + }); + }); + }); + describe('publish', () => { describe('when container.isOffline === false', () => { ([ From 141898e450bb5331bfa88025cd74d9e7ee282751 Mon Sep 17 00:00:00 2001 From: Seb Aebischer <49983670+seb-cr@users.noreply.github.com> Date: Mon, 11 Dec 2023 16:16:52 +0000 Subject: [PATCH 17/28] feat: Return lowercase header keys from `getAllHeaders` (#1187) This is consistent with many other libraries (e.g. http, axios) and makes it easier to work with HTTP headers. The HTTP protocol states that header names are case-insensitive, so currently when using `getAllHeaders` you must worry about the case of the header name: ```js const headers = request.getAllHeaders(); const contentType = headers['Content-Type'] || headers['content-type']; // but what if someone sends us `Content-type`? ``` The existing `getHeader` method solved this by doing a case-insensitive search of all keys. Now, you can also safely use the lowercase key: ```js const headers = request.getAllHeaders(); const contentType = headers['content-type']; ``` Jira: [ENG-2735] BREAKING CHANGE: Header keys returned by `getAllHeaders` are converted to lowercase. Accessing headers through non-lowercase keys will yield `undefined`, even if the header in the request is in the same case. Use lowercase keys, or use `getHeader`. --- docs/migration/v2.md | 10 ++++++++ docs/services/RequestService.md | 4 +-- src/services/RequestService.ts | 20 +++++++++------ tests/unit/services/RequestService.spec.ts | 30 +++++++++++++++++++++- 4 files changed, 53 insertions(+), 11 deletions(-) diff --git a/docs/migration/v2.md b/docs/migration/v2.md index 734f4199..6ada1a51 100644 --- a/docs/migration/v2.md +++ b/docs/migration/v2.md @@ -5,6 +5,8 @@ This doc summarises the breaking changes introduced in v2 and what you need to d - [Configuration](#configuration) - [Wrapping a function](#wrapping-a-function) - [Dependency injection](#dependency-injection) +- [Services](#services) + - [RequestService](#requestservice) - [Models](#models) - [StatusModel](#statusmodel) @@ -150,6 +152,14 @@ The `definitions` property has been removed. The `getEvent`, `getContext` and `getConfiguration` methods have been deprecated and will be removed in a future major release. Use the `event`, `context` and `config` properties directly. +## Services + +There are some small breaking changes to the built-in services. We've tried to minimise disruption here, so most of these are unlikely to affect real-world applications, but are included in the 2.0.0 release as a precaution. + +### `RequestService` + +Header names returned by `getAllHeaders` are now lowercased. This is consistent with many other libraries (e.g. `http`, `axios`) and makes it easier to work with HTTP headers. In some cases it may be easier to change existing code to use `getHeader`, which has provided case-insensitive access to headers since [v1.2.0](https://github.com/comicrelief/lambda-wrapper/releases/tag/v1.2.0). + ## Models The `Model` base class has been removed. It's hard to make it type-safe (it tries to dynamically call setter methods) and we do modelling and validation differently now, using our [data-models](https://github.com/comicrelief/data-models) repo which is based around [Yup](https://github.com/jquense/yup). diff --git a/docs/services/RequestService.md b/docs/services/RequestService.md index eefef05d..5d942a33 100644 --- a/docs/services/RequestService.md +++ b/docs/services/RequestService.md @@ -19,8 +19,8 @@ export default lambdaWrapper.wrap(async (di) => { ### Headers -- `getAllHeaders` returns an object containing all headers -- `getHeader` returns the value of an HTTP header +- `getAllHeaders` returns an object containing all HTTP headers, with all keys lowercase +- `getHeader` returns the value of a single HTTP header - `getAuthorizationToken` extracts a Bearer token from the `Authorization` header ### Body diff --git a/src/services/RequestService.ts b/src/services/RequestService.ts index 4070ffb7..dbae3ffa 100644 --- a/src/services/RequestService.ts +++ b/src/services/RequestService.ts @@ -68,11 +68,19 @@ export default class RequestService extends DependencyAwareClass { /** * Get all HTTP headers included in the request. * + * Header names are converted to lowercase. + * * @returns An object with a key for each header. */ - getAllHeaders() { - const event = this.getContainer().getEvent() as APIGatewayProxyEvent; - return { ...event.headers }; + getAllHeaders(): Record { + const event = this.di.event as APIGatewayProxyEvent; + if (!event.headers) { + return {}; + } + return Object.fromEntries( + Object.entries(event.headers) + .map(([key, value]) => [key.toLowerCase(), value]), + ); } /** @@ -86,12 +94,8 @@ export default class RequestService extends DependencyAwareClass { */ getHeader(name: string, whenMissing = ''): string { const headers = this.getAllHeaders(); - if (!headers) { - return whenMissing; - } const lowerName = name.toLowerCase(); - const key = Object.keys(headers).find((k) => k.toLowerCase() === lowerName); - return (key && headers[key]) || whenMissing; + return headers[lowerName] ?? whenMissing; } /** diff --git a/tests/unit/services/RequestService.spec.ts b/tests/unit/services/RequestService.spec.ts index 82889252..31b2e838 100644 --- a/tests/unit/services/RequestService.spec.ts +++ b/tests/unit/services/RequestService.spec.ts @@ -230,7 +230,35 @@ describe('unit.services.RequestService', () => { const request = getRequestService(event); it('should return all headers from the event', () => { - expect(request.getAllHeaders()).toStrictEqual(event.headers); + const result = request.getAllHeaders(); + expect(result).toEqual({ + /* eslint-disable quote-props */ + 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', + 'accept-encoding': 'gzip, deflate', + 'accept-language': 'en-us', + 'cloudfront-forwarded-proto': 'https', + 'cloudfront-is-desktop-viewer': 'true', + 'cloudfront-is-mobile-viewer': 'false', + 'cloudfront-is-smarttv-viewer': 'false', + 'cloudfront-is-tablet-viewer': 'false', + 'cloudfront-viewer-country': 'US', + 'cookie': '__gads=ID=d51d609e5753330d:T=1443694116:S=ALNI_MbjWKzLwdEpWZ5wR5WXRI2dtjIpHw; __qca=P0-179798513-1443694132017; _ga=GA1.2.344061584.1441769647', + 'host': 'xxx.execute-api.us-east-1.amazonaws.com', + 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/601.6.17 (KHTML, like Gecko) Version/9.1.1 Safari/601.6.17', + 'via': '1.1 c8a5bb0e20655459eaam174e5c41443b.cloudfront.net (CloudFront)', + 'x-amz-cf-id': 'z7Ds7oXaY8hgUn7lcedZjoIoxyvnzF6ycVzBdQmhn3QnOPEjJz4BrQ==', + 'x-forwarded-for': '221.24.103.21, 54.242.148.216', + 'x-forwarded-port': '443', + 'x-forwarded-proto': 'https', + /* eslint-enable quote-props */ + }); + }); + + it('should convert header names to lowercase', () => { + const result = request.getAllHeaders(); + Object.keys(result).forEach((name) => { + expect(name).toEqual(name.toLowerCase()); + }); }); }); From 6a14aca67e4d667c012efdd2b90ab2d5f862808f Mon Sep 17 00:00:00 2001 From: Seb Aebischer <49983670+seb-cr@users.noreply.github.com> Date: Mon, 11 Dec 2023 16:18:55 +0000 Subject: [PATCH 18/28] feat: `SQSMessageModel` validates required fields of message (#1188) Allows us to remove the non-null assertions, making this model more type-safe. This is unlikely to break any existing applications as this model is intended for _received_ messages, which have all required fields. Jira: [ENG-2733] BREAKING CHANGE: `SQSMessageModel` will throw if required fields (`MessageID`, `ReceiptHandle`, `Body`) are missing from the SQS message. --- docs/migration/v2.md | 6 +++- src/models/SQSMessageModel.ts | 22 ++++++++++++--- tests/unit/models/SQSMessageModel.spec.ts | 34 +++++++++++++++++++++++ 3 files changed, 57 insertions(+), 5 deletions(-) diff --git a/docs/migration/v2.md b/docs/migration/v2.md index 6ada1a51..9f2d3ee5 100644 --- a/docs/migration/v2.md +++ b/docs/migration/v2.md @@ -168,7 +168,7 @@ The `MarketingPreference` model is removed, as this is application-specific and The `StatusModel` model has been replaced by a simple object type. See the [StatusModel](#statusmodel) section below. -Other models (`ResponseModel`, `SQSMessageModel`) are unaffected except that they no longer inherit from a common `Model` class. +Other models (`ResponseModel`, `SQSMessageModel`) are largely unaffected except that they no longer inherit from a common `Model` class. ### `StatusModel` @@ -211,3 +211,7 @@ async function checkStatus(): Promise { ``` Note that we can keep `status` unset initially, and TypeScript will complain if you forget to set it before `checkStatus` returns. + +### `SQSMessageModel` + +The model constructor will now validate that `message` has all fields required of a received SQS message. This should not break existing applications that are using this model correctly, but is included in the 2.0.0 release as a precaution. diff --git a/src/models/SQSMessageModel.ts b/src/models/SQSMessageModel.ts index 58d00afe..ad27fc1f 100644 --- a/src/models/SQSMessageModel.ts +++ b/src/models/SQSMessageModel.ts @@ -15,10 +15,24 @@ export default class Message { metadata: Record = {}; constructor(message: SQS.Message) { - // todo: validate rather than assert the type - this.messageId = message.MessageId!; - this.receiptHandle = message.ReceiptHandle!; - this.body = JSON.parse(message.Body!); + if (!message.MessageId) { + throw new TypeError('Message does not have a MessageId'); + } + if (!message.ReceiptHandle) { + throw new TypeError('Message does not have a ReceiptHandle'); + } + if (!message.Body) { + throw new TypeError('Message does not have a Body'); + } + + this.messageId = message.MessageId; + this.receiptHandle = message.ReceiptHandle; + + try { + this.body = JSON.parse(message.Body); + } catch (error) { + throw new TypeError('Message body is not valid JSON'); + } } /** diff --git a/tests/unit/models/SQSMessageModel.spec.ts b/tests/unit/models/SQSMessageModel.spec.ts index 3bcd60c0..f1c8034b 100644 --- a/tests/unit/models/SQSMessageModel.spec.ts +++ b/tests/unit/models/SQSMessageModel.spec.ts @@ -1,3 +1,5 @@ +import type { SQS } from 'aws-sdk'; + import { SQSMessageModel as Message } from '@/src'; describe('unit.models.SQSMessageModel', () => { @@ -13,6 +15,38 @@ describe('unit.models.SQSMessageModel', () => { const messageModel = new Message(mockedMessage); + describe('constructor', () => { + it('should throw if message is missing MessageId', () => { + const message: SQS.Message = { ...mockedMessage }; + delete message.MessageId; + + expect(() => new Message(message)).toThrowError(TypeError); + }); + + it('should throw if message is missing ReceiptHandle', () => { + const message: SQS.Message = { ...mockedMessage }; + delete message.ReceiptHandle; + + expect(() => new Message(message)).toThrowError(TypeError); + }); + + it('should throw if message is missing Body', () => { + const message: SQS.Message = { ...mockedMessage }; + delete message.Body; + + expect(() => new Message(message)).toThrowError(TypeError); + }); + + it('should throw if message body is not valid JSON', () => { + const message: SQS.Message = { + ...mockedMessage, + Body: 'This is not JSON!', + }; + + expect(() => new Message(message)).toThrowError(TypeError); + }); + }); + describe('getMessageId', () => { it('should return the message ID', () => { expect(messageModel.getMessageId()).toEqual(mockedMessage.MessageId); From baa4966d5c3bbac6ff27606013f5091b7f141739 Mon Sep 17 00:00:00 2001 From: Seb Aebischer <49983670+seb-cr@users.noreply.github.com> Date: Mon, 11 Dec 2023 16:22:43 +0000 Subject: [PATCH 19/28] fix: Improve `SQSMessageModel` types (#1189) Further work to make `SQSMessageModel` more type-safe. - Properties that have only a getter (no setter) are made readonly - `body` type changed from `string` (incorrect) to `unknown` Jira: [ENG-2733] --- src/models/SQSMessageModel.ts | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/models/SQSMessageModel.ts b/src/models/SQSMessageModel.ts index ad27fc1f..9979136b 100644 --- a/src/models/SQSMessageModel.ts +++ b/src/models/SQSMessageModel.ts @@ -4,15 +4,15 @@ import { SQS } from 'aws-sdk'; * Message model for SQS. */ export default class Message { - messageId: string; + readonly messageId: string; - receiptHandle: string; + readonly receiptHandle: string; - body: string; + readonly body: unknown; - forDeletion = false; + readonly metadata: Record = {}; - metadata: Record = {}; + forDeletion = false; constructor(message: SQS.Message) { if (!message.MessageId) { @@ -38,21 +38,21 @@ export default class Message { /** * Get message ID. */ - getMessageId() { + getMessageId(): string { return this.messageId; } /** * Get message receipt handle. */ - getReceiptHandle() { + getReceiptHandle(): string { return this.receiptHandle; } /** * Get message body. */ - getBody() { + getBody(): unknown { return this.body; } @@ -61,21 +61,21 @@ export default class Message { * * @param forDeletion */ - setForDeletion(forDeletion: boolean) { + setForDeletion(forDeletion: boolean): void { this.forDeletion = forDeletion; } /** * Whether message is for deletion. */ - isForDeletion() { + isForDeletion(): boolean { return this.forDeletion; } /** * Get all of the message metadata. */ - getMetaData() { + getMetaData(): Record { return this.metadata; } @@ -85,7 +85,7 @@ export default class Message { * @param key * @param value */ - setMetaData(key: string, value: any) { + setMetaData(key: string, value: any): this { this.metadata[key] = value; return this; From 4581d612aed1dea3d35a4d880a639f849ad31c02 Mon Sep 17 00:00:00 2001 From: Seb Aebischer <49983670+seb-cr@users.noreply.github.com> Date: Fri, 15 Dec 2023 10:58:31 +0000 Subject: [PATCH 20/28] refactor: Rename SQS message model class (#1191) Change the name of the class to match the name exported from the package index. This should not be a breaking change because the class is the default export of its module. Just in case there is some contrived case that depends on the class's `name` attribute, we'll include this in the v2.0.0 major release. Jira: [ENG-2733] --- docs/migration/v2.md | 3 +++ src/models/SQSMessageModel.ts | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/migration/v2.md b/docs/migration/v2.md index 9f2d3ee5..ad393629 100644 --- a/docs/migration/v2.md +++ b/docs/migration/v2.md @@ -9,6 +9,7 @@ This doc summarises the breaking changes introduced in v2 and what you need to d - [RequestService](#requestservice) - [Models](#models) - [StatusModel](#statusmodel) + - [SQSMessageModel](#sqsmessagemodel) The new version of dependency injection does not work if code is minified. See [Notes](../../README.md#notes) in the main readme for how to turn this off in webpack. @@ -215,3 +216,5 @@ Note that we can keep `status` unset initially, and TypeScript will complain if ### `SQSMessageModel` The model constructor will now validate that `message` has all fields required of a received SQS message. This should not break existing applications that are using this model correctly, but is included in the 2.0.0 release as a precaution. + +The class name has also changed from `Message` to `SQSMessageModel` in order to be consistent with the name exported from the package index. Again, this is not expected to break anything, and does not affect imports because the class is a default export. diff --git a/src/models/SQSMessageModel.ts b/src/models/SQSMessageModel.ts index 9979136b..0dea109f 100644 --- a/src/models/SQSMessageModel.ts +++ b/src/models/SQSMessageModel.ts @@ -3,7 +3,7 @@ import { SQS } from 'aws-sdk'; /** * Message model for SQS. */ -export default class Message { +export default class SQSMessageModel { readonly messageId: string; readonly receiptHandle: string; From 7bac68db11026cac21d91dd01aa6069834db8ed4 Mon Sep 17 00:00:00 2001 From: Seb Aebischer <49983670+seb-cr@users.noreply.github.com> Date: Mon, 18 Dec 2023 15:28:43 +0000 Subject: [PATCH 21/28] docs: Add instructions to `SQSMessageModel` (#1190) The main point I wanted to add here is that this model is for _received_ messages. I've also added a bit of explanation about its intended use with `SQSService`. Jira: [ENG-2733] --- src/models/SQSMessageModel.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/models/SQSMessageModel.ts b/src/models/SQSMessageModel.ts index 0dea109f..7cf882dc 100644 --- a/src/models/SQSMessageModel.ts +++ b/src/models/SQSMessageModel.ts @@ -1,7 +1,17 @@ import { SQS } from 'aws-sdk'; /** - * Message model for SQS. + * Model for message received from SQS. + * + * This model is used to return messages from `SQSService#receive`, and + * provides access to the message body (parsed from JSON) and everything needed + * for deletion (message ID and receipt handle). + * + * Once you've successfully processed a message, flag it for deletion using + * `setForDeletion(true)`. You can then batch-delete messages using + * `SQSService#batchDelete`. This will _not_ delete messages that have + * not had the `forDeletion` flag set, allowing them to remain in the queue and + * be processed again at a later time. */ export default class SQSMessageModel { readonly messageId: string; From dca76200cf2b1a61a0bcbbe4ef2b069dc8d43de8 Mon Sep 17 00:00:00 2001 From: Seb Aebischer <49983670+seb-cr@users.noreply.github.com> Date: Fri, 19 Jan 2024 14:36:46 +0000 Subject: [PATCH 22/28] feat: Throw by default from failed SQS publish (#1194) Currently, sending an SQS message using `SQSService` does not throw an error by default. This has caught us out a couple of times where we were expecting a failure. The `failureMode` parameter was added to opt in to throwing errors, to avoid introducing a breaking change in v1. In v2 we'll throw errors by default, and you'll have to opt _out_ of this behaviour. BREAKING CHANGE: `SQSService#publish` throws by default instead of handling errors. To maintain old behaviour, you can pass `"catch"` in the `failureMode` parameter. --- docs/migration/v2.md | 12 ++++++++++++ src/services/SQSService.ts | 7 ++++--- tests/unit/services/SQSService.spec.ts | 10 +++++----- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/docs/migration/v2.md b/docs/migration/v2.md index ad393629..127045cf 100644 --- a/docs/migration/v2.md +++ b/docs/migration/v2.md @@ -7,6 +7,7 @@ This doc summarises the breaking changes introduced in v2 and what you need to d - [Dependency injection](#dependency-injection) - [Services](#services) - [RequestService](#requestservice) + - [SQSService](#sqsservice) - [Models](#models) - [StatusModel](#statusmodel) - [SQSMessageModel](#sqsmessagemodel) @@ -161,6 +162,17 @@ There are some small breaking changes to the built-in services. We've tried to m Header names returned by `getAllHeaders` are now lowercased. This is consistent with many other libraries (e.g. `http`, `axios`) and makes it easier to work with HTTP headers. In some cases it may be easier to change existing code to use `getHeader`, which has provided case-insensitive access to headers since [v1.2.0](https://github.com/comicrelief/lambda-wrapper/releases/tag/v1.2.0). +### `SQSService` + +In v1, the default behaviour of `publish` was to catch any error thrown while sending the message to SQS. In v2, this has been changed and it will now throw an error by default. In most scenarios this is the more intuitive mode, as it ensures that the caller is made aware of any SQS-related failure. To maintain the old behaviour, you can pass `"catch"` in the `failureMode` parameter. + +```js +// v1 +sqs.publish(queue, message); +// v2 equivalent +sqs.publish(queue, message, null, 'catch'); +``` + ## Models The `Model` base class has been removed. It's hard to make it type-safe (it tries to dynamically call setter methods) and we do modelling and validation differently now, using our [data-models](https://github.com/comicrelief/data-models) repo which is based around [Yup](https://github.com/jquense/yup). diff --git a/src/services/SQSService.ts b/src/services/SQSService.ts index ff86d048..9de5ea01 100644 --- a/src/services/SQSService.ts +++ b/src/services/SQSService.ts @@ -429,10 +429,11 @@ export default class SQSService< * @param messageObject object * @param messageGroupId string * @param failureMode Choose how failures are handled: - * - `catch`: errors will be caught and logged. This is the default. - * - `throw`: errors will be thrown, causing promise to reject. + * - `throw`: errors will be thrown, causing promise to reject. (default) + * - `catch`: errors will be caught and logged. Useful for non-critical + * messages. */ - async publish(queue: QueueName, messageObject: object, messageGroupId = null, failureMode: 'catch' | 'throw' = SQS_PUBLISH_FAILURE_MODES.CATCH) { + async publish(queue: QueueName, messageObject: object, messageGroupId = null, failureMode: 'catch' | 'throw' = SQS_PUBLISH_FAILURE_MODES.THROW) { if (!Object.values(SQS_PUBLISH_FAILURE_MODES).includes(failureMode)) { throw new Error(`Invalid value for 'failureMode': ${failureMode}`); } diff --git a/tests/unit/services/SQSService.spec.ts b/tests/unit/services/SQSService.spec.ts index 29af39a1..a3a44e48 100644 --- a/tests/unit/services/SQSService.spec.ts +++ b/tests/unit/services/SQSService.spec.ts @@ -318,22 +318,22 @@ describe('unit.services.SQSService', () => { }); describe('failure modes', () => { - it(`catches the error if publish fails with failureMode === ${SQS_PUBLISH_FAILURE_MODES.CATCH}`, async () => { + it('throws the error if publish fails with failureMode omitted', async () => { const service = getService({ sendMessage: new Error('SQS is down!'), }, false); - const promise = service.publish(TEST_QUEUE, { test: 1 }, null, SQS_PUBLISH_FAILURE_MODES.CATCH); + const promise = service.publish(TEST_QUEUE, { test: 1 }, null); - await expect(promise).resolves.toEqual(null); + await expect(promise).rejects.toThrowError('SQS is down!'); }); - it('catches the error if publish fails with failureMode omitted', async () => { + it(`catches the error if publish fails with failureMode === ${SQS_PUBLISH_FAILURE_MODES.CATCH}`, async () => { const service = getService({ sendMessage: new Error('SQS is down!'), }, false); - const promise = service.publish(TEST_QUEUE, { test: 1 }, null); + const promise = service.publish(TEST_QUEUE, { test: 1 }, null, SQS_PUBLISH_FAILURE_MODES.CATCH); await expect(promise).resolves.toEqual(null); }); From 2047e2d8f5e199bd3c31b0cd52bd4dee7bf51e17 Mon Sep 17 00:00:00 2001 From: Seb Aebischer Date: Thu, 29 Feb 2024 10:33:42 +0000 Subject: [PATCH 23/28] fix: More dependency updates Includes major version bump for `axios`. Everything else is minor/patch. --- package.json | 30 +++---- yarn.lock | 233 +++++++++++++++++++++++++++------------------------ 2 files changed, 137 insertions(+), 126 deletions(-) diff --git a/package.json b/package.json index c8d5babd..653458da 100644 --- a/package.json +++ b/package.json @@ -23,40 +23,40 @@ "devDependencies": { "@comicrelief/eslint-config": "^2.0.3", "@istanbuljs/nyc-config-typescript": "^1.0.2", - "@types/async": "^3.2.20", - "@types/jest": "^29.5.4", + "@types/async": "^3.2.24", + "@types/jest": "^29.5.12", "@types/node": "14", - "@types/useragent": "^2.3.2", - "@types/uuid": "^9.0.3", - "@types/xml2js": "^0.4.12", + "@types/useragent": "^2.3.4", + "@types/uuid": "^9.0.8", + "@types/xml2js": "^0.4.14", "@typescript-eslint/eslint-plugin": "^5.33.0", "@typescript-eslint/parser": "^5.33.0", - "aws-sdk": "^2.1456.0", - "eslint": "^8.49.0", + "aws-sdk": "^2.1568.0", + "eslint": "^8.57.0", "eslint-plugin-import": "^2.25.2", "eslint-plugin-jsdoc": "^39.3.2", "jest": "^29.7.0", "nyc": "^15.1.0", "semantic-release": "^19.0.5", - "ts-jest": "^29.1.1", - "ts-node": "^10.9.1", + "ts-jest": "^29.1.2", + "ts-node": "^10.9.2", "tsconfig-paths": "^4.2.0", - "typescript": "^4.7.4" + "typescript": "^4.9.5" }, "peerDependencies": { "aws-sdk": "^2.831.0" }, "dependencies": { - "@lumigo/tracer": "^1.87.0", + "@lumigo/tracer": "^1.91.0", "@sentry/node": "^6.19.7", - "@types/aws-lambda": "^8.10.120", + "@types/aws-lambda": "^8.10.134", "alai": "1.0.3", - "async": "^3.2.4", - "axios": "^0.27.2", + "async": "^3.2.5", + "axios": "^1.6.7", "useragent": "2.3.0", "uuid": "^9.0.1", "validate.js": "0.13.1", - "winston": "^3.10.0", + "winston": "^3.11.0", "xml2js": "^0.6.2" } } diff --git a/yarn.lock b/yarn.lock index 862ddd1b..a73d78d2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -312,6 +312,11 @@ resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== +"@colors/colors@^1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.6.0.tgz#ec6cd237440700bc23ca23087f513c75508958b0" + integrity sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA== + "@comicrelief/eslint-config@^2.0.3": version "2.0.3" resolved "https://registry.yarnpkg.com/@comicrelief/eslint-config/-/eslint-config-2.0.3.tgz#ea1efeea519c386c166fa05cdb283a35838feb93" @@ -357,10 +362,10 @@ resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.8.1.tgz#8c4bb756cc2aa7eaf13cfa5e69c83afb3260c20c" integrity sha512-PWiOzLIUAjN/w5K17PoF4n6sKBw0gqLHPhywmYHP4t1VFQQVYeb1yWsJwnMVEMl3tUHME7X/SJPZLmtG7XBDxQ== -"@eslint/eslintrc@^2.1.2": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.2.tgz#c6936b4b328c64496692f76944e755738be62396" - integrity sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g== +"@eslint/eslintrc@^2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad" + integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== dependencies: ajv "^6.12.4" debug "^4.3.2" @@ -372,23 +377,23 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@8.49.0": - version "8.49.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.49.0.tgz#86f79756004a97fa4df866835093f1df3d03c333" - integrity sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w== +"@eslint/js@8.57.0": + version "8.57.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.0.tgz#a5417ae8427873f1dd08b70b3574b453e67b5f7f" + integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g== "@gar/promisify@^1.1.3": version "1.1.3" resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== -"@humanwhocodes/config-array@^0.11.11": - version "0.11.11" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.11.tgz#88a04c570dbbc7dd943e4712429c3df09bc32844" - integrity sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA== +"@humanwhocodes/config-array@^0.11.14": + version "0.11.14" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b" + integrity sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg== dependencies: - "@humanwhocodes/object-schema" "^1.2.1" - debug "^4.1.1" + "@humanwhocodes/object-schema" "^2.0.2" + debug "^4.3.1" minimatch "^3.0.5" "@humanwhocodes/module-importer@^1.0.1": @@ -396,10 +401,10 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== -"@humanwhocodes/object-schema@^1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" - integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== +"@humanwhocodes/object-schema@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz#d9fae00a2d5cb40f92cfe64b47ad749fbc38f917" + integrity sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw== "@isaacs/cliui@^8.0.2": version "8.0.2" @@ -699,22 +704,22 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" -"@lumigo/node-core@1.13.0": - version "1.13.0" - resolved "https://registry.yarnpkg.com/@lumigo/node-core/-/node-core-1.13.0.tgz#3f67992fc8674f48aa34d4acc732c839ec416cc0" - integrity sha512-RO8q9ZBE3ZkGpN1K5liPE1EZMxfooLgRR6kb++Vuvxm9f9296+PcMQs+io0kEsTaP34KfdaLrr8Wzz49w637FA== +"@lumigo/node-core@1.15.0": + version "1.15.0" + resolved "https://registry.yarnpkg.com/@lumigo/node-core/-/node-core-1.15.0.tgz#30b51241b35475b90bc07388187833b590015887" + integrity sha512-FNODBgApk72hOVNR30mM3OFbCf2MwFPyHhHeb5Lg6pbfl8woBIT/25tr6gKV+bg0TbIUrjCaOMtzb6zeroKX9w== dependencies: shimmer "^1.2.1" utf8 "^3.0.0" -"@lumigo/tracer@^1.87.0": - version "1.87.0" - resolved "https://registry.yarnpkg.com/@lumigo/tracer/-/tracer-1.87.0.tgz#8891fefa59c7c117b58eb8fbc49f8b07b1bd64e1" - integrity sha512-4PGeuVelkQHOXpFhKT4KVJiluZS0mK2AYTEqhGZYklpBecqijh6CWaHE6HqrpFqadJF8pCfMGALA1ceQpJjvZg== +"@lumigo/tracer@^1.91.0": + version "1.91.0" + resolved "https://registry.yarnpkg.com/@lumigo/tracer/-/tracer-1.91.0.tgz#be3249b56ee1487155dfa6f4aad0fb88ddac8c05" + integrity sha512-t/8cXtj9IqJKzN2ok77BFzUO1UjgBC7e8H58wB5SZ9F+5Kn6uOeggH1HvtHU/WPW73q+CnjnMcl3X5g/BOOtxw== dependencies: - "@lumigo/node-core" "1.13.0" + "@lumigo/node-core" "1.15.0" agentkeepalive "^4.1.4" - axios "0.24.0" + axios "1.6.5" shimmer "1.2.1" utf8 "^3.0.0" @@ -1226,15 +1231,15 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e" integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== -"@types/async@^3.2.20": - version "3.2.20" - resolved "https://registry.yarnpkg.com/@types/async/-/async-3.2.20.tgz#53517caaa68c94f99da1c4e986cf7f2954981515" - integrity sha512-6jSBQQugzyX1aWto0CbvOnmxrU9tMoXfA9gc4IrLEtvr3dTwSg5GLGoWiZnGLI6UG/kqpB3JOQKQrqnhUWGKQA== +"@types/async@^3.2.24": + version "3.2.24" + resolved "https://registry.yarnpkg.com/@types/async/-/async-3.2.24.tgz#3a96351047575bbcf2340541b2d955a35339608f" + integrity sha512-8iHVLHsCCOBKjCF2KwFe0p9Z3rfM9mL+sSP8btyR5vTjJRAqpBYD28/ZLgXPf0pjG1VxOvtCV/BgXkQbpSe8Hw== -"@types/aws-lambda@^8.10.120": - version "8.10.120" - resolved "https://registry.yarnpkg.com/@types/aws-lambda/-/aws-lambda-8.10.120.tgz#d654644a4c03372622ef00f94acc9f5653a8ee62" - integrity sha512-ReLUWj/2EYNx7qs17bkBkgSmr0jB1GGTRyorhHJmq8nqlWxEyWyto1GFsGTGdbLI0cAj1AcbBAA1oZYUQEqZng== +"@types/aws-lambda@^8.10.134": + version "8.10.134" + resolved "https://registry.yarnpkg.com/@types/aws-lambda/-/aws-lambda-8.10.134.tgz#8f65d86736839889194f7892b7bec6b8a7ec6fc3" + integrity sha512-cfv422ivDMO+EeA3N4YcshbTHBL+5lLXe+Uz+4HXvIcsCuWvqNFpOs28ZprL8NA3qRCzt95ETiNAJDn4IcC/PA== "@types/babel__core@^7.1.14": version "7.1.19" @@ -1295,10 +1300,10 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@^29.5.4": - version "29.5.4" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.4.tgz#9d0a16edaa009a71e6a71a999acd582514dab566" - integrity sha512-PhglGmhWeD46FYOVLt3X7TiWjzwuVGW9wG/4qocPevXMjCmrIc5b6db9WjeGE4QYVpUAWMDv3v0IiBwObY289A== +"@types/jest@^29.5.12": + version "29.5.12" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.12.tgz#7f7dc6eb4cf246d2474ed78744b05d06ce025544" + integrity sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw== dependencies: expect "^29.0.0" pretty-format "^29.0.0" @@ -1343,20 +1348,20 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== -"@types/useragent@^2.3.2": - version "2.3.2" - resolved "https://registry.yarnpkg.com/@types/useragent/-/useragent-2.3.2.tgz#d8572cf7dfc486e80e164cbd49546749877bddf2" - integrity sha512-68udf5WhUcw0F5ueJ3rVF7LTHqT05DfwxhVubXIyDhqEtCy3KWLd28K76b4WWwVrZfJonY37Ru0sU0MJzUuJ9Q== +"@types/useragent@^2.3.4": + version "2.3.4" + resolved "https://registry.yarnpkg.com/@types/useragent/-/useragent-2.3.4.tgz#689c6c85cfe7200c467d02ae9182c15ea182fa6e" + integrity sha512-4pGiUe4NptP06CwsW+pY/3bgGCUdMf8+DrtyJ+jNR1UTPnSEA57TsskdBtE+oq+GpPCXIR+n51RvzKIsyy4rrw== -"@types/uuid@^9.0.3": - version "9.0.3" - resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.3.tgz#6cdd939b4316b4f81625de9f06028d848c4a1533" - integrity sha512-taHQQH/3ZyI3zP8M/puluDEIEvtQHVYcC6y3N8ijFtAd28+Ey/G4sg1u2gB01S8MwybLOKAp9/yCMu/uR5l3Ug== +"@types/uuid@^9.0.8": + version "9.0.8" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.8.tgz#7545ba4fc3c003d6c756f651f3bf163d8f0f29ba" + integrity sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA== -"@types/xml2js@^0.4.12": - version "0.4.12" - resolved "https://registry.yarnpkg.com/@types/xml2js/-/xml2js-0.4.12.tgz#d9aae03295476fd5cbc74e0b572816208dbec6d1" - integrity sha512-CZPpQKBZ8db66EP5hCjwvYrLThgZvnyZrPXK2W+UI1oOaWezGt34iOaUCX4Jah2X8+rQqjvl9VKEIT8TR1I0rA== +"@types/xml2js@^0.4.14": + version "0.4.14" + resolved "https://registry.yarnpkg.com/@types/xml2js/-/xml2js-0.4.14.tgz#5d462a2a7330345e2309c6b549a183a376de8f9a" + integrity sha512-4YnrRemBShWRO2QjvUin8ESA41rH+9nQGLUGZV/1IDhi3SL9OhdpNC/MrulTWuptXKwhx/aDxE7toV0f/ypIXQ== dependencies: "@types/node" "*" @@ -1452,6 +1457,11 @@ "@typescript-eslint/types" "5.33.0" eslint-visitor-keys "^3.3.0" +"@ungap/structured-clone@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" + integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== + JSONStream@^1.0.4: version "1.3.5" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" @@ -1687,11 +1697,16 @@ asap@^2.0.0: resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== -async@^3.2.3, async@^3.2.4: +async@^3.2.3: version "3.2.4" resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== +async@^3.2.5: + version "3.2.5" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66" + integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg== + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -1702,10 +1717,10 @@ available-typed-arrays@^1.0.5: resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== -aws-sdk@^2.1456.0: - version "2.1456.0" - resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1456.0.tgz#0c89368eac5556f4959a8876386edd79f96808b8" - integrity sha512-0fzxx55Skc44i4q6iMuNjYSHEEaCZIbjvl4jaT3Jki6GNYQAdWb0/+BPaTalkb8UgrY9wSZ0h86DH2w+nf6e7g== +aws-sdk@^2.1568.0: + version "2.1568.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1568.0.tgz#b137e7c2196c9013f31ba36028fed8d104482568" + integrity sha512-ECGJlhn4tnvC+MwNxGDGbhKEOwqLtmtEb3VK5s0z8pcU60Uv1b8+wRPKjqM/eJ+J4N5CL92Y5aq5xAdTquBZRQ== dependencies: buffer "4.9.2" events "1.1.1" @@ -1716,22 +1731,25 @@ aws-sdk@^2.1456.0: url "0.10.3" util "^0.12.4" uuid "8.0.0" - xml2js "0.5.0" + xml2js "0.6.2" -axios@0.24.0: - version "0.24.0" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.24.0.tgz#804e6fa1e4b9c5288501dd9dff56a7a0940d20d6" - integrity sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA== +axios@1.6.5: + version "1.6.5" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.5.tgz#2c090da14aeeab3770ad30c3a1461bc970fb0cd8" + integrity sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg== dependencies: - follow-redirects "^1.14.4" + follow-redirects "^1.15.4" + form-data "^4.0.0" + proxy-from-env "^1.1.0" -axios@^0.27.2: - version "0.27.2" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" - integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ== +axios@^1.6.7: + version "1.6.7" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.7.tgz#7b48c2e27c96f9c68a2f8f31e2ab19f59b06b0a7" + integrity sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA== dependencies: - follow-redirects "^1.14.9" + follow-redirects "^1.15.4" form-data "^4.0.0" + proxy-from-env "^1.1.0" babel-jest@^29.7.0: version "29.7.0" @@ -2361,7 +2379,7 @@ dateformat@^3.0.0: resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== -debug@4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: +debug@4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -2776,18 +2794,19 @@ eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== -eslint@^8.49.0: - version "8.49.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.49.0.tgz#09d80a89bdb4edee2efcf6964623af1054bf6d42" - integrity sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ== +eslint@^8.57.0: + version "8.57.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.0.tgz#c786a6fd0e0b68941aaf624596fb987089195668" + integrity sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@eslint-community/regexpp" "^4.6.1" - "@eslint/eslintrc" "^2.1.2" - "@eslint/js" "8.49.0" - "@humanwhocodes/config-array" "^0.11.11" + "@eslint/eslintrc" "^2.1.4" + "@eslint/js" "8.57.0" + "@humanwhocodes/config-array" "^0.11.14" "@humanwhocodes/module-importer" "^1.0.1" "@nodelib/fs.walk" "^1.2.8" + "@ungap/structured-clone" "^1.2.0" ajv "^6.12.4" chalk "^4.0.0" cross-spawn "^7.0.2" @@ -3045,15 +3064,10 @@ fn.name@1.x.x: resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== -follow-redirects@^1.14.4: - version "1.15.3" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a" - integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q== - -follow-redirects@^1.14.9: - version "1.15.1" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5" - integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA== +follow-redirects@^1.15.4: + version "1.15.5" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020" + integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw== for-each@^0.3.3: version "0.3.3" @@ -5743,6 +5757,11 @@ proto-list@~1.2.1: resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" integrity sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA== +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + pseudomap@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" @@ -6582,10 +6601,10 @@ triple-beam@^1.3.0: resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9" integrity sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw== -ts-jest@^29.1.1: - version "29.1.1" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.1.1.tgz#f58fe62c63caf7bfcc5cc6472082f79180f0815b" - integrity sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA== +ts-jest@^29.1.2: + version "29.1.2" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.1.2.tgz#7613d8c81c43c8cb312c6904027257e814c40e09" + integrity sha512-br6GJoH/WUX4pu7FbZXuWGKGNDuU7b8Uj77g/Sp7puZV6EXzuByl6JrECvm0MzVzSTkSHWTihsXt+5XYER5b+g== dependencies: bs-logger "0.x" fast-json-stable-stringify "2.x" @@ -6596,10 +6615,10 @@ ts-jest@^29.1.1: semver "^7.5.3" yargs-parser "^21.0.1" -ts-node@^10.9.1: - version "10.9.1" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" - integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw== +ts-node@^10.9.2: + version "10.9.2" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" + integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== dependencies: "@cspotcode/source-map-support" "^0.8.0" "@tsconfig/node10" "^1.0.7" @@ -6700,10 +6719,10 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -typescript@^4.7.4: - version "4.7.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235" - integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ== +typescript@^4.9.5: + version "4.9.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== uglify-js@^3.1.4: version "3.16.3" @@ -6969,12 +6988,12 @@ winston-transport@^4.5.0: readable-stream "^3.6.0" triple-beam "^1.3.0" -winston@^3.10.0: - version "3.10.0" - resolved "https://registry.yarnpkg.com/winston/-/winston-3.10.0.tgz#d033cb7bd3ced026fed13bf9d92c55b903116803" - integrity sha512-nT6SIDaE9B7ZRO0u3UvdrimG0HkB7dSTAgInQnNR2SOPJ4bvq5q79+pXLftKmP52lJGW15+H5MCK0nM9D3KB/g== +winston@^3.11.0: + version "3.11.0" + resolved "https://registry.yarnpkg.com/winston/-/winston-3.11.0.tgz#2d50b0a695a2758bb1c95279f0a88e858163ed91" + integrity sha512-L3yR6/MzZAOl0DsysUXHVjOwv8mKZ71TrA/41EIduGpOOV5LQVodqN+QdQ6BS6PJ/RdIshZhq84P/fStEZkk7g== dependencies: - "@colors/colors" "1.5.0" + "@colors/colors" "^1.6.0" "@dabh/diagnostics" "^2.0.2" async "^3.2.3" is-stream "^2.0.0" @@ -7041,15 +7060,7 @@ write-file-atomic@^4.0.0, write-file-atomic@^4.0.1, write-file-atomic@^4.0.2: imurmurhash "^0.1.4" signal-exit "^3.0.7" -xml2js@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.5.0.tgz#d9440631fbb2ed800203fad106f2724f62c493b7" - integrity sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA== - dependencies: - sax ">=0.6.0" - xmlbuilder "~11.0.0" - -xml2js@^0.6.2: +xml2js@0.6.2, xml2js@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.6.2.tgz#dd0b630083aa09c161e25a4d0901e2b2a929b499" integrity sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA== From d4f275b33175cf1931aef004c452979f1a14af16 Mon Sep 17 00:00:00 2001 From: Seb Aebischer Date: Thu, 29 Feb 2024 14:40:44 +0000 Subject: [PATCH 24/28] chore: Revert ts-jest to v29.1.1 v29.1.2 introduces a breaking change, dropping support for Node 14. They don't follow SemVer in order to keep their major version number aligned with Jest. This repo needs updating to Node 18 (at a minimum), but this should not be a blocker for the 2.0.0 release. --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 653458da..393748a5 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "jest": "^29.7.0", "nyc": "^15.1.0", "semantic-release": "^19.0.5", - "ts-jest": "^29.1.2", + "ts-jest": "29.1.1", "ts-node": "^10.9.2", "tsconfig-paths": "^4.2.0", "typescript": "^4.9.5" diff --git a/yarn.lock b/yarn.lock index a73d78d2..b5d47379 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6601,10 +6601,10 @@ triple-beam@^1.3.0: resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9" integrity sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw== -ts-jest@^29.1.2: - version "29.1.2" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.1.2.tgz#7613d8c81c43c8cb312c6904027257e814c40e09" - integrity sha512-br6GJoH/WUX4pu7FbZXuWGKGNDuU7b8Uj77g/Sp7puZV6EXzuByl6JrECvm0MzVzSTkSHWTihsXt+5XYER5b+g== +ts-jest@29.1.1: + version "29.1.1" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.1.1.tgz#f58fe62c63caf7bfcc5cc6472082f79180f0815b" + integrity sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA== dependencies: bs-logger "0.x" fast-json-stable-stringify "2.x" From b42e74f7a8ce999b970cde33abab2738c669d797 Mon Sep 17 00:00:00 2001 From: Seb Aebischer <49983670+seb-cr@users.noreply.github.com> Date: Mon, 25 Mar 2024 16:58:17 +0000 Subject: [PATCH 25/28] test: Add type tests covering `di.get` and `SQSService` (#1198) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First attempt at adding tests for some of the types used in Lambda Wrapper v2. In #1196 I tried to fix one problem and inadvertently caused another more serious problem. I caught this during manual QA, but this is precisely why automated tests are so valuable. Some of the type-related features of Lambda Wrapper are complex enough that we should have their behaviour defined in a test suite. About these tests: they do not "run", as such, and instead the types are checked statically by the TypeScript compiler. All assertions happen at the type system level. Test failures will be flagged by a type error on the failing assertion. I think it still makes sense to organise these tests using `describe` and `it` to give structure and context to the assertions. The docs for [expect-type](https://github.com/mmkal/expect-type#readme) are useful. I tried various packages (`ts-expect`, `tsd`) and found `expect-type` to provide the most expressive API and it doesn't need any particular setup. So far I've written tests for only `di.get` and `SQSService`, as this covers most of the interesting stuff and is where I've been seeing problems – there's enough here to fail following the changes in #1196. It would also be good to cover things relating to Lambda Wrapper configuration, however this was not as straightforward as I hoped so I'm leaving it for now. Jira: [ENG-3188] --- .github/workflows/main.yml | 21 +++++++++ README.md | 2 +- package.json | 2 + tests/type/.eslintrc.yml | 2 + tests/type/DependencyInjection.spec.ts | 39 ++++++++++++++++ tests/type/SQSService.spec.ts | 62 ++++++++++++++++++++++++++ tsconfig-type-test.json | 10 +++++ yarn.lock | 5 +++ 8 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 tests/type/.eslintrc.yml create mode 100644 tests/type/DependencyInjection.spec.ts create mode 100644 tests/type/SQSService.spec.ts create mode 100644 tsconfig-type-test.json diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e84fb5bf..a31c258b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -49,6 +49,27 @@ jobs: env: CI: true + type-test: + name: Type test + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - uses: actions/setup-node@v1 + with: + node-version: 14 + + - name: Restore cache + uses: actions/cache@v2 + with: + path: '**/node_modules' + key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }} + + - run: yarn install + + - run: yarn test:types + publish: name: Publish runs-on: ubuntu-latest diff --git a/README.md b/README.md index 35b9d704..4fbcb67c 100644 --- a/README.md +++ b/README.md @@ -231,7 +231,7 @@ module.exports = { ### Testing -Run `yarn test` to run the unit tests. +Run `yarn test` to run the unit tests, and `yarn test:types` to run the type tests. When writing a bugfix, start by writing a test that reproduces the problem. It should fail with the current version of Lambda Wrapper, and pass once you've implemented the fix. diff --git a/package.json b/package.json index 393748a5..2f18ea06 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "clean": "rm -rf dist", "lint": "eslint src tests", "test": "jest", + "test:types": "tsc -p tsconfig-type-test.json", "coverage": "yarn test --coverage" }, "devDependencies": { @@ -35,6 +36,7 @@ "eslint": "^8.57.0", "eslint-plugin-import": "^2.25.2", "eslint-plugin-jsdoc": "^39.3.2", + "expect-type": "^0.18.0", "jest": "^29.7.0", "nyc": "^15.1.0", "semantic-release": "^19.0.5", diff --git a/tests/type/.eslintrc.yml b/tests/type/.eslintrc.yml new file mode 100644 index 00000000..20548d44 --- /dev/null +++ b/tests/type/.eslintrc.yml @@ -0,0 +1,2 @@ +rules: + '@typescript-eslint/ban-ts-comment': off diff --git a/tests/type/DependencyInjection.spec.ts b/tests/type/DependencyInjection.spec.ts new file mode 100644 index 00000000..cb2a6022 --- /dev/null +++ b/tests/type/DependencyInjection.spec.ts @@ -0,0 +1,39 @@ +import { expectTypeOf } from 'expect-type'; + +import lambdaWrapper, { + DependencyAwareClass, + DependencyInjection, + LoggerService, + SQSService, +} from '../../src/index'; +import { mockContext, mockEvent } from '../mocks/aws'; + +describe('type.DependencyInjection', () => { + const di = new DependencyInjection(lambdaWrapper.config, mockEvent, mockContext); + + describe('get', () => { + it('should return a LoggerService instance if passed LoggerService', () => { + expectTypeOf(di.get(LoggerService)).toEqualTypeOf(); + }); + + it('should return an SQSService instance if passed SQSService', () => { + // for this one we also expect the TConfig type parameter to be correct + expectTypeOf(di.get(SQSService)).toEqualTypeOf>(); + }); + + it('should accept any dependency-aware class', () => { + // Currently, the type system does not restrict you to getting only + // dependencies that are specified in the Lambda Wrapper config, however + // doing so will result in a runtime error. + class Good extends DependencyAwareClass {} + expectTypeOf(di.get).toBeCallableWith(Good); + expectTypeOf(di.get(Good)).toEqualTypeOf>(); + }); + + it('should not accept classes that are not dependency-aware', () => { + class Bad {} + // @ts-expect-error: it should _not_ be callable with Bad + expectTypeOf(di.get).toBeCallableWith(Bad); + }); + }); +}); diff --git a/tests/type/SQSService.spec.ts b/tests/type/SQSService.spec.ts new file mode 100644 index 00000000..6737a712 --- /dev/null +++ b/tests/type/SQSService.spec.ts @@ -0,0 +1,62 @@ +import { expectTypeOf } from 'expect-type'; + +import lambdaWrapper, { + DependencyInjection, + QueueName, + SQSService, +} from '../../src/index'; +import { mockContext, mockEvent } from '../mocks/aws'; + +describe('type.SQSService', () => { + const lwWithQueues = lambdaWrapper.configure({ + sqs: { + queues: { + test1: 'test-queue-1', + test2: 'test-queue-2', + }, + }, + }); + + type expectedQueueNames = 'test1' | 'test2'; + + const di = new DependencyInjection(lwWithQueues.config, mockEvent, mockContext); + const sqs = di.get(SQSService); + + describe('QueueName', () => { + it('should infer queue names from config', () => { + // use our configured LambdaWrapper instance + type queueName = QueueName; + expectTypeOf().toEqualTypeOf(); + }); + + it('should infer `never` if no queues are configured', () => { + // use the package's default out-of-the-box LambdaWrapper instance + type queueName = QueueName; + expectTypeOf().toBeNever(); + }); + }); + + describe('batchDelete', () => { + it('should accept only configured queue names', () => { + expectTypeOf(sqs.batchDelete).parameter(0).toEqualTypeOf(); + }); + }); + + describe('getMessageCount', () => { + it('should accept only configured queue names', () => { + expectTypeOf(sqs.getMessageCount).parameter(0).toEqualTypeOf(); + }); + }); + + describe('publish', () => { + it('should accept only configured queue names', () => { + expectTypeOf(sqs.publish).parameter(0).toEqualTypeOf(); + }); + }); + + describe('receive', () => { + it('should accept only configured queue names', () => { + expectTypeOf(sqs.receive).parameter(0).toEqualTypeOf(); + }); + }); +}); diff --git a/tsconfig-type-test.json b/tsconfig-type-test.json new file mode 100644 index 00000000..e0c6480f --- /dev/null +++ b/tsconfig-type-test.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": true, + }, + "include": [ + "tests/type/*.spec.ts", + "types/*.d.ts", + ] +} diff --git a/yarn.lock b/yarn.lock index b5d47379..ba840d29 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2913,6 +2913,11 @@ exit@^0.1.2: resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== +expect-type@^0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/expect-type/-/expect-type-0.18.0.tgz#16f62ff9d04950d7d286ed9179a52bc3ea366f2b" + integrity sha512-xjKoyyDLoia2h1WF+vwV8AmEpQ0drGW0InRgyywAHyOC+XSPYMxGoMXSwPjXs46D8FgLmp32sHMd1KrVingDuQ== + expect@^29.0.0, expect@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" From 87f765f7dbbde79ff5a152d9713c53e107c2e83a Mon Sep 17 00:00:00 2001 From: Seb Aebischer <49983670+seb-cr@users.noreply.github.com> Date: Thu, 28 Mar 2024 10:13:53 +0000 Subject: [PATCH 26/28] docs: Remove remaining references to Epsagon (#1197) Missed this in #1168 --- docs/services/LoggerService.md | 9 +++------ src/services/LoggerService.ts | 2 +- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/docs/services/LoggerService.md b/docs/services/LoggerService.md index 5c877590..8a690e95 100644 --- a/docs/services/LoggerService.md +++ b/docs/services/LoggerService.md @@ -2,7 +2,7 @@ Provides logging and integrations with our monitoring tools. -For logging we use [Winston](https://github.com/winstonjs/winston). Errors will also be sent to [Sentry](https://sentry.io/) and [Epsagon](https://epsagon.com/) if those are configured. +For logging we use [Winston](https://github.com/winstonjs/winston). Errors will also be sent to [Sentry](https://sentry.io/) and [Lumigo](https://lumigo.io/) if those are configured. ## Usage @@ -42,12 +42,9 @@ The `warning` method is equivalent to `error` by default, but can be switched to This is handy for muting certain errors in staging, where we expect our integration tests to cause a lot of errors deliberately that would otherwise spam us with Epsagon alerts. -### Epsagon +### Lumigo -To configure Epsagon, set the following environment variables: - -- `EPSAGON_TOKEN` – your access token -- `EPSAGON_SERVICE_NAME` – the application name (including stage) to record traces under +To configure Lumigo, either [enable auto-tracing](https://docs.lumigo.io/docs/serverless-applications#automatic-instrumentation), or set the `LUMIGO_TRACER_TOKEN` environment variable. ### Sentry diff --git a/src/services/LoggerService.ts b/src/services/LoggerService.ts index 7c5f2eef..63e9f459 100644 --- a/src/services/LoggerService.ts +++ b/src/services/LoggerService.ts @@ -23,7 +23,7 @@ if (sentryIsAvailable) { * * For logging we use [Winston](https://github.com/winstonjs/winston). * Errors will also be sent to [Sentry](https://sentry.io/) and - * [Epsagon](https://epsagon.com/) if those are available. + * [Lumigo](https://lumigo.io/) if those are configured. */ export default class LoggerService extends DependencyAwareClass { private sentry: typeof Sentry | null; From 0d55ff1461d9b8a54a0227d10b72cf491484f1d7 Mon Sep 17 00:00:00 2001 From: Seb Aebischer <49983670+seb-cr@users.noreply.github.com> Date: Thu, 28 Mar 2024 10:32:26 +0000 Subject: [PATCH 27/28] feat: Runtime config validation (#1199) Adds some basic config validation at runtime, which will help avoid problems in projects that are not using TypeScript. In particular, we will now throw an error if a dependency does not extend `DependencyAwareClass`. Not marking this as a separate breaking change because bad configs will already cause compile-time errors in TypeScript. Jira: [ENG-3207] --- README.md | 11 ++++++ docs/migration/v2.md | 2 ++ src/core/LambdaWrapper.ts | 25 +++++++++++++- tests/unit/core/LambdaWrapper.spec.ts | 50 +++++++++++++++++++++++++++ 4 files changed, 87 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4fbcb67c..de278add 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,17 @@ export default class MyService extends DependencyAwareClass { } ``` +If you need to override the constructor, it must take a `DependencyInjection` instance and pass it to `super`. + +```ts +export default class MyService extends DependencyAwareClass { + constructor(di: DependencyInjection) { + super(di); + // now do your other constructor stuff + } +} +``` + Then add it to your Lambda Wrapper configuration in the `dependencies` key. ```ts diff --git a/docs/migration/v2.md b/docs/migration/v2.md index 127045cf..b24859d3 100644 --- a/docs/migration/v2.md +++ b/docs/migration/v2.md @@ -150,6 +150,8 @@ export default lambdaWrapper.wrap(async (di) => { `get` will also always throw an error when used in a constructor to avoid surprises where other dependencies may be `undefined`. Instead of storing references to dependencies in class members, `get` them just before use. +A further breaking change in v2 is that all dependencies _must_ extend `DependencyAwareClass`. This is enforced at the type level and also at runtime, for those using plain JavaScript. Remember to add a call to `super` if you are overriding the constructor. + The `definitions` property has been removed. The `getEvent`, `getContext` and `getConfiguration` methods have been deprecated and will be removed in a future major release. Use the `event`, `context` and `config` properties directly. diff --git a/src/core/LambdaWrapper.ts b/src/core/LambdaWrapper.ts index 15c2238d..d6467f1a 100644 --- a/src/core/LambdaWrapper.ts +++ b/src/core/LambdaWrapper.ts @@ -4,6 +4,7 @@ import { Context } from '../index'; import ResponseModel from '../models/ResponseModel'; import LoggerService from '../services/LoggerService'; import RequestService from '../services/RequestService'; +import DependencyAwareClass from './DependencyAwareClass'; import DependencyInjection from './DependencyInjection'; import { LambdaWrapperConfig, mergeConfig } from './config'; @@ -20,7 +21,29 @@ export interface WrapOptions { } export default class LambdaWrapper { - constructor(readonly config: TConfig) {} + constructor(readonly config: TConfig) { + LambdaWrapper.validateConfig(config); + } + + /** + * Validate the given config object. + * + * This is mainly to benefit projects that are not using TypeScript, where + * missing properties or incorrect types would not otherwise be flagged up. + * + * @param config + */ + static validateConfig(config: LambdaWrapperConfig): void { + if (!config.dependencies) { + throw new TypeError("config is missing the 'dependencies' key"); + } + + Object.values(config.dependencies).forEach((dep) => { + if (!(dep.prototype instanceof DependencyAwareClass)) { + throw new TypeError(`dependency '${dep.name}' does not extend DependencyAwareClass`); + } + }); + } /** * Returns a new Lambda Wrapper with the given configuration applied. diff --git a/tests/unit/core/LambdaWrapper.spec.ts b/tests/unit/core/LambdaWrapper.spec.ts index d3595f28..020ba57a 100644 --- a/tests/unit/core/LambdaWrapper.spec.ts +++ b/tests/unit/core/LambdaWrapper.spec.ts @@ -1,6 +1,7 @@ import { RESPONSE_HEADERS } from '@/src/models/ResponseModel'; import { + DependencyAwareClass, DependencyInjection, LambdaTermination, LambdaWrapper, @@ -33,6 +34,55 @@ describe('unit.core.LambdaWrapper', () => { afterEach(() => jest.resetAllMocks()); + describe('validateConfig', () => { + describe('valid config', () => { + ([ + { + name: 'empty', + input: { + dependencies: {}, + }, + }, + { + name: 'good dependency', + input: { + dependencies: { + Good: class Good extends DependencyAwareClass {}, + }, + }, + }, + { + name: 'extra keys', + input: { + dependencies: {}, + sqs: {}, + extra: {}, + }, + }, + ] as const).forEach(({ name, input }) => { + it(`should pass on valid config: ${name}`, () => { + expect(() => LambdaWrapper.validateConfig(input)).not.toThrow(); + }); + }); + }); + + describe('invalid config', () => { + // these scenarios are prevented by TypeScript, but may happen in plain JS + + it('should throw if config is missing dependencies', () => { + expect(() => new LambdaWrapper({} as any)).toThrow(TypeError); + }); + + it('should throw if a dependency does not extend DependencyAwareClass', () => { + expect(() => new LambdaWrapper({ + dependencies: { + Bad: class Bad {}, + }, + } as any)).toThrow(TypeError); + }); + }); + }); + describe('config', () => { it('should expose the config object', () => { const lw = new LambdaWrapper(config); From 59b2450dd943c050fc90fe70766f50b497038c3d Mon Sep 17 00:00:00 2001 From: Seb Aebischer <49983670+seb-cr@users.noreply.github.com> Date: Tue, 2 Apr 2024 17:15:03 +0100 Subject: [PATCH 28/28] docs: Remove refs to beta ahead of 2.0.0 release (#1200) To be merged just before #1195, so that the docs in the 2.0.0 release do not tell you to install from the beta channel. --- README.md | 4 ++-- src/core/DependencyInjection.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index de278add..dcb5e927 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,9 @@ If you're coming from v1 and updating to v2, check out the [v2 migration guide]( Install via npm or Yarn: ```bash -npm i @comicrelief/lambda-wrapper@beta +npm i @comicrelief/lambda-wrapper # or -yarn add @comicrelief/lambda-wrapper@beta +yarn add @comicrelief/lambda-wrapper ``` You can then wrap your Lambda handler functions like this: diff --git a/src/core/DependencyInjection.ts b/src/core/DependencyInjection.ts index af48d795..e6d2c562 100644 --- a/src/core/DependencyInjection.ts +++ b/src/core/DependencyInjection.ts @@ -51,7 +51,7 @@ export default class DependencyInjection