diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 53029eb247..0000000000 --- a/.editorconfig +++ /dev/null @@ -1,11 +0,0 @@ -# http://editorconfig.org - -root = true - -[.ts] -charset = utf-8 -indent_style = space -indent_size = 4 -end_of_line = lf -insert_final_newline = true -trim_trailing_whitespace = true diff --git a/.eslintrc.yml b/.eslintrc.yml new file mode 100644 index 0000000000..0733cebec9 --- /dev/null +++ b/.eslintrc.yml @@ -0,0 +1,35 @@ +parser: '@typescript-eslint/parser' +plugins: + - '@typescript-eslint' +parserOptions: + ecmaVersion: 2018 + sourceType: module + project: + - ./tsconfig.json + - ./tsconfig.spec.json +extends: + - 'plugin:@typescript-eslint/recommended' + - 'plugin:@typescript-eslint/recommended-requiring-type-checking' + - 'plugin:jest/recommended' + - 'prettier' +rules: + '@typescript-eslint/explicit-member-accessibility': off + '@typescript-eslint/no-angle-bracket-type-assertion': off + '@typescript-eslint/no-parameter-properties': off + '@typescript-eslint/explicit-function-return-type': off + '@typescript-eslint/member-delimiter-style': off + '@typescript-eslint/no-inferrable-types': off + '@typescript-eslint/no-explicit-any': off + '@typescript-eslint/member-ordering': 'error' + '@typescript-eslint/no-unused-vars': + - 'error' + - args: 'none' + # TODO: Remove these and fixed issues once we merged all the current PRs. + '@typescript-eslint/ban-types': off + '@typescript-eslint/no-unsafe-return': off + '@typescript-eslint/no-unsafe-assignment': off + '@typescript-eslint/no-unsafe-call': off + '@typescript-eslint/no-unsafe-member-access': off + '@typescript-eslint/no-unsafe-argument': off + '@typescript-eslint/explicit-module-boundary-types': off + '@typescript-eslint/restrict-template-expressions': off diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..9d58c92e15 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,15 @@ +version: 2 +updates: +- package-ecosystem: npm + directory: "/" + schedule: + interval: daily + time: "10:00" + timezone: Europe/Budapest + open-pull-requests-limit: 5 + versioning-strategy: increase + commit-message: + prefix: build + include: scope + ignore: + - dependency-name: "husky" \ No newline at end of file diff --git a/.github/lock.yml b/.github/lock.yml deleted file mode 100644 index 9346c21eb8..0000000000 --- a/.github/lock.yml +++ /dev/null @@ -1,16 +0,0 @@ -# Configuration for lock-threads - https://github.com/dessant/lock-threads - -# Number of days of inactivity before a closed issue or pull request is locked -daysUntilLock: 7 - -# Label to add before locking, such as `outdated`. Set to `false` to disable -lockLabel: false - -# Comment to post before locking. Set to `false` to disable -lockComment: > - This thread has been automatically locked since there has not been - any recent activity after it was closed. Please open a new issue for - related bugs. - -# Limit to only `issues` or `pulls` -only: issues diff --git a/.github/semantic.yml b/.github/semantic.yml new file mode 100644 index 0000000000..d74c23950c --- /dev/null +++ b/.github/semantic.yml @@ -0,0 +1,18 @@ +titleAndCommits: true +allowMergeCommits: false +scopes: + - deps + - deps-dev +types: + - feat + - fix + - docs + - style + - refactor + - perf + - test + - build + - ci + - chore + - revert + - merge diff --git a/.github/workflows/auto-approve-dependabot-workflow.yml b/.github/workflows/auto-approve-dependabot-workflow.yml new file mode 100644 index 0000000000..bc330c1b98 --- /dev/null +++ b/.github/workflows/auto-approve-dependabot-workflow.yml @@ -0,0 +1,23 @@ +name: Dependabot auto-merge +on: + pull_request_target +jobs: + dependabot: + runs-on: ubuntu-latest + if: github.actor == 'dependabot[bot]' + steps: + - name: 'Auto approve PR by Dependabot' + uses: hmarr/auto-approve-action@v2.0.0 + with: + github-token: "${{ secrets.TYPESTACK_BOT_TOKEN }}" + - name: 'Comment merge command' + uses: actions/github-script@v3 + with: + github-token: ${{secrets.TYPESTACK_BOT_TOKEN }} + script: | + await github.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body: '@dependabot squash and merge' + }) diff --git a/.github/workflows/continuous-deployment-workflow.yml b/.github/workflows/continuous-deployment-workflow.yml new file mode 100644 index 0000000000..71af4cd8f4 --- /dev/null +++ b/.github/workflows/continuous-deployment-workflow.yml @@ -0,0 +1,29 @@ +name: CD +on: + release: + types: [created] +jobs: + publish: + name: Publish to NPM + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 'lts/*' + registry-url: https://registry.npmjs.org + - run: npm ci --ignore-scripts + - run: npm run prettier:check + - run: npm run lint:check + - run: npm run test:ci + - run: npm run build:es2015 + - run: npm run build:esm5 + - run: npm run build:cjs + - run: npm run build:umd + - run: npm run build:types + - run: cp LICENSE build/LICENSE + - run: cp README.md build/README.md + - run: jq 'del(.devDependencies) | del(.scripts)' package.json > build/package.json + - run: npm publish ./build + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }} diff --git a/.github/workflows/continuous-integration-workflow.yml b/.github/workflows/continuous-integration-workflow.yml new file mode 100644 index 0000000000..678661213c --- /dev/null +++ b/.github/workflows/continuous-integration-workflow.yml @@ -0,0 +1,47 @@ +name: CI +on: [push, pull_request] +jobs: + checks: + name: Linters + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 'lts/*' + - run: npm ci --ignore-scripts + - run: npm run prettier:check + - run: npm run lint:check + tests: + name: Tests + runs-on: ubuntu-latest + strategy: + matrix: + node-version: ['lts/*', 'current'] + fail-fast: false + steps: + - uses: actions/checkout@v3 + - name: Setting up Node.js (v${{ matrix.node-version }}.x) + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + - run: npm ci --ignore-scripts + - run: npm run test:ci + - run: npm install codecov -g + if: ${{ matrix.node-version == 'current' }} + - run: codecov -f ./coverage/clover.xml -t ${{ secrets.CODECOV_TOKEN }} --commit=$GITHUB_SHA --branch=${GITHUB_REF##*/} + if: ${{ matrix.node-version == 'current' }} + build: + name: Build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 'lts/*' + - run: npm ci --ignore-scripts + - run: npm run build:es2015 + - run: npm run build:esm5 + - run: npm run build:cjs + - run: npm run build:umd + - run: npm run build:types diff --git a/.github/workflows/lock-closed-issues-workflow.yml b/.github/workflows/lock-closed-issues-workflow.yml new file mode 100644 index 0000000000..c380eafbd5 --- /dev/null +++ b/.github/workflows/lock-closed-issues-workflow.yml @@ -0,0 +1,22 @@ +name: 'Lock inactive threads' +on: + schedule: + - cron: '0 0 * * *' +jobs: + lock: + name: Lock closed issues + runs-on: ubuntu-latest + steps: + - uses: dessant/lock-threads@v2 + with: + github-token: ${{ github.token }} + issue-lock-inactive-days: 30 + pr-lock-inactive-days: 30 + issue-lock-comment: > + This issue has been automatically locked since there + has not been any recent activity after it was closed. + Please open a new issue for related bugs. + pr-lock-comment: > + This pull request has been automatically locked since there + has not been any recent activity after it was closed. + Please open a new issue for related bugs. diff --git a/.gitignore b/.gitignore index 9eb1ab4ae9..905e735144 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,52 @@ +# Log files +logs +*.log +*.tmp +*.tmp.* +log.txt +npm-debug.log* + +# Testing output +lib-cov/** +coverage/** + +# Environment files +.env + +# Dependency directories +node_modules + +# MacOS related files +*.DS_Store +.AppleDouble +.LSOverride +._* +UserInterfaceState.xcuserstate + +# Windows related files +Thumbs.db +Desktop.ini +$RECYCLE.BIN/ + +# IDE - Sublime +*.sublime-project +*.sublime-workspace + +# IDE - VSCode +.vscode/** +!.vscode/tasks.json +!.vscode/launch.json + +# IDE - IntelliJ +.idea + +# Compilation output folders +dist/ build/ -node_modules/ -coverage/ -npm-debug.log -*.iml -.idea/ \ No newline at end of file +tmp/ +out-tsc/ +temp + +# Files for playing around locally +playground.ts +playground.js diff --git a/.prettierrc.yml b/.prettierrc.yml new file mode 100644 index 0000000000..57c1c99c9e --- /dev/null +++ b/.prettierrc.yml @@ -0,0 +1,8 @@ +printWidth: 120 +tabWidth: 2 +useTabs: false +semi: true +singleQuote: true +trailingComma: es5 +bracketSpacing: true +arrowParens: avoid diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index c84abcae35..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,7 +0,0 @@ -language: node_js -node_js: - - stable - - lts/* - -after_success: - - bash <(curl -s https://codecov.io/bash) \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 884b69c5f9..9784525326 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,76 +1,324 @@ # Changelog -### 0.9.1 +_This changelog follows the [keep a changelog][keep-a-changelog]_ format to maintain a human readable changelog. -#### Features +### [0.14.0](https://github.com/typestack/class-validator/compare/v0.13.2...v0.14.0) (2022-12-09) -* added option to pass custom context for the decorators +### Added -### Fixes +- add `@IsTimeZone` decorator to check if given string is valid IANA time zone +- add `@IsISO4217CurrencyCode` decorator to check if the string is an ISO 4217 currency code +- add `@IsStrongPassword` decorator to check if given password matches specific complexity criteria +- add `@IsBase58` decorator to check if a string is base58 encoded +- add `@IsTaxId` decorator to check if a given string is a valid tax ID in a given locale +- add support for passing function as date generator in `@MinDate` and `@MaxDate` decorators +- add option to print constraint error message instead of constraint type in validation error +- improve decorator metadata lookup performance +- return possible values in error message for `@IsEnum` decorator -* validating against a schema will validate against that one instead of every registered one +### Fixed -### 0.9.0 [BREAKING CHANGE] +- re-added `@types/validator` as dependency +- fix error generation when using `@NestedValidation` +- pass validation options correctly to validator in `@IsDateString` decorator +- support passing `Symbol` as parameter in error message generation +- specify supported locales for `@IsAlphanumeric` decorator +- correctly assign decorator name in metadata instead of loosing it +- fix various spelling errors in documentation +- fix various spelling errors and inconsistencies in JSDoc for decorators -#### Features +### Changed -* updated [validator.js][validator-js] from 9.2.0 to 10.4.0 (Check it's [changelog][validator-js-release-notes] for what has changed.) - * until now fractional numbers was not allowed in the `IsNumberString` decorator, now they are allowed - * until now Gmail addresses could contain multiple dots or random text after a `+` symbol, this is not allowed anymore -* `IsPhoneNumber` decorator has been added which uses the [google-libphonenumber][google-libphonenumber] libary to validate international phone numbers accurately +- remove documentation about deprecated schema based validation and added warning +- update warning message logged about missing decorator metadata +- update `libphonenumber-js` to `^1.10.14` from `^1.9.43` +- update various dev-dependencies -### Fixes +### BREAKING CHANGES -* update `IsURLOptions` to match underlying validator host list options -* added a console warning when no metadata decorator is found as it's possibly not intended -* the `Min` and `Max` decorator will corectly show an inclusive error message when failing -* fixed a runtime error when `validationArguments.value` is not a string +**`forbidUnknownValues` option is enabled by default** -### 0.8.5 +From this release the `forbidUnknownValues` is enabled by default. This is the desired behavior for majority of +use-cases, but this change may break validation for some. The two scenarios that results in failed validation: -#### Fixes +- when attempting to validate a class instance without metadata for it +- when using group validation and the specified validation group results in zero validation applied -* remove `ansicolor` package, because it's incompatible with IE +The old behavior can be restored via specifying `forbidUnknownValues: false` option when calling the validate functions. -## 0.8.4 +For more details see [PR #1798](https://github.com/typestack/class-validator/pull/1798) and [#1422 (comment)](https://github.com/typestack/class-validator/issues/1422#issuecomment-1317953863). -#### Features +**`@NestedValidation` decorator correctly assigns validation errors** -* `ValidatorOptions` now has a `forbidUnknownValues` key to prevent unknown objects to pass validation - * it's highly advised to turn this option on - * now this option defaults to `false` but will be default to `true` after the **1.0** release +Until now the errors from a nested validation in some cases were incorrectly assigned +to the parent instead of the child being validated. Now the validation errors are correctly assigned. -## 0.8.3 +For more details see [#679](https://github.com/typestack/class-validator/issues/679). -#### Fixes +### [0.13.2](https://github.com/typestack/class-validator/compare/v0.13.1...v0.13.2) (2021-11-20) -* handle when `target` property is undefined when calling `ValidationError.toString()` +> **NOTE:** This version fixes a security vulnerability allowing denial of service attacks with a specially crafted request payload. +> Please update as soon as possible. -## 0.8.2 +#### Fixed -#### Features +- switched to use `Array.isArray` in array checks from `instanceof` operator -* added `ValidationError.toString()` method for easier debugging -* added `printError` method to pretty-print errors in NodeJS or the browser +#### Changed -#### Fixes +- `libphonenumber-js` package updated to `1.9.43` from `1.9.7` +- `validator` package updated to `13.5.2` from `13.5.2` +- various dev-dependencies updated -* fixed wrong type info in `ValidatorOptions` -* fixed wrong type info in `ValidationSchema` \(the `options` key now is optional\) -* corrected `IsNumericString` to `IsNumberString` in the README -* fixed type of `host_whitelist` and `host_backlist` in `IsURLOptions` +### [0.13.1](https://github.com/typestack/class-validator/compare/v0.13.0...v0.13.1) (2021-01-14) -## 0.8.1 +#### Added -#### Fixes +- optional mather function has been added to the `ArrayUnique` decorator -* fixed wrong type info in `ValidatorOptions` +#### Fixed -## 0.8.0 \[BREAKING CHANGE\] +- a typo was fixed in the error message generated by the `IsUUID` decorator +- calling `ValidationError.toString()` doesn't result in an error when `forbidNonWhitelisted` parameter was used +- fixed typo in error message generated by `IsIn` decorator +- the `@types/validator` package is correctly installed +- `inlineSources` option is enabled in tsconfig preventing various sourcemap errors when consuming the package -#### Features +#### Changed -* updated [validator.js][validator-js] from 7.0.0 to 9.2.0 (Check it's [changelog][validator-js-release-notes] for what has changed.) +- various dev dependencies has been updated + +### [0.13.0](https://github.com/typestack/class-validator/compare/v0.12.2...v0.13.0) (2021-01-11) + +#### Added + +- **project is restructured to allow three-shaking** +- added option to fail on first validation error (#620) +- two new validator option is added: + - `always` - allows setting global default for `always` option for decorators + - `strictGroups` - ignore decorators with at least one group, when `ValidatorOptions.groups` is empty + +### Fixed + +- the 'any' locale is allowed in the `isPostalCode` decorator (#634) +- the `IsDateString()` decorator now aliases the `IsISO8601()` decorator (#672) + +#### Changed + +- project tooling has been updated significantly +- google-libphonenumber has been replaced with libphonenumber-js (this should have no effect on validation) +- build process generates include both ES/CommonJS and UMD variations +- various dev dependencies has been updated + +### [0.12.2](https://github.com/typestack/class-validator/compare/v0.12.1...v0.12.2) (2020-04-23) + +#### Fixed + +- move `tslib` from `peerDependencies` to `dependencies` ([827eff1](https://github.com/typestack/class-validator/commit/827eff1)), closes [#588](https://github.com/typestack/class-validator/issues/588) + +### [0.12.1](https://github.com/typestack/class-validator/compare/v0.12.0...v0.12.1) (2020-04-18) + +#### Fixed + +- apply only nested validator for ValidateNested multi-dimensional array ([c463be5](https://github.com/typestack/class-validator/commit/c463be5)) + +### [0.12.0](https://github.com/typestack/class-validator/compare/v0.11.1...v0.12.0) (2020-04-18) + +#### Fixed + +- accept negative timezone in isDateString ([#564](https://github.com/typestack/class-validator/issues/564)) ([2012d72](https://github.com/typestack/class-validator/commit/2012d72)), closes [#565](https://github.com/typestack/class-validator/issues/565) +- apply all decorators type PropertyDecorator ([#556](https://github.com/typestack/class-validator/issues/556)) ([5fb36e3](https://github.com/typestack/class-validator/commit/5fb36e3)), closes [#555](https://github.com/typestack/class-validator/issues/555) +- avoiding metadataStorage from DI ([#335](https://github.com/typestack/class-validator/issues/335)) ([b57fef4](https://github.com/typestack/class-validator/commit/b57fef4)), closes [#328](https://github.com/typestack/class-validator/issues/328) [#261](https://github.com/typestack/class-validator/issues/261) [#132](https://github.com/typestack/class-validator/issues/132) +- correct registerDecorator options argument ([7909ec6](https://github.com/typestack/class-validator/commit/7909ec6)), closes [#302](https://github.com/typestack/class-validator/issues/302) +- IsNumberString accept isNumbericOptions as argument ([62b993f](https://github.com/typestack/class-validator/commit/62b993f)), closes [#518](https://github.com/typestack/class-validator/issues/518) [#463](https://github.com/typestack/class-validator/issues/463) +- optional `constraints` property in ValidationError ([#465](https://github.com/typestack/class-validator/issues/465)) ([84680ad](https://github.com/typestack/class-validator/commit/84680ad)), closes [#309](https://github.com/typestack/class-validator/issues/309) +- pass context to ValidationError for async validations ([#533](https://github.com/typestack/class-validator/issues/533)) ([4eb1216](https://github.com/typestack/class-validator/commit/4eb1216)) +- switch isLatitude & isLongitude validators ([#513](https://github.com/typestack/class-validator/issues/513)) ([5497179](https://github.com/typestack/class-validator/commit/5497179)), closes [#502](https://github.com/typestack/class-validator/issues/502) +- switch isLatitude & isLongitude validators ([#537](https://github.com/typestack/class-validator/issues/537)) ([c27500b](https://github.com/typestack/class-validator/commit/c27500b)) +- ValidateNested support multi-dimensional arrays ([#539](https://github.com/typestack/class-validator/issues/539)) ([62678e1](https://github.com/typestack/class-validator/commit/62678e1)) + +#### Changed + +- update build process to enable tree shaking ([#568](https://github.com/typestack/class-validator/issues/568)) ([11a7b8b](https://github.com/typestack/class-validator/commit/11a7b8b)), closes [#258](https://github.com/typestack/class-validator/issues/258) [#248](https://github.com/typestack/class-validator/issues/248) [#247](https://github.com/typestack/class-validator/issues/247) [#212](https://github.com/typestack/class-validator/issues/212) + +#### Added + +- sync validatorjs version from v10.11.3 to v13.0.0 ([09120b7](https://github.com/typestack/class-validator/commit/09120b7)), closes [#576](https://github.com/typestack/class-validator/issues/576) [#425](https://github.com/typestack/class-validator/issues/425) + +### BREAKING CHANGES + +- Validatorjs releases contain some breaking changes e.g. `IsMobileNumber` or `IsHexColor`. Please check validatorjs [CHANGELOG](https://github.com/validatorjs/validator.js/blob/master/CHANGELOG.md) +- Validation functions was removed from `Validator` class to enable tree shaking. + + BEFORE: + + ```ts + import { Validator } from 'class-validator'; + + const validator = new Validator(); + validator.isNotIn(value, possibleValues); + validator.isBoolean(value); + ``` + + AFTER: + + ```ts + import { isNotIn, isBoolean } from 'class-validator'; + + isNotIn(value, possibleValues); + isBoolean(value); + ``` + +- IsNumberString decorator arguments changed to `@IsNumberString(ValidatorJS.IsNumericOptions, ValidationOptions)`. + +### [0.11.1](https://github.com/typestack/class-validator/compare/v0.11.0...v0.11.1) (2020-03-18) + +#### Fixed + +- IsNumber validator now works when maxDecimalPlaces=0 ([#524](https://github.com/typestack/class-validator/issues/524)) ([b8aa922](https://github.com/typestack/class-validator/commit/b8aa922)) + +#### Added + +- add all option in isuuid validator ([#452](https://github.com/typestack/class-validator/issues/452)) ([98e9382](https://github.com/typestack/class-validator/commit/98e9382)) +- add IsFirebasePushId validator ([#548](https://github.com/typestack/class-validator/issues/548)) ([e7e2e53](https://github.com/typestack/class-validator/commit/e7e2e53)) +- add options for isISO8601 validator ([#460](https://github.com/typestack/class-validator/issues/460)) ([90a6638](https://github.com/typestack/class-validator/commit/90a6638)) + +### [0.11.0](https://github.com/typestack/class-validator/compare/v0.10.2...v0.11.0) (2019-11-01) + +#### Fixed + +- create instance of ValidationError for whitelist errors ([#434](https://github.com/typestack/class-validator/issues/434)) ([a98f5dd](https://github.com/typestack/class-validator/commit/a98f5dd)), closes [#325](https://github.com/typestack/class-validator/issues/325) +- pass context for isDefined and custom validators ([#296](https://github.com/typestack/class-validator/issues/296)) ([0ef898e](https://github.com/typestack/class-validator/commit/0ef898e)), closes [#292](https://github.com/typestack/class-validator/issues/292) + +#### Added + +- add isHash validator ([#445](https://github.com/typestack/class-validator/issues/445)) ([c454cf9](https://github.com/typestack/class-validator/commit/c454cf9)) +- add isISSN validator ([#450](https://github.com/typestack/class-validator/issues/450)) ([4bd586e](https://github.com/typestack/class-validator/commit/4bd586e)) +- add isJWT validator ([#444](https://github.com/typestack/class-validator/issues/444)) ([874861b](https://github.com/typestack/class-validator/commit/874861b)) +- add isMACAddress validator ([#449](https://github.com/typestack/class-validator/issues/449)) ([45b7df7](https://github.com/typestack/class-validator/commit/45b7df7)) +- add support for maxDecimalPlaces on IsNumber ([#381](https://github.com/typestack/class-validator/issues/381)) ([a4dc10e](https://github.com/typestack/class-validator/commit/a4dc10e)) + +### BREAKING CHANGES + +- update @types/validator from 11.1.0 to version 12.0.0 - please check it's [changelog][validator-js-release-notes] + +### [0.10.2](https://github.com/typestack/class-validator/compare/v0.10.1...v0.10.2) (2019-10-14) + +#### Fixed + +- apply custom constraint class validation to each item in the array ([#295](https://github.com/typestack/class-validator/issues/295)) ([5bb704e](https://github.com/typestack/class-validator/commit/5bb704e)), closes [#260](https://github.com/typestack/class-validator/issues/260) + +#### Added + +- add isLatLong, isLatitude, isLongtitude validators ([#427](https://github.com/typestack/class-validator/issues/427)) ([3fd15c4](https://github.com/typestack/class-validator/commit/3fd15c4)), closes [#415](https://github.com/typestack/class-validator/issues/415) +- add IsObject and IsNotEmptyObject new decorators ([#334](https://github.com/typestack/class-validator/issues/334)) ([0a41aeb](https://github.com/typestack/class-validator/commit/0a41aeb)) +- support ES6 Map and Set for regular validators with each option ([#430](https://github.com/typestack/class-validator/issues/430)) ([a055bba](https://github.com/typestack/class-validator/commit/a055bba)), closes [#428](https://github.com/typestack/class-validator/issues/428) + +### [0.10.1](https://github.com/typestack/class-validator/compare/v0.10.0...v0.10.1) (2019-09-25) + +#### Fixed + +- add default message for isMilitaryTime validator ([#411](https://github.com/typestack/class-validator/issues/411)) ([204b7df](https://github.com/typestack/class-validator/commit/204b7df)), closes [#287](https://github.com/typestack/class-validator/issues/287) +- add default message for isPort validator ([#404](https://github.com/typestack/class-validator/issues/404)) ([74e568c](https://github.com/typestack/class-validator/commit/74e568c)) +- add locale parameter for isAlpha and isAlphanumeric validat… ([#406](https://github.com/typestack/class-validator/issues/406)) ([2f4bf4e](https://github.com/typestack/class-validator/commit/2f4bf4e)) + +#### Added + +- add `skipUndefinedProperties`, `skipNullProperties` options ([#414](https://github.com/typestack/class-validator/issues/414)) ([76c948a](https://github.com/typestack/class-validator/commit/76c948a)), closes [#308](https://github.com/typestack/class-validator/issues/308) + +### [0.10.0](https://github.com/typestack/class-validator/compare/v0.9.1...v0.10.0) (2019-08-10) + +#### Fixed + +- add correct signature for custom error message handler ([249c41d](https://github.com/typestack/class-validator/commit/249c41d)) + +#### Added + +- add `IsISO31661Alpha3` and `IsISO31661Alpha2` validators ([#273](https://github.com/typestack/class-validator/issues/273)) ([55c57b3](https://github.com/typestack/class-validator/commit/55c57b3)) +- **IsDecimal:** implement `IsDecimal` from validatorjs ([#359](https://github.com/typestack/class-validator/issues/359)) ([b4c8e21](https://github.com/typestack/class-validator/commit/b4c8e21)) +- add `isPort` decorator ([#282](https://github.com/typestack/class-validator/issues/282)) ([36684ec](https://github.com/typestack/class-validator/commit/36684ec)) +- allow validate Map/Set ([#365](https://github.com/typestack/class-validator/issues/365)) ([f6fcdc5](https://github.com/typestack/class-validator/commit/f6fcdc5)) +- new `ValidatePromise` decorator - resolve promise before validate ([#369](https://github.com/typestack/class-validator/issues/369)) ([35ec04d](https://github.com/typestack/class-validator/commit/35ec04d)) +- replace instanceof Promise and support Promise/A+ ([#310](https://github.com/typestack/class-validator/issues/310)) ([59eac09](https://github.com/typestack/class-validator/commit/59eac09)) +- `isNumberString` now accept validator.js `IsNumericOptions` as second parameter ([#262](https://github.com/typestack/class-validator/issues/262)) + +### BREAKING CHANGES + +- update @types/validator from 10.4.0 to version 10.11.2 - please check it's [changelog][validator-js-release-notes] ([cb960dd](https://github.com/typestack/class-validator/commit/cb960dd)) +- `isDateString` now check to match only entire ISO Date ([#275](https://github.com/typestack/class-validator/issues/275)) ([5012464](https://github.com/typestack/class-validator/commit/5012464)) +- remove `IsCurrencyOptions`, `IsURLOptions`, `IsEmailOptions`, `IsFQDNOptions` interfaces and replace with interfaces from `@types/validator` + +### [0.9.1](https://github.com/typestack/class-validator/compare/v0.9.0...v0.9.1) + +#### Added + +- added option to pass custom context for the decorators + +#### Fixed + +- validating against a schema will validate against that one instead of every registered one + +### [0.9.0](https://github.com/typestack/class-validator/compare/v0.8.5...v0.9.0) [BREAKING CHANGE] + +#### Added + +- updated [validator.js][validator-js] from 9.2.0 to 10.4.0 (Check it's [changelog][validator-js-release-notes] for what has changed.) + - until now fractional numbers was not allowed in the `IsNumberString` decorator, now they are allowed + - until now Gmail addresses could contain multiple dots or random text after a `+` symbol, this is not allowed anymore +- `IsPhoneNumber` decorator has been added which uses the [google-libphonenumber][google-libphonenumber] library to validate international phone numbers accurately + +#### Fixed + +- update `IsURLOptions` to match underlying validator host list options +- added a console warning when no metadata decorator is found as it's possibly not intended +- the `Min` and `Max` decorator will corectly show an inclusive error message when failing +- fixed a runtime error when `validationArguments.value` is not a string + +### [0.8.5](https://github.com/typestack/class-validator/compare/v0.8.4...v0.8.5) + +#### Fixed + +- remove `ansicolor` package, because it's incompatible with IE + +### [0.8.4](https://github.com/typestack/class-validator/compare/v0.8.3...v0.8.4) + +#### Added + +- `ValidatorOptions` now has a `forbidUnknownValues` key to prevent unknown objects to pass validation + - it's highly advised to turn this option on + - now this option defaults to `false` but will be default to `true` after the **1.0** release + +### [0.8.3](https://github.com/typestack/class-validator/compare/v0.8.2...v0.8.3) + +#### Fixed + +- handle when `target` property is undefined when calling `ValidationError.toString()` + +### [0.8.2](https://github.com/typestack/class-validator/compare/v0.8.1...v0.8.2) + +#### Added + +- added `ValidationError.toString()` method for easier debugging +- added `printError` method to pretty-print errors in NodeJS or the browser + +#### Fixed + +- fixed wrong type info in `ValidatorOptions` +- fixed wrong type info in `ValidationSchema` \(the `options` key now is optional\) +- corrected `IsNumericString` to `IsNumberString` in the README +- fixed type of `host_whitelist` and `host_backlist` in `IsURLOptions` + +### [0.8.1](https://github.com/typestack/class-validator/compare/v0.8.0...v0.8.1) + +#### Fixed + +- fixed wrong type info in `ValidatorOptions` + +### 0.8.0 \[BREAKING CHANGE\] + +##### Added + +- updated [validator.js][validator-js] from 7.0.0 to 9.2.0 (Check it's [changelog][validator-js-release-notes] for what has changed.) This caused breaking change, if you used the `IsUrl` decorator to validate `localhost` as a valid url, from now you must use the `require_tld: false` option @@ -79,110 +327,107 @@ url: string; ``` -* added `@IsInstance` decorator and `validator.isInstance(value, target)` method. -* changed `@IsNumber` decorator has been changed to `@IsNumber(options: IsNumberOptions)` -* added option to strip unknown properties \(`whitelist: true`\) -* added option to throw error on unknown properties \(`forbidNonWhitelisted: true`\) -* added `@Allow` decorator to prevent stripping properties without other constraint +- added `@IsInstance` decorator and `validator.isInstance(value, target)` method. +- changed `@IsNumber` decorator has been changed to `@IsNumber(options: IsNumberOptions)` +- added option to strip unknown properties \(`whitelist: true`\) +- added option to throw error on unknown properties \(`forbidNonWhitelisted: true`\) +- added `@Allow` decorator to prevent stripping properties without other constraint -#### Fixes +#### Fixed -* fixed issue with `@IsDateString` now it allow dates without fraction seconds to be set -* fixed issue with `@IsDateString` now it allow dates without with timezones to be set -* `@ValidateNested` correctly generates validation error on non object and non array values +- fixed issue with `@IsDateString` now it allow dates without fraction seconds to be set +- fixed issue with `@IsDateString` now it allow dates without with timezones to be set +- `@ValidateNested` correctly generates validation error on non object and non array values -## 0.6.7 +### 0.6.7 -#### Fixes +#### Fixed -* fixed issue with `@ValidateNested` when nested property is not defined and it throw an error \(\#59\) +- fixed issue with `@ValidateNested` when nested property is not defined and it throw an error \(\#59\) -## 0.6.5 +### 0.6.5 -#### Fixes +#### Fixed -* fixed bugs with `@IsUrl`, `@IsEmail` and several other decorators +- fixed bugs with `@IsUrl`, `@IsEmail` and several other decorators -## 0.6.4 +### 0.6.4 -#### Features +#### Added -* added `@IsMilitaryTime` decorator. +- added `@IsMilitaryTime` decorator. -## 0.6.3 +### 0.6.3 -#### Features +#### Added -* added `validateOrReject` method which rejects promise instead of returning array of errors in resolved result +- added `validateOrReject` method which rejects promise instead of returning array of errors in resolved result -## 0.6.1 +### 0.6.1 -#### Features +#### Added -* added `@IsArray` decorator. +- added `@IsArray` decorator. -## 0.6.0 \[BREAKING CHANGE\] +### 0.6.0 \[BREAKING CHANGE\] -#### Features +#### Added -* breaking change with `@ValidateNested` on arrays: Validator now groups the validation errors by sub-object, rather than them all being grouped together. See \#32 for a demonstration of the updated structure. -* added `@ValidateIf` decorator, see conditional validation in docs. +- breaking change with `@ValidateNested` on arrays: Validator now groups the validation errors by sub-object, rather than them all being grouped together. See \#32 for a demonstration of the updated structure. +- added `@ValidateIf` decorator, see conditional validation in docs. -## 0.5.0 \[BREAKING CHANGE\] +### 0.5.0 \[BREAKING CHANGE\] -#### Features +#### Added -* async validations must be marked with `{ async: true }` option now. +- async validations must be marked with `{ async: true }` option now. This is optional, but it helps to determine which decorators are async to prevent their execution in `validateSync` method. -* added `validateSync` method that performs non asynchronous validation and ignores validations that marked with `async: true`. -* there is a breaking change in `registerDecorator` method. Now it accepts options object. -* breaking change with `@ValidatorConstraint` decorator. Now it accepts option object instead of single name. - -## 0.4.1 - -#### Fixes +- added `validateSync` method that performs non asynchronous validation and ignores validations that marked with `async: true`. +- there is a breaking change in `registerDecorator` method. Now it accepts options object. +- breaking change with `@ValidatorConstraint` decorator. Now it accepts option object instead of single name. -* fixed issue with wrong source maps packaged +### 0.4.1 -## 0.4.0 \[BREAKING CHANGE\] +#### Fixed -#### Features +- fixed issue with wrong source maps packaged -* everything should be imported from "class-validator" main entry point now -* `ValidatorInterface` has been renamed to `ValidatorConstraintInterface` -* contain can be set in the main entry point now -* some decorator's names changed. Be aware of this -* added few more non-string decorators -* validator now returns array of ValidationError instead of ValidationErrorInterface. Removed old ValidationError -* removed all other validation methods except `validator.validate` -* finally validate method is async now, so custom async validations are supported now -* added ability to validate inherited properties -* added support of separate validation schemas -* added support of default validation messages -* added support of special tokens in validation messages -* added support of message functions in validation options -* added support of custom decorators -* if no groups were specified, decorators with groups now are being ignored -* changed signature of the `ValidationError`. Now if it has nested errors it does not return them in a flat array +### 0.4.0 \[BREAKING CHANGE\] -#### Fixes +#### Added -* fixed all decorators that should not work only with strings +- everything should be imported from "class-validator" main entry point now +- `ValidatorInterface` has been renamed to `ValidatorConstraintInterface` +- contain can be set in the main entry point now +- some decorator's names changed. Be aware of this +- added few more non-string decorators +- validator now returns array of ValidationError instead of ValidationErrorInterface. Removed old ValidationError +- removed all other validation methods except `validator.validate` +- finally validate method is async now, so custom async validations are supported now +- added ability to validate inherited properties +- added support of separate validation schemas +- added support of default validation messages +- added support of special tokens in validation messages +- added support of message functions in validation options +- added support of custom decorators +- if no groups were specified, decorators with groups now are being ignored +- changed signature of the `ValidationError`. Now if it has nested errors it does not return them in a flat array -## 0.3.0 +#### Fixed -#### Features +- fixed all decorators that should not work only with strings -* package has changed its name from `validator.ts` to `class-validator`. -* sanitation functionality has been removed from this library. Use [class-sanitizer][1] instead. +### 0.3.0 -#### Fixes +#### Added -_no fixes in this release._ +- package has changed its name from `validator.ts` to `class-validator`. +- sanitation functionality has been removed from this library. Use [class-sanitizer][class-sanitizer] instead. -[1]: https://github.com/pleerock/class-sanitizer +[class-sanitizer]: https://github.com/typestack/class-validator/class-sanitizer [validator-js]: https://github.com/chriso/validator.js [validator-js-release-notes]: https://github.com/chriso/validator.js/blob/master/CHANGELOG.md -[google-libphonenumber]: https://github.com/ruimarinho/google-libphonenumber \ No newline at end of file +[google-libphonenumber]: https://github.com/ruimarinho/google-libphonenumber +[keep-a-changelog]: https://keepachangelog.com/en/1.0.0/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000..abdf4ab616 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ + +The MIT License + +Copyright (c) 2015-2020 TypeStack + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index c800fa9006..458a5e2de4 100644 --- a/README.md +++ b/README.md @@ -1,953 +1,950 @@ -# class-validator - -[![Build Status](https://travis-ci.org/typestack/class-validator.svg?branch=master)](https://travis-ci.org/typestack/class-validator) -[![npm version](https://badge.fury.io/js/class-validator.svg)](https://badge.fury.io/js/class-validator) -[![Join the chat at https://gitter.im/typestack/class-validator](https://badges.gitter.im/typestack/class-validator.svg)](https://gitter.im/typestack/class-validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) - -Allows use of decorator and non-decorator based validation. -Internally uses [validator.js][1] to perform validation. -Class-validator works on both browser and node.js platforms. - -## Table of Contents - - * [Installation](#installation) - - [Old versions of node.js/browser](#old-versions-of-nodejsbrowser) - - [Using in browser](#using-in-browser) - * [Usage](#usage) - + [Validation errors](#validation-errors) - + [Validation messages](#validation-messages) - + [Validating arrays](#validating-arrays) - + [Validating nested objects](#validating-nested-objects) - + [Inheriting Validation decorators](#inheriting-validation-decorators) - + [Conditional validation](#conditional-validation) - + [Whitelisting](#whitelisting) - + [Passing context to decorators](#passing-context-to-decorators) - + [Skipping missing properties](#skipping-missing-properties) - + [Validation groups](#validation-groups) - + [Custom validation classes](#custom-validation-classes) - + [Custom validation decorators](#custom-validation-decorators) - + [Using service container](#using-service-container) - + [Synchronous validation](#synchronous-validation) - + [Manual validation](#manual-validation) - + [Validation decorators](#validation-decorators) - + [Defining validation schema without decorators](#defining-validation-schema-without-decorators) - + [Validating plain objects](#validating-plain-objects) - * [Samples](#samples) - * [Extensions](#extensions) - * [Release notes](#release-notes) - -## Installation - -``` -npm install class-validator --save -``` - -> Note: Please use at least npm@6 when using class-validator as from npm@6 the dependency tree is flatterned what is good for us. - -## Usage - -Create your class and put some validation decorators on the properties you want to validate: - -```typescript -import {validate, Contains, IsInt, Length, IsEmail, IsFQDN, IsDate, Min, Max} from "class-validator"; - -export class Post { - - @Length(10, 20) - title: string; - - @Contains("hello") - text: string; - - @IsInt() - @Min(0) - @Max(10) - rating: number; - - @IsEmail() - email: string; - - @IsFQDN() - site: string; - - @IsDate() - createDate: Date; - -} - -let post = new Post(); -post.title = "Hello"; // should not pass -post.text = "this is a great post about hell world"; // should not pass -post.rating = 11; // should not pass -post.email = "google.com"; // should not pass -post.site = "googlecom"; // should not pass - -validate(post).then(errors => { // errors is an array of validation errors - if (errors.length > 0) { - console.log("validation failed. errors: ", errors); - } else { - console.log("validation succeed"); - } -}); -``` - -### Passing options - -The `validate` function optionally expects a `ValidatorOptions` object as a second parameter. - -```ts -export interface ValidatorOptions { - - skipMissingProperties?: boolean; - whitelist?: boolean; - forbidNonWhitelisted?: boolean; - groups?: string[]; - dismissDefaultMessages?: boolean; - validationError?: { - target?: boolean; - value?: boolean; - }; - - forbidUnknownValues?: boolean; -} -``` - -> It's highly advised to enable on `forbidUnknownValues` what prevent unknown objects to pass validation. - -## Validation errors - -`validate` method returns you an array of `ValidationError` objects. Each `ValidationError` is: - -```typescript -{ - target: Object; // Object that was validated. - property: string; // Object's property that haven't pass validation. - value: any; // Value that haven't pass a validation. - constraints?: { // Constraints that failed validation with error messages. - [type: string]: string; - }; - children?: ValidationError[]; // Contains all nested validation errors of the property -} -``` - -In our case, when we validated a Post object, we have such array of ValidationErrors: - -```typescript -[{ - target: /* post object */, - property: "title", - value: "Hello", - constraints: { - length: "$property must be longer than or equal to 10 characters" - } -}, { - target: /* post object */, - property: "text", - value: "this is a great post about hell world", - constraints: { - contains: "text must contain a hello string" - } -}, -// and other errors -] -``` - -If you don't want a `target` to be exposed in validation errors, there is a special option when you use validator: - -```typescript -validator.validate(post, { validationError: { target: false } }); -``` - -This is especially useful when you send errors back over http, and you most probably don't want to expose -the whole target object. - -## Validation messages - -You can specify validation message in the decorator options and that message will be returned in `ValidationError` -returned by `validate` method in the case that validation for this field fails. - -```typescript -import {MinLength, MaxLength} from "class-validator"; - -export class Post { - - @MinLength(10, { - message: "Title is too short" - }) - @MaxLength(50, { - message: "Title is too long" - }) - title: string; -} -``` - -There are few special tokens you can use in your messages: -* `$value` - the value that is being validated -* `$property` - name of the object's property being validated -* `$target` - name of the object's class being validated -* `$constraint1`, `$constraint2`, ... `$constraintN` - constraints defined by specific validation type - -Example of usage: - -```typescript -import {MinLength, MaxLength} from "class-validator"; - -export class Post { - - @MinLength(10, { // here, $constraint1 will be replaced with "10", and $value with actual supplied value - message: "Title is too short. Minimal length is $constraint1 characters, but actual is $value" - }) - @MaxLength(50, { // here, $constraint1 will be replaced with "50", and $value with actual supplied value - message: "Title is too long. Maximal length is $constraint1 characters, but actual is $value" - }) - title: string; -} -``` - -Also you can provide a function, that returns a message. This way allows to create more granular messages: - -```typescript -import {MinLength, MaxLength, ValidationArguments} from "class-validator"; - -export class Post { - - @MinLength(10, { - message: (args: ValidationArguments) => { - if (args.value.length === 1) { - return "Too short, minimum length is 1 character"; - } else { - return "Too short, minimum length is " + args.constraints[0] + " characters"; - } - } - }) - title: string; -} -``` - -Message function accepts `ValidationArguments` which contains following information: -* `value` - the value that is being validated -* `constraints` - array of constraints defined by specific validation type -* `targetName` - name of the object's class being validated -* `object` - object that is being validated -* `property` - name of the object's property being validated - -## Validating arrays - -If your field is an array and you want to perform validation of each item in the array you must specify a -special `each: true` decorator option: - -```typescript -import {MinLength, MaxLength} from "class-validator"; - -export class Post { - - @MaxLength(20, { - each: true - }) - tags: string[]; -} -``` - -This will validate each item in `post.tags` array. - -## Validating nested objects - -If your object contains nested objects and you want the validator to perform their validation too, then you need to -use the `@ValidateNested()` decorator: - -```typescript -import {ValidateNested} from "class-validator"; - -export class Post { - - @ValidateNested() - user: User; - -} -``` - -## Inheriting Validation decorators - -When you define a subclass which extends from another one, the subclass will automatically inherit the parent's decorators. If a property is redefined in the descendant class decorators will be applied on it both from that and the base class. - -```typescript -import {validate} from "class-validator"; - -class BaseContent { - - @IsEmail() - email: string; - - @IsString() - password: string; -} - -class User extends BaseContent { - - @MinLength(10) - @MaxLength(20) - name: string; - - @Contains("hello") - welcome: string; - - @MinLength(20) - password: string; / -} - -let user = new User(); - -user.email = "invalid email"; // inherited property -user.password = "too short" // password wil be validated not only against IsString, but against MinLength as well -user.name = "not valid"; -user.welcome = "helo"; - -validate(user).then(errors => { - // ... -}); // it will return errors for email, title and text properties - -``` - -## Conditional validation - -The conditional validation decorator (`@ValidateIf`) can be used to ignore the validators on a property when the provided condition function returns false. The condition function takes the object being validated and must return a `boolean`. - -```typescript -import {ValidateIf, IsNotEmpty} from "class-validator"; - -export class Post { - otherProperty:string; - - @ValidateIf(o => o.otherProperty === "value") - @IsNotEmpty() - example:string; -} -``` - -In the example above, the validation rules applied to `example` won't be run unless the object's `otherProperty` is `"value"`. - -Note that when the condition is false all validation decorators are ignored, including `isDefined`. - -## Whitelisting - -Even if your object is an instance of a validation class it can contain additional properties that are not defined. -If you do not want to have such properties on your object, pass special flag to `validate` method: - -```typescript -import {validate} from "class-validator"; -// ... -validate(post, { whitelist: true }); -``` - -This will strip all properties that don't have any decorators. If no other decorator is suitable for your property, -you can use @Allow decorator: - -```typescript -import {validate, Allow, Min} from "class-validator"; - -export class Post { - - @Allow() - title: string; - - @Min(0) - views: number; - - nonWhitelistedProperty: number; -} - -let post = new Post(); -post.title = 'Hello world!'; -post.views = 420; - -post.nonWhitelistedProperty = 69; -(post as any).anotherNonWhitelistedProperty = "something"; - -validate(post).then(errors => { - // post.nonWhitelistedProperty is not defined - // (post as any).anotherNonWhitelistedProperty is not defined - ... -}); -```` - -If you would rather to have an error thrown when any non-whitelisted properties are present, pass another flag to -`validate` method: - -```typescript -import {validate} from "class-validator"; -// ... -validate(post, { whitelist: true, forbidNonWhitelisted: true }); -``` - -## Passing context to decorators - -It's possible to pass a custom object to decorators which will be accessible on the `ValidationError` instance of the property if validation failed. - -```ts -import { validate } from 'class-validator'; - -class MyClass { - @MinLength(32, { - message: "EIC code must be at least 32 charatcers", - context: { - errorCode: 1003, - developerNote: "The validated string must contain 32 or more characters." - } - }) - eicCode: string; -} - -const model = new MyClass(); - -validate(model).then(errors => { - //errors[0].contexts['minLength'].errorCode === 1003 -}); -``` - -## Skipping missing properties - -Sometimes you may want to skip validation of the properties that does not exist in the validating object. This is -usually desirable when you want to update some parts of the object, and want to validate only updated parts, -but skip everything else, e.g. skip missing properties. -In such situations you will need to pass a special flag to `validate` method: - -```typescript -import {validate} from "class-validator"; -// ... -validate(post, { skipMissingProperties: true }); -``` - -When skipping missing properties, sometimes you want not to skip all missing properties, some of them maybe required -for you, even if skipMissingProperties is set to true. For such cases you should use `@IsDefined()` decorator. -`@IsDefined()` is the only decorator that ignores `skipMissingProperties` option. - -## Validation groups - -In different situations you may want to use different validation schemas of the same object. - In such cases you can use validation groups. - -```typescript -import {validate, Min, Length} from "class-validator"; - -export class User { - - @Min(12, { - groups: ["registration"] - }) - age: number; - - @Length(2, 20, { - groups: ["registration", "admin"] - }) - name: string; -} - -let user = new User(); -user.age = 10; -user.name = "Alex"; - -validate(user, { - groups: ["registration"] -}); // this will not pass validation - -validate(user, { - groups: ["admin"] -}); // this will pass validation - -validate(user, { - groups: ["registration", "admin"] -}); // this will not pass validation - -validate(user, { - groups: [] -}); // this will not pass validation -``` - -There is also a special flag `always: true` in validation options that you can use. This flag says that this validation -must be applied always no matter which group is used. - -## Custom validation classes - -If you have custom validation logic you can create a *Constraint class*: - -1. First create a file, lets say `CustomTextLength.ts`, and define a new class: - - ```typescript - import {ValidatorConstraint, ValidatorConstraintInterface, ValidationArguments} from "class-validator"; - - @ValidatorConstraint({ name: "customText", async: false }) - export class CustomTextLength implements ValidatorConstraintInterface { - - validate(text: string, args: ValidationArguments) { - return text.length > 1 && text.length < 10; // for async validations you must return a Promise here - } - - defaultMessage(args: ValidationArguments) { // here you can provide default error message if validation failed - return "Text ($value) is too short or too long!"; - } - - } - ``` - - We marked our class with `@ValidatorConstraint` decorator. - You can also supply a validation constraint name - this name will be used as "error type" in ValidationError. - If you will not supply a constraint name - it will be auto-generated. - - Our class must implement `ValidatorConstraintInterface` interface and its `validate` method, - which defines validation logic. If validation succeeds, method returns true, otherwise false. - Custom validator can be asynchronous, if you want to perform validation after some asynchronous - operations, simply return a promise with boolean inside in `validate` method. - - Also we defined optional method `defaultMessage` which defines a default error message, - in the case that the decorator's implementation doesn't set an error message. - - -2. Then you can use your new validation constraint in your class: - - ```typescript - import {Validate} from "class-validator"; - import {CustomTextLength} from "./CustomTextLength"; - - export class Post { - - @Validate(CustomTextLength, { - message: "Title is too short or long!" - }) - title: string; - - } - ``` - - Here we set our newly created `CustomTextLength` validation constraint for `Post.title`. - -3. And use validator as usual: - - ```typescript - import {validate} from "class-validator"; - - validate(post).then(errors => { - // ... - }); - ``` - -You can also pass constraints to your validator, like this: - -```typescript -import {Validate} from "class-validator"; -import {CustomTextLength} from "./CustomTextLength"; - -export class Post { - - @Validate(CustomTextLength, [3, 20], { - message: "Wrong post title" - }) - title: string; - -} -``` - -And use them from `validationArguments` object: - -```typescript -import {ValidationArguments, ValidatorConstraint, ValidatorConstraintInterface} from "class-validator"; - -@ValidatorConstraint() -export class CustomTextLength implements ValidatorConstraintInterface { - - validate(text: string, validationArguments: ValidationArguments) { - return text.length > validationArguments.constraints[0] && text.length < validationArguments.constraints[1]; - } - -} -``` - -## Custom validation decorators - -You can also create a custom decorators. Its the most elegant way of using a custom validations. -Lets create a decorator called `@IsLongerThan`: - -1. Create a decorator itself: - - ```typescript - import {registerDecorator, ValidationOptions, ValidationArguments} from "class-validator"; - - export function IsLongerThan(property: string, validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - registerDecorator({ - name: "isLongerThan", - target: object.constructor, - propertyName: propertyName, - constraints: [property], - options: validationOptions, - validator: { - validate(value: any, args: ValidationArguments) { - const [relatedPropertyName] = args.constraints; - const relatedValue = (args.object as any)[relatedPropertyName]; - return typeof value === "string" && - typeof relatedValue === "string" && - value.length > relatedValue.length; // you can return a Promise here as well, if you want to make async validation - } - } - }); - }; - } - ``` - -2. Put it to use: - - ```typescript - import {IsLongerThan} from "./IsLongerThan"; - - export class Post { - - title: string; - - @IsLongerThan("title", { - /* you can also use additional validation options, like "groups" in your custom validation decorators. "each" is not supported */ - message: "Text must be longer than the title" - }) - text: string; - - } - ``` - -In your custom decorators you can also use `ValidationConstraint`. -Lets create another custom validation decorator called `IsUserAlreadyExist`: - -1. Create a ValidationConstraint and decorator: - - ```typescript - import {registerDecorator, ValidationOptions, ValidatorConstraint, ValidatorConstraintInterface, ValidationArguments} from "class-validator"; - - @ValidatorConstraint({ async: true }) - export class IsUserAlreadyExistConstraint implements ValidatorConstraintInterface { - - validate(userName: any, args: ValidationArguments) { - return UserRepository.findOneByName(userName).then(user => { - if (user) return false; - return true; - }); - } - - } - - export function IsUserAlreadyExist(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - registerDecorator({ - target: object.constructor, - propertyName: propertyName, - options: validationOptions, - constraints: [], - validator: IsUserAlreadyExistConstraint - }); - }; - } - ``` - - note that we marked our constraint that it will by async by adding `{ async: true }` in validation options. - -2. And put it to use: - - ```typescript - import {IsUserAlreadyExist} from "./IsUserAlreadyExist"; - - export class User { - - @IsUserAlreadyExist({ - message: "User $value already exists. Choose another name." - }) - name: string; - - } - ``` - -## Using service container - -Validator supports service container in the case if want to inject dependencies into your custom validator constraint -classes. Here is example how to integrate it with [typedi][2]: - -```typescript -import {Container} from "typedi"; -import {useContainer, Validator} from "class-validator"; - -// do this somewhere in the global application level: -useContainer(Container); -let validator = Container.get(Validator); - -// now everywhere you can inject Validator class which will go from the container -// also you can inject classes using constructor injection into your custom ValidatorConstraint-s -``` - -## Synchronous validation - -If you want to perform a simple non async validation you can use `validateSync` method instead of regular `validate` - method. It has the same arguments as `validate` method. But note, this method **ignores** all async validations - you have. - -## Manual validation - -There are several method exist in the Validator that allows to perform non-decorator based validation: - -```typescript -import {Validator} from "class-validator"; - -// Validation methods -const validator = new Validator(); - -// common validation methods -validator.isDefined(value); // Checks if value is defined ("!==undefined"). -validator.equals(value, comparison); // Checks if value matches ("===") the comparison. -validator.notEquals(value, comparison); // Checks if value does not match ("!==") the comparison. -validator.isEmpty(value); // Checks if given value is empty (=== '', === null, === undefined). -validator.isNotEmpty(value); // Checks if given value is not empty (!== '', !== null, !== undefined). -validator.isIn(value, possibleValues); // Checks if given value is in a array of allowed values. -validator.isNotIn(value, possibleValues); // Checks if given value not in a array of allowed values. - -// type validation methods -validator.isBoolean(value); // Checks if a given value is a real boolean. -validator.isDate(value); // Checks if a given value is a real date. -validator.isString(value); // Checks if a given value is a real string. -validator.isArray(value); // Checks if a given value is an array. -validator.isNumber(value, options); // Checks if a given value is a real number. -validator.isInt(value); // Checks if value is an integer. -validator.isEnum(value, entity); // Checks if value is valid for a certain enum entity. - -// number validation methods -validator.isDivisibleBy(value, num); // Checks if value is a number that's divisible by another. -validator.isPositive(value); // Checks if the value is a positive number. -validator.isNegative(value); // Checks if the value is a negative number. -validator.min(num, min); // Checks if the first number is greater than or equal to the second. -validator.max(num, max); // Checks if the first number is less than or equal to the second. - -// date validation methods -validator.minDate(date, minDate); // Checks if the value is a date that's after the specified date. -validator.maxDate(date, minDate); // Checks if the value is a date that's before the specified date. - -// string-type validation methods -validator.isBooleanString(str); // Checks if a string is a boolean. -validator.isNumberString(str); // Checks if the string is numeric. - -// string validation methods -validator.contains(str, seed); // Checks if the string contains the seed. -validator.notContains(str, seed); // Checks if the string does not contain the seed. -validator.isAlpha(str); // Checks if the string contains only letters (a-zA-Z). -validator.isAlphanumeric(str); // Checks if the string contains only letters and numbers. -validator.isAscii(str); // Checks if the string contains ASCII chars only. -validator.isBase64(str); // Checks if a string is base64 encoded. -validator.isByteLength(str, min, max); // Checks if the string's length (in bytes) falls in a range. -validator.isCreditCard(str); // Checks if the string is a credit card. -validator.isCurrency(str, options); // Checks if the string is a valid currency amount. -validator.isEmail(str, options); // Checks if the string is an email. -validator.isFQDN(str, options); // Checks if the string is a fully qualified domain name (e.g. domain.com). -validator.isFullWidth(str); // Checks if the string contains any full-width chars. -validator.isHalfWidth(str); // Checks if the string contains any half-width chars. -validator.isVariableWidth(str); // Checks if the string contains variable-width chars. -validator.isHexColor(str); // Checks if the string is a hexadecimal color. -validator.isHexadecimal(str); // Checks if the string is a hexadecimal number. -validator.isIP(str, version); // Checks if the string is an IP (version 4 or 6). -validator.isISBN(str, version); // Checks if the string is an ISBN (version 10 or 13). -validator.isISIN(str); // Checks if the string is an ISIN (stock/security identifier). -validator.isISO8601(str); // Checks if the string is a valid ISO 8601 date. -validator.isJSON(str); // Checks if the string is valid JSON (note: uses JSON.parse). -validator.isLowercase(str); // Checks if the string is lowercase. -validator.isMobilePhone(str, locale); // Checks if the string is a mobile phone number. -validator.isMongoId(str); // Checks if the string is a valid hex-encoded representation of a MongoDB ObjectId. -validator.isMultibyte(str); // Checks if the string contains one or more multibyte chars. -validator.isSurrogatePair(str); // Checks if the string contains any surrogate pairs chars. -validator.isURL(str, options); // Checks if the string is an url. -validator.isUUID(str, version); // Checks if the string is a UUID (version 3, 4 or 5). -validator.isUppercase(str); // Checks if the string is uppercase. -validator.length(str, min, max); // Checks if the string's length falls in a range. -validator.minLength(str, min); // Checks if the string's length is not less than given number. -validator.maxLength(str, max); // Checks if the string's length is not more than given number. -validator.matches(str, pattern, modifiers); // Checks if string matches the pattern. Either matches('foo', /foo/i) or matches('foo', 'foo', 'i'). -validator.isMilitaryTime(str); // Checks if the string is a valid representation of military time in the format HH:MM. - -// array validation methods -validator.arrayContains(array, values); // Checks if array contains all values from the given array of values. -validator.arrayNotContains(array, values); // Checks if array does not contain any of the given values. -validator.arrayNotEmpty(array); // Checks if given array is not empty. -validator.arrayMinSize(array, min); // Checks if array's length is at least `min` number. -validator.arrayMaxSize(array, max); // Checks if array's length is as most `max` number. -validator.arrayUnique(array); // Checks if all array's values are unique. Comparison for objects is reference-based. - -// object validation methods -validator.isInstance(value, target); // Checks value is an instance of the target. -``` - -## Validation decorators - -| Decorator | Description | -|-------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------| -| **Common validation decorators** | -| `@IsDefined(value: any)` | Checks if value is defined (!== undefined, !== null). This is the only decorator that ignores skipMissingProperties option. | -| `@IsOptional()` | Checks if given value is empty (=== null, === undefined) and if so, ignores all the validators on the property. | -| `@Equals(comparison: any)` | Checks if value equals ("===") comparison. | -| `@NotEquals(comparison: any)` | Checks if value not equal ("!==") comparison. | -| `@IsEmpty()` | Checks if given value is empty (=== '', === null, === undefined). | -| `@IsNotEmpty()` | Checks if given value is not empty (!== '', !== null, !== undefined). | -| `@IsIn(values: any[])` | Checks if value is in a array of allowed values. | -| `@IsNotIn(values: any[])` | Checks if value is not in a array of disallowed values. | -| **Type validation decorators** | -| `@IsBoolean()` | Checks if a value is a boolean. | -| `@IsDate()` | Checks if the value is a date. | -| `@IsString()` | Checks if the string is a string. | -| `@IsNumber(options: IsNumberOptions)` | Checks if the value is a number. | -| `@IsInt()` | Checks if the value is an integer number. | -| `@IsArray()` | Checks if the value is an array | -| `@IsEnum(entity: object)` | Checks if the value is an valid enum | -| **Number validation decorators** | -| `@IsDivisibleBy(num: number)` | Checks if the value is a number that's divisible by another. | -| `@IsPositive()` | Checks if the value is a positive number. | -| `@IsNegative()` | Checks if the value is a negative number. | -| `@Min(min: number)` | Checks if the given number is greater than or equal to given number. | -| `@Max(max: number)` | Checks if the given number is less than or equal to given number. | -| **Date validation decorators** | -| `@MinDate(date: Date)` | Checks if the value is a date that's after the specified date. | -| `@MaxDate(date: Date)` | Checks if the value is a date that's before the specified date. | | -| **String-type validation decorators** | -| `@IsBooleanString()` | Checks if a string is a boolean (e.g. is "true" or "false"). | -| `@IsDateString()` | Checks if a string is a complete representation of a date (e.g. "2017-06-07T14:34:08.700Z", "2017-06-07T14:34:08.700 or "2017-06-07T14:34:08+04:00"). | -| `@IsNumberString()` | Checks if a string is a number. | -| **String validation decorators** | -| `@Contains(seed: string)` | Checks if the string contains the seed. | -| `@NotContains(seed: string)` | Checks if the string not contains the seed. | -| `@IsAlpha()` | Checks if the string contains only letters (a-zA-Z). | -| `@IsAlphanumeric()` | Checks if the string contains only letters and numbers. | -| `@IsAscii()` | Checks if the string contains ASCII chars only. | -| `@IsBase64()` | Checks if a string is base64 encoded. | -| `@IsByteLength(min: number, max?: number)` | Checks if the string's length (in bytes) falls in a range. | -| `@IsCreditCard()` | Checks if the string is a credit card. | -| `@IsCurrency(options?: IsCurrencyOptions)` | Checks if the string is a valid currency amount. | -| `@IsEmail(options?: IsEmailOptions)` | Checks if the string is an email. | -| `@IsFQDN(options?: IsFQDNOptions)` | Checks if the string is a fully qualified domain name (e.g. domain.com). | -| `@IsFullWidth()` | Checks if the string contains any full-width chars. | -| `@IsHalfWidth()` | Checks if the string contains any half-width chars. | -| `@IsVariableWidth()` | Checks if the string contains a mixture of full and half-width chars. | -| `@IsHexColor()` | Checks if the string is a hexadecimal color. | -| `@IsHexadecimal()` | Checks if the string is a hexadecimal number. | -| `@IsIP(version?: "4"\|"6")` | Checks if the string is an IP (version 4 or 6). | -| `@IsISBN(version?: "10"\|"13")` | Checks if the string is an ISBN (version 10 or 13). | -| `@IsISIN()` | Checks if the string is an ISIN (stock/security identifier). | -| `@IsISO8601()` | Checks if the string is a valid ISO 8601 date. | -| `@IsJSON()` | Checks if the string is valid JSON. | -| `@IsLowercase()` | Checks if the string is lowercase. | -| `@IsMobilePhone(locale: string)` | Checks if the string is a mobile phone number. | -| `@IsMongoId()` | Checks if the string is a valid hex-encoded representation of a MongoDB ObjectId. | -| `@IsMultibyte()` | Checks if the string contains one or more multibyte chars. | -| `@IsNumberString()` | Checks if the string is numeric. | -| `@IsSurrogatePair()` | Checks if the string contains any surrogate pairs chars. | -| `@IsUrl(options?: IsURLOptions)` | Checks if the string is an url. | -| `@IsUUID(version?: "3"\|"4"\|"5")` | Checks if the string is a UUID (version 3, 4 or 5). | -| `@IsUppercase()` | Checks if the string is uppercase. | -| `@Length(min: number, max?: number)` | Checks if the string's length falls in a range. | -| `@MinLength(min: number)` | Checks if the string's length is not less than given number. | -| `@MaxLength(max: number)` | Checks if the string's length is not more than given number. | -| `@Matches(pattern: RegExp, modifiers?: string)` | Checks if string matches the pattern. Either matches('foo', /foo/i) or matches('foo', 'foo', 'i'). -| `@IsMilitaryTime()` | Checks if the string is a valid representation of military time in the format HH:MM. | -| **Array validation decorators** | -| `@ArrayContains(values: any[])` | Checks if array contains all values from the given array of values. | -| `@ArrayNotContains(values: any[])` | Checks if array does not contain any of the given values. | -| `@ArrayNotEmpty()` | Checks if given array is not empty. | -| `@ArrayMinSize(min: number)` | Checks if array's length is as minimal this number. | -| `@ArrayMaxSize(max: number)` | Checks if array's length is as maximal this number. | -| `@ArrayUnique()` | Checks if all array's values are unique. Comparison for objects is reference-based. | -| **Object validation decorators** | -| `@IsInstance(value: any)` | Checks if the property is an instance of the passed value. | - **Other decorators** | -| `@Allow()` | Prevent stripping off the property when no other constraint is specified for it. | - -## Defining validation schema without decorators - -You can define your validation schemas without decorators: - -* you can define it in the separate object -* you can define it in the `.json` file - -This feature maybe useful in the cases if: - -* are using es5/es6 and don't have decorators available -* you don't have a classes, and instead using interfaces -* you don't want to use model at all -* you want to have a validation schema separate of your model -* you want beautiful json-schema based validation models -* you simply hate decorators - -Here is an example of using it: - -1. Create a schema object: - - ```typescript - import {ValidationSchema} from "class-validator"; - export let UserValidationSchema: ValidationSchema = { // using interface here is not required, its just for type-safety - name: "myUserSchema", // this is required, and must be unique - properties: { - firstName: [{ - type: "minLength", // validation type. All validation types are listed in ValidationTypes class. - constraints: [2] - }, { - type: "maxLength", - constraints: [20] - }], - lastName: [{ - type: "minLength", - constraints: [2] - }, { - type: "maxLength", - constraints: [20] - }], - email: [{ - type: "isEmail" - }] - } - }; - ``` - - Same schema can be provided in `.json` file, depend on your wish. - -2. Register your schema: - - ```typescript - import {registerSchema} from "class-validator"; - import {UserValidationSchema} from "./UserValidationSchema"; - registerSchema(schema); // if schema is in .json file, then you can simply do registerSchema(require("path-to-schema.json")); - ``` - - Better to put this code in a global place, maybe when you bootstrap your application, for example in `app.ts`. - -3. Validate your object using validation schema: - - ```typescript - import {validate} from "class-validator"; - const user = { firstName: "Johny", secondName: "Cage", email: "johny@cage.com" }; - validate("myUserSchema", user).then(errors => { - if (errors.length > 0) { - console.log("Validation failed: ", errors); - } else { - console.log("Validation succeed."); - } - }); - ``` - - That's it. Here `"myUserSchema"` is the name of our validation schema. - `validate` method will perform validation based on this schema - -## Validating plain objects -Due to nature of the decorators, the validated object has to be instantiated using `new Class()` syntax. If you have your class defined using class-validator decorators and you want to validate plain JS object (literal object or returned by JSON.parse), you need to transform it to the class instance (e.g. using [class-transformer](https://github.com/pleerock/class-transformer)) or just use the [class-transformer-validator](https://github.com/19majkel94/class-transformer-validator) extension which can do that for you. - -## Samples - -Take a look on samples in [./sample](https://github.com/pleerock/class-validator/tree/master/sample) for more examples of -usages. - -## Extensions -There are several extensions that simplify class-validator integration with other modules: -- [class-validator integration](https://github.com/19majkel94/class-transformer-validator) with [class-transformer](https://github.com/pleerock/class-transformer) - -## Release notes - -See information about breaking changes and release notes [here][3]. - -[1]: https://github.com/chriso/validator.js -[2]: https://github.com/pleerock/typedi -[3]: CHANGELOG.md +# class-validator + +![Build Status](https://github.com/typestack/class-validator/workflows/CI/badge.svg) +[![codecov](https://codecov.io/gh/typestack/class-validator/branch/develop/graph/badge.svg)](https://codecov.io/gh/typestack/class-validator) +[![npm version](https://badge.fury.io/js/class-validator.svg)](https://badge.fury.io/js/class-validator) +[![install size](https://packagephobia.now.sh/badge?p=class-validator)](https://packagephobia.now.sh/result?p=class-validator) + +Allows use of decorator and non-decorator based validation. +Internally uses [validator.js][1] to perform validation. +Class-validator works on both browser and node.js platforms. + +## Table of Contents + +- [class-validator](#class-validator) + - [Table of Contents](#table-of-contents) + - [Installation](#installation) + - [Usage](#usage) + - [Passing options](#passing-options) + - [Validation errors](#validation-errors) + - [Validation messages](#validation-messages) + - [Validating arrays](#validating-arrays) + - [Validating sets](#validating-sets) + - [Validating maps](#validating-maps) + - [Validating nested objects](#validating-nested-objects) + - [Validating promises](#validating-promises) + - [Inheriting Validation decorators](#inheriting-validation-decorators) + - [Conditional validation](#conditional-validation) + - [Whitelisting](#whitelisting) + - [Passing context to decorators](#passing-context-to-decorators) + - [Skipping missing properties](#skipping-missing-properties) + - [Validation groups](#validation-groups) + - [Custom validation classes](#custom-validation-classes) + - [Custom validation decorators](#custom-validation-decorators) + - [Using service container](#using-service-container) + - [Synchronous validation](#synchronous-validation) + - [Manual validation](#manual-validation) + - [Validation decorators](#validation-decorators) + - [Defining validation schema without decorators](#defining-validation-schema-without-decorators) + - [Validating plain objects](#validating-plain-objects) + - [Samples](#samples) + - [Extensions](#extensions) + - [Release notes](#release-notes) + - [Contributing](#contributing) + +## Installation + +``` +npm install class-validator --save +``` + +> Note: Please use at least npm@6 when using class-validator. From npm@6 the dependency tree is flattened, which is required by `class-validator` to function properly. + +## Usage + +Create your class and put some validation decorators on the properties you want to validate: + +```typescript +import { + validate, + validateOrReject, + Contains, + IsInt, + Length, + IsEmail, + IsFQDN, + IsDate, + Min, + Max, +} from 'class-validator'; + +export class Post { + @Length(10, 20) + title: string; + + @Contains('hello') + text: string; + + @IsInt() + @Min(0) + @Max(10) + rating: number; + + @IsEmail() + email: string; + + @IsFQDN() + site: string; + + @IsDate() + createDate: Date; +} + +let post = new Post(); +post.title = 'Hello'; // should not pass +post.text = 'this is a great post about hell world'; // should not pass +post.rating = 11; // should not pass +post.email = 'google.com'; // should not pass +post.site = 'googlecom'; // should not pass + +validate(post).then(errors => { + // errors is an array of validation errors + if (errors.length > 0) { + console.log('validation failed. errors: ', errors); + } else { + console.log('validation succeed'); + } +}); + +validateOrReject(post).catch(errors => { + console.log('Promise rejected (validation failed). Errors: ', errors); +}); +// or +async function validateOrRejectExample(input) { + try { + await validateOrReject(input); + } catch (errors) { + console.log('Caught promise rejection (validation failed). Errors: ', errors); + } +} +``` + +### Passing options + +The `validate` function optionally expects a `ValidatorOptions` object as a second parameter: + +```ts +export interface ValidatorOptions { + skipMissingProperties?: boolean; + whitelist?: boolean; + forbidNonWhitelisted?: boolean; + groups?: string[]; + dismissDefaultMessages?: boolean; + validationError?: { + target?: boolean; + value?: boolean; + }; + + forbidUnknownValues?: boolean; + stopAtFirstError?: boolean; +} +``` + +> **IMPORTANT** +> The `forbidUnknownValues` value is set to `true` by default and **it is highly advised to keep the default**. +> Setting it to `false` will result unknown objects passing the validation! + +## Validation errors + +The `validate` method returns an array of `ValidationError` objects. Each `ValidationError` is: + +```typescript +{ + target: Object; // Object that was validated. + property: string; // Object's property that haven't pass validation. + value: any; // Value that haven't pass a validation. + constraints?: { // Constraints that failed validation with error messages. + [type: string]: string; + }; + children?: ValidationError[]; // Contains all nested validation errors of the property +} +``` + +In our case, when we validated a Post object, we have such an array of `ValidationError` objects: + +```typescript +[{ + target: /* post object */, + property: "title", + value: "Hello", + constraints: { + length: "$property must be longer than or equal to 10 characters" + } +}, { + target: /* post object */, + property: "text", + value: "this is a great post about hell world", + constraints: { + contains: "text must contain a hello string" + } +}, +// and other errors +] +``` + +If you don't want a `target` to be exposed in validation errors, there is a special option when you use validator: + +```typescript +validator.validate(post, { validationError: { target: false } }); +``` + +This is especially useful when you send errors back over http, and you most probably don't want to expose +the whole target object. + +## Validation messages + +You can specify validation message in the decorator options and that message will be returned in the `ValidationError` +returned by the `validate` method (in the case that validation for this field fails). + +```typescript +import { MinLength, MaxLength } from 'class-validator'; + +export class Post { + @MinLength(10, { + message: 'Title is too short', + }) + @MaxLength(50, { + message: 'Title is too long', + }) + title: string; +} +``` + +There are few special tokens you can use in your messages: + +- `$value` - the value that is being validated +- `$property` - name of the object's property being validated +- `$target` - name of the object's class being validated +- `$constraint1`, `$constraint2`, ... `$constraintN` - constraints defined by specific validation type + +Example of usage: + +```typescript +import { MinLength, MaxLength } from 'class-validator'; + +export class Post { + @MinLength(10, { + // here, $constraint1 will be replaced with "10", and $value with actual supplied value + message: 'Title is too short. Minimal length is $constraint1 characters, but actual is $value', + }) + @MaxLength(50, { + // here, $constraint1 will be replaced with "50", and $value with actual supplied value + message: 'Title is too long. Maximal length is $constraint1 characters, but actual is $value', + }) + title: string; +} +``` + +Also you can provide a function, that returns a message. This allows you to create more granular messages: + +```typescript +import { MinLength, MaxLength, ValidationArguments } from 'class-validator'; + +export class Post { + @MinLength(10, { + message: (args: ValidationArguments) => { + if (args.value.length === 1) { + return 'Too short, minimum length is 1 character'; + } else { + return 'Too short, minimum length is ' + args.constraints[0] + ' characters'; + } + }, + }) + title: string; +} +``` + +Message function accepts `ValidationArguments` which contains the following information: + +- `value` - the value that is being validated +- `constraints` - array of constraints defined by specific validation type +- `targetName` - name of the object's class being validated +- `object` - object that is being validated +- `property` - name of the object's property being validated + +## Validating arrays + +If your field is an array and you want to perform validation of each item in the array you must specify a +special `each: true` decorator option: + +```typescript +import { MinLength, MaxLength } from 'class-validator'; + +export class Post { + @MaxLength(20, { + each: true, + }) + tags: string[]; +} +``` + +This will validate each item in `post.tags` array. + +## Validating sets + +If your field is a set and you want to perform validation of each item in the set you must specify a +special `each: true` decorator option: + +```typescript +import { MinLength, MaxLength } from 'class-validator'; + +export class Post { + @MaxLength(20, { + each: true, + }) + tags: Set; +} +``` + +This will validate each item in `post.tags` set. + +## Validating maps + +If your field is a map and you want to perform validation of each item in the map you must specify a +special `each: true` decorator option: + +```typescript +import { MinLength, MaxLength } from 'class-validator'; + +export class Post { + @MaxLength(20, { + each: true, + }) + tags: Map; +} +``` + +This will validate each item in `post.tags` map. + +## Validating nested objects + +If your object contains nested objects and you want the validator to perform their validation too, then you need to +use the `@ValidateNested()` decorator: + +```typescript +import { ValidateNested } from 'class-validator'; + +export class Post { + @ValidateNested() + user: User; +} +``` + +Please note that nested object _must_ be an instance of a class, otherwise `@ValidateNested` won't know what class is target of validation. Check also [Validating plain objects](#validating-plain-objects). + +It also works with multi-dimensional array, like : + +```typescript +import { ValidateNested } from 'class-validator'; + +export class Plan2D { + @ValidateNested() + matrix: Point[][]; +} +``` + +## Validating promises + +If your object contains property with `Promise`-returned value that should be validated, then you need to use the `@ValidatePromise()` decorator: + +```typescript +import { ValidatePromise, Min } from 'class-validator'; + +export class Post { + @Min(0) + @ValidatePromise() + userId: Promise; +} +``` + +It also works great with `@ValidateNested` decorator: + +```typescript +import { ValidateNested, ValidatePromise } from 'class-validator'; + +export class Post { + @ValidateNested() + @ValidatePromise() + user: Promise; +} +``` + +## Inheriting Validation decorators + +When you define a subclass which extends from another one, the subclass will automatically inherit the parent's decorators. If a property is redefined in the descendant, class decorators will be applied on it from both its own class and the base class. + +```typescript +import { validate } from 'class-validator'; + +class BaseContent { + @IsEmail() + email: string; + + @IsString() + password: string; +} + +class User extends BaseContent { + @MinLength(10) + @MaxLength(20) + name: string; + + @Contains('hello') + welcome: string; + + @MinLength(20) + password: string; +} + +let user = new User(); + +user.email = 'invalid email'; // inherited property +user.password = 'too short'; // password wil be validated not only against IsString, but against MinLength as well +user.name = 'not valid'; +user.welcome = 'helo'; + +validate(user).then(errors => { + // ... +}); // it will return errors for email, title and text properties +``` + +## Conditional validation + +The conditional validation decorator (`@ValidateIf`) can be used to ignore the validators on a property when the provided condition function returns false. The condition function takes the object being validated and must return a `boolean`. + +```typescript +import { ValidateIf, IsNotEmpty } from 'class-validator'; + +export class Post { + otherProperty: string; + + @ValidateIf(o => o.otherProperty === 'value') + @IsNotEmpty() + example: string; +} +``` + +In the example above, the validation rules applied to `example` won't be run unless the object's `otherProperty` is `"value"`. + +Note that when the condition is false all validation decorators are ignored, including `isDefined`. + +## Whitelisting + +Even if your object is an instance of a validation class it can contain additional properties that are not defined. +If you do not want to have such properties on your object, pass special flag to `validate` method: + +```typescript +import { validate } from 'class-validator'; +// ... +validate(post, { whitelist: true }); +``` + +This will strip all properties that don't have any decorators. If no other decorator is suitable for your property, +you can use @Allow decorator: + +```typescript +import {validate, Allow, Min} from "class-validator"; + +export class Post { + + @Allow() + title: string; + + @Min(0) + views: number; + + nonWhitelistedProperty: number; +} + +let post = new Post(); +post.title = 'Hello world!'; +post.views = 420; + +post.nonWhitelistedProperty = 69; +(post as any).anotherNonWhitelistedProperty = "something"; + +validate(post).then(errors => { + // post.nonWhitelistedProperty is not defined + // (post as any).anotherNonWhitelistedProperty is not defined + ... +}); +``` + +If you would rather to have an error thrown when any non-whitelisted properties are present, pass another flag to +`validate` method: + +```typescript +import { validate } from 'class-validator'; +// ... +validate(post, { whitelist: true, forbidNonWhitelisted: true }); +``` + +## Passing context to decorators + +It's possible to pass a custom object to decorators which will be accessible on the `ValidationError` instance of the property if validation failed. + +```ts +import { validate } from 'class-validator'; + +class MyClass { + @MinLength(32, { + message: 'EIC code must be at least 32 characters', + context: { + errorCode: 1003, + developerNote: 'The validated string must contain 32 or more characters.', + }, + }) + eicCode: string; +} + +const model = new MyClass(); + +validate(model).then(errors => { + //errors[0].contexts['minLength'].errorCode === 1003 +}); +``` + +## Skipping missing properties + +Sometimes you may want to skip validation of the properties that do not exist in the validating object. This is +usually desirable when you want to update some parts of the object, and want to validate only updated parts, +but skip everything else, e.g. skip missing properties. +In such situations you will need to pass a special flag to `validate` method: + +```typescript +import { validate } from 'class-validator'; +// ... +validate(post, { skipMissingProperties: true }); +``` + +When skipping missing properties, sometimes you want not to skip all missing properties, some of them maybe required +for you, even if skipMissingProperties is set to true. For such cases you should use `@IsDefined()` decorator. +`@IsDefined()` is the only decorator that ignores `skipMissingProperties` option. + +## Validation groups + +In different situations you may want to use different validation schemas of the same object. +In such cases you can use validation groups. + +> **IMPORTANT** +> Calling a validation with a group combination that would not result in a validation (eg: non existent group name) +> will result in a unknown value error. When validating with groups the provided group combination should match at least one decorator. + +```typescript +import { validate, Min, Length } from 'class-validator'; + +export class User { + @Min(12, { + groups: ['registration'], + }) + age: number; + + @Length(2, 20, { + groups: ['registration', 'admin'], + }) + name: string; +} + +let user = new User(); +user.age = 10; +user.name = 'Alex'; + +validate(user, { + groups: ['registration'], +}); // this will not pass validation + +validate(user, { + groups: ['admin'], +}); // this will pass validation + +validate(user, { + groups: ['registration', 'admin'], +}); // this will not pass validation + +validate(user, { + groups: undefined, // the default +}); // this will not pass validation since all properties get validated regardless of their groups + +validate(user, { + groups: [], +}); // this will not pass validation, (equivalent to 'groups: undefined', see above) +``` + +There is also a special flag `always: true` in validation options that you can use. This flag says that this validation +must be applied always no matter which group is used. + +## Custom validation classes + +If you have custom validation logic you can create a _Constraint class_: + +1. First create a file, lets say `CustomTextLength.ts`, and define a new class: + + ```typescript + import { ValidatorConstraint, ValidatorConstraintInterface, ValidationArguments } from 'class-validator'; + + @ValidatorConstraint({ name: 'customText', async: false }) + export class CustomTextLength implements ValidatorConstraintInterface { + validate(text: string, args: ValidationArguments) { + return text.length > 1 && text.length < 10; // for async validations you must return a Promise here + } + + defaultMessage(args: ValidationArguments) { + // here you can provide default error message if validation failed + return 'Text ($value) is too short or too long!'; + } + } + ``` + + We marked our class with `@ValidatorConstraint` decorator. + You can also supply a validation constraint name - this name will be used as "error type" in ValidationError. + If you will not supply a constraint name - it will be auto-generated. + + Our class must implement `ValidatorConstraintInterface` interface and its `validate` method, + which defines validation logic. If validation succeeds, method returns true, otherwise false. + Custom validator can be asynchronous, if you want to perform validation after some asynchronous + operations, simply return a promise with boolean inside in `validate` method. + + Also we defined optional method `defaultMessage` which defines a default error message, + in the case that the decorator's implementation doesn't set an error message. + +2) Then you can use your new validation constraint in your class: + + ```typescript + import { Validate } from 'class-validator'; + import { CustomTextLength } from './CustomTextLength'; + + export class Post { + @Validate(CustomTextLength, { + message: 'Title is too short or long!', + }) + title: string; + } + ``` + + Here we set our newly created `CustomTextLength` validation constraint for `Post.title`. + +3) And use validator as usual: + + ```typescript + import { validate } from 'class-validator'; + + validate(post).then(errors => { + // ... + }); + ``` + +You can also pass constraints to your validator, like this: + +```typescript +import { Validate } from 'class-validator'; +import { CustomTextLength } from './CustomTextLength'; + +export class Post { + @Validate(CustomTextLength, [3, 20], { + message: 'Wrong post title', + }) + title: string; +} +``` + +And use them from `validationArguments` object: + +```typescript +import { ValidationArguments, ValidatorConstraint, ValidatorConstraintInterface } from 'class-validator'; + +@ValidatorConstraint() +export class CustomTextLength implements ValidatorConstraintInterface { + validate(text: string, validationArguments: ValidationArguments) { + return text.length > validationArguments.constraints[0] && text.length < validationArguments.constraints[1]; + } +} +``` + +## Custom validation decorators + +You can also create a custom decorators. Its the most elegant way of using a custom validations. +Lets create a decorator called `@IsLongerThan`: + +1. Create a decorator itself: + + ```typescript + import { registerDecorator, ValidationOptions, ValidationArguments } from 'class-validator'; + + export function IsLongerThan(property: string, validationOptions?: ValidationOptions) { + return function (object: Object, propertyName: string) { + registerDecorator({ + name: 'isLongerThan', + target: object.constructor, + propertyName: propertyName, + constraints: [property], + options: validationOptions, + validator: { + validate(value: any, args: ValidationArguments) { + const [relatedPropertyName] = args.constraints; + const relatedValue = (args.object as any)[relatedPropertyName]; + return typeof value === 'string' && typeof relatedValue === 'string' && value.length > relatedValue.length; // you can return a Promise here as well, if you want to make async validation + }, + }, + }); + }; + } + ``` + +2. Put it to use: + + ```typescript + import { IsLongerThan } from './IsLongerThan'; + + export class Post { + title: string; + + @IsLongerThan('title', { + /* you can also use additional validation options, like "groups" in your custom validation decorators. "each" is not supported */ + message: 'Text must be longer than the title', + }) + text: string; + } + ``` + +In your custom decorators you can also use `ValidationConstraint`. +Lets create another custom validation decorator called `IsUserAlreadyExist`: + +1. Create a ValidationConstraint and decorator: + + ```typescript + import { + registerDecorator, + ValidationOptions, + ValidatorConstraint, + ValidatorConstraintInterface, + ValidationArguments, + } from 'class-validator'; + + @ValidatorConstraint({ async: true }) + export class IsUserAlreadyExistConstraint implements ValidatorConstraintInterface { + validate(userName: any, args: ValidationArguments) { + return UserRepository.findOneByName(userName).then(user => { + if (user) return false; + return true; + }); + } + } + + export function IsUserAlreadyExist(validationOptions?: ValidationOptions) { + return function (object: Object, propertyName: string) { + registerDecorator({ + target: object.constructor, + propertyName: propertyName, + options: validationOptions, + constraints: [], + validator: IsUserAlreadyExistConstraint, + }); + }; + } + ``` + + note that we marked our constraint that it will by async by adding `{ async: true }` in validation options. + +2. And put it to use: + + ```typescript + import { IsUserAlreadyExist } from './IsUserAlreadyExist'; + + export class User { + @IsUserAlreadyExist({ + message: 'User $value already exists. Choose another name.', + }) + name: string; + } + ``` + +## Using service container + +Validator supports service container in the case if want to inject dependencies into your custom validator constraint +classes. Here is example how to integrate it with [typedi][2]: + +```typescript +import { Container } from 'typedi'; +import { useContainer, Validator } from 'class-validator'; + +// do this somewhere in the global application level: +useContainer(Container); +let validator = Container.get(Validator); + +// now everywhere you can inject Validator class which will go from the container +// also you can inject classes using constructor injection into your custom ValidatorConstraint-s +``` + +## Synchronous validation + +If you want to perform a simple non async validation you can use `validateSync` method instead of regular `validate` +method. It has the same arguments as `validate` method. But note, this method **ignores** all async validations +you have. + +## Manual validation + +There are several method exist in the Validator that allows to perform non-decorator based validation: + +```typescript +import { isEmpty, isBoolean } from 'class-validator'; + +isEmpty(value); +isBoolean(value); +``` + +## Validation decorators + + + +| Decorator | Description | +| ------------------------------------------------| ----------- | +| **Common validation decorators** | | +| `@IsDefined(value: any)` | Checks if value is defined (!== undefined, !== null). This is the only decorator that ignores skipMissingProperties option. | +| `@IsOptional()` | Checks if given value is empty (=== null, === undefined) and if so, ignores all the validators on the property. | +| `@Equals(comparison: any)` | Checks if value equals ("===") comparison. | +| `@NotEquals(comparison: any)` | Checks if value not equal ("!==") comparison. | +| `@IsEmpty()` | Checks if given value is empty (=== '', === null, === undefined). | +| `@IsNotEmpty()` | Checks if given value is not empty (!== '', !== null, !== undefined). | +| `@IsIn(values: any[])` | Checks if value is in an array of allowed values. | +| `@IsNotIn(values: any[])` | Checks if value is not in an array of disallowed values. | +| **Type validation decorators** | | +| `@IsBoolean()` | Checks if a value is a boolean. | +| `@IsDate()` | Checks if the value is a date. | +| `@IsString()` | Checks if the value is a string. | +| `@IsNumber(options: IsNumberOptions)` | Checks if the value is a number. | +| `@IsInt()` | Checks if the value is an integer number. | +| `@IsArray()` | Checks if the value is an array | +| `@IsEnum(entity: object)` | Checks if the value is a valid enum | +| **Number validation decorators** | +| `@IsDivisibleBy(num: number)` | Checks if the value is a number that's divisible by another. | +| `@IsPositive()` | Checks if the value is a positive number greater than zero. | +| `@IsNegative()` | Checks if the value is a negative number smaller than zero. | +| `@Min(min: number)` | Checks if the given number is greater than or equal to given number. | +| `@Max(max: number)` | Checks if the given number is less than or equal to given number. | +| **Date validation decorators** | +| `@MinDate(date: Date | (() => Date))` | Checks if the value is a date that's after the specified date. | +| `@MaxDate(date: Date | (() => Date))` | Checks if the value is a date that's before the specified date. | +| **String-type validation decorators** | | +| `@IsBooleanString()` | Checks if a string is a boolean (e.g. is "true" or "false" or "1", "0"). | +| `@IsDateString()` | Alias for `@IsISO8601()`. | +| `@IsNumberString(options?: IsNumericOptions)` | Checks if a string is a number. | +| **String validation decorators** | | +| `@Contains(seed: string)` | Checks if the string contains the seed. | +| `@NotContains(seed: string)` | Checks if the string not contains the seed. | +| `@IsAlpha()` | Checks if the string contains only letters (a-zA-Z). | +| `@IsAlphanumeric()` | Checks if the string contains only letters and numbers. | +| `@IsDecimal(options?: IsDecimalOptions)` | Checks if the string is a valid decimal value. Default IsDecimalOptions are `force_decimal=False`, `decimal_digits: '1,'`, `locale: 'en-US'` | +| `@IsAscii()` | Checks if the string contains ASCII chars only. | +| `@IsBase32()` | Checks if a string is base32 encoded. | +| `@IsBase58()` | Checks if a string is base58 encoded. | +| `@IsBase64()` | Checks if a string is base64 encoded. | +| `@IsIBAN()` | Checks if a string is a IBAN (International Bank Account Number). | +| `@IsBIC()` | Checks if a string is a BIC (Bank Identification Code) or SWIFT code. | +| `@IsByteLength(min: number, max?: number)` | Checks if the string's length (in bytes) falls in a range. | +| `@IsCreditCard()` | Checks if the string is a credit card. | +| `@IsCurrency(options?: IsCurrencyOptions)` | Checks if the string is a valid currency amount. | +| `@IsISO4217CurrencyCode()` | Checks if the string is an ISO 4217 currency code. | +| `@IsEthereumAddress()` | Checks if the string is an Ethereum address using basic regex. Does not validate address checksums. | +| `@IsBtcAddress()` | Checks if the string is a valid BTC address. | +| `@IsDataURI()` | Checks if the string is a data uri format. | +| `@IsEmail(options?: IsEmailOptions)` | Checks if the string is an email.| +| `@IsFQDN(options?: IsFQDNOptions)` | Checks if the string is a fully qualified domain name (e.g. domain.com). | +| `@IsFullWidth()` | Checks if the string contains any full-width chars. | +| `@IsHalfWidth()` | Checks if the string contains any half-width chars. | +| `@IsVariableWidth()` | Checks if the string contains a mixture of full and half-width chars. | +| `@IsHexColor()` | Checks if the string is a hexadecimal color. | +| `@IsHSL()` | Checks if the string is an HSL color based on [CSS Colors Level 4 specification](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value). | +| `@IsRgbColor(options?: IsRgbOptions)` | Checks if the string is a rgb or rgba color. | +| `@IsIdentityCard(locale?: string)` | Checks if the string is a valid identity card code. | +| `@IsPassportNumber(countryCode?: string)` | Checks if the string is a valid passport number relative to a specific country code. | +| `@IsPostalCode(locale?: string)` | Checks if the string is a postal code. | +| `@IsHexadecimal()` | Checks if the string is a hexadecimal number. | +| `@IsOctal()` | Checks if the string is a octal number. | +| `@IsMACAddress(options?: IsMACAddressOptions)` | Checks if the string is a MAC Address. | +| `@IsIP(version?: "4"\|"6")` | Checks if the string is an IP (version 4 or 6). | +| `@IsPort()` | Checks if the string is a valid port number. | +| `@IsISBN(version?: "10"\|"13")` | Checks if the string is an ISBN (version 10 or 13). | +| `@IsEAN()` | Checks if the string is an if the string is an EAN (European Article Number). | +| `@IsISIN()` | Checks if the string is an ISIN (stock/security identifier). | +| `@IsISO8601(options?: IsISO8601Options)` | Checks if the string is a valid ISO 8601 date format. Use the option strict = true for additional checks for a valid date. | +| `@IsJSON()` | Checks if the string is valid JSON. | +| `@IsJWT()` | Checks if the string is valid JWT. | +| `@IsObject()` | Checks if the object is valid Object (null, functions, arrays will return false). | +| `@IsNotEmptyObject()` | Checks if the object is not empty. | +| `@IsLowercase()` | Checks if the string is lowercase. | +| `@IsLatLong()` | Checks if the string is a valid latitude-longitude coordinate in the format lat, long. | +| `@IsLatitude()` | Checks if the string or number is a valid latitude coordinate. | +| `@IsLongitude()` | Checks if the string or number is a valid longitude coordinate. | +| `@IsMobilePhone(locale: string)` | Checks if the string is a mobile phone number. | +| `@IsISO31661Alpha2()` | Checks if the string is a valid ISO 3166-1 alpha-2 officially assigned country code. | +| `@IsISO31661Alpha3()` | Checks if the string is a valid ISO 3166-1 alpha-3 officially assigned country code. | +| `@IsLocale()` | Checks if the string is a locale. | +| `@IsPhoneNumber(region: string)` | Checks if the string is a valid phone number using libphonenumber-js. | +| `@IsMongoId()` | Checks if the string is a valid hex-encoded representation of a MongoDB ObjectId. | +| `@IsMultibyte()` | Checks if the string contains one or more multibyte chars. | +| `@IsNumberString(options?: IsNumericOptions)` | Checks if the string is numeric. | +| `@IsSurrogatePair()` | Checks if the string contains any surrogate pairs chars. | +| `@IsTaxId()` | Checks if the string is a valid tax ID. Default locale is `en-US`. +| `@IsUrl(options?: IsURLOptions)` | Checks if the string is a URL. | +| `@IsMagnetURI()` | Checks if the string is a [magnet uri format](https://en.wikipedia.org/wiki/Magnet_URI_scheme). | +| `@IsUUID(version?: "3"\|"4"\|"5"\|"all")` | Checks if the string is a UUID (version 3, 4, 5 or all ). | +| `@IsFirebasePushId()` | Checks if the string is a [Firebase Push ID](https://firebase.googleblog.com/2015/02/the-2120-ways-to-ensure-unique_68.html) | +| `@IsUppercase()` | Checks if the string is uppercase. | +| `@Length(min: number, max?: number)` | Checks if the string's length falls in a range. | +| `@MinLength(min: number)` | Checks if the string's length is not less than given number. | +| `@MaxLength(max: number)` | Checks if the string's length is not more than given number. | +| `@Matches(pattern: RegExp, modifiers?: string)` | Checks if string matches the pattern. Either matches('foo', /foo/i) or matches('foo', 'foo', 'i'). | +| `@IsMilitaryTime()` | Checks if the string is a valid representation of military time in the format HH:MM. | +| `@IsTimeZone()` | Checks if the string represents a valid IANA time-zone. | +| `@IsHash(algorithm: string)` | Checks if the string is a hash The following types are supported:`md4`, `md5`, `sha1`, `sha256`, `sha384`, `sha512`, `ripemd128`, `ripemd160`, `tiger128`, `tiger160`, `tiger192`, `crc32`, `crc32b`. | +| `@IsMimeType()` | Checks if the string matches to a valid [MIME type](https://en.wikipedia.org/wiki/Media_type) format | +| `@IsSemVer()` | Checks if the string is a Semantic Versioning Specification (SemVer). | +| `@IsISSN(options?: IsISSNOptions)` | Checks if the string is a ISSN. | +| `@IsISRC()` | Checks if the string is a [ISRC](https://en.wikipedia.org/wiki/International_Standard_Recording_Code). | +| `@IsRFC3339()` | Checks if the string is a valid [RFC 3339](https://tools.ietf.org/html/rfc3339) date. | +| `@IsStrongPassword(options?: IsStrongPasswordOptions)` | Checks if the string is a strong password. | +| **Array validation decorators** | | +| `@ArrayContains(values: any[])` | Checks if array contains all values from the given array of values. | +| `@ArrayNotContains(values: any[])` | Checks if array does not contain any of the given values. | +| `@ArrayNotEmpty()` | Checks if given array is not empty. | +| `@ArrayMinSize(min: number)` | Checks if the array's length is greater than or equal to the specified number. | +| `@ArrayMaxSize(max: number)` | Checks if the array's length is less or equal to the specified number. | +| `@ArrayUnique(identifier?: (o) => any)` | Checks if all array's values are unique. Comparison for objects is reference-based. Optional function can be speciefied which return value will be used for the comparsion. | +| **Object validation decorators** | +| `@IsInstance(value: any)` | Checks if the property is an instance of the passed value. | +| **Other decorators** | | +| `@Allow()` | Prevent stripping off the property when no other constraint is specified for it. | + +## Defining validation schema without decorators + +Schema-based validation without decorators is no longer supported by `class-validator`. This feature was broken in version 0.12 and it will not be fixed. If you are interested in schema-based validation, you can find several such frameworks in [the zod readme's comparison section](https://github.com/colinhacks/zod#comparison). + +## Validating plain objects + +Due to nature of the decorators, the validated object has to be instantiated using `new Class()` syntax. If you have your class defined using class-validator decorators and you want to validate plain JS object (literal object or returned by JSON.parse), you need to transform it to the class instance via using [class-transformer](https://github.com/pleerock/class-transformer)). + +## Samples + +Take a look on samples in [./sample](https://github.com/pleerock/class-validator/tree/master/sample) for more examples of +usages. + +## Extensions + +There are several extensions that simplify class-validator integration with other modules: + +- [class-validator integration](https://github.com/19majkel94/class-transformer-validator) with [class-transformer](https://github.com/pleerock/class-transformer) +- [class-validator-rule](https://github.com/yantrab/class-validator-rule) +- [ngx-dynamic-form-builder](https://github.com/EndyKaufman/ngx-dynamic-form-builder) +- [abarghoud/ngx-reactive-form-class-validator](https://github.com/abarghoud/ngx-reactive-form-class-validator) + +## Release notes + +See information about breaking changes and release notes [here][3]. + +[1]: https://github.com/chriso/validator.js +[2]: https://github.com/pleerock/typedi +[3]: CHANGELOG.md + +## Contributing + +For information about how to contribute to this project, see [TypeStack's general contribution guide](https://github.com/typestack/.github/blob/master/CONTRIBUTING.md). diff --git a/codecov.yml b/codecov.yml index db2472009c..1e7f6e0cdf 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1 +1,14 @@ +coverage: + range: 70..100 + round: down + precision: 2 + status: + project: + default: + threshold: 0% + paths: + - src/**/*.ts comment: off +ignore: + - testing/**/*.ts + - src/**/*.interface.ts diff --git a/docs/README.md b/docs/README.md index 72ff2e0f01..28324125ba 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,38 +1,38 @@ # Table of Contents -* [Read Me](../README.md) -* Getting Started - * [Installation](introduction/installation.md) - * [Usage with Typescript](introduction/usage-with-typescript.md) - * [Usage with Javascript](introduction/usage-with-javascript.md) - * [Dependency Injection](introduction/usage-with-di.md) - * [Core Principes](introduction/core-principes.md) -* [Basic Usage](basics/README.md) - * [Validating objects](basics/validating-objects.md) - * [Validating arrays](basics/validating-arrays.md) - * [Validating nested objects](basics/validating-nested-objects.md) -* [Advanced Usage](advanced/README.md) - * [Conditional Validation](advanced/conditional-validation.md) - * [Validation Groups](advanced/validation-groups.md) - * [Inheritance](advanced/inheritance.md) - * [Custom Validation Decorators](advanced/validations-decoratos.md) - * [Custom Validation Classes](advanced/validation-classes.md) -* [Decorators Reference](reference/decoratos.md) - * [Common Decorators](reference/common-decoratos.md) - * [Number Decorators](reference/number-decoratos.md) - * [String Decorators](reference/string-decoratos.md) - * [Date Decorators](reference/date-decoratos.md) - * [Array Decorators](reference/array-decoratos.md) -* [Recipes](recipes/README.md) - * [Simple Validations](https://stackblitz.com/edit/class-transformer-simple-validations) - * [Nested Objects](https://stackblitz.com/edit/class-transformer-nested-objects) - * [Using Groups](https://stackblitz.com/edit/class-transformer-using-groups) - * [Custom Validators](https://stackblitz.com/edit/class-transformer-custom-validator) - * [Custom Decorators](https://stackblitz.com/edit/class-transformer-custom-decorators) - * [Using Schemas](https://stackblitz.com/edit/class-transformer-schemas) - * [Inheritance](https://stackblitz.com/edit/class-transformer-inheritance) -* [API Reference](api/README.md) - * [validate](api/validate.md) - * [ValidatorOptions ](api/ValidatorOptions.md) - * [ValidationError ](api/ValidationError.md) -* [Change Log](../CHANGELOG.md) \ No newline at end of file +- [Read Me](../README.md) +- Getting Started + - [Installation](introduction/installation.md) + - [Usage with Typescript](introduction/usage-with-typescript.md) + - [Usage with Javascript](introduction/usage-with-javascript.md) + - [Dependency Injection](introduction/usage-with-di.md) + - [Core Principes](introduction/core-principes.md) +- [Basic Usage](basics/README.md) + - [Validating objects](basics/validating-objects.md) + - [Validating arrays](basics/validating-arrays.md) + - [Validating nested objects](basics/validating-nested-objects.md) +- [Advanced Usage](advanced/README.md) + - [Conditional Validation](advanced/conditional-validation.md) + - [Validation Groups](advanced/validation-groups.md) + - [Inheritance](advanced/inheritance.md) + - [Custom Validation Decorators](advanced/validations-decoratos.md) + - [Custom Validation Classes](advanced/validation-classes.md) +- [Decorators Reference](reference/decoratos.md) + - [Common Decorators](reference/common-decoratos.md) + - [Number Decorators](reference/number-decoratos.md) + - [String Decorators](reference/string-decoratos.md) + - [Date Decorators](reference/date-decoratos.md) + - [Array Decorators](reference/array-decoratos.md) +- [Recipes](recipes/README.md) + - [Simple Validations](https://stackblitz.com/edit/class-transformer-simple-validations) + - [Nested Objects](https://stackblitz.com/edit/class-transformer-nested-objects) + - [Using Groups](https://stackblitz.com/edit/class-transformer-using-groups) + - [Custom Validators](https://stackblitz.com/edit/class-transformer-custom-validator) + - [Custom Decorators](https://stackblitz.com/edit/class-transformer-custom-decorators) + - [Using Schemas](https://stackblitz.com/edit/class-transformer-schemas) + - [Inheritance](https://stackblitz.com/edit/class-transformer-inheritance) +- [API Reference](api/README.md) + - [validate](api/validate.md) + - [ValidatorOptions ](api/ValidatorOptions.md) + - [ValidationError ](api/ValidationError.md) +- [Change Log](../CHANGELOG.md) diff --git a/docs/basics/validating-objects.md b/docs/basics/validating-objects.md index f7215bdbc3..ba93e25166 100644 --- a/docs/basics/validating-objects.md +++ b/docs/basics/validating-objects.md @@ -1,10 +1,19 @@ # Validating objects ```ts -import { validate, IsString, IsInt, IsDate, MaxLength, Min, Max} from "class-validator"; +import { + validate, + validateOrReject, + IsString, + IsInt, + IsDate, + MaxLength, + Min, + Max, + ValidationError, +} from 'class-validator'; export class Book { - @IsString() @MaxLength(255) title: string; @@ -20,21 +29,33 @@ export class Book { @IsDate() publishDate: Date; - } const book = new Book(); -post.title = 'Don Quixote'; -post.author = 'Miguel De Cervantes'; -post.rating = 11; -post.publishDate = 1615; +book.title = 'Don Quixote'; +book.author = 'Miguel De Cervantes'; +book.rating = 11; +book.publishDate = new Date(); -validate(book).then(errors => { - // errors is an array of ValidationErrors +validate(book).then((errors: ValidationError[]) => { if (errors.length > 0) { - console.warn("validation failed. errors: ", errors); + console.warn('validate() - Validation failed. Errors: ', errors); } }); + +validateOrReject(book).catch((errors: ValidationError[]) => { + console.warn('validateOrReject() - Validation failed. Errors: ', errors); +}); + +awaitExample(); + +async function awaitExample() { + try { + await validateOrReject(book); + } catch (errors) { + console.warn('Async validateOrReject() - Validation failed. Errors: ', errors); + } +} ``` -Run this example on [Stackblitz](https://stackblitz.com/edit/class-validator-simple-example) \ No newline at end of file +Run this example on [Stackblitz](https://stackblitz.com/edit/class-validator-simple-example-u9h1ve?file=index.ts) diff --git a/docs/introduction/installation.md b/docs/introduction/installation.md index 6b0d692c29..0667e60daa 100644 --- a/docs/introduction/installation.md +++ b/docs/introduction/installation.md @@ -18,4 +18,4 @@ You can install the next version of `class-validator` via npm install --save class-validator@next ``` -> Note: The next version can break anytime without notice. Do not use this in production. \ No newline at end of file +> Note: The next version can break anytime without notice. Do not use this in production. diff --git a/gulpfile.ts b/gulpfile.ts deleted file mode 100644 index 2bd0f3e078..0000000000 --- a/gulpfile.ts +++ /dev/null @@ -1,210 +0,0 @@ -import {Gulpclass, Task, SequenceTask, MergedTask} from "gulpclass"; -import * as gulp from "gulp"; - -const del = require("del"); -const shell = require("gulp-shell"); -const replace = require("gulp-replace"); -const mocha = require("gulp-mocha"); -const chai = require("chai"); -const tslint = require("gulp-tslint"); -const stylish = require("tslint-stylish"); -const ts = require("gulp-typescript"); -const sourcemaps = require("gulp-sourcemaps"); -const istanbul = require("gulp-istanbul"); -const remapIstanbul = require("remap-istanbul/lib/gulpRemapIstanbul"); - -@Gulpclass() -export class Gulpfile { - - // ------------------------------------------------------------------------- - // General tasks - // ------------------------------------------------------------------------- - - /** - * Cleans build folder. - */ - @Task() - clean(cb: Function) { - return del([ - "build/**", - "!build", - "!build/package", - "!build/package/node_modules", - "!build/package/node_modules/**" - ], cb); - } - - /** - * Runs typescript files compilation. - */ - @Task() - compile() { - return gulp.src("*.ts", { read: false }) - .pipe(shell(["tsc"])); - } - - // ------------------------------------------------------------------------- - // Packaging and Publishing tasks - // ------------------------------------------------------------------------- - - /** - * Publishes a package to npm from ./build/package directory. - */ - @Task() - npmPublish() { - return gulp.src("*.js", { read: false }) - .pipe(shell([ - "cd ./build/package && npm publish" - ])); - } - - /** - * Copies all sources to the package directory. - */ - @MergedTask() - packageCompile() { - const tsProject = ts.createProject("tsconfig.json"); - const tsResult = gulp.src(["./src/**/*.ts"]) - .pipe(sourcemaps.init()) - .pipe(tsProject()); - - return [ - tsResult.dts.pipe(gulp.dest("./build/package")), - tsResult.js - .pipe(sourcemaps.write(".", { sourceRoot: "", includeContent: true })) - .pipe(gulp.dest("./build/package")) - ]; - } - - /** - * Moves all compiled files to the final package directory. - */ - @Task() - packageMoveCompiledFiles() { - return gulp.src("./build/package/src/**/*") - .pipe(gulp.dest("./build/package")); - } - - /** - * Moves all compiled files to the final package directory. - */ - @Task() - packageClearCompileDirectory(cb: Function) { - return del([ - "build/package/src/**" - ], cb); - } - - /** - * Change the "private" state of the packaged package.json file to public. - */ - @Task() - packagePreparePackageFile() { - return gulp.src("./package.json") - .pipe(replace("\"private\": true,", "\"private\": false,")) - .pipe(gulp.dest("./build/package")); - } - - /** - * This task will replace all typescript code blocks in the README (since npm does not support typescript syntax - * highlighting) and copy this README file into the package folder. - */ - @Task() - packageReadmeFile() { - return gulp.src("./README.md") - .pipe(replace(/```typescript([\s\S]*?)```/g, "```javascript$1```")) - .pipe(gulp.dest("./build/package")); - } - - /** - * Creates a package that can be published to npm. - */ - @SequenceTask() - package() { - return [ - "clean", - "packageCompile", - "packageMoveCompiledFiles", - "packageClearCompileDirectory", - ["packagePreparePackageFile", "packageReadmeFile"] - ]; - } - - /** - * Creates a package and publishes it to npm. - */ - @SequenceTask() - publish() { - return ["package", "npmPublish"]; - } - - // ------------------------------------------------------------------------- - // Run tests tasks - // ------------------------------------------------------------------------- - - /** - * Runs ts linting to validate source code. - */ - @Task() - tslint() { - return gulp.src(["./src/**/*.ts", "./test/**/*.ts", "./sample/**/*.ts"]) - .pipe(tslint()) - .pipe(tslint.report(stylish, { - emitError: true, - sort: true, - bell: true - })); - } - - /** - * Runs unit-tests. - */ - @Task() - unit() { - chai.should(); - chai.use(require("sinon-chai")); - chai.use(require("chai-as-promised")); - return gulp.src("./build/compiled/test/**/*.js") - .pipe(mocha()); - } - - /** - * Runs before test coverage, required step to perform a test coverage. - */ - @Task() - coveragePre() { - return gulp.src(["./build/compiled/src/**/*.js"]) - .pipe(istanbul()) - .pipe(istanbul.hookRequire()); - } - - /** - * Runs post coverage operations. - */ - @Task("coveragePost", ["coveragePre"]) - coveragePost() { - chai.should(); - chai.use(require("sinon-chai")); - chai.use(require("chai-as-promised")); - - return gulp.src(["./build/compiled/test/**/*.js"]) - .pipe(mocha()) - .pipe(istanbul.writeReports()); - } - - @Task() - coverageRemap() { - return gulp.src("./coverage/coverage-final.json") - .pipe(remapIstanbul()) - .pipe(gulp.dest("./coverage")); - } - - /** - * Compiles the code and runs tests. - */ - @SequenceTask() - tests() { - return ["compile", "tslint", "coveragePost", "coverageRemap"]; - } - -} diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000000..16504e0128 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,10 @@ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + collectCoverageFrom: ['src/**/*.ts', '!src/**/index.ts', '!src/**/*.interface.ts'], + globals: { + 'ts-jest': { + tsconfig: 'tsconfig.spec.json', + }, + }, +}; diff --git a/package-lock.json b/package-lock.json index 4f40ca38ce..684e4cb728 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5033 +1,10427 @@ { "name": "class-validator", - "version": "0.9.1", - "lockfileVersion": 1, + "version": "0.14.0", + "lockfileVersion": 2, "requires": true, - "dependencies": { - "@gulp-sourcemaps/identity-map": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/identity-map/-/identity-map-1.0.2.tgz", - "integrity": "sha512-ciiioYMLdo16ShmfHBXJBOFm3xPC4AuwO4xeRpFeHz7WK9PYsWCmigagG2XyzZpubK4a3qNKoUBDhbzHfa50LQ==", + "packages": { + "": { + "name": "class-validator", + "version": "0.14.0", + "license": "MIT", + "dependencies": { + "@types/validator": "^13.7.10", + "libphonenumber-js": "^1.10.14", + "validator": "^13.7.0" + }, + "devDependencies": { + "@rollup/plugin-commonjs": "^23.0.4", + "@rollup/plugin-node-resolve": "^15.0.1", + "@types/jest": "^29.2.4", + "@types/node": "^18.11.12", + "@typescript-eslint/eslint-plugin": "^5.46.0", + "@typescript-eslint/parser": "^5.46.0", + "eslint": "^8.29.0", + "eslint-config-prettier": "^8.5.0", + "eslint-plugin-jest": "^27.1.6", + "husky": "^4.3.8", + "jest": "^29.3.1", + "lint-staged": "^13.1.0", + "prettier": "^2.8.1", + "reflect-metadata": "0.1.13", + "rimraf": "3.0.2", + "rollup": "^2.79.1", + "rollup-plugin-terser": "^7.0.2", + "ts-jest": "^29.0.3", + "ts-node": "^10.9.1", + "typescript": "^4.9.4" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", "dev": true, - "requires": { - "acorn": "^5.0.3", - "css": "^2.2.1", - "normalize-path": "^2.1.1", - "source-map": "^0.6.0", - "through2": "^2.0.3" + "dependencies": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dev": true, "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } + "@babel/highlight": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" } }, - "@gulp-sourcemaps/map-sources": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/map-sources/-/map-sources-1.0.0.tgz", - "integrity": "sha1-iQrnxdjId/bThIYCFazp1+yUW9o=", + "node_modules/@babel/compat-data": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.1.tgz", + "integrity": "sha512-EWZ4mE2diW3QALKvDMiXnbZpRvlj+nayZ112nK93SnhqOtpdsbVD4W+2tEoT3YNBAG9RBR0ISY758ZkOgsn6pQ==", "dev": true, - "requires": { - "normalize-path": "^2.0.1", - "through2": "^2.0.3" + "engines": { + "node": ">=6.9.0" } }, - "@sinonjs/formatio": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz", - "integrity": "sha512-ls6CAMA6/5gG+O/IdsBcblvnd8qcO/l1TYoNeAzp3wcISOxlPXQEus0mLcdwazEkWjaBdaJ3TaxmNgCLWwvWzg==", + "node_modules/@babel/core": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.2.tgz", + "integrity": "sha512-w7DbG8DtMrJcFOi4VrLm+8QM4az8Mo+PuLBKLp2zrYRCow8W/f9xiXm5sN53C8HksCyDQwCKha9JiDoIyPjT2g==", "dev": true, - "requires": { - "samsam": "1.3.0" + "dependencies": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.2", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-module-transforms": "^7.20.2", + "@babel/helpers": "^7.20.1", + "@babel/parser": "^7.20.2", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.1", + "@babel/types": "^7.20.2", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" } }, - "@sinonjs/samsam": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-2.0.0.tgz", - "integrity": "sha512-D7VxhADdZbDJ0HjUTMnSQ5xIGb4H2yWpg8k9Sf1T08zfFiQYlaxM8LZydpR4FQ2E6LZJX8IlabNZ5io4vdChwg==", + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", "dev": true }, - "@types/chai": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.1.4.tgz", - "integrity": "sha512-h6+VEw2Vr3ORiFCyyJmcho2zALnUq9cvdB/IO8Xs9itrJVCenC7o26A6+m7D0ihTTr65eS259H5/Ghl/VjYs6g==", - "dev": true + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } }, - "@types/chai-as-promised": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.0.tgz", - "integrity": "sha512-MFiW54UOSt+f2bRw8J7LgQeIvE/9b4oGvwU7XW30S9QGAiHGnU/fmiOprsyMkdmH2rl8xSPc0/yrQw8juXU6bQ==", + "node_modules/@babel/generator": { + "version": "7.20.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.4.tgz", + "integrity": "sha512-luCf7yk/cm7yab6CAW1aiFnmEfBJplb/JojV56MYEK7ziWfGmFlTfmL9Ehwfy4gFhbjBfWO1wj7/TuSbVNEEtA==", "dev": true, - "requires": { - "@types/chai": "*" + "dependencies": { + "@babel/types": "^7.20.2", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" } }, - "@types/chokidar": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/@types/chokidar/-/chokidar-1.7.5.tgz", - "integrity": "sha512-PDkSRY7KltW3M60hSBlerxI8SFPXsO3AL/aRVsO4Kh9IHRW74Ih75gUuTd/aE4LSSFqypb10UIX3QzOJwBQMGQ==", + "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", "dev": true, - "requires": { - "@types/events": "*", - "@types/node": "*" + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" } }, - "@types/events": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", - "integrity": "sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA==", - "dev": true + "node_modules/@babel/helper-compilation-targets": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz", + "integrity": "sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.20.0", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.21.3", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } }, - "@types/fancy-log": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@types/fancy-log/-/fancy-log-1.3.0.tgz", - "integrity": "sha512-mQjDxyOM1Cpocd+vm1kZBP7smwKZ4TNokFeds9LV7OZibmPJFEzY3+xZMrKfUdNT71lv8GoCPD6upKwHxubClw==", - "dev": true + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } }, - "@types/glob": { - "version": "5.0.35", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-5.0.35.tgz", - "integrity": "sha512-wc+VveszMLyMWFvXLkloixT4n0harUIVZjnpzztaZ0nKLuul7Z32iMt2fUFGAaZ4y1XWjFRMtCI5ewvyh4aIeg==", + "node_modules/@babel/helper-environment-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", "dev": true, - "requires": { - "@types/events": "*", - "@types/minimatch": "*", - "@types/node": "*" + "engines": { + "node": ">=6.9.0" } }, - "@types/glob-stream": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@types/glob-stream/-/glob-stream-6.1.0.tgz", - "integrity": "sha512-RHv6ZQjcTncXo3thYZrsbAVwoy4vSKosSWhuhuQxLOTv74OJuFQxXkmUuZCr3q9uNBEVCvIzmZL/FeRNbHZGUg==", + "node_modules/@babel/helper-function-name": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", + "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", "dev": true, - "requires": { - "@types/glob": "*", - "@types/node": "*" + "dependencies": { + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" } }, - "@types/gulp": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@types/gulp/-/gulp-4.0.5.tgz", - "integrity": "sha512-nx1QjPTiRpvLfYsZ7MBu7bT6Cm7tAXyLbY0xbdx2IEMxCK2v2urIhJMQZHW0iV1TskM71Xl6p2uRRuWDbk+/7g==", + "node_modules/@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", "dev": true, - "requires": { - "@types/chokidar": "*", - "@types/undertaker": "*", - "@types/vinyl-fs": "*" + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" } }, - "@types/minimatch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", - "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", - "dev": true + "node_modules/@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } }, - "@types/mocha": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.5.tgz", - "integrity": "sha512-lAVp+Kj54ui/vLUFxsJTMtWvZraZxum3w3Nwkble2dNuV5VnPA+Mi2oGX9XYJAaIvZi3tn3cbjS/qcJXRb6Bww==", - "dev": true + "node_modules/@babel/helper-module-transforms": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.2.tgz", + "integrity": "sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.20.2", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.1", + "@babel/types": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + } }, - "@types/node": { - "version": "10.5.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.5.2.tgz", - "integrity": "sha512-m9zXmifkZsMHZBOyxZWilMwmTlpC8x5Ty360JKTiXvlXZfBWYpsg9ZZvP/Ye+iZUh+Q+MxDLjItVTWIsfwz+8Q==", - "dev": true + "node_modules/@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } }, - "@types/sinon": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-5.0.1.tgz", - "integrity": "sha512-yxzBCIjE3lp9lYjfBbIK/LRCoXgCLLbIIBIje7eNCcUIIR2CZZtyX5uto2hVoMSMqLrsRrT6mwwUEd0yFgOwpA==", - "dev": true + "node_modules/@babel/helper-simple-access": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", + "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + } }, - "@types/undertaker": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@types/undertaker/-/undertaker-1.2.0.tgz", - "integrity": "sha512-bx/5nZCGkasXs6qaA3B6SVDjBZqdyk04UO12e0uEPSzjt5H8jEJw0DKe7O7IM0hM2bVHRh70pmOH7PEHqXwzOw==", + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", "dev": true, - "requires": { - "@types/events": "*", - "@types/undertaker-registry": "*" + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" } }, - "@types/undertaker-registry": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/undertaker-registry/-/undertaker-registry-1.0.1.tgz", - "integrity": "sha512-Z4TYuEKn9+RbNVk1Ll2SS4x1JeLHecolIbM/a8gveaHsW0Hr+RQMraZACwTO2VD7JvepgA6UO1A1VrbktQrIbQ==", - "dev": true + "node_modules/@babel/helper-string-parser": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } }, - "@types/vinyl": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@types/vinyl/-/vinyl-2.0.2.tgz", - "integrity": "sha512-2iYpNuOl98SrLPBZfEN9Mh2JCJ2EI9HU35SfgBEb51DcmaHkhp8cKMblYeBqMQiwXMgAD3W60DbQ4i/UdLiXhw==", + "node_modules/@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", "dev": true, - "requires": { - "@types/node": "*" + "engines": { + "node": ">=6.9.0" } }, - "@types/vinyl-fs": { - "version": "2.4.8", - "resolved": "https://registry.npmjs.org/@types/vinyl-fs/-/vinyl-fs-2.4.8.tgz", - "integrity": "sha512-yE2pN9OOrxJVeO7IZLHAHrh5R4Q0osbn5WQRuQU6GdXoK7dNFrMK3K7YhATkzf3z0yQBkol3+gafs7Rp0s7dDg==", + "node_modules/@babel/helper-validator-option": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", "dev": true, - "requires": { - "@types/glob-stream": "*", - "@types/node": "*", - "@types/vinyl": "*" + "engines": { + "node": ">=6.9.0" } }, - "abbrev": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", - "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=", - "dev": true + "node_modules/@babel/helpers": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.1.tgz", + "integrity": "sha512-J77mUVaDTUJFZ5BpP6mMn6OIl3rEWymk2ZxDBQJUG3P+PbmyMcF3bYWvz0ma69Af1oobDqT/iAsvzhB58xhQUg==", + "dev": true, + "dependencies": { + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.1", + "@babel/types": "^7.20.0" + }, + "engines": { + "node": ">=6.9.0" + } }, - "acorn": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.1.tgz", - "integrity": "sha512-d+nbxBUGKg7Arpsvbnlq61mc12ek3EY8EQldM3GPAhWJ1UVxC6TDGbIvUMNU6obBX3i1+ptCIzV4vq0gFPEGVQ==", - "dev": true + "node_modules/@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } }, - "ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, - "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" } }, - "align-text": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", - "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, - "requires": { - "kind-of": "^3.0.2", - "longest": "^1.0.1", - "repeat-string": "^1.5.2" + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "color-name": "1.1.3" } }, - "amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, - "ansi-colors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", - "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, - "requires": { - "ansi-wrap": "^0.1.0" + "engines": { + "node": ">=0.8.0" } }, - "ansi-cyan": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-cyan/-/ansi-cyan-0.1.1.tgz", - "integrity": "sha1-U4rlKK+JgvKK4w2G8vF0VtJgmHM=", + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, - "requires": { - "ansi-wrap": "0.1.0" + "engines": { + "node": ">=4" } }, - "ansi-gray": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", - "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=", + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, - "requires": { - "ansi-wrap": "0.1.0" + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, - "ansi-red": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz", - "integrity": "sha1-jGOPnRCAgAo1PJwoyKgcpHBdlGw=", + "node_modules/@babel/parser": { + "version": "7.20.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.3.tgz", + "integrity": "sha512-OP/s5a94frIPXwjzEcv5S/tpQfc6XhxYUnmWpgdqMWGgYCuErA3SzozaRAMQgSZWKeTJxht9aWAkUY+0UzvOFg==", "dev": true, - "requires": { - "ansi-wrap": "0.1.0" + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" } }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "ansi-wrap": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", - "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=", - "dev": true + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "append-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/append-buffer/-/append-buffer-1.0.2.tgz", - "integrity": "sha1-2CIM9GYIFSXv6lBhTz3mUU36WPE=", + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", "dev": true, - "requires": { - "buffer-equal": "^1.0.0" + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", - "dev": true + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", + "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", "dev": true, - "requires": { - "sprintf-js": "~1.0.2" + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "argv": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/argv/-/argv-0.0.2.tgz", - "integrity": "sha1-7L0W+JSbFXGDcRsb2jNPN4QBhas=", - "dev": true + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "array-differ": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", - "integrity": "sha1-7/UuN1gknTO+QCuLuOVkuytdQDE=", - "dev": true + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "array-each": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", - "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=", - "dev": true + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "array-slice": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", - "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", - "dev": true + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", + "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", "dev": true, - "requires": { - "array-uniq": "^1.0.1" + "dependencies": { + "@babel/helper-plugin-utils": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", - "dev": true + "node_modules/@babel/template": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", + "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.18.10", + "@babel/types": "^7.18.10" + }, + "engines": { + "node": ">=6.9.0" + } }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true + "node_modules/@babel/traverse": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.1.tgz", + "integrity": "sha512-d3tN8fkVJwFLkHkBN479SOsw4DMZnz8cdbL/gvuDuzy3TS6Nfw80HuQqhw1pITbIruHyh7d1fMA47kWzmcUEGA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.1", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.20.1", + "@babel/types": "^7.20.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } }, - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", - "dev": true + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } }, - "asn1": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", - "dev": true + "node_modules/@babel/types": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.2.tgz", + "integrity": "sha512-FnnvsNWgZCr232sqtXggapvlkk/tuwR/qhGzcmxI0GXLCjmPYQPzio2FbdlWuY6y1sHFfQKk+rRbUZ9VStQMog==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, - "assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } }, - "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", - "dev": true + "node_modules/@eslint/eslintrc": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", + "integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.4.0", + "globals": "^13.15.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" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.7", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz", + "integrity": "sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } }, - "atob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.1.tgz", - "integrity": "sha1-ri1acpR38onWDdf5amMUoi3Wwio=", - "dev": true + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, - "aws4": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz", - "integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w==", - "dev": true + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } }, - "babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, - "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" + "dependencies": { + "sprintf-js": "~1.0.2" } }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "dev": true, - "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" } }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, - "optional": true, - "requires": { - "tweetnacl": "^0.14.3" + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "beeper": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/beeper/-/beeper-1.1.1.tgz", - "integrity": "sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak=", - "dev": true + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } }, - "binaryextensions": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-2.1.1.tgz", - "integrity": "sha512-XBaoWE9RW8pPdPQNibZsW2zh8TW6gcarXp1FZPwT8Uop8ScSNldJEWf2k9l3HeTqdrEwsOsFcq74RiJECW34yA==", - "dev": true + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "engines": { + "node": ">=8" } }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "node_modules/@jest/console": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.3.1.tgz", + "integrity": "sha512-IRE6GD47KwcqA09RIWrabKdHPiKDGgtAL31xDxbi/RjQMsr+lY+ppxmHwY0dUEV3qvvxZzoe5Hl0RXZJOjQNUg==", "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" + "dependencies": { + "@jest/types": "^29.3.1", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.3.1", + "jest-util": "^29.3.1", + "slash": "^3.0.0" }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.3.1.tgz", + "integrity": "sha512-0ohVjjRex985w5MmO5L3u5GR1O30DexhBSpuwx2P+9ftyqHdJXnk7IUWiP80oHMvt7ubHCJHxV0a0vlKVuZirw==", + "dev": true, "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } + "@jest/console": "^29.3.1", + "@jest/reporters": "^29.3.1", + "@jest/test-result": "^29.3.1", + "@jest/transform": "^29.3.1", + "@jest/types": "^29.3.1", + "@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": "^29.2.0", + "jest-config": "^29.3.1", + "jest-haste-map": "^29.3.1", + "jest-message-util": "^29.3.1", + "jest-regex-util": "^29.2.0", + "jest-resolve": "^29.3.1", + "jest-resolve-dependencies": "^29.3.1", + "jest-runner": "^29.3.1", + "jest-runtime": "^29.3.1", + "jest-snapshot": "^29.3.1", + "jest-util": "^29.3.1", + "jest-validate": "^29.3.1", + "jest-watcher": "^29.3.1", + "micromatch": "^4.0.4", + "pretty-format": "^29.3.1", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true } } }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "buffer-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz", - "integrity": "sha1-WWFrSYME1Var1GaWayLu2j7KX74=", - "dev": true - }, - "buffer-from": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.0.tgz", - "integrity": "sha512-c5mRlguI/Pe2dSZmpER62rSCu0ryKmWddzRYsuXc50U2/g8jMOulc31VZMa4mYx31U5xsmSOpDCgH88Vl9cDGQ==", - "dev": true - }, - "builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", - "dev": true - }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "node_modules/@jest/core/node_modules/ci-info": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.6.0.tgz", + "integrity": "sha512-RfY02LtqkzB/aiyTrh0TtDS0rQnQY3kyvkVkdBnPTUbkiGbkkCJhS7Oj8b37Qa/5eGiMilF3kH+/Km1EZxpuyQ==", "dev": true, - "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" + "engines": { + "node": ">=8" } }, - "camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "node_modules/@jest/environment": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.3.1.tgz", + "integrity": "sha512-pMmvfOPmoa1c1QpfFW0nXYtNLpofqo4BrCIk6f2kW4JFeNlHV2t3vd+3iDLf31e2ot2Mec0uqZfmI+U0K2CFag==", "dev": true, - "optional": true - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true + "dependencies": { + "@jest/fake-timers": "^29.3.1", + "@jest/types": "^29.3.1", + "@types/node": "*", + "jest-mock": "^29.3.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } }, - "center-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", - "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "node_modules/@jest/expect": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.3.1.tgz", + "integrity": "sha512-QivM7GlSHSsIAWzgfyP8dgeExPRZ9BIe2LsdPyEhCGkZkoyA+kGsoIzbKAfZCvvRzfZioKwPtCZIt5SaoxYCvg==", "dev": true, - "optional": true, - "requires": { - "align-text": "^0.1.3", - "lazy-cache": "^1.0.3" + "dependencies": { + "expect": "^29.3.1", + "jest-snapshot": "^29.3.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "chai": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.1.2.tgz", - "integrity": "sha1-D2RYS6ZC8PKs4oBiefTwbKI61zw=", + "node_modules/@jest/expect-utils": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.3.1.tgz", + "integrity": "sha512-wlrznINZI5sMjwvUoLVk617ll/UYfGIZNxmbU+Pa7wmkL4vYzhV9R2pwVqUh4NWWuLQWkI8+8mOkxs//prKQ3g==", "dev": true, - "requires": { - "assertion-error": "^1.0.1", - "check-error": "^1.0.1", - "deep-eql": "^3.0.0", - "get-func-name": "^2.0.0", - "pathval": "^1.0.0", - "type-detect": "^4.0.0" + "dependencies": { + "jest-get-type": "^29.2.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "chai-as-promised": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz", - "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==", + "node_modules/@jest/fake-timers": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.3.1.tgz", + "integrity": "sha512-iHTL/XpnDlFki9Tq0Q1GGuVeQ8BHZGIYsvCO5eN/O/oJaRzofG9Xndd9HuSDBI/0ZS79pg0iwn07OMTQ7ngF2A==", "dev": true, - "requires": { - "check-error": "^1.0.2" + "dependencies": { + "@jest/types": "^29.3.1", + "@sinonjs/fake-timers": "^9.1.2", + "@types/node": "*", + "jest-message-util": "^29.3.1", + "jest-mock": "^29.3.1", + "jest-util": "^29.3.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "node_modules/@jest/globals": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.3.1.tgz", + "integrity": "sha512-cTicd134vOcwO59OPaB6AmdHQMCtWOe+/DitpTZVxWgMJ+YvXL1HNAmPyiGbSHmF/mXVBkvlm8YYtQhyHPnV6Q==", "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "dependencies": { + "@jest/environment": "^29.3.1", + "@jest/expect": "^29.3.1", + "@jest/types": "^29.3.1", + "jest-mock": "^29.3.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", - "dev": true - }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "node_modules/@jest/reporters": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.3.1.tgz", + "integrity": "sha512-GhBu3YFuDrcAYW/UESz1JphEAbvUjaY2vShRZRoRY1mxpCMB3yGSJ4j9n0GxVlEOdCf7qjvUfBCrTUUqhVfbRA==", "dev": true, - "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.3.1", + "@jest/test-result": "^29.3.1", + "@jest/transform": "^29.3.1", + "@jest/types": "^29.3.1", + "@jridgewell/trace-mapping": "^0.3.15", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.3.1", + "jest-util": "^29.3.1", + "jest-worker": "^29.3.1", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true } } }, - "cliui": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", - "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "node_modules/@jest/reporters/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, - "optional": true, - "requires": { - "center-align": "^0.1.1", - "right-align": "^0.1.1", - "wordwrap": "0.0.2" + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@jest/schemas": { + "version": "29.0.0", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", + "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", + "dev": true, "dependencies": { - "wordwrap": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", - "dev": true, - "optional": true - } + "@sinclair/typebox": "^0.24.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", - "dev": true + "node_modules/@jest/source-map": { + "version": "29.2.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.2.0.tgz", + "integrity": "sha512-1NX9/7zzI0nqa6+kgpSdKPK+WU1p+SJk3TloWZf5MzPbxri9UEeXX5bWZAPCzbQcyuAzubcdUHA7hcNznmRqWQ==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.15", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } }, - "clone-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", - "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=", - "dev": true + "node_modules/@jest/test-result": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.3.1.tgz", + "integrity": "sha512-qeLa6qc0ddB0kuOZyZIhfN5q0e2htngokyTWsGriedsDhItisW7SDYZ7ceOe57Ii03sL988/03wAcBh3TChMGw==", + "dev": true, + "dependencies": { + "@jest/console": "^29.3.1", + "@jest/types": "^29.3.1", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } }, - "clone-stats": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", - "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=", - "dev": true + "node_modules/@jest/test-sequencer": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.3.1.tgz", + "integrity": "sha512-IqYvLbieTv20ArgKoAMyhLHNrVHJfzO6ARZAbQRlY4UGWfdDnLlZEF0BvKOMd77uIiIjSZRwq3Jb3Fa3I8+2UA==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.3.1", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.3.1", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } }, - "cloneable-readable": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.2.tgz", - "integrity": "sha512-Bq6+4t+lbM8vhTs/Bef5c5AdEMtapp/iFb6+s4/Hh9MVTt8OLKH7ZOOZSCT+Ys7hsHvqv0GuMPJ1lnQJVHvxpg==", + "node_modules/@jest/transform": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.3.1.tgz", + "integrity": "sha512-8wmCFBTVGYqFNLWfcOWoVuMuKYPUBTnTMDkdvFtAYELwDOl9RGwOsvQWGPFxDJ8AWY9xM/8xCXdqmPK3+Q5Lug==", "dev": true, - "requires": { - "inherits": "^2.0.1", - "process-nextick-args": "^2.0.0", - "readable-stream": "^2.3.5" + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.3.1", + "@jridgewell/trace-mapping": "^0.3.15", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.3.1", + "jest-regex-util": "^29.2.0", + "jest-util": "^29.3.1", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.1" }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.3.1.tgz", + "integrity": "sha512-d0S0jmmTpjnhCmNpApgX3jrUZgZ22ivKJRvL2lli5hpCRoNnp1f85r2/wpKfXuYu8E7Jjh1hGfhPyup1NM5AmA==", + "dev": true, "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } + "@jest/schemas": "^29.0.0", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true + "node_modules/@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + }, + "engines": { + "node": ">=6.0.0" + } }, - "codecov": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/codecov/-/codecov-3.0.4.tgz", - "integrity": "sha512-KJyzHdg9B8U9LxXa7hS6jnEW5b1cNckLYc2YpnJ1nEFiOW+/iSzDHp+5MYEIQd9fN3/tC6WmGZmYiwxzkuGp/A==", + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", "dev": true, - "requires": { - "argv": "^0.0.2", - "ignore-walk": "^3.0.1", - "request": "^2.87.0", - "urlgrey": "^0.4.4" + "engines": { + "node": ">=6.0.0" } }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", "dev": true, - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" + "engines": { + "node": ">=6.0.0" } }, - "color-convert": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.2.tgz", - "integrity": "sha512-3NUJZdhMhcdPn8vJ9v2UQJoH0qqoGUkYTgFEPZaPjEtwmmKUfNV46zZmgB2M5M4DCEQHMaCfWHCxiBflLm04Tg==", + "node_modules/@jridgewell/source-map": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", "dev": true, - "requires": { - "color-name": "1.1.1" + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" } }, - "color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok=", - "dev": true + "node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } }, - "color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", "dev": true }, - "combined-stream": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", - "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", "dev": true, - "requires": { - "delayed-stream": "~1.0.0" + "dependencies": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" } }, - "commander": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", - "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", - "dev": true + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } }, - "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "convert-source-map": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz", - "integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=", - "dev": true - }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@rollup/plugin-commonjs": { + "version": "23.0.4", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-23.0.4.tgz", + "integrity": "sha512-bOPJeTZg56D2MCm+TT4psP8e8Jmf1Jsi7pFUMl8BN5kOADNzofNHe47+84WVCt7D095xPghC235/YKuNDEhczg==", + "dev": true, "dependencies": { - "semver": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", - "dev": true + "@rollup/pluginutils": "^5.0.1", + "commondir": "^1.0.1", + "estree-walker": "^2.0.2", + "glob": "^8.0.3", + "is-reference": "1.2.1", + "magic-string": "^0.26.4" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.68.0||^3.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true } } }, - "css": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/css/-/css-2.2.3.tgz", - "integrity": "sha512-0W171WccAjQGGTKLhw4m2nnl0zPHUlTO/I8td4XzJgIB8Hg3ZZx71qT4G4eX8OVsSiaAKiUMy73E3nsbPlg2DQ==", + "node_modules/@rollup/plugin-node-resolve": { + "version": "15.0.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.0.1.tgz", + "integrity": "sha512-ReY88T7JhJjeRVbfCyNj+NXAG3IIsVMsX9b5/9jC98dRP8/yxlZdz7mHZbHk5zHr24wZZICS5AcXsFZAXYUQEg==", "dev": true, - "requires": { - "inherits": "^2.0.1", - "source-map": "^0.1.38", - "source-map-resolve": "^0.5.1", - "urix": "^0.1.0" - }, "dependencies": { - "source-map": { - "version": "0.1.43", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", - "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", - "dev": true, - "requires": { - "amdefine": ">=0.0.4" - } + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-builtin-module": "^3.2.0", + "is-module": "^1.0.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.78.0||^3.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true } } }, - "d": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", - "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", + "node_modules/@rollup/pluginutils": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.2.tgz", + "integrity": "sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==", "dev": true, - "requires": { - "es5-ext": "^0.10.9" + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } } }, - "dargs": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/dargs/-/dargs-5.1.0.tgz", - "integrity": "sha1-7H6lDHhWTNNsnV7Bj2Yyn63ieCk=", + "node_modules/@sinclair/typebox": { + "version": "0.24.51", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", + "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", "dev": true }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "node_modules/@sinonjs/commons": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.5.tgz", + "integrity": "sha512-rTpCA0wG1wUxglBSFdMMY0oTrKYvgf4fNgv/sXbfCVAdf+FnPBdKJR/7XbpTCwbCrvCbdPYnlWaUUYz4V2fPDA==", "dev": true, - "requires": { - "assert-plus": "^1.0.0" + "dependencies": { + "type-detect": "4.0.8" } }, - "dateformat": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz", - "integrity": "sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI=", + "node_modules/@sinonjs/fake-timers": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", + "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", "dev": true }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "dev": true + }, + "node_modules/@types/babel__core": { + "version": "7.1.20", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.20.tgz", + "integrity": "sha512-PVb6Bg2QuscZ30FvOU7z4guG6c926D9YRvOxEaelzndpMsvP+YM74Q/dAFASpg2l6+XLalxSGxcq/lrgYWZtyQ==", "dev": true, - "requires": { - "ms": "2.0.0" + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" } }, - "debug-fabulous": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/debug-fabulous/-/debug-fabulous-1.1.0.tgz", - "integrity": "sha512-GZqvGIgKNlUnHUPQhepnUZFIMoi3dgZKQBzKDeL2g7oJF9SNAji/AAu36dusFUas0O+pae74lNeoIPHqXWDkLg==", + "node_modules/@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", "dev": true, - "requires": { - "debug": "3.X", - "memoizee": "0.4.X", - "object-assign": "4.X" - }, "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } + "@babel/types": "^7.0.0" } }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "node_modules/@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", "dev": true, - "optional": true - }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "dev": true + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } }, - "deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "node_modules/@types/babel__traverse": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.2.tgz", + "integrity": "sha512-FcFaxOr2V5KZCviw1TnutEMVUVsGt4D2hP1TAfXZAMKuHYW3xQhe3jTxNPWutgCJ3/X1c5yX8ZoGVEItxKbwBg==", "dev": true, - "requires": { - "type-detect": "^4.0.0" + "dependencies": { + "@babel/types": "^7.3.0" } }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "node_modules/@types/estree": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz", + "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==", "dev": true }, - "defaults": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "node_modules/@types/graceful-fs": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", + "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", "dev": true, - "requires": { - "clone": "^1.0.2" + "dependencies": { + "@types/node": "*" } }, - "define-properties": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", - "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", "dev": true, - "requires": { - "foreach": "^2.0.5", - "object-keys": "^1.0.8" + "dependencies": { + "@types/istanbul-lib-coverage": "*" } }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", "dev": true, - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } + "@types/istanbul-lib-report": "*" } }, - "del": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", - "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=", + "node_modules/@types/jest": { + "version": "29.2.4", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.2.4.tgz", + "integrity": "sha512-PipFB04k2qTRPePduVLTRiPzQfvMeLwUN3Z21hsAKaB/W9IIzgB2pizCL466ftJlcyZqnHoC9ZHpxLGl3fS86A==", "dev": true, - "requires": { - "globby": "^6.1.0", - "is-path-cwd": "^1.0.0", - "is-path-in-cwd": "^1.0.0", - "p-map": "^1.1.1", - "pify": "^3.0.0", - "rimraf": "^2.2.8" + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" } }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", "dev": true }, - "deprecated": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/deprecated/-/deprecated-0.0.1.tgz", - "integrity": "sha1-+cmvVGSvoeepcUWKi97yqpTVuxk=", + "node_modules/@types/node": { + "version": "18.11.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.12.tgz", + "integrity": "sha512-FgD3NtTAKvyMmD44T07zz2fEf+OKwutgBCEVM8GcvMGVGaDktiLNTDvPwC/LUe3PinMW+X6CuLOF2Ui1mAlSXg==", "dev": true }, - "detect-file": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", - "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", + "node_modules/@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", "dev": true }, - "detect-newline": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", - "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=", + "node_modules/@types/prettier": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.1.tgz", + "integrity": "sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow==", "dev": true }, - "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "node_modules/@types/resolve": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", + "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", + "dev": true + }, + "node_modules/@types/semver": { + "version": "7.3.13", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", + "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", + "dev": true + }, + "node_modules/@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", "dev": true }, - "duplexer2": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", - "integrity": "sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds=", + "node_modules/@types/validator": { + "version": "13.7.10", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.7.10.tgz", + "integrity": "sha512-t1yxFAR2n0+VO6hd/FJ9F2uezAZVWHLmpmlJzm1eX03+H7+HsuTAp7L8QJs+2pQCfWkP1+EXsGK9Z9v7o/qPVQ==" + }, + "node_modules/@types/yargs": { + "version": "17.0.13", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.13.tgz", + "integrity": "sha512-9sWaruZk2JGxIQU+IhI1fhPYRcQ0UuTNuKuCW9bR5fp7qi2Llf7WDzNa17Cy7TKnh3cdxDOiyTu6gaLS0eDatg==", "dev": true, - "requires": { - "readable-stream": "~1.1.9" + "dependencies": { + "@types/yargs-parser": "*" } }, - "duplexify": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.0.tgz", - "integrity": "sha512-fO3Di4tBKJpYTFHAxTU00BcfWMY9w24r/x21a6rZRbsD/ToUgGxsMbiGRmB7uVAXeGKXD9MwiLZa5E97EVgIRQ==", + "node_modules/@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.46.0.tgz", + "integrity": "sha512-QrZqaIOzJAjv0sfjY4EjbXUi3ZOFpKfzntx22gPGr9pmFcTjcFw/1sS1LJhEubfAGwuLjNrPV0rH+D1/XZFy7Q==", "dev": true, - "requires": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" - }, "dependencies": { - "end-of-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } + "@typescript-eslint/scope-manager": "5.46.0", + "@typescript-eslint/type-utils": "5.46.0", + "@typescript-eslint/utils": "5.46.0", + "debug": "^4.3.4", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "regexpp": "^3.2.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true } } }, - "ecc-jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", - "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "node_modules/@typescript-eslint/parser": { + "version": "5.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.46.0.tgz", + "integrity": "sha512-joNO6zMGUZg+C73vwrKXCd8usnsmOYmgW/w5ZW0pG0RGvqeznjtGDk61EqqTpNrFLUYBW2RSBFrxdAZMqA4OZA==", "dev": true, - "optional": true, - "requires": { - "jsbn": "~0.1.0" + "dependencies": { + "@typescript-eslint/scope-manager": "5.46.0", + "@typescript-eslint/types": "5.46.0", + "@typescript-eslint/typescript-estree": "5.46.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "editions": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/editions/-/editions-1.3.4.tgz", - "integrity": "sha512-gzao+mxnYDzIysXKMQi/+M1mjy/rjestjg6OPoYTtI+3Izp23oiGZitsl9lPDPiTGXbcSIk1iJWhliSaglxnUg==", - "dev": true - }, - "end-of-stream": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-0.1.5.tgz", - "integrity": "sha1-jhdyBsPICDfYVjLouTWd/osvbq8=", + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.46.0.tgz", + "integrity": "sha512-7wWBq9d/GbPiIM6SqPK9tfynNxVbfpihoY5cSFMer19OYUA3l4powA2uv0AV2eAZV6KoAh6lkzxv4PoxOLh1oA==", "dev": true, - "requires": { - "once": "~1.3.0" + "dependencies": { + "@typescript-eslint/types": "5.46.0", + "@typescript-eslint/visitor-keys": "5.46.0" }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.46.0.tgz", + "integrity": "sha512-dwv4nimVIAsVS2dTA0MekkWaRnoYNXY26dKz8AN5W3cBFYwYGFQEqm/cG+TOoooKlncJS4RTbFKgcFY/pOiBCg==", + "dev": true, "dependencies": { - "once": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", - "integrity": "sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA=", - "dev": true, - "requires": { - "wrappy": "1" - } + "@typescript-eslint/typescript-estree": "5.46.0", + "@typescript-eslint/utils": "5.46.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true } } }, - "es5-ext": { - "version": "0.10.45", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.45.tgz", - "integrity": "sha512-FkfM6Vxxfmztilbxxz5UKSD4ICMf5tSpRFtDNtkAhOxZ0EKtX6qwmXNyH/sFyIbX2P/nU5AMiA9jilWsUGJzCQ==", + "node_modules/@typescript-eslint/types": { + "version": "5.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.46.0.tgz", + "integrity": "sha512-wHWgQHFB+qh6bu0IAPAJCdeCdI0wwzZnnWThlmHNY01XJ9Z97oKqKOzWYpR2I83QmshhQJl6LDM9TqMiMwJBTw==", "dev": true, - "requires": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.1", - "next-tick": "1" + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.46.0.tgz", + "integrity": "sha512-kDLNn/tQP+Yp8Ro2dUpyyVV0Ksn2rmpPpB0/3MO874RNmXtypMwSeazjEN/Q6CTp8D7ExXAAekPEcCEB/vtJkw==", "dev": true, - "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" + "dependencies": { + "@typescript-eslint/types": "5.46.0", + "@typescript-eslint/visitor-keys": "5.46.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "es6-shim": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/es6-shim/-/es6-shim-0.35.3.tgz", - "integrity": "sha1-m/tzY/7//4emzbbNk+QF7DxLbyY=", - "dev": true - }, - "es6-symbol": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", - "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", + "node_modules/@typescript-eslint/utils": { + "version": "5.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.46.0.tgz", + "integrity": "sha512-4O+Ps1CRDw+D+R40JYh5GlKLQERXRKW5yIQoNDpmXPJ+C7kaPF9R7GWl+PxGgXjB3PQCqsaaZUpZ9dG4U6DO7g==", "dev": true, - "requires": { - "d": "1", - "es5-ext": "~0.10.14" + "dependencies": { + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.46.0", + "@typescript-eslint/types": "5.46.0", + "@typescript-eslint/typescript-estree": "5.46.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "es6-weak-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz", - "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=", + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.46.0.tgz", + "integrity": "sha512-E13gBoIXmaNhwjipuvQg1ByqSAu/GbEpP/qzFihugJ+MomtoJtFAJG/+2DRPByf57B863m0/q7Zt16V9ohhANw==", "dev": true, - "requires": { - "d": "1", - "es5-ext": "^0.10.14", - "es6-iterator": "^2.0.1", - "es6-symbol": "^3.1.1" + "dependencies": { + "@typescript-eslint/types": "5.46.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "escodegen": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", - "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=", + "node_modules/acorn": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", "dev": true, - "requires": { - "esprima": "^2.7.1", - "estraverse": "^1.9.1", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.2.0" + "bin": { + "acorn": "bin/acorn" }, - "dependencies": { - "source-map": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", - "integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=", - "dev": true, - "optional": true, - "requires": { - "amdefine": ">=0.0.4" - } - } + "engines": { + "node": ">=0.4.0" } }, - "esprima": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", - "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", - "dev": true - }, - "estraverse": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", - "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=", - "dev": true - }, - "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", - "dev": true - }, - "event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, - "requires": { - "d": "1", - "es5-ext": "~0.10.14" + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "execa": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz", - "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==", + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" + "engines": { + "node": ">=0.4.0" } }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "expand-tilde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, - "requires": { - "homedir-polyfill": "^1.0.1" + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "extend": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", - "dev": true - }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "engines": { + "node": ">=10" }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } }, - "fancy-log": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.2.tgz", - "integrity": "sha1-9BEl49hPLn2JpD0G2VjI94vha+E=", + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "requires": { - "ansi-gray": "^0.1.1", - "color-support": "^1.1.3", - "time-stamp": "^1.0.0" + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", - "dev": true + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", "dev": true }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } + "engines": { + "node": ">=8" } }, - "find-index": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/find-index/-/find-index-0.1.1.tgz", - "integrity": "sha1-Z101iyyjiS15Whq0cjL4tuLg3eQ=", - "dev": true - }, - "findup-sync": { + "node_modules/astral-regex": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", - "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true, - "requires": { - "detect-file": "^1.0.0", - "is-glob": "^3.1.0", - "micromatch": "^3.0.4", - "resolve-dir": "^1.0.1" + "engines": { + "node": ">=8" } }, - "fined": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fined/-/fined-1.1.0.tgz", - "integrity": "sha1-s33IRLdqL15wgeiE98CuNE8VNHY=", + "node_modules/babel-jest": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.3.1.tgz", + "integrity": "sha512-aard+xnMoxgjwV70t0L6wkW/3HQQtV+O0PEimxKgzNqCJnbYmroPojdP2tqKSOAt8QAKV/uSZU8851M7B5+fcA==", "dev": true, - "requires": { - "expand-tilde": "^2.0.2", - "is-plain-object": "^2.0.3", - "object.defaults": "^1.1.0", - "object.pick": "^1.2.0", - "parse-filepath": "^1.0.1" + "dependencies": { + "@jest/transform": "^29.3.1", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.2.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" } }, - "first-chunk-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz", - "integrity": "sha1-Wb+1DNkF9g18OUzT2ayqtOatk04=", - "dev": true + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } }, - "flagged-respawn": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.0.tgz", - "integrity": "sha1-Tnmumy6zi/hrO7Vr8+ClaqX8q9c=", - "dev": true + "node_modules/babel-plugin-jest-hoist": { + "version": "29.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.2.0.tgz", + "integrity": "sha512-TnspP2WNiR3GLfCsUNHqeXw0RoQ2f9U5hQ5L3XFpwuO8htQmSrhh8qsB6vi5Yi8+kuynN1yjDjQsPfkebmB6ZA==", + "dev": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } }, - "flush-write-stream": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.3.tgz", - "integrity": "sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw==", + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", "dev": true, - "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.4" + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@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-top-level-await": "^7.8.3" }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.2.0.tgz", + "integrity": "sha512-z9JmMJppMxNv8N7fNRHvhMg9cvIkMxQBXgFkane3yKVEvEOP+kB50lk8DFRvF9PGqbyXxlmebKWhuDORO8RgdA==", + "dev": true, "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } + "babel-plugin-jest-hoist": "^29.2.0", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "for-in": { + "node_modules/balanced-match": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, - "for-own": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", - "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "requires": { - "for-in": "^1.0.1" + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "foreach": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", - "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", - "dev": true - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true - }, - "form-data": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", - "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "1.0.6", - "mime-types": "^2.1.12" + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" } }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "node_modules/browserslist": { + "version": "4.21.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", "dev": true, - "requires": { - "map-cache": "^0.2.2" + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.9" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "fs-mkdirp-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz", - "integrity": "sha1-C3gV/DIBxqaeFNuYzgmMFpNSWes=", + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "through2": "^2.0.3" - }, "dependencies": { - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true - } + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" } }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "gaze": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/gaze/-/gaze-0.5.2.tgz", - "integrity": "sha1-QLcJU30k0dRXZ9takIaJ3+aaxE8=", + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", "dev": true, - "requires": { - "globule": "~0.1.0" + "dependencies": { + "node-int64": "^0.4.0" } }, - "get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", - "dev": true - }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true - }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", "dev": true, - "requires": { - "assert-plus": "^1.0.0" + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "engines": { + "node": ">=6" } }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true, - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" + "engines": { + "node": ">=6" } }, - "glob-stream": { - "version": "3.1.18", - "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-3.1.18.tgz", - "integrity": "sha1-kXCl8St5Awb9/lmPMT+PeVT9FDs=", + "node_modules/caniuse-lite": { + "version": "1.0.30001431", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001431.tgz", + "integrity": "sha512-zBUoFU0ZcxpvSt9IU66dXVT/3ctO1cy4y9cscs1szkPlcWb6pasYM144GqrUygUbT+k7cmUCW61cvskjcv0enQ==", "dev": true, - "requires": { - "glob": "^4.3.1", - "glob2base": "^0.0.12", - "minimatch": "^2.0.1", - "ordered-read-streams": "^0.1.0", - "through2": "^0.6.1", - "unique-stream": "^1.0.0" - }, - "dependencies": { - "glob": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-4.5.3.tgz", - "integrity": "sha1-xstz0yJsHv7wTePFbQEvAzd+4V8=", - "dev": true, - "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^2.0.1", - "once": "^1.3.0" - } - }, - "minimatch": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz", - "integrity": "sha1-jQh8OcazjAAbl/ynzm0OHoCvusc=", - "dev": true, - "requires": { - "brace-expansion": "^1.0.0" - } - }, - "readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" }, - "through2": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", - "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", - "dev": true, - "requires": { - "readable-stream": ">=1.0.33-1 <1.1.0-0", - "xtend": ">=4.0.0 <4.1.0-0" - } + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" } - } + ] }, - "glob-watcher": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-0.0.6.tgz", - "integrity": "sha1-uVtKjfdLOcgymLDAXJeLTZo7cQs=", + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "requires": { - "gaze": "^0.5.1" + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "glob2base": { - "version": "0.0.12", - "resolved": "https://registry.npmjs.org/glob2base/-/glob2base-0.0.12.tgz", - "integrity": "sha1-nUGbPijxLoOjYhZKJ3BVkiycDVY=", + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", "dev": true, - "requires": { - "find-index": "^0.1.1" + "engines": { + "node": ">=10" } }, - "global-modules": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", - "dev": true, - "requires": { - "global-prefix": "^1.0.1", - "is-windows": "^1.0.1", - "resolve-dir": "^1.0.0" - } + "node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true }, - "global-prefix": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", - "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", - "dev": true, - "requires": { - "expand-tilde": "^2.0.2", - "homedir-polyfill": "^1.0.1", - "ini": "^1.3.4", - "is-windows": "^1.0.1", - "which": "^1.2.14" - } + "node_modules/cjs-module-lexer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", + "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "dev": true }, - "globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "dev": true, - "requires": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } + "engines": { + "node": ">=6" } }, - "globule": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/globule/-/globule-0.1.0.tgz", - "integrity": "sha1-2cjt3h2nnRJaFRt5UzuXhnY0auU=", + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "dev": true, - "requires": { - "glob": "~3.1.21", - "lodash": "~1.0.1", - "minimatch": "~0.2.11" - }, "dependencies": { - "glob": { - "version": "3.1.21", - "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz", - "integrity": "sha1-0p4KBV3qUTj00H7UDomC6DwgZs0=", - "dev": true, - "requires": { - "graceful-fs": "~1.2.0", - "inherits": "1", - "minimatch": "~0.2.11" - } - }, - "graceful-fs": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz", - "integrity": "sha1-FaSAaldUfLLS2/J/QuiajDRRs2Q=", - "dev": true - }, - "inherits": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.2.tgz", - "integrity": "sha1-ykMJ2t7mtUzAuNJH6NfHoJdb3Js=", - "dev": true - }, - "minimatch": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", - "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", - "dev": true, - "requires": { - "lru-cache": "2", - "sigmund": "~1.0.0" - } - } + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" } }, - "glogg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.1.tgz", - "integrity": "sha512-ynYqXLoluBKf9XGR1gA59yEJisIL7YHEH4xr3ZziHB5/yl4qWfaK8Js9jGe6gBGCSCKVqiyO30WnRZADvemUNw==", + "node_modules/cli-truncate": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", + "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", "dev": true, - "requires": { - "sparkles": "^1.0.0" + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "google-libphonenumber": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/google-libphonenumber/-/google-libphonenumber-3.1.10.tgz", - "integrity": "sha512-rbq206gtM62kwEMDLuc2K2CcfUsIxGlOcN5psDFx6cpJmTzgQrspO4A4ombKn9UxDidxs0j+jN+z9/Y3M1D4Ig==" - }, - "graceful-fs": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.11.tgz", - "integrity": "sha1-dhPHeKGv6mLyXGMKCG1/Osu92Bg=", + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, - "requires": { - "natives": "^1.1.0" + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" } }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "gulp": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/gulp/-/gulp-3.9.1.tgz", - "integrity": "sha1-VxzkWSjdQK9lFPxAEYZgFsE4RbQ=", + "node_modules/cliui/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, - "requires": { - "archy": "^1.0.0", - "chalk": "^1.0.0", - "deprecated": "^0.0.1", - "gulp-util": "^3.0.0", - "interpret": "^1.0.0", - "liftoff": "^2.1.0", - "minimist": "^1.1.0", - "orchestrator": "^0.3.0", - "pretty-hrtime": "^1.0.0", - "semver": "^4.1.0", - "tildify": "^1.0.0", - "v8flags": "^2.0.2", - "vinyl-fs": "^0.3.0" + "engines": { + "node": ">=8" } }, - "gulp-istanbul": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/gulp-istanbul/-/gulp-istanbul-1.1.3.tgz", - "integrity": "sha512-uMLSdqPDnBAV/B9rNyOgVMgrVC1tPbe+5GH6P13UOyxbRDT/w4sKYHWftPMA8j9om+NFvfeRlqpDXL2fixFWNA==", + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, - "requires": { - "istanbul": "^0.4.0", - "istanbul-threshold-checker": "^0.2.1", - "lodash": "^4.0.0", - "plugin-error": "^0.1.2", - "through2": "^2.0.0", - "vinyl-sourcemaps-apply": "^0.2.1" + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, + "engines": { + "node": ">=8" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "dev": true + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "dependencies": { - "lodash": { - "version": "4.17.10", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", - "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", - "dev": true - } + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "gulp-mocha": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/gulp-mocha/-/gulp-mocha-6.0.0.tgz", - "integrity": "sha512-FfBldW5ttnDpKf4Sg6/BLOOKCCbr5mbixDGK1t02/8oSrTCwNhgN/mdszG3cuQuYNzuouUdw4EH/mlYtgUscPg==", + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/colorette": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", + "dev": true + }, + "node_modules/commander": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.1.tgz", + "integrity": "sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==", "dev": true, - "requires": { - "dargs": "^5.1.0", - "execa": "^0.10.0", - "mocha": "^5.2.0", - "npm-run-path": "^2.0.2", - "plugin-error": "^1.0.1", - "supports-color": "^5.4.0", - "through2": "^2.0.3" + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, + "node_modules/compare-versions": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz", + "integrity": "sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "dev": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, "dependencies": { - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "plugin-error": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", - "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", - "dev": true, - "requires": { - "ansi-colors": "^1.0.1", - "arr-diff": "^4.0.0", - "arr-union": "^3.1.0", - "extend-shallow": "^3.0.2" - } - }, + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } + "optional": true } } }, - "gulp-replace": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/gulp-replace/-/gulp-replace-1.0.0.tgz", - "integrity": "sha512-lgdmrFSI1SdhNMXZQbrC75MOl1UjYWlOWNbNRnz+F/KHmgxt3l6XstBoAYIdadwETFyG/6i+vWUSCawdC3pqOw==", + "node_modules/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", + "dev": true + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", "dev": true, - "requires": { - "istextorbinary": "2.2.1", - "readable-stream": "^2.0.1", - "replacestream": "^4.0.0" + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff-sequences": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.3.1.tgz", + "integrity": "sha512-hlM3QR272NXCi4pq+N4Kok4kOp6EsgOM3ZSpJI7Da3UAs+Ttsi8MRmB6trM/lhyzUxGfOgnpkHtgqm5Q/CTcfQ==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" } }, - "gulp-shell": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/gulp-shell/-/gulp-shell-0.6.5.tgz", - "integrity": "sha512-f3m1WcS0o2B72/PGj1Jbv9zYR9rynBh/EQJv64n01xQUo7j7anols0eww9GG/WtDTzGVQLrupVDYkifRFnj5Zg==", + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/electron-to-chromium": { + "version": "1.4.284", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", + "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", + "dev": true + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", "dev": true, - "requires": { - "async": "^2.1.5", - "chalk": "^2.3.0", - "fancy-log": "^1.3.2", - "lodash": "^4.17.4", - "lodash.template": "^4.4.0", - "plugin-error": "^0.1.2", - "through2": "^2.0.3" + "engines": { + "node": ">=12" }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "async": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", - "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", - "dev": true, - "requires": { - "lodash": "^4.17.10" - } - }, - "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "lodash": { - "version": "4.17.10", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", - "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", - "dev": true - }, - "lodash.template": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.4.0.tgz", - "integrity": "sha1-5zoDhcg1VZF0bgILmWecaQ5o+6A=", - "dev": true, - "requires": { - "lodash._reinterpolate": "~3.0.0", - "lodash.templatesettings": "^4.0.0" - } - }, - "lodash.templatesettings": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.1.0.tgz", - "integrity": "sha1-K01OlbpEDZFf8IvImeRVNmZxMxY=", - "dev": true, - "requires": { - "lodash._reinterpolate": "~3.0.0" - } - }, - "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } + "is-arrayish": "^0.2.1" } }, - "gulp-sourcemaps": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-2.6.4.tgz", - "integrity": "sha1-y7IAhFCxvM5s0jv5gze+dRv24wo=", + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "dev": true, - "requires": { - "@gulp-sourcemaps/identity-map": "1.X", - "@gulp-sourcemaps/map-sources": "1.X", - "acorn": "5.X", - "convert-source-map": "1.X", - "css": "2.X", - "debug-fabulous": "1.X", - "detect-newline": "2.X", - "graceful-fs": "4.X", - "source-map": "~0.6.0", - "strip-bom-string": "1.X", - "through2": "2.X" + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.29.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.29.0.tgz", + "integrity": "sha512-isQ4EEiyUjZFbEKvEGJKKGBwXtvXX+zJbkVKCgTuB9t/+jUBcy8avhkEwWJecI15BkRkOYmvIM5ynbhRjEkoeg==", + "dev": true, "dependencies": { - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } + "@eslint/eslintrc": "^1.3.3", + "@humanwhocodes/config-array": "^0.11.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.10.0", + "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.4.0", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.15.0", + "grapheme-splitter": "^1.0.4", + "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-sdsl": "^4.1.4", + "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", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "gulp-tslint": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/gulp-tslint/-/gulp-tslint-8.1.3.tgz", - "integrity": "sha512-KEP350N5B9Jg6o6jnyCyKVBPemJePYpMsGfIQq0G0ErvY7tw4Lrfb/y3L4WRf7ek0OsaE8nnj86w+lcLXW8ovw==", + "node_modules/eslint-config-prettier": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", + "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", "dev": true, - "requires": { - "@types/fancy-log": "1.3.0", - "chalk": "2.3.1", - "fancy-log": "1.3.2", - "map-stream": "~0.0.7", - "plugin-error": "1.0.1", - "through": "~2.3.8" + "bin": { + "eslint-config-prettier": "bin/cli.js" }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-jest": { + "version": "27.1.6", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.1.6.tgz", + "integrity": "sha512-XA7RFLSrlQF9IGtAmhddkUkBuICCTuryfOTfCSWcZHiHb69OilIH05oozH2XA6CEOtztnOd0vgXyvxZodkxGjg==", + "dev": true, "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.1.tgz", - "integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.2.0" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "plugin-error": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", - "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", - "dev": true, - "requires": { - "ansi-colors": "^1.0.1", - "arr-diff": "^4.0.0", - "arr-union": "^3.1.0", - "extend-shallow": "^3.0.2" - } + "@typescript-eslint/utils": "^5.10.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^5.0.0", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true }, - "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } + "jest": { + "optional": true } } }, - "gulp-typescript": { - "version": "5.0.0-alpha.3", - "resolved": "https://registry.npmjs.org/gulp-typescript/-/gulp-typescript-5.0.0-alpha.3.tgz", - "integrity": "sha512-6iSBjqBXAUqRsLUh/9XtlOnSzpPMbLrr5rqGj4UPLtGpDwFHW/fVTuRgv6LAWiKesLIUDDM0ourxvcpu2trecQ==", + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, - "requires": { - "ansi-colors": "^2.0.2", - "plugin-error": "^1.0.1", - "source-map": "^0.7.3", - "through2": "^2.0.3", - "vinyl": "^2.1.0", - "vinyl-fs": "^3.0.3" + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, "dependencies": { - "ansi-colors": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-2.0.3.tgz", - "integrity": "sha512-Lt6iEoO5+sCDxNFbJRKee7etk5Rv2i7SWcVLhwZmiOpsuJmmnwN8Ob0TZpCeSRIg1Ns7YzGitvoKS7D6Y8TtNQ==", - "dev": true - }, - "clone": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.1.tgz", - "integrity": "sha1-0hfR6WERjjrJpLi7oyhVU79kfNs=", - "dev": true - }, - "clone-stats": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", - "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", - "dev": true - }, - "glob-stream": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz", - "integrity": "sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ=", - "dev": true, - "requires": { - "extend": "^3.0.0", - "glob": "^7.1.1", - "glob-parent": "^3.1.0", - "is-negated-glob": "^1.0.0", - "ordered-read-streams": "^1.0.0", - "pumpify": "^1.3.5", - "readable-stream": "^2.1.5", - "remove-trailing-separator": "^1.0.1", - "to-absolute-glob": "^2.0.0", - "unique-stream": "^2.0.2" - } - }, - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "ordered-read-streams": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz", - "integrity": "sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4=", - "dev": true, - "requires": { - "readable-stream": "^2.0.1" - } - }, - "plugin-error": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", - "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", - "dev": true, - "requires": { - "ansi-colors": "^1.0.1", - "arr-diff": "^4.0.0", - "arr-union": "^3.1.0", - "extend-shallow": "^3.0.2" - }, - "dependencies": { - "ansi-colors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", - "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", - "dev": true, - "requires": { - "ansi-wrap": "^0.1.0" - } - } - } - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "replace-ext": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", - "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", - "dev": true - }, - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/espree": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", + "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", + "dev": true, + "dependencies": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.3.1.tgz", + "integrity": "sha512-gGb1yTgU30Q0O/tQq+z30KBWv24ApkMgFUpvKBkyLUBL68Wv8dHdJxTBZFl/iT8K/bqDHvUYRH6IIN3rToopPA==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "^29.3.1", + "jest-get-type": "^29.2.0", + "jest-matcher-utils": "^29.3.1", + "jest-message-util": "^29.3.1", + "jest-util": "^29.3.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/find-versions": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-4.0.0.tgz", + "integrity": "sha512-wgpWy002tA+wgmO27buH/9KzyEOQnKsG/R0yrcjPT9BOFm0zRBVQbZ95nRGXWMywS8YR5knRbpohio0bcJABxQ==", + "dev": true, + "dependencies": { + "semver-regex": "^3.1.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/globals": { + "version": "13.17.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", + "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "node_modules/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/husky": { + "version": "4.3.8", + "resolved": "https://registry.npmjs.org/husky/-/husky-4.3.8.tgz", + "integrity": "sha512-LCqqsB0PzJQ/AlCgfrfzRe3e3+NvmefAdKQhRYpxS4u6clblBoDdzzvHi8fmxKRzvMxPY/1WZWzomPZww0Anow==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "chalk": "^4.0.0", + "ci-info": "^2.0.0", + "compare-versions": "^3.6.0", + "cosmiconfig": "^7.0.0", + "find-versions": "^4.0.0", + "opencollective-postinstall": "^2.0.2", + "pkg-dir": "^5.0.0", + "please-upgrade-node": "^3.2.0", + "slash": "^3.0.0", + "which-pm-runs": "^1.0.0" + }, + "bin": { + "husky-run": "bin/run.js", + "husky-upgrade": "lib/upgrader/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/husky" + } + }, + "node_modules/ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-local/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-local/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-local/node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-builtin-module": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.0.tgz", + "integrity": "sha512-phDA4oSGt7vl1n5tJvTWooWWAsXLY+2xCnxNqvKhGEzujg+A43wPlPOyDg3C8XQHN+6k/JTQWJ/j0dQh/qr+Hw==", + "dev": true, + "dependencies": { + "builtin-modules": "^3.3.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-core-module": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "dev": true + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-reference": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", + "dev": true, + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.3.1.tgz", + "integrity": "sha512-6iWfL5DTT0Np6UYs/y5Niu7WIfNv/wRTtN5RSXt2DIEft3dx3zPuw/3WJQBCJfmEzvDiEKwoqMbGD9n49+qLSA==", + "dev": true, + "dependencies": { + "@jest/core": "^29.3.1", + "@jest/types": "^29.3.1", + "import-local": "^3.0.2", + "jest-cli": "^29.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.2.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.2.0.tgz", + "integrity": "sha512-qPVmLLyBmvF5HJrY7krDisx6Voi8DmlV3GZYX0aFNbaQsZeoz1hfxcCMbqDGuQCxU1dJy9eYc2xscE8QrCCYaA==", + "dev": true, + "dependencies": { + "execa": "^5.0.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.3.1.tgz", + "integrity": "sha512-wpr26sEvwb3qQQbdlmei+gzp6yoSSoSL6GsLPxnuayZSMrSd5Ka7IjAvatpIernBvT2+Ic6RLTg+jSebScmasg==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.3.1", + "@jest/expect": "^29.3.1", + "@jest/test-result": "^29.3.1", + "@jest/types": "^29.3.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.3.1", + "jest-matcher-utils": "^29.3.1", + "jest-message-util": "^29.3.1", + "jest-runtime": "^29.3.1", + "jest-snapshot": "^29.3.1", + "jest-util": "^29.3.1", + "p-limit": "^3.1.0", + "pretty-format": "^29.3.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.3.1.tgz", + "integrity": "sha512-TO/ewvwyvPOiBBuWZ0gm04z3WWP8TIK8acgPzE4IxgsLKQgb377NYGrQLc3Wl/7ndWzIH2CDNNsUjGxwLL43VQ==", + "dev": true, + "dependencies": { + "@jest/core": "^29.3.1", + "@jest/test-result": "^29.3.1", + "@jest/types": "^29.3.1", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^29.3.1", + "jest-util": "^29.3.1", + "jest-validate": "^29.3.1", + "prompts": "^2.0.1", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.3.1.tgz", + "integrity": "sha512-y0tFHdj2WnTEhxmGUK1T7fgLen7YK4RtfvpLFBXfQkh2eMJAQq24Vx9472lvn5wg0MAO6B+iPfJfzdR9hJYalg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.3.1", + "@jest/types": "^29.3.1", + "babel-jest": "^29.3.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.3.1", + "jest-environment-node": "^29.3.1", + "jest-get-type": "^29.2.0", + "jest-regex-util": "^29.2.0", + "jest-resolve": "^29.3.1", + "jest-runner": "^29.3.1", + "jest-util": "^29.3.1", + "jest-validate": "^29.3.1", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.3.1", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-config/node_modules/ci-info": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.6.0.tgz", + "integrity": "sha512-RfY02LtqkzB/aiyTrh0TtDS0rQnQY3kyvkVkdBnPTUbkiGbkkCJhS7Oj8b37Qa/5eGiMilF3kH+/Km1EZxpuyQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-config/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-diff": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.3.1.tgz", + "integrity": "sha512-vU8vyiO7568tmin2lA3r2DP8oRvzhvRcD4DjpXc6uGveQodyk7CKLhQlCSiwgx3g0pFaE88/KLZ0yaTWMc4Uiw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.3.1", + "jest-get-type": "^29.2.0", + "pretty-format": "^29.3.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.2.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.2.0.tgz", + "integrity": "sha512-bkxUsxTgWQGbXV5IENmfiIuqZhJcyvF7tU4zJ/7ioTutdz4ToB5Yx6JOFBpgI+TphRY4lhOyCWGNH/QFQh5T6A==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.3.1.tgz", + "integrity": "sha512-qrZH7PmFB9rEzCSl00BWjZYuS1BSOH8lLuC0azQE9lQrAx3PWGKHTDudQiOSwIy5dGAJh7KA0ScYlCP7JxvFYA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.3.1", + "chalk": "^4.0.0", + "jest-get-type": "^29.2.0", + "jest-util": "^29.3.1", + "pretty-format": "^29.3.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.3.1.tgz", + "integrity": "sha512-xm2THL18Xf5sIHoU7OThBPtuH6Lerd+Y1NLYiZJlkE3hbE+7N7r8uvHIl/FkZ5ymKXJe/11SQuf3fv4v6rUMag==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.3.1", + "@jest/fake-timers": "^29.3.1", + "@jest/types": "^29.3.1", + "@types/node": "*", + "jest-mock": "^29.3.1", + "jest-util": "^29.3.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.2.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.2.0.tgz", + "integrity": "sha512-uXNJlg8hKFEnDgFsrCjznB+sTxdkuqiCL6zMgA75qEbAJjJYTs9XPrvDctrEig2GDow22T/LvHgO57iJhXB/UA==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.3.1.tgz", + "integrity": "sha512-/FFtvoG1xjbbPXQLFef+WSU4yrc0fc0Dds6aRPBojUid7qlPqZvxdUBA03HW0fnVHXVCnCdkuoghYItKNzc/0A==", + "dev": true, + "dependencies": { + "@jest/types": "^29.3.1", + "@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": "^29.2.0", + "jest-util": "^29.3.1", + "jest-worker": "^29.3.1", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.3.1.tgz", + "integrity": "sha512-3DA/VVXj4zFOPagGkuqHnSQf1GZBmmlagpguxEERO6Pla2g84Q1MaVIB3YMxgUaFIaYag8ZnTyQgiZ35YEqAQA==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.2.0", + "pretty-format": "^29.3.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.3.1.tgz", + "integrity": "sha512-fkRMZUAScup3txIKfMe3AIZZmPEjWEdsPJFK3AIy5qRohWqQFg1qrmKfYXR9qEkNc7OdAu2N4KPHibEmy4HPeQ==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.3.1", + "jest-get-type": "^29.2.0", + "pretty-format": "^29.3.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.3.1.tgz", + "integrity": "sha512-lMJTbgNcDm5z+6KDxWtqOFWlGQxD6XaYwBqHR8kmpkP+WWWG90I35kdtQHY67Ay5CSuydkTBbJG+tH9JShFCyA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.3.1", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.3.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.3.1.tgz", + "integrity": "sha512-H8/qFDtDVMFvFP4X8NuOT3XRDzOUTz+FeACjufHzsOIBAxivLqkB1PoLCaJx9iPPQ8dZThHPp/G3WRWyMgA3JA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.3.1", + "@types/node": "*", + "jest-util": "^29.3.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", + "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", + "dev": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.2.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.2.0.tgz", + "integrity": "sha512-6yXn0kg2JXzH30cr2NlThF+70iuO/3irbaB4mh5WyqNIvLLP+B6sFdluO1/1RJmslyh/f9osnefECflHvTbwVA==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.3.1.tgz", + "integrity": "sha512-amXJgH/Ng712w3Uz5gqzFBBjxV8WFLSmNjoreBGMqxgCz5cH7swmBZzgBaCIOsvb0NbpJ0vgaSFdJqMdT+rADw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.3.1", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.3.1", + "jest-validate": "^29.3.1", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.3.1.tgz", + "integrity": "sha512-Vk0cYq0byRw2WluNmNWGqPeRnZ3p3hHmjJMp2dyyZeYIfiBskwq4rpiuGFR6QGAdbj58WC7HN4hQHjf2mpvrLA==", + "dev": true, + "dependencies": { + "jest-regex-util": "^29.2.0", + "jest-snapshot": "^29.3.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.3.1.tgz", + "integrity": "sha512-oFvcwRNrKMtE6u9+AQPMATxFcTySyKfLhvso7Sdk/rNpbhg4g2GAGCopiInk1OP4q6gz3n6MajW4+fnHWlU3bA==", + "dev": true, + "dependencies": { + "@jest/console": "^29.3.1", + "@jest/environment": "^29.3.1", + "@jest/test-result": "^29.3.1", + "@jest/transform": "^29.3.1", + "@jest/types": "^29.3.1", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.2.0", + "jest-environment-node": "^29.3.1", + "jest-haste-map": "^29.3.1", + "jest-leak-detector": "^29.3.1", + "jest-message-util": "^29.3.1", + "jest-resolve": "^29.3.1", + "jest-runtime": "^29.3.1", + "jest-util": "^29.3.1", + "jest-watcher": "^29.3.1", + "jest-worker": "^29.3.1", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.3.1.tgz", + "integrity": "sha512-jLzkIxIqXwBEOZx7wx9OO9sxoZmgT2NhmQKzHQm1xwR1kNW/dn0OjxR424VwHHf1SPN6Qwlb5pp1oGCeFTQ62A==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.3.1", + "@jest/fake-timers": "^29.3.1", + "@jest/globals": "^29.3.1", + "@jest/source-map": "^29.2.0", + "@jest/test-result": "^29.3.1", + "@jest/transform": "^29.3.1", + "@jest/types": "^29.3.1", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.3.1", + "jest-message-util": "^29.3.1", + "jest-mock": "^29.3.1", + "jest-regex-util": "^29.2.0", + "jest-resolve": "^29.3.1", + "jest-snapshot": "^29.3.1", + "jest-util": "^29.3.1", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-snapshot": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.3.1.tgz", + "integrity": "sha512-+3JOc+s28upYLI2OJM4PWRGK9AgpsMs/ekNryUV0yMBClT9B1DF2u2qay8YxcQd338PPYSFNb0lsar1B49sLDA==", + "dev": true, + "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": "^29.3.1", + "@jest/transform": "^29.3.1", + "@jest/types": "^29.3.1", + "@types/babel__traverse": "^7.0.6", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.3.1", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.3.1", + "jest-get-type": "^29.2.0", + "jest-haste-map": "^29.3.1", + "jest-matcher-utils": "^29.3.1", + "jest-message-util": "^29.3.1", + "jest-util": "^29.3.1", + "natural-compare": "^1.4.0", + "pretty-format": "^29.3.1", + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.3.1.tgz", + "integrity": "sha512-7YOVZaiX7RJLv76ZfHt4nbNEzzTRiMW/IiOG7ZOKmTXmoGBxUDefgMAxQubu6WPVqP5zSzAdZG0FfLcC7HOIFQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.3.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util/node_modules/ci-info": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.6.0.tgz", + "integrity": "sha512-RfY02LtqkzB/aiyTrh0TtDS0rQnQY3kyvkVkdBnPTUbkiGbkkCJhS7Oj8b37Qa/5eGiMilF3kH+/Km1EZxpuyQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-validate": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.3.1.tgz", + "integrity": "sha512-N9Lr3oYR2Mpzuelp1F8negJR3YE+L1ebk1rYA5qYo9TTY3f9OWdptLoNSPP9itOCBIRBqjt/S5XHlzYglLN67g==", + "dev": true, + "dependencies": { + "@jest/types": "^29.3.1", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.2.0", + "leven": "^3.1.0", + "pretty-format": "^29.3.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.3.1.tgz", + "integrity": "sha512-RspXG2BQFDsZSRKGCT/NiNa8RkQ1iKAjrO0//soTMWx/QUt+OcxMqMSBxz23PYGqUuWm2+m2mNNsmj0eIoOaFg==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.3.1", + "@jest/types": "^29.3.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.3.1", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.3.1.tgz", + "integrity": "sha512-lY4AnnmsEWeiXirAIA0c9SDPbuCBq8IYuDVL8PMm0MZ2PEs2yPvRA/J64QBXuZp7CYKrDM/rmNrc9/i3KJQncw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.3.1", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-sdsl": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.5.tgz", + "integrity": "sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q==", + "dev": true + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/libphonenumber-js": { + "version": "1.10.14", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.14.tgz", + "integrity": "sha512-McGS7GV/WjJ2KjfOGhJU1oJn29RYeo7Q+RpANRbUNMQ9gj5XArpbjurSuyYPTejFwbaUojstQ4XyWCrAzGOUXw==" + }, + "node_modules/lilconfig": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", + "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/lint-staged": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.1.0.tgz", + "integrity": "sha512-pn/sR8IrcF/T0vpWLilih8jmVouMlxqXxKuAojmbiGX5n/gDnz+abdPptlj0vYnbfE0SQNl3CY/HwtM0+yfOVQ==", + "dev": true, + "dependencies": { + "cli-truncate": "^3.1.0", + "colorette": "^2.0.19", + "commander": "^9.4.1", + "debug": "^4.3.4", + "execa": "^6.1.0", + "lilconfig": "2.0.6", + "listr2": "^5.0.5", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-inspect": "^1.12.2", + "pidtree": "^0.6.0", + "string-argv": "^0.3.1", + "yaml": "^2.1.3" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": "^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/lint-staged/node_modules/execa": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-6.1.0.tgz", + "integrity": "sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^3.0.1", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/lint-staged/node_modules/human-signals": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-3.0.1.tgz", + "integrity": "sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==", + "dev": true, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/lint-staged/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/yaml": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.1.3.tgz", + "integrity": "sha512-AacA8nRULjKMX2DvWvOAdBZMOfQlypSFkjcOcu9FalllIDJ1kvlREzcdIZmidQUqqeMv7jorHjq2HlLv/+c2lg==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, + "node_modules/listr2": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-5.0.5.tgz", + "integrity": "sha512-DpBel6fczu7oQKTXMekeprc0o3XDgGMkD7JNYyX+X0xbwK+xgrx9dcyKoXKqpLSUvAWfmoePS7kavniOcq3r4w==", + "dev": true, + "dependencies": { + "cli-truncate": "^2.1.0", + "colorette": "^2.0.19", + "log-update": "^4.0.0", + "p-map": "^4.0.0", + "rfdc": "^1.3.0", + "rxjs": "^7.5.6", + "through": "^2.3.8", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": "^14.13.1 || >=16.0.0" + }, + "peerDependencies": { + "enquirer": ">= 2.3.0 < 3" + }, + "peerDependenciesMeta": { + "enquirer": { + "optional": true + } + } + }, + "node_modules/listr2/node_modules/cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "dependencies": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/listr2/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/listr2/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/listr2/node_modules/slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/listr2/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/log-update": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.3.0", + "cli-cursor": "^3.1.0", + "slice-ansi": "^4.0.0", + "wrap-ansi": "^6.2.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/magic-string": { + "version": "0.26.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.7.tgz", + "integrity": "sha512-hX9XH3ziStPoPhJxLq1syWuZMxbDvGNbVchfrdCtanC7D13888bMFow61x8axrx+GfHLtVeAx2kxL7tTGRl+Ow==", + "dev": true, + "dependencies": { + "sourcemap-codec": "^1.4.8" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/opencollective-postinstall": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", + "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", + "dev": true, + "bin": { + "opencollective-postinstall": "index.js" + } + }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true, + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-5.0.0.tgz", + "integrity": "sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==", + "dev": true, + "dependencies": { + "find-up": "^5.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/please-upgrade-node": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", + "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", + "dev": true, + "dependencies": { + "semver-compare": "^1.0.0" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.1.tgz", + "integrity": "sha512-lqGoSJBQNJidqCHE80vqZJHWHRFoNYsSpP9AjFhlhi9ODCJA541svILes/+/1GM3VaL/abZi7cpFzOpdR9UPKg==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty-format": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.3.1.tgz", + "integrity": "sha512-FyLnmb1cYJV8biEIiRyzRFvs2lry7PPIvOqKVe1GCUEYg4YGmlx1qG9EJNMxArYm7piII4qb8UV1Pncq5dxmcg==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.0.0", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", + "dev": true + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve.exports": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", + "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", + "dev": true + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "2.79.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", + "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=10.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/rollup-plugin-terser": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", + "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "jest-worker": "^26.2.1", + "serialize-javascript": "^4.0.0", + "terser": "^5.0.0" + }, + "peerDependencies": { + "rollup": "^2.0.0" + } + }, + "node_modules/rollup-plugin-terser/node_modules/jest-worker": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", + "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.7.tgz", + "integrity": "sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "dev": true + }, + "node_modules/semver-regex": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-3.1.4.tgz", + "integrity": "sha512-6IiqeZNgq01qGf0TId0t3NvKzSvUsjcpdEO3AQNeIjR6A2+ckTnQlDpl4qu1bjRv0RzN3FP9hzFmws3lKqRWkA==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "dev": true, + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/terser": { + "version": "5.15.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.15.1.tgz", + "integrity": "sha512-K1faMUvpm/FBxjBXud0LWVAGxmvoPbZbfTCYbSgaaYQaIXI3/TdI7a7ZGA73Zrou6Q8Zmz3oeUTsp/dj+ag2Xw==", + "dev": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/terser/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-jest": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.0.3.tgz", + "integrity": "sha512-Ibygvmuyq1qp/z3yTh9QTwVVAbFdDy/+4BtIQR2sp6baF2SJU/8CKK/hhnGIDY2L90Az2jIqTwZPnN2p+BweiQ==", + "dev": true, + "dependencies": { + "bs-logger": "0.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^29.0.0", + "json5": "^2.2.1", + "lodash.memoize": "4.x", + "make-error": "1.x", + "semver": "7.x", + "yargs-parser": "^21.0.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/types": "^29.0.0", + "babel-jest": "^29.0.0", + "jest": "^29.0.0", + "typescript": ">=4.3" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, + "node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "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" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/tslib": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==", + "dev": true + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "4.9.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", + "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist-lint": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/v8-to-istanbul": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", + "integrity": "sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/v8-to-istanbul/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "node_modules/validator": { + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", + "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-pm-runs": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.1.0.tgz", + "integrity": "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "17.6.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", + "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/yargs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + }, + "dependencies": { + "@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dev": true, + "requires": { + "@babel/highlight": "^7.18.6" + } + }, + "@babel/compat-data": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.1.tgz", + "integrity": "sha512-EWZ4mE2diW3QALKvDMiXnbZpRvlj+nayZ112nK93SnhqOtpdsbVD4W+2tEoT3YNBAG9RBR0ISY758ZkOgsn6pQ==", + "dev": true + }, + "@babel/core": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.2.tgz", + "integrity": "sha512-w7DbG8DtMrJcFOi4VrLm+8QM4az8Mo+PuLBKLp2zrYRCow8W/f9xiXm5sN53C8HksCyDQwCKha9JiDoIyPjT2g==", + "dev": true, + "requires": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.2", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-module-transforms": "^7.20.2", + "@babel/helpers": "^7.20.1", + "@babel/parser": "^7.20.2", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.1", + "@babel/types": "^7.20.2", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.1", + "semver": "^6.3.0" + }, + "dependencies": { + "convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.20.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.4.tgz", + "integrity": "sha512-luCf7yk/cm7yab6CAW1aiFnmEfBJplb/JojV56MYEK7ziWfGmFlTfmL9Ehwfy4gFhbjBfWO1wj7/TuSbVNEEtA==", + "dev": true, + "requires": { + "@babel/types": "^7.20.2", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + }, + "dependencies": { + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + } + } + }, + "@babel/helper-compilation-targets": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz", + "integrity": "sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.20.0", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.21.3", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/helper-environment-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "dev": true + }, + "@babel/helper-function-name": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", + "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", + "dev": true, + "requires": { + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-module-transforms": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.2.tgz", + "integrity": "sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.20.2", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.1", + "@babel/types": "^7.20.2" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + }, + "@babel/helper-simple-access": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", + "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "dev": true, + "requires": { + "@babel/types": "^7.20.2" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-string-parser": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "dev": true + }, + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "dev": true + }, + "@babel/helpers": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.1.tgz", + "integrity": "sha512-J77mUVaDTUJFZ5BpP6mMn6OIl3rEWymk2ZxDBQJUG3P+PbmyMcF3bYWvz0ma69Af1oobDqT/iAsvzhB58xhQUg==", + "dev": true, + "requires": { + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.1", + "@babel/types": "^7.20.0" + } + }, + "@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/parser": { + "version": "7.20.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.3.tgz", + "integrity": "sha512-OP/s5a94frIPXwjzEcv5S/tpQfc6XhxYUnmWpgdqMWGgYCuErA3SzozaRAMQgSZWKeTJxht9aWAkUY+0UzvOFg==", + "dev": true + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-jsx": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", + "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-typescript": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", + "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.19.0" + } + }, + "@babel/template": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", + "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.18.10", + "@babel/types": "^7.18.10" + } + }, + "@babel/traverse": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.1.tgz", + "integrity": "sha512-d3tN8fkVJwFLkHkBN479SOsw4DMZnz8cdbL/gvuDuzy3TS6Nfw80HuQqhw1pITbIruHyh7d1fMA47kWzmcUEGA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.1", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.20.1", + "@babel/types": "^7.20.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "dependencies": { + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.2.tgz", + "integrity": "sha512-FnnvsNWgZCr232sqtXggapvlkk/tuwR/qhGzcmxI0GXLCjmPYQPzio2FbdlWuY6y1sHFfQKk+rRbUZ9VStQMog==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + } + }, + "@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "dependencies": { + "@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + } + } + }, + "@eslint/eslintrc": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", + "integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.4.0", + "globals": "^13.15.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" + } + }, + "@humanwhocodes/config-array": { + "version": "0.11.7", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz", + "integrity": "sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + } + }, + "@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true + }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" } }, - "unique-stream": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.2.1.tgz", - "integrity": "sha1-WqADz76Uxf+GbE59ZouxxNuts2k=", + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "requires": { - "json-stable-stringify": "^1.0.0", - "through2-filter": "^2.0.0" + "p-try": "^2.0.0" } }, - "vinyl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.0.tgz", - "integrity": "sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==", + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "requires": { - "clone": "^2.1.1", - "clone-buffer": "^1.0.0", - "clone-stats": "^1.0.0", - "cloneable-readable": "^1.0.0", - "remove-trailing-separator": "^1.0.1", - "replace-ext": "^1.0.0" + "p-limit": "^2.2.0" } }, - "vinyl-fs": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-3.0.3.tgz", - "integrity": "sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng==", + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + } + } + }, + "@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true + }, + "@jest/console": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.3.1.tgz", + "integrity": "sha512-IRE6GD47KwcqA09RIWrabKdHPiKDGgtAL31xDxbi/RjQMsr+lY+ppxmHwY0dUEV3qvvxZzoe5Hl0RXZJOjQNUg==", + "dev": true, + "requires": { + "@jest/types": "^29.3.1", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.3.1", + "jest-util": "^29.3.1", + "slash": "^3.0.0" + } + }, + "@jest/core": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.3.1.tgz", + "integrity": "sha512-0ohVjjRex985w5MmO5L3u5GR1O30DexhBSpuwx2P+9ftyqHdJXnk7IUWiP80oHMvt7ubHCJHxV0a0vlKVuZirw==", + "dev": true, + "requires": { + "@jest/console": "^29.3.1", + "@jest/reporters": "^29.3.1", + "@jest/test-result": "^29.3.1", + "@jest/transform": "^29.3.1", + "@jest/types": "^29.3.1", + "@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": "^29.2.0", + "jest-config": "^29.3.1", + "jest-haste-map": "^29.3.1", + "jest-message-util": "^29.3.1", + "jest-regex-util": "^29.2.0", + "jest-resolve": "^29.3.1", + "jest-resolve-dependencies": "^29.3.1", + "jest-runner": "^29.3.1", + "jest-runtime": "^29.3.1", + "jest-snapshot": "^29.3.1", + "jest-util": "^29.3.1", + "jest-validate": "^29.3.1", + "jest-watcher": "^29.3.1", + "micromatch": "^4.0.4", + "pretty-format": "^29.3.1", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ci-info": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.6.0.tgz", + "integrity": "sha512-RfY02LtqkzB/aiyTrh0TtDS0rQnQY3kyvkVkdBnPTUbkiGbkkCJhS7Oj8b37Qa/5eGiMilF3kH+/Km1EZxpuyQ==", + "dev": true + } + } + }, + "@jest/environment": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.3.1.tgz", + "integrity": "sha512-pMmvfOPmoa1c1QpfFW0nXYtNLpofqo4BrCIk6f2kW4JFeNlHV2t3vd+3iDLf31e2ot2Mec0uqZfmI+U0K2CFag==", + "dev": true, + "requires": { + "@jest/fake-timers": "^29.3.1", + "@jest/types": "^29.3.1", + "@types/node": "*", + "jest-mock": "^29.3.1" + } + }, + "@jest/expect": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.3.1.tgz", + "integrity": "sha512-QivM7GlSHSsIAWzgfyP8dgeExPRZ9BIe2LsdPyEhCGkZkoyA+kGsoIzbKAfZCvvRzfZioKwPtCZIt5SaoxYCvg==", + "dev": true, + "requires": { + "expect": "^29.3.1", + "jest-snapshot": "^29.3.1" + } + }, + "@jest/expect-utils": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.3.1.tgz", + "integrity": "sha512-wlrznINZI5sMjwvUoLVk617ll/UYfGIZNxmbU+Pa7wmkL4vYzhV9R2pwVqUh4NWWuLQWkI8+8mOkxs//prKQ3g==", + "dev": true, + "requires": { + "jest-get-type": "^29.2.0" + } + }, + "@jest/fake-timers": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.3.1.tgz", + "integrity": "sha512-iHTL/XpnDlFki9Tq0Q1GGuVeQ8BHZGIYsvCO5eN/O/oJaRzofG9Xndd9HuSDBI/0ZS79pg0iwn07OMTQ7ngF2A==", + "dev": true, + "requires": { + "@jest/types": "^29.3.1", + "@sinonjs/fake-timers": "^9.1.2", + "@types/node": "*", + "jest-message-util": "^29.3.1", + "jest-mock": "^29.3.1", + "jest-util": "^29.3.1" + } + }, + "@jest/globals": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.3.1.tgz", + "integrity": "sha512-cTicd134vOcwO59OPaB6AmdHQMCtWOe+/DitpTZVxWgMJ+YvXL1HNAmPyiGbSHmF/mXVBkvlm8YYtQhyHPnV6Q==", + "dev": true, + "requires": { + "@jest/environment": "^29.3.1", + "@jest/expect": "^29.3.1", + "@jest/types": "^29.3.1", + "jest-mock": "^29.3.1" + } + }, + "@jest/reporters": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.3.1.tgz", + "integrity": "sha512-GhBu3YFuDrcAYW/UESz1JphEAbvUjaY2vShRZRoRY1mxpCMB3yGSJ4j9n0GxVlEOdCf7qjvUfBCrTUUqhVfbRA==", + "dev": true, + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.3.1", + "@jest/test-result": "^29.3.1", + "@jest/transform": "^29.3.1", + "@jest/types": "^29.3.1", + "@jridgewell/trace-mapping": "^0.3.15", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.3.1", + "jest-util": "^29.3.1", + "jest-worker": "^29.3.1", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "dependencies": { + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "@jest/schemas": { + "version": "29.0.0", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", + "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", + "dev": true, + "requires": { + "@sinclair/typebox": "^0.24.1" + } + }, + "@jest/source-map": { + "version": "29.2.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.2.0.tgz", + "integrity": "sha512-1NX9/7zzI0nqa6+kgpSdKPK+WU1p+SJk3TloWZf5MzPbxri9UEeXX5bWZAPCzbQcyuAzubcdUHA7hcNznmRqWQ==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.15", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + } + }, + "@jest/test-result": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.3.1.tgz", + "integrity": "sha512-qeLa6qc0ddB0kuOZyZIhfN5q0e2htngokyTWsGriedsDhItisW7SDYZ7ceOe57Ii03sL988/03wAcBh3TChMGw==", + "dev": true, + "requires": { + "@jest/console": "^29.3.1", + "@jest/types": "^29.3.1", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/test-sequencer": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.3.1.tgz", + "integrity": "sha512-IqYvLbieTv20ArgKoAMyhLHNrVHJfzO6ARZAbQRlY4UGWfdDnLlZEF0BvKOMd77uIiIjSZRwq3Jb3Fa3I8+2UA==", + "dev": true, + "requires": { + "@jest/test-result": "^29.3.1", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.3.1", + "slash": "^3.0.0" + } + }, + "@jest/transform": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.3.1.tgz", + "integrity": "sha512-8wmCFBTVGYqFNLWfcOWoVuMuKYPUBTnTMDkdvFtAYELwDOl9RGwOsvQWGPFxDJ8AWY9xM/8xCXdqmPK3+Q5Lug==", + "dev": true, + "requires": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.3.1", + "@jridgewell/trace-mapping": "^0.3.15", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.3.1", + "jest-regex-util": "^29.2.0", + "jest-util": "^29.3.1", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.1" + } + }, + "@jest/types": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.3.1.tgz", + "integrity": "sha512-d0S0jmmTpjnhCmNpApgX3jrUZgZ22ivKJRvL2lli5hpCRoNnp1f85r2/wpKfXuYu8E7Jjh1hGfhPyup1NM5AmA==", + "dev": true, + "requires": { + "@jest/schemas": "^29.0.0", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true + }, + "@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true + }, + "@jridgewell/source-map": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "dependencies": { + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", "dev": true, "requires": { - "fs-mkdirp-stream": "^1.0.0", - "glob-stream": "^6.1.0", - "graceful-fs": "^4.0.0", - "is-valid-glob": "^1.0.0", - "lazystream": "^1.0.0", - "lead": "^1.0.0", - "object.assign": "^4.0.4", - "pumpify": "^1.3.5", - "readable-stream": "^2.3.3", - "remove-bom-buffer": "^3.0.0", - "remove-bom-stream": "^1.2.0", - "resolve-options": "^1.1.0", - "through2": "^2.0.0", - "to-through": "^2.0.0", - "value-or-function": "^3.0.0", - "vinyl": "^2.0.0", - "vinyl-sourcemap": "^1.1.0" + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" } } } }, - "gulp-util": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-3.0.8.tgz", - "integrity": "sha1-AFTh50RQLifATBh8PsxQXdVLu08=", - "dev": true, - "requires": { - "array-differ": "^1.0.0", - "array-uniq": "^1.0.2", - "beeper": "^1.0.0", - "chalk": "^1.0.0", - "dateformat": "^2.0.0", - "fancy-log": "^1.1.0", - "gulplog": "^1.0.0", - "has-gulplog": "^0.1.0", - "lodash._reescape": "^3.0.0", - "lodash._reevaluate": "^3.0.0", - "lodash._reinterpolate": "^3.0.0", - "lodash.template": "^3.0.0", - "minimist": "^1.1.0", - "multipipe": "^0.1.2", - "object-assign": "^3.0.0", - "replace-ext": "0.0.1", - "through2": "^2.0.0", - "vinyl": "^0.5.0" - }, - "dependencies": { - "object-assign": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", - "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=", + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@rollup/plugin-commonjs": { + "version": "23.0.4", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-23.0.4.tgz", + "integrity": "sha512-bOPJeTZg56D2MCm+TT4psP8e8Jmf1Jsi7pFUMl8BN5kOADNzofNHe47+84WVCt7D095xPghC235/YKuNDEhczg==", + "dev": true, + "requires": { + "@rollup/pluginutils": "^5.0.1", + "commondir": "^1.0.1", + "estree-walker": "^2.0.2", + "glob": "^8.0.3", + "is-reference": "1.2.1", + "magic-string": "^0.26.4" + } + }, + "@rollup/plugin-node-resolve": { + "version": "15.0.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.0.1.tgz", + "integrity": "sha512-ReY88T7JhJjeRVbfCyNj+NXAG3IIsVMsX9b5/9jC98dRP8/yxlZdz7mHZbHk5zHr24wZZICS5AcXsFZAXYUQEg==", + "dev": true, + "requires": { + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-builtin-module": "^3.2.0", + "is-module": "^1.0.0", + "resolve": "^1.22.1" + } + }, + "@rollup/pluginutils": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.2.tgz", + "integrity": "sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==", + "dev": true, + "requires": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + } + }, + "@sinclair/typebox": { + "version": "0.24.51", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", + "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", + "dev": true + }, + "@sinonjs/commons": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.5.tgz", + "integrity": "sha512-rTpCA0wG1wUxglBSFdMMY0oTrKYvgf4fNgv/sXbfCVAdf+FnPBdKJR/7XbpTCwbCrvCbdPYnlWaUUYz4V2fPDA==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", + "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, + "@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "dev": true + }, + "@types/babel__core": { + "version": "7.1.20", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.20.tgz", + "integrity": "sha512-PVb6Bg2QuscZ30FvOU7z4guG6c926D9YRvOxEaelzndpMsvP+YM74Q/dAFASpg2l6+XLalxSGxcq/lrgYWZtyQ==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.2.tgz", + "integrity": "sha512-FcFaxOr2V5KZCviw1TnutEMVUVsGt4D2hP1TAfXZAMKuHYW3xQhe3jTxNPWutgCJ3/X1c5yX8ZoGVEItxKbwBg==", + "dev": true, + "requires": { + "@babel/types": "^7.3.0" + } + }, + "@types/estree": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz", + "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==", + "dev": true + }, + "@types/graceful-fs": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", + "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true + }, + "@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "@types/jest": { + "version": "29.2.4", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.2.4.tgz", + "integrity": "sha512-PipFB04k2qTRPePduVLTRiPzQfvMeLwUN3Z21hsAKaB/W9IIzgB2pizCL466ftJlcyZqnHoC9ZHpxLGl3fS86A==", + "dev": true, + "requires": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "@types/node": { + "version": "18.11.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.12.tgz", + "integrity": "sha512-FgD3NtTAKvyMmD44T07zz2fEf+OKwutgBCEVM8GcvMGVGaDktiLNTDvPwC/LUe3PinMW+X6CuLOF2Ui1mAlSXg==", + "dev": true + }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "@types/prettier": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.1.tgz", + "integrity": "sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow==", + "dev": true + }, + "@types/resolve": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", + "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", + "dev": true + }, + "@types/semver": { + "version": "7.3.13", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", + "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", + "dev": true + }, + "@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "dev": true + }, + "@types/validator": { + "version": "13.7.10", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.7.10.tgz", + "integrity": "sha512-t1yxFAR2n0+VO6hd/FJ9F2uezAZVWHLmpmlJzm1eX03+H7+HsuTAp7L8QJs+2pQCfWkP1+EXsGK9Z9v7o/qPVQ==" + }, + "@types/yargs": { + "version": "17.0.13", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.13.tgz", + "integrity": "sha512-9sWaruZk2JGxIQU+IhI1fhPYRcQ0UuTNuKuCW9bR5fp7qi2Llf7WDzNa17Cy7TKnh3cdxDOiyTu6gaLS0eDatg==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true + }, + "@typescript-eslint/eslint-plugin": { + "version": "5.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.46.0.tgz", + "integrity": "sha512-QrZqaIOzJAjv0sfjY4EjbXUi3ZOFpKfzntx22gPGr9pmFcTjcFw/1sS1LJhEubfAGwuLjNrPV0rH+D1/XZFy7Q==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.46.0", + "@typescript-eslint/type-utils": "5.46.0", + "@typescript-eslint/utils": "5.46.0", + "debug": "^4.3.4", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "regexpp": "^3.2.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/parser": { + "version": "5.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.46.0.tgz", + "integrity": "sha512-joNO6zMGUZg+C73vwrKXCd8usnsmOYmgW/w5ZW0pG0RGvqeznjtGDk61EqqTpNrFLUYBW2RSBFrxdAZMqA4OZA==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.46.0", + "@typescript-eslint/types": "5.46.0", + "@typescript-eslint/typescript-estree": "5.46.0", + "debug": "^4.3.4" + } + }, + "@typescript-eslint/scope-manager": { + "version": "5.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.46.0.tgz", + "integrity": "sha512-7wWBq9d/GbPiIM6SqPK9tfynNxVbfpihoY5cSFMer19OYUA3l4powA2uv0AV2eAZV6KoAh6lkzxv4PoxOLh1oA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.46.0", + "@typescript-eslint/visitor-keys": "5.46.0" + } + }, + "@typescript-eslint/type-utils": { + "version": "5.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.46.0.tgz", + "integrity": "sha512-dwv4nimVIAsVS2dTA0MekkWaRnoYNXY26dKz8AN5W3cBFYwYGFQEqm/cG+TOoooKlncJS4RTbFKgcFY/pOiBCg==", + "dev": true, + "requires": { + "@typescript-eslint/typescript-estree": "5.46.0", + "@typescript-eslint/utils": "5.46.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/types": { + "version": "5.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.46.0.tgz", + "integrity": "sha512-wHWgQHFB+qh6bu0IAPAJCdeCdI0wwzZnnWThlmHNY01XJ9Z97oKqKOzWYpR2I83QmshhQJl6LDM9TqMiMwJBTw==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.46.0.tgz", + "integrity": "sha512-kDLNn/tQP+Yp8Ro2dUpyyVV0Ksn2rmpPpB0/3MO874RNmXtypMwSeazjEN/Q6CTp8D7ExXAAekPEcCEB/vtJkw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.46.0", + "@typescript-eslint/visitor-keys": "5.46.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": { + "version": "5.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.46.0.tgz", + "integrity": "sha512-4O+Ps1CRDw+D+R40JYh5GlKLQERXRKW5yIQoNDpmXPJ+C7kaPF9R7GWl+PxGgXjB3PQCqsaaZUpZ9dG4U6DO7g==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.46.0", + "@typescript-eslint/types": "5.46.0", + "@typescript-eslint/typescript-estree": "5.46.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0", + "semver": "^7.3.7" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.46.0.tgz", + "integrity": "sha512-E13gBoIXmaNhwjipuvQg1ByqSAu/GbEpP/qzFihugJ+MomtoJtFAJG/+2DRPByf57B863m0/q7Zt16V9ohhANw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.46.0", + "eslint-visitor-keys": "^3.3.0" + } + }, + "acorn": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", + "dev": true + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "requires": {} + }, + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true + }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "requires": { + "type-fest": "^0.21.3" + }, + "dependencies": { + "type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true } } }, - "gulpclass": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/gulpclass/-/gulpclass-0.1.2.tgz", - "integrity": "sha1-0m9cOdw5nhHEWpCHh8hkkLwx6FA=", + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, + "babel-jest": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.3.1.tgz", + "integrity": "sha512-aard+xnMoxgjwV70t0L6wkW/3HQQtV+O0PEimxKgzNqCJnbYmroPojdP2tqKSOAt8QAKV/uSZU8851M7B5+fcA==", + "dev": true, + "requires": { + "@jest/transform": "^29.3.1", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.2.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + } + }, + "babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + } + }, + "babel-plugin-jest-hoist": { + "version": "29.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.2.0.tgz", + "integrity": "sha512-TnspP2WNiR3GLfCsUNHqeXw0RoQ2f9U5hQ5L3XFpwuO8htQmSrhh8qsB6vi5Yi8+kuynN1yjDjQsPfkebmB6ZA==", + "dev": true, + "requires": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "requires": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@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-top-level-await": "^7.8.3" + } + }, + "babel-preset-jest": { + "version": "29.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.2.0.tgz", + "integrity": "sha512-z9JmMJppMxNv8N7fNRHvhMg9cvIkMxQBXgFkane3yKVEvEOP+kB50lk8DFRvF9PGqbyXxlmebKWhuDORO8RgdA==", + "dev": true, + "requires": { + "babel-plugin-jest-hoist": "^29.2.0", + "babel-preset-current-node-syntax": "^1.0.0" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browserslist": { + "version": "4.21.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", "dev": true, "requires": { - "gulp": "^3.9.0", - "merge2": "^0.3.6", - "run-sequence": "^1.1.5" + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.9" } }, - "gulplog": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz", - "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=", + "bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", "dev": true, "requires": { - "glogg": "^1.0.0" + "fast-json-stable-stringify": "2.x" } }, - "handlebars": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.11.tgz", - "integrity": "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw=", + "bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", "dev": true, "requires": { - "async": "^1.4.0", - "optimist": "^0.6.1", - "source-map": "^0.4.4", - "uglify-js": "^2.6" - }, - "dependencies": { - "source-map": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", - "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", - "dev": true, - "requires": { - "amdefine": ">=0.0.4" - } - } + "node-int64": "^0.4.0" } }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, - "har-validator": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", - "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", + "caniuse-lite": { + "version": "1.0.30001431", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001431.tgz", + "integrity": "sha512-zBUoFU0ZcxpvSt9IU66dXVT/3ctO1cy4y9cscs1szkPlcWb6pasYM144GqrUygUbT+k7cmUCW61cvskjcv0enQ==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "requires": { - "ajv": "^5.1.0", - "har-schema": "^2.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } }, - "has-ansi": { + "char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true + }, + "ci-info": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "cjs-module-lexer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", + "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", "dev": true }, - "has-gulplog": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/has-gulplog/-/has-gulplog-0.1.0.tgz", - "integrity": "sha1-ZBTIKRNpfaUVkDl9r7EvIpZ4Ec4=", + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "dev": true, "requires": { - "sparkles": "^1.0.0" + "restore-cursor": "^3.1.0" } }, - "has-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", - "dev": true - }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "cli-truncate": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", + "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", "dev": true, "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" + "slice-ansi": "^5.0.0", + "string-width": "^5.0.0" } }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" }, "dependencies": { - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" } } } }, - "he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", "dev": true }, - "homedir-polyfill": { + "collect-v8-coverage": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz", - "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "dev": true + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "parse-passwd": "^1.0.0" + "color-name": "~1.1.4" } }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "colorette": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", + "dev": true + }, + "commander": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.1.tgz", + "integrity": "sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, + "compare-versions": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz", + "integrity": "sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", "dev": true, "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" } }, - "ignore-walk": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", - "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "requires": { - "minimatch": "^3.0.4" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" } }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "requires": { - "once": "^1.3.0", - "wrappy": "1" + "ms": "2.1.2" } }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", "dev": true }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, - "interpret": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", - "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", "dev": true }, - "is-absolute": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", - "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", - "dev": true, - "requires": { - "is-relative": "^1.0.0", - "is-windows": "^1.0.1" - } + "detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "diff-sequences": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.3.1.tgz", + "integrity": "sha512-hlM3QR272NXCi4pq+N4Kok4kOp6EsgOM3ZSpJI7Da3UAs+Ttsi8MRmB6trM/lhyzUxGfOgnpkHtgqm5Q/CTcfQ==", "dev": true }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "path-type": "^4.0.0" } }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } + "esutils": "^2.0.2" } }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "electron-to-chromium": { + "version": "1.4.284", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", + "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", "dev": true }, - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "dev": true, "requires": { - "is-extglob": "^2.1.0" + "is-arrayish": "^0.2.1" } }, - "is-negated-glob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", - "integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI=", + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "dev": true }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "eslint": { + "version": "8.29.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.29.0.tgz", + "integrity": "sha512-isQ4EEiyUjZFbEKvEGJKKGBwXtvXX+zJbkVKCgTuB9t/+jUBcy8avhkEwWJecI15BkRkOYmvIM5ynbhRjEkoeg==", + "dev": true, + "requires": { + "@eslint/eslintrc": "^1.3.3", + "@humanwhocodes/config-array": "^0.11.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.10.0", + "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.4.0", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.15.0", + "grapheme-splitter": "^1.0.4", + "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-sdsl": "^4.1.4", + "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", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0" }, "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true } } }, - "is-path-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", - "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", - "dev": true + "eslint-config-prettier": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", + "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", + "dev": true, + "requires": {} }, - "is-path-in-cwd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", - "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", + "eslint-plugin-jest": { + "version": "27.1.6", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.1.6.tgz", + "integrity": "sha512-XA7RFLSrlQF9IGtAmhddkUkBuICCTuryfOTfCSWcZHiHb69OilIH05oozH2XA6CEOtztnOd0vgXyvxZodkxGjg==", "dev": true, "requires": { - "is-path-inside": "^1.0.0" + "@typescript-eslint/utils": "^5.10.0" } }, - "is-path-inside": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, "requires": { - "path-is-inside": "^1.0.1" + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" } }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", "dev": true, "requires": { - "isobject": "^3.0.1" + "eslint-visitor-keys": "^2.0.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + } } }, - "is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", "dev": true }, - "is-relative": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", - "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", + "espree": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", + "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", "dev": true, "requires": { - "is-unc-path": "^1.0.0" + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" } }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } }, - "is-unc-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", - "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "requires": { - "unc-path-regex": "^0.1.2" + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } } }, - "is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true }, - "is-valid-glob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-1.0.0.tgz", - "integrity": "sha1-Kb8+/3Ab4tTTFdusw5vDn+j2Aao=", + "estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", "dev": true }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", "dev": true }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true + "expect": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.3.1.tgz", + "integrity": "sha512-gGb1yTgU30Q0O/tQq+z30KBWv24ApkMgFUpvKBkyLUBL68Wv8dHdJxTBZFl/iT8K/bqDHvUYRH6IIN3rToopPA==", + "dev": true, + "requires": { + "@jest/expect-utils": "^29.3.1", + "jest-get-type": "^29.2.0", + "jest-matcher-utils": "^29.3.1", + "jest-message-util": "^29.3.1", + "jest-util": "^29.3.1" + } }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, - "istanbul": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/istanbul/-/istanbul-0.4.5.tgz", - "integrity": "sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs=", + "fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", "dev": true, "requires": { - "abbrev": "1.0.x", - "async": "1.x", - "escodegen": "1.8.x", - "esprima": "2.7.x", - "glob": "^5.0.15", - "handlebars": "^4.0.1", - "js-yaml": "3.x", - "mkdirp": "0.5.x", - "nopt": "3.x", - "once": "1.x", - "resolve": "1.1.x", - "supports-color": "^3.1.0", - "which": "^1.1.1", - "wordwrap": "^1.0.0" + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" }, "dependencies": { - "glob": { - "version": "5.0.15", - "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", - "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", - "dev": true, - "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", - "dev": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "requires": { - "has-flag": "^1.0.0" + "is-glob": "^4.0.1" } } } }, - "istanbul-threshold-checker": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/istanbul-threshold-checker/-/istanbul-threshold-checker-0.2.1.tgz", - "integrity": "sha1-xdyU6PLMXNP/0zVFL4S1U8QkgzE=", + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "requires": { + "bser": "2.1.1" + } + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, "requires": { - "istanbul": "~0.4.5", - "lodash": "~4.17.2" - }, - "dependencies": { - "lodash": { - "version": "4.17.10", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", - "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", - "dev": true - } + "to-regex-range": "^5.0.1" } }, - "istextorbinary": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-2.2.1.tgz", - "integrity": "sha512-TS+hoFl8Z5FAFMK38nhBkdLt44CclNRgDHWeMgsV8ko3nDlr/9UI2Sf839sW7enijf8oKsZYXRvM8g0it9Zmcw==", + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "requires": { - "binaryextensions": "2", - "editions": "^1.3.3", - "textextensions": "2" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" } }, - "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", - "dev": true - }, - "js-yaml": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", - "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", + "find-versions": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-4.0.0.tgz", + "integrity": "sha512-wgpWy002tA+wgmO27buH/9KzyEOQnKsG/R0yrcjPT9BOFm0zRBVQbZ95nRGXWMywS8YR5knRbpohio0bcJABxQ==", "dev": true, "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "dependencies": { - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - } + "semver-regex": "^3.1.2" } }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", "dev": true, - "optional": true + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, - "json-stable-stringify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, - "requires": { - "jsonify": "~0.0.0" - } + "optional": true }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, - "jsonify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "just-extend": { - "version": "1.1.27", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-1.1.27.tgz", - "integrity": "sha512-mJVp13Ix6gFo3SBAy9U/kL+oeZqzlYYYLQBwXVBlVzIsZwBqGREnOro24oC/8s8aox+rJhtZ2DiQof++IrkA+g==", + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true }, - "lazy-cache": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", - "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", - "dev": true, - "optional": true + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true }, - "lazystream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", - "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", + "glob": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", "dev": true, "requires": { - "readable-stream": "^2.0.5" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" }, "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "balanced-match": "^1.0.0" } }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "brace-expansion": "^2.0.1" } } } }, - "lead": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lead/-/lead-1.0.0.tgz", - "integrity": "sha1-bxT5mje+Op3XhPVJVpDlkDRm7kI=", + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "requires": { - "flush-write-stream": "^1.0.2" + "is-glob": "^4.0.3" } }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "globals": { + "version": "13.17.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", + "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", "dev": true, "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" + "type-fest": "^0.20.2" } }, - "liftoff": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-2.5.0.tgz", - "integrity": "sha1-IAkpG7Mc6oYbvxCnwVooyvdcMew=", + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "requires": { - "extend": "^3.0.0", - "findup-sync": "^2.0.0", - "fined": "^1.0.1", - "flagged-respawn": "^1.0.0", - "is-plain-object": "^2.0.4", - "object.map": "^1.0.0", - "rechoir": "^0.6.2", - "resolve": "^1.1.7" + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" } }, - "lodash": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-1.0.2.tgz", - "integrity": "sha1-j1dWDIO1n8JwvT1WG2kAQ0MOJVE=", - "dev": true - }, - "lodash._basecopy": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", - "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=", - "dev": true - }, - "lodash._basetostring": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz", - "integrity": "sha1-0YYdh3+CSlL2aYMtyvPuFVZqB9U=", + "graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, - "lodash._basevalues": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz", - "integrity": "sha1-W3dXYoAr3j0yl1A+JjAIIP32Ybc=", + "grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", "dev": true }, - "lodash._getnative": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", - "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=", - "dev": true + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } }, - "lodash._isiterateecall": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", - "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=", + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, - "lodash._reescape": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._reescape/-/lodash._reescape-3.0.0.tgz", - "integrity": "sha1-Kx1vXf4HyKNVdT5fJ/rH8c3hYWo=", + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, - "lodash._reevaluate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz", - "integrity": "sha1-WLx0xAZklTrgsSTYBpltrKQx4u0=", + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "dev": true }, - "lodash._reinterpolate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", - "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", - "dev": true + "husky": { + "version": "4.3.8", + "resolved": "https://registry.npmjs.org/husky/-/husky-4.3.8.tgz", + "integrity": "sha512-LCqqsB0PzJQ/AlCgfrfzRe3e3+NvmefAdKQhRYpxS4u6clblBoDdzzvHi8fmxKRzvMxPY/1WZWzomPZww0Anow==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "ci-info": "^2.0.0", + "compare-versions": "^3.6.0", + "cosmiconfig": "^7.0.0", + "find-versions": "^4.0.0", + "opencollective-postinstall": "^2.0.2", + "pkg-dir": "^5.0.0", + "please-upgrade-node": "^3.2.0", + "slash": "^3.0.0", + "which-pm-runs": "^1.0.0" + } }, - "lodash._root": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz", - "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=", + "ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", "dev": true }, - "lodash.escape": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-3.2.0.tgz", - "integrity": "sha1-mV7g3BjBtIzJLv+ucaEKq1tIdpg=", + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, "requires": { - "lodash._root": "^3.0.0" + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" } }, - "lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", - "dev": true - }, - "lodash.isarguments": { + "import-local": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", - "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "requires": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + } + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true }, - "lodash.isarray": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", - "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true }, - "lodash.keys": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", - "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, "requires": { - "lodash._getnative": "^3.0.0", - "lodash.isarguments": "^3.0.0", - "lodash.isarray": "^3.0.0" + "once": "^1.3.0", + "wrappy": "1" } }, - "lodash.restparam": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz", - "integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=", + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "lodash.template": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz", - "integrity": "sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8=", - "dev": true, - "requires": { - "lodash._basecopy": "^3.0.0", - "lodash._basetostring": "^3.0.0", - "lodash._basevalues": "^3.0.0", - "lodash._isiterateecall": "^3.0.0", - "lodash._reinterpolate": "^3.0.0", - "lodash.escape": "^3.0.0", - "lodash.keys": "^3.0.0", - "lodash.restparam": "^3.0.0", - "lodash.templatesettings": "^3.0.0" - } + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true }, - "lodash.templatesettings": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz", - "integrity": "sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU=", + "is-builtin-module": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.0.tgz", + "integrity": "sha512-phDA4oSGt7vl1n5tJvTWooWWAsXLY+2xCnxNqvKhGEzujg+A43wPlPOyDg3C8XQHN+6k/JTQWJ/j0dQh/qr+Hw==", "dev": true, "requires": { - "lodash._reinterpolate": "^3.0.0", - "lodash.escape": "^3.0.0" + "builtin-modules": "^3.3.0" } }, - "log-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", - "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=", + "is-core-module": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", "dev": true, "requires": { - "chalk": "^1.0.0" + "has": "^1.0.3" } }, - "lolex": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-2.7.1.tgz", - "integrity": "sha512-Oo2Si3RMKV3+lV5MsSWplDQFoTClz/24S0MMHYcgGWWmFXr6TMlqcqk/l1GtH+d5wLBwNRiqGnwDRMirtFalJw==", + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true }, - "longest": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", - "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", + "is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", "dev": true }, - "lru-cache": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", - "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=", + "is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", "dev": true }, - "lru-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", - "integrity": "sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM=", + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "requires": { - "es5-ext": "~0.10.2" + "is-extglob": "^2.1.1" } }, - "make-error": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.4.tgz", - "integrity": "sha512-0Dab5btKVPhibSalc9QGXb559ED7G7iLjFXBaj9Wq8O3vorueR5K5jaE3hkG6ZQINyhA/JgG6Qk4qdFQjsYV6g==", + "is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", "dev": true }, - "make-iterator": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", - "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true + }, + "is-reference": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", "dev": true, "requires": { - "kind-of": "^6.0.2" + "@types/estree": "*" } }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true }, - "map-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", - "integrity": "sha1-ih8HiW2CsQkmvTdEokIACfiJdKg=", + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", "dev": true, "requires": { - "object-visit": "^1.0.0" + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, - "memoizee": { - "version": "0.4.12", - "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.12.tgz", - "integrity": "sha512-sprBu6nwxBWBvBOh5v2jcsGqiGLlL2xr2dLub3vR8dnE8YB17omwtm/0NSHl8jjNbcsJd5GMWJAnTSVe/O0Wfg==", + "istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", "dev": true, "requires": { - "d": "1", - "es5-ext": "^0.10.30", - "es6-weak-map": "^2.0.2", - "event-emitter": "^0.3.5", - "is-promise": "^2.1", - "lru-queue": "0.1", - "next-tick": "1", - "timers-ext": "^0.1.2" + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" } }, - "merge2": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-0.3.7.tgz", - "integrity": "sha1-GOAKAGcQkN4RZQJfyGvT53wgf2M=", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", "dev": true, "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" } }, - "mime-db": { - "version": "1.35.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.35.0.tgz", - "integrity": "sha512-JWT/IcCTsB0Io3AhWUMjRqucrHSPsSf2xKLaRldJVULioggvkJvggZ3VXNNSRkCddE6D+BUI4HEIZIA2OjwIvg==", - "dev": true - }, - "mime-types": { - "version": "2.1.19", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.19.tgz", - "integrity": "sha512-P1tKYHVSZ6uFo26mtnve4HQFE3koh1UWVkp8YUC+ESBHe945xWSoXuHHiGarDqcEZ+whpCDnlNw5LON0kLo+sw==", + "istanbul-reports": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", "dev": true, "requires": { - "mime-db": "~1.35.0" + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" } }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "jest": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.3.1.tgz", + "integrity": "sha512-6iWfL5DTT0Np6UYs/y5Niu7WIfNv/wRTtN5RSXt2DIEft3dx3zPuw/3WJQBCJfmEzvDiEKwoqMbGD9n49+qLSA==", "dev": true, "requires": { - "brace-expansion": "^1.1.7" + "@jest/core": "^29.3.1", + "@jest/types": "^29.3.1", + "import-local": "^3.0.2", + "jest-cli": "^29.3.1" } }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - }, - "mixin-deep": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", - "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "jest-changed-files": { + "version": "29.2.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.2.0.tgz", + "integrity": "sha512-qPVmLLyBmvF5HJrY7krDisx6Voi8DmlV3GZYX0aFNbaQsZeoz1hfxcCMbqDGuQCxU1dJy9eYc2xscE8QrCCYaA==", "dev": true, "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } + "execa": "^5.0.0", + "p-limit": "^3.1.0" } }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "jest-circus": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.3.1.tgz", + "integrity": "sha512-wpr26sEvwb3qQQbdlmei+gzp6yoSSoSL6GsLPxnuayZSMrSd5Ka7IjAvatpIernBvT2+Ic6RLTg+jSebScmasg==", "dev": true, "requires": { - "minimist": "0.0.8" + "@jest/environment": "^29.3.1", + "@jest/expect": "^29.3.1", + "@jest/test-result": "^29.3.1", + "@jest/types": "^29.3.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.3.1", + "jest-matcher-utils": "^29.3.1", + "jest-message-util": "^29.3.1", + "jest-runtime": "^29.3.1", + "jest-snapshot": "^29.3.1", + "jest-util": "^29.3.1", + "p-limit": "^3.1.0", + "pretty-format": "^29.3.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + } + }, + "jest-cli": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.3.1.tgz", + "integrity": "sha512-TO/ewvwyvPOiBBuWZ0gm04z3WWP8TIK8acgPzE4IxgsLKQgb377NYGrQLc3Wl/7ndWzIH2CDNNsUjGxwLL43VQ==", + "dev": true, + "requires": { + "@jest/core": "^29.3.1", + "@jest/test-result": "^29.3.1", + "@jest/types": "^29.3.1", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^29.3.1", + "jest-util": "^29.3.1", + "jest-validate": "^29.3.1", + "prompts": "^2.0.1", + "yargs": "^17.3.1" + } + }, + "jest-config": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.3.1.tgz", + "integrity": "sha512-y0tFHdj2WnTEhxmGUK1T7fgLen7YK4RtfvpLFBXfQkh2eMJAQq24Vx9472lvn5wg0MAO6B+iPfJfzdR9hJYalg==", + "dev": true, + "requires": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.3.1", + "@jest/types": "^29.3.1", + "babel-jest": "^29.3.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.3.1", + "jest-environment-node": "^29.3.1", + "jest-get-type": "^29.2.0", + "jest-regex-util": "^29.2.0", + "jest-resolve": "^29.3.1", + "jest-runner": "^29.3.1", + "jest-util": "^29.3.1", + "jest-validate": "^29.3.1", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.3.1", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" }, "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - } - } - }, - "mocha": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", - "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", - "dev": true, - "requires": { - "browser-stdout": "1.3.1", - "commander": "2.15.1", - "debug": "3.1.0", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "glob": "7.1.2", - "growl": "1.10.5", - "he": "1.1.1", - "minimatch": "3.0.4", - "mkdirp": "0.5.1", - "supports-color": "5.4.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "ci-info": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.6.0.tgz", + "integrity": "sha512-RfY02LtqkzB/aiyTrh0TtDS0rQnQY3kyvkVkdBnPTUbkiGbkkCJhS7Oj8b37Qa/5eGiMilF3kH+/Km1EZxpuyQ==", "dev": true }, - "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } } } }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "multipipe": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/multipipe/-/multipipe-0.1.2.tgz", - "integrity": "sha1-Ko8t33Du1WTf8tV/HhoTfZ8FB4s=", - "dev": true, - "requires": { - "duplexer2": "0.0.2" - } - }, - "nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - } - }, - "natives": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.4.tgz", - "integrity": "sha512-Q29yeg9aFKwhLVdkTAejM/HvYG0Y1Am1+HUkFQGn5k2j8GS+v60TVmZh6nujpEAj/qql+wGUrlryO8bF+b1jEg==", - "dev": true - }, - "next-tick": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", - "dev": true - }, - "nice-try": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.4.tgz", - "integrity": "sha512-2NpiFHqC87y/zFke0fC0spBXL3bBsoh/p5H1EFhshxjCR5+0g2d6BiXbUFz9v1sAcxsk2htp2eQnNIci2dIYcA==", - "dev": true - }, - "nise": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/nise/-/nise-1.4.2.tgz", - "integrity": "sha512-BxH/DxoQYYdhKgVAfqVy4pzXRZELHOIewzoesxpjYvpU+7YOalQhGNPf7wAx8pLrTNPrHRDlLOkAl8UI0ZpXjw==", - "dev": true, - "requires": { - "@sinonjs/formatio": "^2.0.0", - "just-extend": "^1.1.27", - "lolex": "^2.3.2", - "path-to-regexp": "^1.7.0", - "text-encoding": "^0.6.4" - } - }, - "nopt": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", - "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "jest-diff": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.3.1.tgz", + "integrity": "sha512-vU8vyiO7568tmin2lA3r2DP8oRvzhvRcD4DjpXc6uGveQodyk7CKLhQlCSiwgx3g0pFaE88/KLZ0yaTWMc4Uiw==", "dev": true, "requires": { - "abbrev": "1" + "chalk": "^4.0.0", + "diff-sequences": "^29.3.1", + "jest-get-type": "^29.2.0", + "pretty-format": "^29.3.1" } }, - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "jest-docblock": { + "version": "29.2.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.2.0.tgz", + "integrity": "sha512-bkxUsxTgWQGbXV5IENmfiIuqZhJcyvF7tU4zJ/7ioTutdz4ToB5Yx6JOFBpgI+TphRY4lhOyCWGNH/QFQh5T6A==", "dev": true, "requires": { - "remove-trailing-separator": "^1.0.1" + "detect-newline": "^3.0.0" } }, - "now-and-later": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.0.tgz", - "integrity": "sha1-vGHLtFbXnLMiB85HygUTb/Ln1u4=", + "jest-each": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.3.1.tgz", + "integrity": "sha512-qrZH7PmFB9rEzCSl00BWjZYuS1BSOH8lLuC0azQE9lQrAx3PWGKHTDudQiOSwIy5dGAJh7KA0ScYlCP7JxvFYA==", "dev": true, "requires": { - "once": "^1.3.2" + "@jest/types": "^29.3.1", + "chalk": "^4.0.0", + "jest-get-type": "^29.2.0", + "jest-util": "^29.3.1", + "pretty-format": "^29.3.1" } }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "jest-environment-node": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.3.1.tgz", + "integrity": "sha512-xm2THL18Xf5sIHoU7OThBPtuH6Lerd+Y1NLYiZJlkE3hbE+7N7r8uvHIl/FkZ5ymKXJe/11SQuf3fv4v6rUMag==", "dev": true, "requires": { - "path-key": "^2.0.0" + "@jest/environment": "^29.3.1", + "@jest/fake-timers": "^29.3.1", + "@jest/types": "^29.3.1", + "@types/node": "*", + "jest-mock": "^29.3.1", + "jest-util": "^29.3.1" } }, - "oauth-sign": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", - "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", + "jest-get-type": { + "version": "29.2.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.2.0.tgz", + "integrity": "sha512-uXNJlg8hKFEnDgFsrCjznB+sTxdkuqiCL6zMgA75qEbAJjJYTs9XPrvDctrEig2GDow22T/LvHgO57iJhXB/UA==", "dev": true }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "jest-haste-map": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.3.1.tgz", + "integrity": "sha512-/FFtvoG1xjbbPXQLFef+WSU4yrc0fc0Dds6aRPBojUid7qlPqZvxdUBA03HW0fnVHXVCnCdkuoghYItKNzc/0A==", "dev": true, "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "@jest/types": "^29.3.1", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.3.2", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.2.0", + "jest-util": "^29.3.1", + "jest-worker": "^29.3.1", + "micromatch": "^4.0.4", + "walker": "^1.0.8" } }, - "object-keys": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz", - "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==", - "dev": true - }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "jest-leak-detector": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.3.1.tgz", + "integrity": "sha512-3DA/VVXj4zFOPagGkuqHnSQf1GZBmmlagpguxEERO6Pla2g84Q1MaVIB3YMxgUaFIaYag8ZnTyQgiZ35YEqAQA==", "dev": true, "requires": { - "isobject": "^3.0.0" + "jest-get-type": "^29.2.0", + "pretty-format": "^29.3.1" } }, - "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "jest-matcher-utils": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.3.1.tgz", + "integrity": "sha512-fkRMZUAScup3txIKfMe3AIZZmPEjWEdsPJFK3AIy5qRohWqQFg1qrmKfYXR9qEkNc7OdAu2N4KPHibEmy4HPeQ==", "dev": true, "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" + "chalk": "^4.0.0", + "jest-diff": "^29.3.1", + "jest-get-type": "^29.2.0", + "pretty-format": "^29.3.1" } }, - "object.defaults": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", - "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", + "jest-message-util": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.3.1.tgz", + "integrity": "sha512-lMJTbgNcDm5z+6KDxWtqOFWlGQxD6XaYwBqHR8kmpkP+WWWG90I35kdtQHY67Ay5CSuydkTBbJG+tH9JShFCyA==", "dev": true, "requires": { - "array-each": "^1.0.1", - "array-slice": "^1.0.0", - "for-own": "^1.0.0", - "isobject": "^3.0.0" + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.3.1", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.3.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" } }, - "object.map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", - "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=", + "jest-mock": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.3.1.tgz", + "integrity": "sha512-H8/qFDtDVMFvFP4X8NuOT3XRDzOUTz+FeACjufHzsOIBAxivLqkB1PoLCaJx9iPPQ8dZThHPp/G3WRWyMgA3JA==", "dev": true, "requires": { - "for-own": "^1.0.0", - "make-iterator": "^1.0.0" + "@jest/types": "^29.3.1", + "@types/node": "*", + "jest-util": "^29.3.1" } }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "jest-pnp-resolver": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", + "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", + "dev": true, + "requires": {} + }, + "jest-regex-util": { + "version": "29.2.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.2.0.tgz", + "integrity": "sha512-6yXn0kg2JXzH30cr2NlThF+70iuO/3irbaB4mh5WyqNIvLLP+B6sFdluO1/1RJmslyh/f9osnefECflHvTbwVA==", + "dev": true + }, + "jest-resolve": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.3.1.tgz", + "integrity": "sha512-amXJgH/Ng712w3Uz5gqzFBBjxV8WFLSmNjoreBGMqxgCz5cH7swmBZzgBaCIOsvb0NbpJ0vgaSFdJqMdT+rADw==", "dev": true, "requires": { - "isobject": "^3.0.1" + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.3.1", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.3.1", + "jest-validate": "^29.3.1", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", + "slash": "^3.0.0" } }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "jest-resolve-dependencies": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.3.1.tgz", + "integrity": "sha512-Vk0cYq0byRw2WluNmNWGqPeRnZ3p3hHmjJMp2dyyZeYIfiBskwq4rpiuGFR6QGAdbj58WC7HN4hQHjf2mpvrLA==", "dev": true, "requires": { - "wrappy": "1" + "jest-regex-util": "^29.2.0", + "jest-snapshot": "^29.3.1" } }, - "optimist": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "jest-runner": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.3.1.tgz", + "integrity": "sha512-oFvcwRNrKMtE6u9+AQPMATxFcTySyKfLhvso7Sdk/rNpbhg4g2GAGCopiInk1OP4q6gz3n6MajW4+fnHWlU3bA==", "dev": true, "requires": { - "minimist": "~0.0.1", - "wordwrap": "~0.0.2" + "@jest/console": "^29.3.1", + "@jest/environment": "^29.3.1", + "@jest/test-result": "^29.3.1", + "@jest/transform": "^29.3.1", + "@jest/types": "^29.3.1", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.2.0", + "jest-environment-node": "^29.3.1", + "jest-haste-map": "^29.3.1", + "jest-leak-detector": "^29.3.1", + "jest-message-util": "^29.3.1", + "jest-resolve": "^29.3.1", + "jest-runtime": "^29.3.1", + "jest-util": "^29.3.1", + "jest-watcher": "^29.3.1", + "jest-worker": "^29.3.1", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + } + }, + "jest-runtime": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.3.1.tgz", + "integrity": "sha512-jLzkIxIqXwBEOZx7wx9OO9sxoZmgT2NhmQKzHQm1xwR1kNW/dn0OjxR424VwHHf1SPN6Qwlb5pp1oGCeFTQ62A==", + "dev": true, + "requires": { + "@jest/environment": "^29.3.1", + "@jest/fake-timers": "^29.3.1", + "@jest/globals": "^29.3.1", + "@jest/source-map": "^29.2.0", + "@jest/test-result": "^29.3.1", + "@jest/transform": "^29.3.1", + "@jest/types": "^29.3.1", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.3.1", + "jest-message-util": "^29.3.1", + "jest-mock": "^29.3.1", + "jest-regex-util": "^29.2.0", + "jest-resolve": "^29.3.1", + "jest-snapshot": "^29.3.1", + "jest-util": "^29.3.1", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" }, "dependencies": { - "minimist": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", - "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", - "dev": true - }, - "wordwrap": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", - "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "jest-snapshot": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.3.1.tgz", + "integrity": "sha512-+3JOc+s28upYLI2OJM4PWRGK9AgpsMs/ekNryUV0yMBClT9B1DF2u2qay8YxcQd338PPYSFNb0lsar1B49sLDA==", + "dev": true, + "requires": { + "@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": "^29.3.1", + "@jest/transform": "^29.3.1", + "@jest/types": "^29.3.1", + "@types/babel__traverse": "^7.0.6", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.3.1", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.3.1", + "jest-get-type": "^29.2.0", + "jest-haste-map": "^29.3.1", + "jest-matcher-utils": "^29.3.1", + "jest-message-util": "^29.3.1", + "jest-util": "^29.3.1", + "natural-compare": "^1.4.0", + "pretty-format": "^29.3.1", + "semver": "^7.3.5" + } + }, + "jest-util": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.3.1.tgz", + "integrity": "sha512-7YOVZaiX7RJLv76ZfHt4nbNEzzTRiMW/IiOG7ZOKmTXmoGBxUDefgMAxQubu6WPVqP5zSzAdZG0FfLcC7HOIFQ==", + "dev": true, + "requires": { + "@jest/types": "^29.3.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "dependencies": { + "ci-info": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.6.0.tgz", + "integrity": "sha512-RfY02LtqkzB/aiyTrh0TtDS0rQnQY3kyvkVkdBnPTUbkiGbkkCJhS7Oj8b37Qa/5eGiMilF3kH+/Km1EZxpuyQ==", "dev": true } } }, - "optionator": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", - "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "jest-validate": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.3.1.tgz", + "integrity": "sha512-N9Lr3oYR2Mpzuelp1F8negJR3YE+L1ebk1rYA5qYo9TTY3f9OWdptLoNSPP9itOCBIRBqjt/S5XHlzYglLN67g==", "dev": true, "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.4", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "wordwrap": "~1.0.0" + "@jest/types": "^29.3.1", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.2.0", + "leven": "^3.1.0", + "pretty-format": "^29.3.1" + }, + "dependencies": { + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + } } }, - "orchestrator": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/orchestrator/-/orchestrator-0.3.8.tgz", - "integrity": "sha1-FOfp4nZPcxX7rBhOUGx6pt+UrX4=", + "jest-watcher": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.3.1.tgz", + "integrity": "sha512-RspXG2BQFDsZSRKGCT/NiNa8RkQ1iKAjrO0//soTMWx/QUt+OcxMqMSBxz23PYGqUuWm2+m2mNNsmj0eIoOaFg==", "dev": true, "requires": { - "end-of-stream": "~0.1.5", - "sequencify": "~0.0.7", - "stream-consume": "~0.1.0" + "@jest/test-result": "^29.3.1", + "@jest/types": "^29.3.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.3.1", + "string-length": "^4.0.1" } }, - "ordered-read-streams": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-0.1.0.tgz", - "integrity": "sha1-/VZamvjrRHO6abbtijQ1LLVS8SY=", - "dev": true - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "dev": true + "jest-worker": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.3.1.tgz", + "integrity": "sha512-lY4AnnmsEWeiXirAIA0c9SDPbuCBq8IYuDVL8PMm0MZ2PEs2yPvRA/J64QBXuZp7CYKrDM/rmNrc9/i3KJQncw==", + "dev": true, + "requires": { + "@types/node": "*", + "jest-util": "^29.3.1", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "dependencies": { + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "js-sdsl": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.5.tgz", + "integrity": "sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q==", "dev": true }, - "p-map": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", - "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, - "parse-filepath": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", - "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=", + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "requires": { - "is-absolute": "^1.0.0", - "map-cache": "^0.2.0", - "path-root": "^0.1.1" + "argparse": "^2.0.1" } }, - "parse-passwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, - "path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, - "path-is-absolute": { + "json-stable-stringify-without-jsonify": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "json5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", "dev": true }, - "path-parse": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", - "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "dev": true }, - "path-root": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", - "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", - "dev": true, - "requires": { - "path-root-regex": "^0.1.0" - } - }, - "path-root-regex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", - "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=", + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", "dev": true }, - "path-to-regexp": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", - "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "requires": { - "isarray": "0.0.1" + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" } }, - "pathval": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", - "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", - "dev": true - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "dev": true, - "requires": { - "pinkie": "^2.0.0" - } + "libphonenumber-js": { + "version": "1.10.14", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.14.tgz", + "integrity": "sha512-McGS7GV/WjJ2KjfOGhJU1oJn29RYeo7Q+RpANRbUNMQ9gj5XArpbjurSuyYPTejFwbaUojstQ4XyWCrAzGOUXw==" }, - "plugin-error": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", - "integrity": "sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4=", - "dev": true, - "requires": { - "ansi-cyan": "^0.1.1", - "ansi-red": "^0.1.1", - "arr-diff": "^1.0.1", - "arr-union": "^2.0.1", - "extend-shallow": "^1.1.2" + "lilconfig": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", + "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==", + "dev": true + }, + "lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "lint-staged": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.1.0.tgz", + "integrity": "sha512-pn/sR8IrcF/T0vpWLilih8jmVouMlxqXxKuAojmbiGX5n/gDnz+abdPptlj0vYnbfE0SQNl3CY/HwtM0+yfOVQ==", + "dev": true, + "requires": { + "cli-truncate": "^3.1.0", + "colorette": "^2.0.19", + "commander": "^9.4.1", + "debug": "^4.3.4", + "execa": "^6.1.0", + "lilconfig": "2.0.6", + "listr2": "^5.0.5", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-inspect": "^1.12.2", + "pidtree": "^0.6.0", + "string-argv": "^0.3.1", + "yaml": "^2.1.3" }, "dependencies": { - "arr-diff": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", - "integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=", + "execa": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-6.1.0.tgz", + "integrity": "sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==", "dev": true, "requires": { - "arr-flatten": "^1.0.1", - "array-slice": "^0.2.3" + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^3.0.1", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" } }, - "arr-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", - "integrity": "sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0=", + "human-signals": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-3.0.1.tgz", + "integrity": "sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==", + "dev": true + }, + "is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", "dev": true }, - "array-slice": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", - "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", + "mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", "dev": true }, - "extend-shallow": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", - "integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=", + "npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "dev": true, + "requires": { + "path-key": "^4.0.0" + } + }, + "onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", "dev": true, "requires": { - "kind-of": "^1.1.0" + "mimic-fn": "^4.0.0" } }, - "kind-of": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", - "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=", + "path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true + }, + "strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true + }, + "yaml": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.1.3.tgz", + "integrity": "sha512-AacA8nRULjKMX2DvWvOAdBZMOfQlypSFkjcOcu9FalllIDJ1kvlREzcdIZmidQUqqeMv7jorHjq2HlLv/+c2lg==", "dev": true } } }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true - }, - "pretty-hrtime": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", - "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", - "dev": true - }, - "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", - "dev": true - }, - "pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "listr2": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-5.0.5.tgz", + "integrity": "sha512-DpBel6fczu7oQKTXMekeprc0o3XDgGMkD7JNYyX+X0xbwK+xgrx9dcyKoXKqpLSUvAWfmoePS7kavniOcq3r4w==", "dev": true, "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "cli-truncate": "^2.1.0", + "colorette": "^2.0.19", + "log-update": "^4.0.0", + "p-map": "^4.0.0", + "rfdc": "^1.3.0", + "rxjs": "^7.5.6", + "through": "^2.3.8", + "wrap-ansi": "^7.0.0" }, "dependencies": { - "end-of-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "requires": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "requires": { - "once": "^1.4.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" } } } }, - "pumpify": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", - "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "requires": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" + "p-locate": "^5.0.0" } }, - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", "dev": true }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", - "dev": true, - "requires": { - "resolve": "^1.1.6" - } - }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - } - }, - "remap-istanbul": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/remap-istanbul/-/remap-istanbul-0.11.1.tgz", - "integrity": "sha512-Itv3XvYjD6G+9xDzAeFohx4GUwbFjfqFt0UXlC826jHR18E49fEiEGqZUxUASwMq4z7wwUv2H9/XF2d6qj0iaQ==", + "log-update": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", "dev": true, "requires": { - "amdefine": "^1.0.0", - "istanbul": "0.4.5", - "minimatch": "^3.0.3", - "plugin-error": "^0.1.2", - "source-map": "^0.6.1", - "through2": "2.0.1" + "ansi-escapes": "^4.3.0", + "cli-cursor": "^3.1.0", + "slice-ansi": "^4.0.0", + "wrap-ansi": "^6.2.0" }, "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true }, - "readable-stream": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", - "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "~1.0.0", - "process-nextick-args": "~1.0.6", - "string_decoder": "~0.10.x", - "util-deprecate": "~1.0.1" + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" } }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } }, - "through2": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.1.tgz", - "integrity": "sha1-OE51MU1J8y3hLuu4E2uOtrXVnak=", + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, "requires": { - "readable-stream": "~2.0.0", - "xtend": "~4.0.0" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" } } } }, - "remove-bom-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz", - "integrity": "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==", + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "requires": { - "is-buffer": "^1.1.5", - "is-utf8": "^0.2.1" + "yallist": "^4.0.0" } }, - "remove-bom-stream": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz", - "integrity": "sha1-BfGlk/FuQuH7kOv1nejlaVJflSM=", + "magic-string": { + "version": "0.26.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.7.tgz", + "integrity": "sha512-hX9XH3ziStPoPhJxLq1syWuZMxbDvGNbVchfrdCtanC7D13888bMFow61x8axrx+GfHLtVeAx2kxL7tTGRl+Ow==", "dev": true, "requires": { - "remove-bom-buffer": "^3.0.0", - "safe-buffer": "^5.1.0", - "through2": "^2.0.3" + "sourcemap-codec": "^1.4.8" } }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, - "repeat-element": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", - "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", + "makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "requires": { + "tmpl": "1.0.5" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true }, - "replace-ext": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", - "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=", + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true }, - "replacestream": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/replacestream/-/replacestream-4.0.3.tgz", - "integrity": "sha512-AC0FiLS352pBBiZhd4VXB1Ab/lh0lEgpP+GGvZqbQh8a5cmXVoTe5EX/YeTFArnp4SRGTHh1qCHu9lGs1qG8sA==", + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "requires": { - "escape-string-regexp": "^1.0.3", - "object-assign": "^4.0.1", - "readable-stream": "^2.0.2" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } + "brace-expansion": "^1.1.7" } }, - "request": { - "version": "2.87.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.87.0.tgz", - "integrity": "sha512-fcogkm7Az5bsS6Sl0sibkbhcKsnyon/jV1kF3ajGmF0c8HrttdKTPRT9hieOaQHA5HEq6r8OyWOo/o781C1tNw==", - "dev": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.6.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.5", - "extend": "~3.0.1", - "forever-agent": "~0.6.1", - "form-data": "~2.3.1", - "har-validator": "~5.0.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.17", - "oauth-sign": "~0.8.2", - "performance-now": "^2.1.0", - "qs": "~6.5.1", - "safe-buffer": "^5.1.1", - "tough-cookie": "~2.3.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.1.0" - } + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, + "node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "dev": true }, - "resolve": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", - "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==", + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, "requires": { - "path-parse": "^1.0.5" + "path-key": "^3.0.0" } }, - "resolve-dir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", - "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "requires": { - "expand-tilde": "^2.0.0", - "global-modules": "^1.0.0" + "wrappy": "1" } }, - "resolve-options": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-1.1.0.tgz", - "integrity": "sha1-MrueOcBtZzONyTeMDW1gdFZq0TE=", + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, "requires": { - "value-or-function": "^3.0.0" + "mimic-fn": "^2.1.0" } }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "opencollective-postinstall": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", + "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", "dev": true }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "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" + } }, - "right-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", - "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, - "optional": true, "requires": { - "align-text": "^0.1.1" + "yocto-queue": "^0.1.0" } }, - "rimraf": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "requires": { - "glob": "^7.0.5" + "p-limit": "^3.0.2" } }, - "run-sequence": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/run-sequence/-/run-sequence-1.2.2.tgz", - "integrity": "sha1-UJWgvr6YczsBQL0I3YDsAw3azes=", + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", "dev": true, "requires": { - "chalk": "*", - "gulp-util": "*" + "aggregate-error": "^3.0.0" } }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, - "safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "requires": { - "ret": "~0.1.10" + "callsites": "^3.0.0" } }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } }, - "samsam": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.3.0.tgz", - "integrity": "sha512-1HwIYD/8UlOtFS3QO3w7ey+SdSDFE4HRNLZoZRYVQefrOY3l17epswImeB1ijgJFQJodIaHcwkp3r/myBjFVbg==", + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true }, - "semver": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz", - "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=", + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true }, - "sequencify": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/sequencify/-/sequencify-0.0.7.tgz", - "integrity": "sha1-kM/xnQLgcCf9dn9erT57ldHnOAw=", + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, - "set-value": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true }, - "shebang-regex": { + "picocolors": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "dev": true }, - "sigmund": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", - "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=", + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true + }, + "pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", "dev": true }, - "sinon": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-6.1.3.tgz", - "integrity": "sha512-yeTza8xIZZdiXntCHJAzKll/sSYE+DuJOS8hiSapzaLqdW8eCNVVC9je9SZYYTkPm2bLts9x6UYxwuMAVVrM6Q==", + "pkg-dir": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-5.0.0.tgz", + "integrity": "sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==", "dev": true, "requires": { - "@sinonjs/formatio": "^2.0.0", - "@sinonjs/samsam": "^2.0.0", - "diff": "^3.5.0", - "lodash.get": "^4.4.2", - "lolex": "^2.4.2", - "nise": "^1.3.3", - "supports-color": "^5.4.0", - "type-detect": "^4.0.8" - }, - "dependencies": { - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } + "find-up": "^5.0.0" } }, - "sinon-chai": { + "please-upgrade-node": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/sinon-chai/-/sinon-chai-3.2.0.tgz", - "integrity": "sha512-Z72B4a0l0IQe5uWi9yzcqX/Ml6K9e1Hp03NmkjJnRG3gDsKTX7KvLFZsVUmCaz0eqeXLLK089mwTsP1P1W+DUQ==", + "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", + "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", + "dev": true, + "requires": { + "semver-compare": "^1.0.0" + } + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "prettier": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.1.tgz", + "integrity": "sha512-lqGoSJBQNJidqCHE80vqZJHWHRFoNYsSpP9AjFhlhi9ODCJA541svILes/+/1GM3VaL/abZi7cpFzOpdR9UPKg==", + "dev": true + }, + "pretty-format": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.3.1.tgz", + "integrity": "sha512-FyLnmb1cYJV8biEIiRyzRFvs2lry7PPIvOqKVe1GCUEYg4YGmlx1qG9EJNMxArYm7piII4qb8UV1Pncq5dxmcg==", "dev": true, "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" + "@jest/schemas": "^29.0.0", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" }, "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true } } }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", "dev": true, "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" } }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, "requires": { - "kind-of": "^3.2.0" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "safe-buffer": "^5.1.0" } }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", + "dev": true + }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true }, - "source-map-resolve": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", - "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", "dev": true, "requires": { - "atob": "^2.1.1", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" } }, - "source-map-support": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.6.tgz", - "integrity": "sha512-N4KXEz7jcKqPf2b2vZF11lQIz9W5ZMuUcIOGj243lduidkf2fjkVKJS9vNxVWn3u/uxX38AcE8U9nnH9FPcq+g==", + "resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "dev": true, "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" + "resolve-from": "^5.0.0" }, "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true } } }, - "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, - "sparkles": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz", - "integrity": "sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==", + "resolve.exports": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", + "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", "dev": true }, - "split-string": { + "restore-cursor": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "dev": true, "requires": { - "extend-shallow": "^3.0.0" + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" } }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", "dev": true }, - "sshpk": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", - "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=", + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + }, + "dependencies": { + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "rollup": { + "version": "2.79.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", + "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", "dev": true, "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" + "fsevents": "~2.3.2" } }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "rollup-plugin-terser": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", + "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", "dev": true, "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" + "@babel/code-frame": "^7.10.4", + "jest-worker": "^26.2.1", + "serialize-javascript": "^4.0.0", + "terser": "^5.0.0" }, "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "jest-worker": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", + "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" } } } }, - "stream-consume": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/stream-consume/-/stream-consume-0.1.1.tgz", - "integrity": "sha512-tNa3hzgkjEP7XbCkbRXe1jpg+ievoa0O4SCFlMOYEscGSS4JJsckGL8swUyAa/ApGU3Ae4t6Honor4HhL+tRyg==", - "dev": true - }, - "stream-shift": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", - "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", - "dev": true - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "rxjs": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.7.tgz", + "integrity": "sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==", "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "tslib": "^2.1.0" } }, - "strip-bom": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-1.0.0.tgz", - "integrity": "sha1-hbiGLzhEtabV7IRnqTWYFzo295Q=", + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "dev": true, "requires": { - "first-chunk-stream": "^1.0.0", - "is-utf8": "^0.2.0" + "lru-cache": "^6.0.0" } }, - "strip-bom-string": { + "semver-compare": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", - "integrity": "sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI=", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", "dev": true }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "semver-regex": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-3.1.4.tgz", + "integrity": "sha512-6IiqeZNgq01qGf0TId0t3NvKzSvUsjcpdEO3AQNeIjR6A2+ckTnQlDpl4qu1bjRv0RzN3FP9hzFmws3lKqRWkA==", "dev": true }, - "supports-color": { + "serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "shebang-command": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } }, - "text-encoding": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz", - "integrity": "sha1-45mpgiV6J22uQou5KEXLcb3CbRk=", + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, - "textextensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-2.2.0.tgz", - "integrity": "sha512-j5EMxnryTvKxwH2Cq+Pb43tsf6sdEgw6Pdwxk83mPaq0ToeFJt6WE4J3s5BqY7vmjlLgkgXvhtXUxo80FyBhCA==", + "sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", "dev": true }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, - "through2": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", - "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", "dev": true, "requires": { - "readable-stream": "^2.1.5", - "xtend": "~4.0.1" + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" }, "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } } } }, - "through2-filter": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-2.0.0.tgz", - "integrity": "sha1-YLxVoNrLdghdsfna6Zq0P4PWIuw=", - "dev": true, - "requires": { - "through2": "~2.0.0", - "xtend": "~4.0.0" - } + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true }, - "tildify": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/tildify/-/tildify-1.2.0.tgz", - "integrity": "sha1-3OwD9V3Km3qj5bBPIYF+tW5jWIo=", + "source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", "dev": true, "requires": { - "os-homedir": "^1.0.0" + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, - "time-stamp": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", - "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=", + "sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, - "timers-ext": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.5.tgz", - "integrity": "sha512-tsEStd7kmACHENhsUPaxb8Jf8/+GZZxyNFQbZD07HQOyooOa6At1rQqjffgvg7n+dxscQa9cjjMdWhJtsP2sxg==", + "stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", "dev": true, "requires": { - "es5-ext": "~0.10.14", - "next-tick": "1" + "escape-string-regexp": "^2.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true + } } }, - "to-absolute-glob": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", - "integrity": "sha1-GGX0PZ50sIItufFFt4z/fQ98hJs=", + "string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "dev": true + }, + "string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", "dev": true, "requires": { - "is-absolute": "^1.0.0", - "is-negated-glob": "^1.0.0" + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" } }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, "requires": { - "kind-of": "^3.0.2" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "ansi-regex": "^6.0.1" } } } }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" + "ansi-regex": "^5.0.1" } }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true }, - "to-through": { + "strip-final-newline": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-through/-/to-through-2.0.0.tgz", - "integrity": "sha1-/JKtq6ByZHvAtn1rA2ZKoZUJOvY=", - "dev": true, - "requires": { - "through2": "^2.0.3" - } + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true }, - "tough-cookie": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", - "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", - "dev": true, - "requires": { - "punycode": "^1.4.1" - } + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true }, - "ts-node": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.0.tgz", - "integrity": "sha512-klJsfswHP0FuOLsvBZ/zzCfUvakOSSxds78mVeK7I+qP76YWtxf16hEZsp3U+b0kIo82R5UatGFeblYMqabb2Q==", + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { - "arrify": "^1.0.0", - "buffer-from": "^1.1.0", - "diff": "^3.1.0", - "make-error": "^1.1.1", - "minimist": "^1.2.0", - "mkdirp": "^0.5.1", - "source-map-support": "^0.5.6", - "yn": "^2.0.0" + "has-flag": "^4.0.0" } }, - "tslib": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", - "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true }, - "tslint": { - "version": "5.11.0", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.11.0.tgz", - "integrity": "sha1-mPMMAurjzecAYgHkwzywi0hYHu0=", + "terser": { + "version": "5.15.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.15.1.tgz", + "integrity": "sha512-K1faMUvpm/FBxjBXud0LWVAGxmvoPbZbfTCYbSgaaYQaIXI3/TdI7a7ZGA73Zrou6Q8Zmz3oeUTsp/dj+ag2Xw==", "dev": true, "requires": { - "babel-code-frame": "^6.22.0", - "builtin-modules": "^1.1.1", - "chalk": "^2.3.0", - "commander": "^2.12.1", - "diff": "^3.2.0", - "glob": "^7.1.1", - "js-yaml": "^3.7.0", - "minimatch": "^3.0.4", - "resolve": "^1.3.2", - "semver": "^5.3.0", - "tslib": "^1.8.0", - "tsutils": "^2.27.2" + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" }, "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "semver": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, - "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } } } }, - "tslint-stylish": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tslint-stylish/-/tslint-stylish-2.1.0.tgz", - "integrity": "sha1-jNo8OMnKtU57It989CuO8RXc2V8=", + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", "dev": true, "requires": { - "chalk": "^1.1.1", - "lodash": "^3.10.1", - "log-symbols": "^1.0.2", - "text-table": "^0.2.0", - "tslint": "^2.5.0" + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" }, "dependencies": { - "findup-sync": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.2.1.tgz", - "integrity": "sha1-4KkKRQB1xJRm7lE3MgV1FLgeh4w=", - "dev": true, - "requires": { - "glob": "~4.3.0" - } - }, "glob": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-4.3.5.tgz", - "integrity": "sha1-gPuwjKVA8jiszl0R0em8QedRc9M=", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, "requires": { + "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^2.0.1", - "once": "^1.3.0" - } - }, - "lodash": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", - "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", - "dev": true - }, - "minimatch": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz", - "integrity": "sha1-jQh8OcazjAAbl/ynzm0OHoCvusc=", - "dev": true, - "requires": { - "brace-expansion": "^1.0.0" - } - }, - "tslint": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-2.5.1.tgz", - "integrity": "sha1-veTJnpfNXRxvuzAz9kt9iX/UGpI=", - "dev": true, - "requires": { - "findup-sync": "~0.2.1", - "optimist": "~0.6.0", - "underscore.string": "~3.1.1" + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } } } }, - "tsutils": { - "version": "2.28.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.28.0.tgz", - "integrity": "sha512-bh5nAtW0tuhvOJnx1GLRn5ScraRLICGyJV5wJhtRWOLsxW70Kk5tZtpK3O/hW6LDnqKS9mlUMPZj9fEMJ0gxqA==", + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "requires": { - "tslib": "^1.8.1" + "is-number": "^7.0.0" } }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "ts-jest": { + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.0.3.tgz", + "integrity": "sha512-Ibygvmuyq1qp/z3yTh9QTwVVAbFdDy/+4BtIQR2sp6baF2SJU/8CKK/hhnGIDY2L90Az2jIqTwZPnN2p+BweiQ==", "dev": true, "requires": { - "safe-buffer": "^5.0.1" + "bs-logger": "0.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^29.0.0", + "json5": "^2.2.1", + "lodash.memoize": "4.x", + "make-error": "1.x", + "semver": "7.x", + "yargs-parser": "^21.0.1" + } + }, + "ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "requires": { + "@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" } }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "tslib": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==", + "dev": true + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", "dev": true, - "optional": true + "requires": { + "tslib": "^1.8.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } }, "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "requires": { - "prelude-ls": "~1.1.2" + "prelude-ls": "^1.2.1" } }, "type-detect": { @@ -5036,379 +10430,223 @@ "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + }, "typescript": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz", - "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==", + "version": "4.9.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", + "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", "dev": true }, - "uglify-js": { - "version": "2.8.29", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", - "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "update-browserslist-db": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", "dev": true, - "optional": true, "requires": { - "source-map": "~0.5.1", - "uglify-to-browserify": "~1.0.0", - "yargs": "~3.10.0" + "escalade": "^3.1.1", + "picocolors": "^1.0.0" } }, - "uglify-to-browserify": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", - "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", - "dev": true, - "optional": true - }, - "unc-path-regex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", - "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=", - "dev": true - }, - "underscore.string": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.1.1.tgz", - "integrity": "sha1-DN1rytDARv12Y9MF2KeFtdoQ8zU=", - "dev": true - }, - "union-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", - "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^0.4.3" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "set-value": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", - "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.1", - "to-object-path": "^0.3.0" - } - } + "punycode": "^2.1.0" } }, - "unique-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-1.0.0.tgz", - "integrity": "sha1-1ZpKdUJ0R9mqbJHnAmP40mpLEEs=", + "v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "dev": true }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "v8-to-istanbul": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", + "integrity": "sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==", "dev": true, "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0" }, "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dev": true, - "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", "dev": true } } }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true - }, - "urlgrey": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/urlgrey/-/urlgrey-0.4.4.tgz", - "integrity": "sha1-iS/pWWCAXoVRnxzUOJ8stMu3ZS8=", - "dev": true - }, - "use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "dev": true - }, - "user-home": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/user-home/-/user-home-1.1.1.tgz", - "integrity": "sha1-K1viOjK2Onyd640PKNSFcko98ZA=", - "dev": true - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", - "dev": true - }, - "v8flags": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-2.1.1.tgz", - "integrity": "sha1-qrGh+jDUX4jdMhFIh1rALAtV5bQ=", - "dev": true, - "requires": { - "user-home": "^1.1.1" - } - }, "validator": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-10.4.0.tgz", - "integrity": "sha512-Q/wBy3LB1uOyssgNlXSRmaf22NxjvDNZM2MtIQ4jaEOAB61xsh1TQxsq1CgzUMBV1lDrVMogIh8GjG1DYW0zLg==" - }, - "value-or-function": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-3.0.0.tgz", - "integrity": "sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM=", - "dev": true + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", + "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==" }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", "dev": true, "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" + "makeerror": "1.0.12" } }, - "vinyl": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.5.3.tgz", - "integrity": "sha1-sEVbOPxeDPMNQyUTLkYZcMIJHN4=", + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "requires": { - "clone": "^1.0.0", - "clone-stats": "^0.0.1", - "replace-ext": "0.0.1" + "isexe": "^2.0.0" } }, - "vinyl-fs": { - "version": "0.3.14", - "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-0.3.14.tgz", - "integrity": "sha1-mmhRzhysHBzqX+hsCTHWIMLPqeY=", + "which-pm-runs": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.1.0.tgz", + "integrity": "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==", + "dev": true + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "requires": { - "defaults": "^1.0.0", - "glob-stream": "^3.1.5", - "glob-watcher": "^0.0.6", - "graceful-fs": "^3.0.0", - "mkdirp": "^0.5.0", - "strip-bom": "^1.0.0", - "through2": "^0.6.1", - "vinyl": "^0.4.0" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "dependencies": { - "clone": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz", - "integrity": "sha1-xhJqkK1Pctv1rNskPMN3JP6T/B8=", + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "through2": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", - "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", - "dev": true, - "requires": { - "readable-stream": ">=1.0.33-1 <1.1.0-0", - "xtend": ">=4.0.0 <4.1.0-0" - } + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true }, - "vinyl": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", - "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "requires": { - "clone": "^0.2.0", - "clone-stats": "^0.0.1" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" } } } }, - "vinyl-sourcemap": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz", - "integrity": "sha1-kqgAWTo4cDqM2xHYswCtS+Y7PhY=", + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", "dev": true, "requires": { - "append-buffer": "^1.0.2", - "convert-source-map": "^1.5.0", - "graceful-fs": "^4.1.6", - "normalize-path": "^2.1.1", - "now-and-later": "^2.0.0", - "remove-bom-buffer": "^3.0.0", - "vinyl": "^2.0.0" + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + } + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true + }, + "yargs": { + "version": "17.6.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", + "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", + "dev": true, + "requires": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" }, "dependencies": { - "clone": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.1.tgz", - "integrity": "sha1-0hfR6WERjjrJpLi7oyhVU79kfNs=", - "dev": true - }, - "clone-stats": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", - "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true - }, - "replace-ext": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", - "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true }, - "vinyl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.0.tgz", - "integrity": "sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==", + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "requires": { - "clone": "^2.1.1", - "clone-buffer": "^1.0.0", - "clone-stats": "^1.0.0", - "cloneable-readable": "^1.0.0", - "remove-trailing-separator": "^1.0.1", - "replace-ext": "^1.0.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" } } } }, - "vinyl-sourcemaps-apply": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz", - "integrity": "sha1-q2VJ1h0XLCsbh75cUI0jnI74dwU=", - "dev": true, - "requires": { - "source-map": "^0.5.1" - } - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "window-size": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", - "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", - "dev": true, - "optional": true - }, - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true }, - "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true }, - "yargs": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", - "dev": true, - "optional": true, - "requires": { - "camelcase": "^1.0.2", - "cliui": "^2.1.0", - "decamelize": "^1.0.0", - "window-size": "0.1.0" - } - }, - "yn": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", - "integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=", + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true } } diff --git a/package.json b/package.json index f169c40847..3a8c61affd 100644 --- a/package.json +++ b/package.json @@ -1,63 +1,65 @@ { "name": "class-validator", - "private": true, - "version": "0.9.1", - "description": "Class-based validation with Typescript / ES6 / ES5 using decorators or validation schemas. Supports both node.js and browser", + "version": "0.14.0", + "description": "Decorator-based property validation for classes.", + "author": "TypeStack contributors", "license": "MIT", - "readmeFilename": "README.md", - "author": { - "name": "Umed Khudoiberdiev", - "email": "pleerock.me@gmail.com" - }, + "sideEffects": false, + "main": "./cjs/index.js", + "module": "./esm5/index.js", + "es2015": "./esm2015/index.js", + "typings": "./types/index.d.ts", "repository": { "type": "git", - "url": "https://github.com/pleerock/class-validator.git" - }, - "bugs": { - "url": "https://github.com/pleerock/class-validator/issues" + "url": "https://github.com/typestack/class-validator.git" }, "tags": [ "validator", "validation", - "typescript", - "typescript-validator" + "decorators", + "typescript" ], + "scripts": { + "build": "npm run build:cjs", + "build:clean": "rimraf build", + "build:es2015": "tsc --project tsconfig.prod.esm2015.json", + "build:esm5": "tsc --project tsconfig.prod.esm5.json", + "build:cjs": "tsc --project tsconfig.prod.cjs.json", + "build:umd": "rollup --config rollup.config.js", + "build:types": "tsc --project tsconfig.prod.types.json", + "prettier:fix": "prettier --write \"**/*.{ts,md}\"", + "prettier:check": "prettier --check \"**/*.{ts,md}\"", + "lint:fix": "eslint --max-warnings 0 --fix --ext .ts src/", + "lint:check": "eslint --max-warnings 0 --ext .ts src/", + "test": "jest --coverage --verbose", + "test:watch": "jest --watch", + "test:ci": "jest --runInBand --no-cache --coverage --verbose" + }, "dependencies": { - "google-libphonenumber": "^3.1.6", - "validator": "10.4.0" + "@types/validator": "^13.7.10", + "libphonenumber-js": "^1.10.14", + "validator": "^13.7.0" }, "devDependencies": { - "@types/chai": "^4.1.4", - "@types/chai-as-promised": "^7.1.0", - "@types/gulp": "^4.0.2", - "@types/mocha": "^5.2.5", - "@types/node": "^10.5.2", - "@types/sinon": "^5.0.1", - "chai": "^4.1.2", - "chai-as-promised": "^7.1.1", - "codecov": "^3.0.4", - "del": "^3.0.0", - "es6-shim": "^0.35.3", - "gulp": "^3.9.1", - "gulp-istanbul": "^1.1.3", - "gulp-mocha": "^6.0.0", - "gulp-replace": "^1.0.0", - "gulp-shell": "^0.6.5", - "gulp-sourcemaps": "^2.6.4", - "gulp-tslint": "^8.1.3", - "gulp-typescript": "^5.0.0-alpha.3", - "gulpclass": "^0.1.1", - "mocha": "^5.2.0", - "remap-istanbul": "^0.11.1", - "sinon": "^6.1.3", - "sinon-chai": "^3.2.0", - "ts-node": "^7.0.0", - "tslint": "^5.11.0", - "tslint-stylish": "^2.1.0", - "typescript": "^2.9.2" - }, - "scripts": { - "build": "gulp package", - "test": "gulp tests" + "@rollup/plugin-commonjs": "^23.0.4", + "@rollup/plugin-node-resolve": "^15.0.1", + "@types/jest": "^29.2.4", + "@types/node": "^18.11.12", + "@typescript-eslint/eslint-plugin": "^5.46.0", + "@typescript-eslint/parser": "^5.46.0", + "eslint": "^8.29.0", + "eslint-config-prettier": "^8.5.0", + "eslint-plugin-jest": "^27.1.6", + "husky": "^4.3.8", + "jest": "^29.3.1", + "lint-staged": "^13.1.0", + "prettier": "^2.8.1", + "reflect-metadata": "0.1.13", + "rimraf": "3.0.2", + "rollup": "^2.79.1", + "rollup-plugin-terser": "^7.0.2", + "ts-jest": "^29.0.3", + "ts-node": "^10.9.1", + "typescript": "^4.9.4" } } diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 0000000000..32d1ba61fb --- /dev/null +++ b/rollup.config.js @@ -0,0 +1,23 @@ +import { nodeResolve } from '@rollup/plugin-node-resolve'; +import commonjs from '@rollup/plugin-commonjs'; +import { terser } from 'rollup-plugin-terser'; + +export default { + input: 'build/esm5/index.js', + output: [ + { + name: 'ClassValidator', + format: 'umd', + file: 'build/bundles/class-validator.umd.js', + sourcemap: true, + }, + { + name: 'ClassValidator', + format: 'umd', + file: 'build/bundles/class-validator.umd.min.js', + sourcemap: true, + plugins: [terser()], + }, + ], + plugins: [commonjs(), nodeResolve()], +}; diff --git a/sample/sample1-simple-validation/Post.ts b/sample/sample1-simple-validation/Post.ts index ee9b0d8edb..07bda197f1 100644 --- a/sample/sample1-simple-validation/Post.ts +++ b/sample/sample1-simple-validation/Post.ts @@ -1,38 +1,49 @@ -import {Contains, IsInt, MinLength, MaxLength, IsEmail, IsFQDN, IsDate, ArrayNotEmpty, ArrayMinSize, ArrayMaxSize, IsEnum} from "../../src/decorator/decorators"; +import { + Contains, + IsInt, + MinLength, + MaxLength, + IsEmail, + IsFQDN, + IsDate, + ArrayNotEmpty, + ArrayMinSize, + ArrayMaxSize, + IsEnum, +} from '../../src/decorator/decorators'; export enum PostType { - Public, - Private + Public, + Private, } export class Post { + @MinLength(10) + @MaxLength(20) + title: string; - @MinLength(10) - @MaxLength(20) - title: string; + @Contains('hello') + text: string; - @Contains("hello") - text: string; + @IsInt() + rating: number; - @IsInt() - rating: number; + @IsEmail() + email: string; - @IsEmail() - email: string; + @IsFQDN() + site: string; - @IsFQDN() - site: string; + @IsDate() + createDate: Date; - @IsDate() - createDate: Date; + @ArrayNotEmpty() + @ArrayMinSize(2) + @ArrayMaxSize(5) + @MinLength(3, { each: true, message: 'Tag is too short. Minimal length is $value characters' }) + @MaxLength(50, { each: true, message: 'Tag is too long. Maximal length is $value characters' }) + tags: string[]; - @ArrayNotEmpty() - @ArrayMinSize(2) - @ArrayMaxSize(5) - @MinLength(3, { each: true, message: "Tag is too short. Minimal length is $value characters" }) - @MaxLength(50, { each: true, message: "Tag is too long. Maximal length is $value characters" }) - tags: string[]; - - @IsEnum(PostType) - type: PostType; -} \ No newline at end of file + @IsEnum(PostType) + type: PostType; +} diff --git a/sample/sample1-simple-validation/app.ts b/sample/sample1-simple-validation/app.ts index 3c6cd171ae..71f717b05f 100644 --- a/sample/sample1-simple-validation/app.ts +++ b/sample/sample1-simple-validation/app.ts @@ -1,171 +1,171 @@ -import {validate} from "../../src/index"; -import {Post, PostType} from "./Post"; +import { validate } from '../../src/index'; +import { Post, PostType } from './Post'; // Sample1. simple validation let post1 = new Post(); -post1.title = "Hello world"; // should pass -post1.text = "this is a great post about hello world"; // should pass +post1.title = 'Hello world'; // should pass +post1.text = 'this is a great post about hello world'; // should pass post1.rating = 10; // should pass -post1.email = "info@google.com"; // should pass -post1.site = "google.com"; // should pass +post1.email = 'info@google.com'; // should pass +post1.site = 'google.com'; // should pass post1.createDate = new Date(); // should pass -post1.tags = ["abcd1", "abcd2", "abcd3", "abcd4", "abcd4"]; // should pass +post1.tags = ['abcd1', 'abcd2', 'abcd3', 'abcd4', 'abcd4']; // should pass post1.type = PostType.Private; validate(post1).then(result => { - console.log("1. should pass: ", result); // should pass completely, e.g. return empty array + console.log('1. should pass: ', result); // should pass completely, e.g. return empty array }); let post2 = new Post(); -post2.title = "Hello"; // should not pass -post2.text = "this is a great post about hell world"; // should not pass -post2.rating = 11; // should not pass -post2.email = "google.com"; // should not pass -post2.site = "googlecom"; // should not pass -post2.type = PostType.Private; -// should not pass because date property is missing +post2.title = 'Hello'; // should not pass +post2.text = 'this is a great post about hell world'; // should not pass +post2.rating = 1.1; // should not pass +post2.email = 'google.com'; // should not pass +post2.site = 'googlecom'; // should not pass +post2.type = PostType.Private; // should pass +// should not pass because date property is missing validate(post2).then(result => { - console.log("2. should not pass: ", result); // should not pass completely, must return array of ValidationError-s + console.log('2. should not pass: ', result); // should not pass completely, must return array of ValidationError-s }); // Sample2. using validation options to skip properties that are not defined let post3 = new Post(); -post3.title = "Hello"; // should not pass -post3.text = "this is a great post about hell world"; // should not pass -post3.rating = 11; // should not pass -post3.email = "google.com"; // should not pass -post3.site = "googlecom"; // should not pass +post3.title = 'Hello'; // should not pass +post3.text = 'this is a great post about hell world'; // should not pass +post3.rating = 1.1; // should not pass +post3.email = 'google.com'; // should not pass +post3.site = 'googlecom'; // should not pass post3.type = PostType.Private; validate(post3, { skipMissingProperties: true }).then(result => { - console.log("3. should not pass: ", result); // should not pass, but returned ValidationError-s should not have error about date field + console.log('3. should not pass: ', result); // should not pass, but returned ValidationError-s should not have error about date field }); let post4 = new Post(); -post4.title = "Hello world"; // should pass -post4.text = "this is a great post about hello world"; // should pass +post4.title = 'Hello world'; // should pass +post4.text = 'this is a great post about hello world'; // should pass post4.rating = 10; // should pass -post4.email = "info@google.com"; // should pass -post4.site = "google.com"; // should pass +post4.email = 'info@google.com'; // should pass +post4.site = 'google.com'; // should pass post4.type = PostType.Private; validate(post4, { skipMissingProperties: true }).then(result => { - console.log("4. should pass: ", result); // should pass even if date is not set + console.log('4. should pass: ', result); // should pass even if date is not set }); // Sample3. using validation groups let post5 = new Post(); -post5.title = "Hello world"; // should pass -post5.text = "this is a great post about hello world"; // should pass +post5.title = 'Hello world'; // should pass +post5.text = 'this is a great post about hello world'; // should pass post5.rating = 10; // should pass -post5.email = "info@google.com"; // should pass -post5.site = "google.com"; // should pass +post5.email = 'info@google.com'; // should pass +post5.site = 'google.com'; // should pass post5.type = PostType.Private; validate(post5, { skipMissingProperties: true }).then(result => { - console.log("5. should pass: ", result); // should pass even if date is not set + console.log('5. should pass: ', result); // should pass even if date is not set }); // Sample4. array validation let post6 = new Post(); -post6.title = "Hello world"; // should pass -post6.text = "this is a great post about hello world"; // should pass +post6.title = 'Hello world'; // should pass +post6.text = 'this is a great post about hello world'; // should pass post6.rating = 10; // should pass -post6.email = "info@google.com"; // should pass -post6.site = "google.com"; // should pass +post6.email = 'info@google.com'; // should pass +post6.site = 'google.com'; // should pass post6.createDate = new Date(); // should pass -post6.tags = ["abcd1", "abcd2", "abcd3", "abcd4", "abcd4"]; +post6.tags = ['abcd1', 'abcd2', 'abcd3', 'abcd4', 'abcd4']; post6.type = PostType.Private; validate(post6).then(result => { - console.log("6. should pass: ", result); // should pass completely, e.g. return empty array + console.log('6. should pass: ', result); // should pass completely, e.g. return empty array }); let post7 = new Post(); -post7.title = "Hello world"; // should pass -post7.text = "this is a great post about hello world"; // should pass +post7.title = 'Hello world'; // should pass +post7.text = 'this is a great post about hello world'; // should pass post7.rating = 10; // should pass -post7.email = "info@google.com"; // should pass -post7.site = "google.com"; // should pass +post7.email = 'info@google.com'; // should pass +post7.site = 'google.com'; // should pass post7.createDate = new Date(); // should pass -post7.tags = ["news", "a"]; +post7.tags = ['news', 'a']; post7.type = PostType.Private; validate(post7).then(result => { - console.log("7. should not pass: ", result); // should not pass + console.log('7. should not pass: ', result); // should not pass }); let post8 = new Post(); -post8.title = "Hello world"; // should pass -post8.text = "this is a great post about hello world"; // should pass +post8.title = 'Hello world'; // should pass +post8.text = 'this is a great post about hello world'; // should pass post8.rating = 10; // should pass -post8.email = "info@google.com"; // should pass -post8.site = "google.com"; // should pass +post8.email = 'info@google.com'; // should pass +post8.site = 'google.com'; // should pass post8.createDate = new Date(); // should pass post8.tags = []; post8.type = PostType.Private; validate(post8).then(result => { - console.log("8. should not pass: ", result); // should not pass + console.log('8. should not pass: ', result); // should not pass }); let post9 = new Post(); -post9.title = "Hello world"; // should pass -post9.text = "this is a great post about hello world"; // should pass +post9.title = 'Hello world'; // should pass +post9.text = 'this is a great post about hello world'; // should pass post9.rating = 10; // should pass -post9.email = "info@google.com"; // should pass -post9.site = "google.com"; // should pass +post9.email = 'info@google.com'; // should pass +post9.site = 'google.com'; // should pass post9.createDate = new Date(); // should pass -post9.tags = ["abcd1", "abcd2", "abcd3", "abcd4", "abcd4", "abcd4"]; +post9.tags = ['abcd1', 'abcd2', 'abcd3', 'abcd4', 'abcd4', 'abcd4']; post9.type = PostType.Private; validate(post9).then(result => { - console.log("9. should not pass: ", result); // should not pass + console.log('9. should not pass: ', result); // should not pass }); let post10 = new Post(); -post10.title = "Hello world"; // should pass -post10.text = "this is a great post about hello world"; // should pass +post10.title = 'Hello world'; // should pass +post10.text = 'this is a great post about hello world'; // should pass post10.rating = 10; // should pass -post10.email = "info@google.com"; // should pass -post10.site = "google.com"; // should pass +post10.email = 'info@google.com'; // should pass +post10.site = 'google.com'; // should pass post10.createDate = new Date(); // should pass -post10.tags = ["abcd1", "abcd2", "abcd3", "abcd4", "abcd4"]; +post10.tags = ['abcd1', 'abcd2', 'abcd3', 'abcd4', 'abcd4']; post10.type = PostType.Private; validate(post10).then(result => { - console.log("10. should pass: ", result); // should pass + console.log('10. should pass: ', result); // should pass }); let post11 = new Post(); -post11.title = "Hello world"; // should pass -post11.text = "this is a great post about hello world"; // should pass +post11.title = 'Hello world'; // should pass +post11.text = 'this is a great post about hello world'; // should pass post11.rating = 10; // should pass -post11.email = "info@google.com"; // should pass -post11.site = "google.com"; // should pass +post11.email = 'info@google.com'; // should pass +post11.site = 'google.com'; // should pass post11.createDate = new Date(); // should pass post11.tags = null; post11.type = PostType.Private; validate(post11).then(result => { - console.log("11. should not pass: ", result); // should not pass + console.log('11. should not pass: ', result); // should not pass }); let post12 = new Post(); -post12.title = "Hello world"; // should pass -post12.text = "this is a great post about hello world"; // should pass +post12.title = 'Hello world'; // should pass +post12.text = 'this is a great post about hello world'; // should pass post12.rating = 10; // should pass -post12.email = "info@google.com"; // should pass -post12.site = "google.com"; // should pass +post12.email = 'info@google.com'; // should pass +post12.site = 'google.com'; // should pass post12.createDate = new Date(); // should pass -post12.tags = ["abcd1", "abcd2", "abcd3", "abcd4", "abcd4"]; // should pass +post12.tags = ['abcd1', 'abcd2', 'abcd3', 'abcd4', 'abcd4']; // should pass post12.type = 99; // should not pass validate(post1).then(result => { - console.log("12. should not pass: ", result); // should not pass as type is not a valid enum -}); \ No newline at end of file + console.log('12. should not pass: ', result); // should not pass as type is not a valid enum +}); diff --git a/sample/sample2-using-groups/Post.ts b/sample/sample2-using-groups/Post.ts index 4ae81ce862..9e2916733e 100644 --- a/sample/sample2-using-groups/Post.ts +++ b/sample/sample2-using-groups/Post.ts @@ -1,38 +1,36 @@ -import {Contains, IsInt, Length, IsEmail, IsFQDN, IsDate} from "../../src/decorator/decorators"; +import { Contains, IsInt, Length, IsEmail, IsFQDN, IsDate } from '../../src/decorator/decorators'; export class Post { - - @Length(10, 20, { - message: "Incorrect length!", - groups: ["users", "moderators"] - }) - @Length(0, 20, { - message: "Incorrect length!", - groups: ["admins"] - }) - title: string; - - @Contains("hello", { - message: "It should contain word \"hello!\"", - groups: ["users", "moderators"] - }) - text: string; - - @IsInt() - rating: number; - - @IsEmail(undefined, { - always: true - }) - email: string; - - @IsFQDN(undefined, { - message: "Site address should be correct", - groups: ["users"] - }) - site: string; - - @IsDate() - createDate: Date; - -} \ No newline at end of file + @Length(10, 20, { + message: 'Incorrect length!', + groups: ['users', 'moderators'], + }) + @Length(0, 20, { + message: 'Incorrect length!', + groups: ['admins'], + }) + title: string; + + @Contains('hello', { + message: 'It should contain word "hello!"', + groups: ['users', 'moderators'], + }) + text: string; + + @IsInt() + rating: number; + + @IsEmail(undefined, { + always: true, + }) + email: string; + + @IsFQDN(undefined, { + message: 'Site address should be correct', + groups: ['users'], + }) + site: string; + + @IsDate() + createDate: Date; +} diff --git a/sample/sample2-using-groups/app.ts b/sample/sample2-using-groups/app.ts index c0aa999f62..64cbe298a5 100644 --- a/sample/sample2-using-groups/app.ts +++ b/sample/sample2-using-groups/app.ts @@ -1,76 +1,76 @@ -import {Validator} from "../../src/validation/Validator"; -import {Post} from "./Post"; +import { Validator } from '../../src/validation/Validator'; +import { Post } from './Post'; let validator = new Validator(); let post1 = new Post(); -post1.title = "Hello world"; // should pass -post1.text = "this is a great post about hello world"; // should pass +post1.title = 'Hello world'; // should pass +post1.text = 'this is a great post about hello world'; // should pass post1.rating = 10; // should pass -post1.email = "info@google.com"; // should pass -post1.site = "google.com"; // should pass +post1.email = 'info@google.com'; // should pass +post1.site = 'google.com'; // should pass post1.createDate = new Date(); // should pass -validator.validate(post1, { groups: ["users"] }).then(result => { - console.log("1.1. should pass: ", result); +validator.validate(post1, { groups: ['users'] }).then(result => { + console.log('1.1. should pass: ', result); }); -validator.validate(post1, { groups: ["admins"] }).then(result => { - console.log("1.2. should pass: ", result); +validator.validate(post1, { groups: ['admins'] }).then(result => { + console.log('1.2. should pass: ', result); }); let post2 = new Post(); -post2.title = "Hi!"; // should not pass for user or moderator, but should pass for admin -post2.text = "this is a great post about hello world"; // should pass +post2.title = 'Hi!'; // should not pass for user or moderator, but should pass for admin +post2.text = 'this is a great post about hello world'; // should pass post2.rating = 10; // should pass -post2.email = "info@google.com"; // should pass -post2.site = "google.com"; // should pass +post2.email = 'info@google.com'; // should pass +post2.site = 'google.com'; // should pass post2.createDate = new Date(); // should pass -validator.validate(post2, { groups: ["users"] }).then(result => { - console.log("2.1. should not pass: ", result); +validator.validate(post2, { groups: ['users'] }).then(result => { + console.log('2.1. should not pass: ', result); }); -validator.validate(post2, { groups: ["moderators"] }).then(result => { - console.log("2.2. should not pass: ", result); +validator.validate(post2, { groups: ['moderators'] }).then(result => { + console.log('2.2. should not pass: ', result); }); -validator.validate(post2, { groups: ["admins"] }).then(result => { - console.log("2.3. should pass: ", result); +validator.validate(post2, { groups: ['admins'] }).then(result => { + console.log('2.3. should pass: ', result); }); -validator.validate(post2, { groups: ["users", "admins"] }).then(result => { - console.log("2.4. should not pass: ", result); +validator.validate(post2, { groups: ['users', 'admins'] }).then(result => { + console.log('2.4. should not pass: ', result); }); let post3 = new Post(); -post3.title = "Hello world"; // should not pass for user or moderator, but should pass for admin -post3.text = "this is a great post about hello world"; // should pass +post3.title = 'Hello world'; // should not pass for user or moderator, but should pass for admin +post3.text = 'this is a great post about hello world'; // should pass post3.rating = 10; // should pass -post3.email = "info@google.com"; // should pass -post3.site = "google.com"; // should pass +post3.email = 'info@google.com'; // should pass +post3.site = 'google.com'; // should pass // note that we dont set date -validator.validate(post3, { groups: ["users"] }).then(result => { - console.log("3.1. should pass: ", result); +validator.validate(post3, { groups: ['users'] }).then(result => { + console.log('3.1. should pass: ', result); }); validator.validate(post3).then(result => { - console.log("3.2. should not pass: ", result); + console.log('3.2. should not pass: ', result); }); let post4 = new Post(); -post4.title = "Hello world"; // should not pass for user or moderator, but should pass for admin -post4.text = "this is a great post about hello world"; // should pass +post4.title = 'Hello world'; // should not pass for user or moderator, but should pass for admin +post4.text = 'this is a great post about hello world'; // should pass post4.rating = 10; // should pass -post4.email = ""; // should not pass -post4.site = "google.com"; // should pass +post4.email = ''; // should not pass +post4.site = 'google.com'; // should pass // note that we dont set date -validator.validate(post4, { groups: ["users"] }).then(result => { - console.log("4.1. should not pass: ", result); +validator.validate(post4, { groups: ['users'] }).then(result => { + console.log('4.1. should not pass: ', result); }); validator.validate(post4).then(result => { - console.log("4.2. should not pass: ", result); -}); \ No newline at end of file + console.log('4.2. should not pass: ', result); +}); diff --git a/sample/sample3-nested-objects/Post.ts b/sample/sample3-nested-objects/Post.ts index 85e794be87..7b55a240ca 100644 --- a/sample/sample3-nested-objects/Post.ts +++ b/sample/sample3-nested-objects/Post.ts @@ -1,14 +1,12 @@ -import {Contains, IsInt, Length, IsEmail, IsFQDN, IsDate, ValidateNested} from "../../src/decorator/decorators"; -import {Tag} from "./Tag"; +import { Contains, IsInt, Length, IsEmail, IsFQDN, IsDate, ValidateNested } from '../../src/decorator/decorators'; +import { Tag } from './Tag'; export class Post { - - @Length(10, 20, { - message: "Incorrect length!" - }) - title: string; - - @ValidateNested() - tags: Tag[]; - -} \ No newline at end of file + @Length(10, 20, { + message: 'Incorrect length!', + }) + title: string; + + @ValidateNested() + tags: Tag[]; +} diff --git a/sample/sample3-nested-objects/Tag.ts b/sample/sample3-nested-objects/Tag.ts index c547f90ec3..1b2878acbf 100644 --- a/sample/sample3-nested-objects/Tag.ts +++ b/sample/sample3-nested-objects/Tag.ts @@ -1,10 +1,8 @@ -import {Contains, IsInt, Length, IsEmail, IsFQDN, IsDate} from "../../src/decorator/decorators"; +import { Contains, IsInt, Length, IsEmail, IsFQDN, IsDate } from '../../src/decorator/decorators'; export class Tag { - - @Length(10, 20, { - message: "Tag is too short or long" - }) - name: string; - -} \ No newline at end of file + @Length(10, 20, { + message: 'Tag is too short or long', + }) + name: string; +} diff --git a/sample/sample3-nested-objects/app.ts b/sample/sample3-nested-objects/app.ts index 494791d623..61ff03d5ad 100644 --- a/sample/sample3-nested-objects/app.ts +++ b/sample/sample3-nested-objects/app.ts @@ -1,19 +1,19 @@ -import {Validator} from "../../src/validation/Validator"; -import {Post} from "./Post"; -import {Tag} from "./Tag"; +import { Validator } from '../../src/validation/Validator'; +import { Post } from './Post'; +import { Tag } from './Tag'; let validator = new Validator(); let tag1 = new Tag(); -tag1.name = "ja"; +tag1.name = 'ja'; let tag2 = new Tag(); -tag2.name = "node.js"; +tag2.name = 'node.js'; let post1 = new Post(); -post1.title = "Hello world"; +post1.title = 'Hello world'; post1.tags = [tag1, tag2]; validator.validate(post1).then(result => { - console.log("1. should not pass: ", result); -}); \ No newline at end of file + console.log('1. should not pass: ', result); +}); diff --git a/sample/sample4-custom-validator/CustomTextLength.ts b/sample/sample4-custom-validator/CustomTextLength.ts index 04e3746e83..b5a9a2dd14 100644 --- a/sample/sample4-custom-validator/CustomTextLength.ts +++ b/sample/sample4-custom-validator/CustomTextLength.ts @@ -1,11 +1,9 @@ -import {ValidatorConstraintInterface} from "../../src/validation/ValidatorConstraintInterface"; -import {ValidatorConstraint} from "../../src/decorator/decorators"; +import { ValidatorConstraintInterface } from '../../src/validation/ValidatorConstraintInterface'; +import { ValidatorConstraint } from '../../src/decorator/decorators'; @ValidatorConstraint() export class CustomTextLength implements ValidatorConstraintInterface { - - validate(text: string) { - return text.length > 1 && text.length < 10; - } - -} \ No newline at end of file + validate(text: string) { + return text.length > 1 && text.length < 10; + } +} diff --git a/sample/sample4-custom-validator/Post.ts b/sample/sample4-custom-validator/Post.ts index 6a4d226c0a..e2978c7780 100644 --- a/sample/sample4-custom-validator/Post.ts +++ b/sample/sample4-custom-validator/Post.ts @@ -1,12 +1,22 @@ -import {Contains, IsInt, MinLength, MaxLength, IsEmail, IsFQDN, IsDate, IsNotEmpty, ArrayNotEmpty, ArrayMinSize, ArrayMaxSize} from "../../src/decorator/decorators"; -import {Validate} from "../../src/decorator/decorators"; -import {CustomTextLength} from "./CustomTextLength"; +import { + Contains, + IsInt, + MinLength, + MaxLength, + IsEmail, + IsFQDN, + IsDate, + IsNotEmpty, + ArrayNotEmpty, + ArrayMinSize, + ArrayMaxSize, +} from '../../src/decorator/decorators'; +import { Validate } from '../../src/decorator/decorators'; +import { CustomTextLength } from './CustomTextLength'; export class Post { - - @Validate(CustomTextLength, { - message: "Wrong post title" - }) - title: string; - -} \ No newline at end of file + @Validate(CustomTextLength, { + message: 'Wrong post title', + }) + title: string; +} diff --git a/sample/sample4-custom-validator/app.ts b/sample/sample4-custom-validator/app.ts index ada11cbfd7..83bb705bbd 100644 --- a/sample/sample4-custom-validator/app.ts +++ b/sample/sample4-custom-validator/app.ts @@ -1,18 +1,18 @@ -import {Validator} from "../../src/validation/Validator"; -import {Post} from "./Post"; +import { Validator } from '../../src/validation/Validator'; +import { Post } from './Post'; let validator = new Validator(); let post1 = new Post(); -post1.title = "Hello world"; +post1.title = 'Hello world'; validator.validate(post1).then(result => { - console.log("1. should not pass: ", result); + console.log('1. should not pass: ', result); }); let post2 = new Post(); -post2.title = "Hello !!!"; +post2.title = 'Hello !!!'; validator.validate(post2).then(result => { - console.log("2. should pass: ", result); + console.log('2. should pass: ', result); }); diff --git a/sample/sample5-schemas/Post.ts b/sample/sample5-schemas/Post.ts index 234dfc312f..1d132407d0 100644 --- a/sample/sample5-schemas/Post.ts +++ b/sample/sample5-schemas/Post.ts @@ -1,11 +1,9 @@ export class Post { - - title: string; - text: string; - rating: number; - email: string; - site: string; - createDate: Date; - tags: string[]; - -} \ No newline at end of file + title: string; + text: string; + rating: number; + email: string; + site: string; + createDate: Date; + tags: string[]; +} diff --git a/sample/sample5-schemas/app.ts b/sample/sample5-schemas/app.ts index 9c5c4a6dde..592b3a77df 100644 --- a/sample/sample5-schemas/app.ts +++ b/sample/sample5-schemas/app.ts @@ -1,8 +1,8 @@ -import {validate, registerSchema} from "../../src/index"; -import {Post} from "./Post"; +import { validate, registerSchema } from '../../src/index'; +import { Post } from './Post'; // load schema. we load it a bit tricky way because we output source code into separate directory, so our json resource left in another directory -const postSchema = require(__dirname + "/../../../../sample/sample5-schemas/post.json"); +const postSchema = require(__dirname + '/../../../../sample/sample5-schemas/post.json'); // register this schema registerSchema(postSchema); @@ -10,143 +10,143 @@ registerSchema(postSchema); // Sample1. simple validation let post1 = new Post(); -post1.title = "Hello world"; // should pass -post1.text = "this is a great post about hello world"; // should pass +post1.title = 'Hello world'; // should pass +post1.text = 'this is a great post about hello world'; // should pass post1.rating = 10; // should pass -post1.email = "info@google.com"; // should pass -post1.site = "google.com"; // should pass +post1.email = 'info@google.com'; // should pass +post1.site = 'google.com'; // should pass post1.createDate = new Date(); // should pass -post1.tags = ["abcd1", "abcd2", "abcd3", "abcd4", "abcd4"]; // should pass +post1.tags = ['abcd1', 'abcd2', 'abcd3', 'abcd4', 'abcd4']; // should pass -validate("post", post1).then(result => { - console.log("1. should pass: ", result); // should pass completely, e.g. return empty array +validate('post', post1).then(result => { + console.log('1. should pass: ', result); // should pass completely, e.g. return empty array }); let post2 = new Post(); -post2.title = "Hello"; // should not pass -post2.text = "this is a great post about hell world"; // should not pass +post2.title = 'Hello'; // should not pass +post2.text = 'this is a great post about hell world'; // should not pass post2.rating = 11; // should not pass -post2.email = "google.com"; // should not pass -post2.site = "googlecom"; // should not pass +post2.email = 'google.com'; // should not pass +post2.site = 'googlecom'; // should not pass // should not pass because date property is missing -validate("post", post2).then(result => { - console.log("2. should not pass: ", result); // should not pass completely, must return array of ValidationError-s +validate('post', post2).then(result => { + console.log('2. should not pass: ', result); // should not pass completely, must return array of ValidationError-s }); // Sample2. using validation options to skip properties that are not defined let post3 = new Post(); -post3.title = "Hello"; // should not pass -post3.text = "this is a great post about hell world"; // should not pass +post3.title = 'Hello'; // should not pass +post3.text = 'this is a great post about hell world'; // should not pass post3.rating = 11; // should not pass -post3.email = "google.com"; // should not pass -post3.site = "googlecom"; // should not pass +post3.email = 'google.com'; // should not pass +post3.site = 'googlecom'; // should not pass -validate("post", post3, { skipMissingProperties: true }).then(result => { - console.log("3. should not pass: ", result); // should not pass, but returned ValidationError-s should not have error about date field +validate('post', post3, { skipMissingProperties: true }).then(result => { + console.log('3. should not pass: ', result); // should not pass, but returned ValidationError-s should not have error about date field }); let post4 = new Post(); -post4.title = "Hello world"; // should pass -post4.text = "this is a great post about hello world"; // should pass +post4.title = 'Hello world'; // should pass +post4.text = 'this is a great post about hello world'; // should pass post4.rating = 10; // should pass -post4.email = "info@google.com"; // should pass -post4.site = "google.com"; // should pass +post4.email = 'info@google.com'; // should pass +post4.site = 'google.com'; // should pass -validate("post", post4, { skipMissingProperties: true }).then(result => { - console.log("4. should pass: ", result); // should pass even if date is not set +validate('post', post4, { skipMissingProperties: true }).then(result => { + console.log('4. should pass: ', result); // should pass even if date is not set }); // Sample3. using validation groups let post5 = new Post(); -post5.title = "Hello world"; // should pass -post5.text = "this is a great post about hello world"; // should pass +post5.title = 'Hello world'; // should pass +post5.text = 'this is a great post about hello world'; // should pass post5.rating = 10; // should pass -post5.email = "info@google.com"; // should pass -post5.site = "google.com"; // should pass +post5.email = 'info@google.com'; // should pass +post5.site = 'google.com'; // should pass -validate("post", post5, { skipMissingProperties: true }).then(result => { - console.log("5. should pass: ", result); // should pass even if date is not set +validate('post', post5, { skipMissingProperties: true }).then(result => { + console.log('5. should pass: ', result); // should pass even if date is not set }); // Sample4. array validation let post6 = new Post(); -post6.title = "Hello world"; // should pass -post6.text = "this is a great post about hello world"; // should pass +post6.title = 'Hello world'; // should pass +post6.text = 'this is a great post about hello world'; // should pass post6.rating = 10; // should pass -post6.email = "info@google.com"; // should pass -post6.site = "google.com"; // should pass +post6.email = 'info@google.com'; // should pass +post6.site = 'google.com'; // should pass post6.createDate = new Date(); // should pass -post6.tags = ["abcd1", "abcd2", "abcd3", "abcd4", "abcd4"]; +post6.tags = ['abcd1', 'abcd2', 'abcd3', 'abcd4', 'abcd4']; -validate("post", post6).then(result => { - console.log("6. should pass: ", result); // should pass completely, e.g. return empty array +validate('post', post6).then(result => { + console.log('6. should pass: ', result); // should pass completely, e.g. return empty array }); let post7 = new Post(); -post7.title = "Hello world"; // should pass -post7.text = "this is a great post about hello world"; // should pass +post7.title = 'Hello world'; // should pass +post7.text = 'this is a great post about hello world'; // should pass post7.rating = 10; // should pass -post7.email = "info@google.com"; // should pass -post7.site = "google.com"; // should pass +post7.email = 'info@google.com'; // should pass +post7.site = 'google.com'; // should pass post7.createDate = new Date(); // should pass -post7.tags = ["news", "a"]; +post7.tags = ['news', 'a']; -validate("post", post7).then(result => { - console.log("7. should not pass: ", result); // should not pass +validate('post', post7).then(result => { + console.log('7. should not pass: ', result); // should not pass }); let post8 = new Post(); -post8.title = "Hello world"; // should pass -post8.text = "this is a great post about hello world"; // should pass +post8.title = 'Hello world'; // should pass +post8.text = 'this is a great post about hello world'; // should pass post8.rating = 10; // should pass -post8.email = "info@google.com"; // should pass -post8.site = "google.com"; // should pass +post8.email = 'info@google.com'; // should pass +post8.site = 'google.com'; // should pass post8.createDate = new Date(); // should pass post8.tags = []; -validate("post", post8).then(result => { - console.log("8. should not pass: ", result); // should not pass +validate('post', post8).then(result => { + console.log('8. should not pass: ', result); // should not pass }); let post9 = new Post(); -post9.title = "Hello world"; // should pass -post9.text = "this is a great post about hello world"; // should pass +post9.title = 'Hello world'; // should pass +post9.text = 'this is a great post about hello world'; // should pass post9.rating = 10; // should pass -post9.email = "info@google.com"; // should pass -post9.site = "google.com"; // should pass +post9.email = 'info@google.com'; // should pass +post9.site = 'google.com'; // should pass post9.createDate = new Date(); // should pass -post9.tags = ["a", "abcd1", "abcd2", "abcd3", "abcd4", "abcd4", "abcd4"]; +post9.tags = ['a', 'abcd1', 'abcd2', 'abcd3', 'abcd4', 'abcd4', 'abcd4']; -validate("post", post9).then(result => { - console.log("9. should not pass: ", result); // should not pass +validate('post', post9).then(result => { + console.log('9. should not pass: ', result); // should not pass }); let post10 = new Post(); -post10.title = "Hello world"; // should pass -post10.text = "this is a great post about hello world"; // should pass +post10.title = 'Hello world'; // should pass +post10.text = 'this is a great post about hello world'; // should pass post10.rating = 10; // should pass -post10.email = "info@google.com"; // should pass -post10.site = "google.com"; // should pass +post10.email = 'info@google.com'; // should pass +post10.site = 'google.com'; // should pass post10.createDate = new Date(); // should pass -post10.tags = ["abcd1", "abcd2", "abcd3", "abcd4", "abcd4"]; +post10.tags = ['abcd1', 'abcd2', 'abcd3', 'abcd4', 'abcd4']; -validate("post", post10).then(result => { - console.log("10. should pass: ", result); // should pass +validate('post', post10).then(result => { + console.log('10. should pass: ', result); // should pass }); let post11 = new Post(); -post11.title = "Hello world"; // should pass -post11.text = "this is a great post about hello world"; // should pass +post11.title = 'Hello world'; // should pass +post11.text = 'this is a great post about hello world'; // should pass post11.rating = 10; // should pass -post11.email = "info@google.com"; // should pass -post11.site = "google.com"; // should pass +post11.email = 'info@google.com'; // should pass +post11.site = 'google.com'; // should pass post11.createDate = new Date(); // should pass post11.tags = null; -validate("post", post11).then(result => { - console.log("11. should not pass: ", result); // should not pass -}); \ No newline at end of file +validate('post', post11).then(result => { + console.log('11. should not pass: ', result); // should not pass +}); diff --git a/sample/sample6-custom-decorator/IsLongerThan.ts b/sample/sample6-custom-decorator/IsLongerThan.ts index 90bde6e092..21be1f3417 100644 --- a/sample/sample6-custom-decorator/IsLongerThan.ts +++ b/sample/sample6-custom-decorator/IsLongerThan.ts @@ -1,30 +1,26 @@ -import {registerDecorator} from "../../src/index"; -import {ValidationOptions} from "../../src/decorator/ValidationOptions"; -import {ValidatorConstraintInterface} from "../../src/validation/ValidatorConstraintInterface"; -import {ValidatorConstraint} from "../../src/decorator/decorators"; -import {ValidationArguments} from "../../src/validation/ValidationArguments"; +import { registerDecorator } from '../../src/index'; +import { ValidationOptions } from '../../src/decorator/ValidationOptions'; +import { ValidatorConstraintInterface } from '../../src/validation/ValidatorConstraintInterface'; +import { ValidatorConstraint } from '../../src/decorator/decorators'; +import { ValidationArguments } from '../../src/validation/ValidationArguments'; export function IsLongerThan(property: string, validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - registerDecorator({ - target: object.constructor, - propertyName: propertyName, - options: validationOptions, - constraints: [property], - validator: IsLongerThanConstraint - }); - }; + return function (object: Object, propertyName: string) { + registerDecorator({ + target: object.constructor, + propertyName: propertyName, + options: validationOptions, + constraints: [property], + validator: IsLongerThanConstraint, + }); + }; } -@ValidatorConstraint({ name: "isLongerThan" }) +@ValidatorConstraint({ name: 'isLongerThan' }) export class IsLongerThanConstraint implements ValidatorConstraintInterface { - - validate(value: any, args: ValidationArguments) { - const [relatedPropertyName] = args.constraints; - const relatedValue = (args.object as any)[relatedPropertyName]; - return typeof value === "string" && - typeof relatedValue === "string" && - value.length > relatedValue.length; - } - -} \ No newline at end of file + validate(value: any, args: ValidationArguments) { + const [relatedPropertyName] = args.constraints; + const relatedValue = (args.object as any)[relatedPropertyName]; + return typeof value === 'string' && typeof relatedValue === 'string' && value.length > relatedValue.length; + } +} diff --git a/sample/sample6-custom-decorator/IsUserAlreadyExist.ts b/sample/sample6-custom-decorator/IsUserAlreadyExist.ts index 49474e9c61..6cf59f7a3a 100644 --- a/sample/sample6-custom-decorator/IsUserAlreadyExist.ts +++ b/sample/sample6-custom-decorator/IsUserAlreadyExist.ts @@ -1,26 +1,26 @@ -import {registerDecorator} from "../../src/index"; -import {ValidationOptions} from "../../src/decorator/ValidationOptions"; -import {ValidationArguments} from "../../src/validation/ValidationArguments"; +import { registerDecorator } from '../../src/index'; +import { ValidationOptions } from '../../src/decorator/ValidationOptions'; +import { ValidationArguments } from '../../src/validation/ValidationArguments'; export function IsUserAlreadyExist(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - registerDecorator({ - name: "isUserAlreadyExist", - async: true, - target: object.constructor, - propertyName: propertyName, - options: validationOptions, - validator: { - validate(value: any, args: ValidationArguments) { - return new Promise(ok => { - if (value !== "admin" && value !== "user") { - ok(true); - } else { - ok(false); - } - }); - } + return function (object: Object, propertyName: string) { + registerDecorator({ + name: 'isUserAlreadyExist', + async: true, + target: object.constructor, + propertyName: propertyName, + options: validationOptions, + validator: { + validate(value: any, args: ValidationArguments) { + return new Promise(ok => { + if (value !== 'admin' && value !== 'user') { + ok(true); + } else { + ok(false); } - }); - }; -} \ No newline at end of file + }); + }, + }, + }); + }; +} diff --git a/sample/sample6-custom-decorator/User.ts b/sample/sample6-custom-decorator/User.ts index 549fe706a6..6c0950a1ad 100644 --- a/sample/sample6-custom-decorator/User.ts +++ b/sample/sample6-custom-decorator/User.ts @@ -1,16 +1,14 @@ -import {IsUserAlreadyExist} from "./IsUserAlreadyExist"; -import {IsLongerThan} from "./IsLongerThan"; +import { IsUserAlreadyExist } from './IsUserAlreadyExist'; +import { IsLongerThan } from './IsLongerThan'; export class User { + @IsUserAlreadyExist({ + message: 'User with name $value already exists', + }) + firstName: string; - @IsUserAlreadyExist({ - message: "User with name $value already exists" - }) - firstName: string; - - @IsLongerThan("firstName", { - message: "User's last name must be longer than firstName" - }) - lastName: string; - -} \ No newline at end of file + @IsLongerThan('firstName', { + message: "User's last name must be longer than firstName", + }) + lastName: string; +} diff --git a/sample/sample6-custom-decorator/app.ts b/sample/sample6-custom-decorator/app.ts index a0bed65ff3..1f3eb93db5 100644 --- a/sample/sample6-custom-decorator/app.ts +++ b/sample/sample6-custom-decorator/app.ts @@ -1,41 +1,41 @@ -import {Validator} from "../../src/validation/Validator"; -import {User} from "./User"; +import { Validator } from '../../src/validation/Validator'; +import { User } from './User'; let validator = new Validator(); let user1 = new User(); -user1.firstName = "Umed"; +user1.firstName = 'Umed'; validator.validate(user1, { skipMissingProperties: true }).then(result => { - console.log("1. should pass: ", result); + console.log('1. should pass: ', result); }); let user2 = new User(); -user2.firstName = "admin"; +user2.firstName = 'admin'; validator.validate(user2, { skipMissingProperties: true }).then(result => { - console.log("2. should not pass: ", result); + console.log('2. should not pass: ', result); }); let user3 = new User(); -user3.firstName = "user"; +user3.firstName = 'user'; validator.validate(user3, { skipMissingProperties: true }).then(result => { - console.log("3. should not pass: ", result); + console.log('3. should not pass: ', result); }); let user4 = new User(); -user4.firstName = "Zak"; -user4.lastName = "Henry"; +user4.firstName = 'Zak'; +user4.lastName = 'Henry'; validator.validate(user4).then(result => { - console.log("4. should pass: ", result); + console.log('4. should pass: ', result); }); let user5 = new User(); -user5.firstName = "Henry"; -user5.lastName = "Zak"; +user5.firstName = 'Henry'; +user5.lastName = 'Zak'; validator.validate(user5).then(result => { - console.log("5. should not pass: ", result); -}); \ No newline at end of file + console.log('5. should not pass: ', result); +}); diff --git a/sample/sample7-inheritance-support/BaseContent.ts b/sample/sample7-inheritance-support/BaseContent.ts index e6b0171fbd..3b6958eec8 100644 --- a/sample/sample7-inheritance-support/BaseContent.ts +++ b/sample/sample7-inheritance-support/BaseContent.ts @@ -1,8 +1,6 @@ -import {IsEmail} from "../../src/decorator/decorators"; +import { IsEmail } from '../../src/decorator/decorators'; export class BaseContent { - - @IsEmail() - email: string; - -} \ No newline at end of file + @IsEmail() + email: string; +} diff --git a/sample/sample7-inheritance-support/Post.ts b/sample/sample7-inheritance-support/Post.ts index 6d3a6d2325..e57db37d1b 100644 --- a/sample/sample7-inheritance-support/Post.ts +++ b/sample/sample7-inheritance-support/Post.ts @@ -1,16 +1,14 @@ -import {Contains, IsInt, MinLength, MaxLength} from "../../src/decorator/decorators"; -import {BaseContent} from "./BaseContent"; +import { Contains, IsInt, MinLength, MaxLength } from '../../src/decorator/decorators'; +import { BaseContent } from './BaseContent'; export class Post extends BaseContent { + @MinLength(10) + @MaxLength(20) + title: string; - @MinLength(10) - @MaxLength(20) - title: string; + @Contains('hello') + text: string; - @Contains("hello") - text: string; - - @IsInt() - rating: number; - -} \ No newline at end of file + @IsInt() + rating: number; +} diff --git a/sample/sample7-inheritance-support/app.ts b/sample/sample7-inheritance-support/app.ts index 9b4d534c84..d2cf9d8a76 100644 --- a/sample/sample7-inheritance-support/app.ts +++ b/sample/sample7-inheritance-support/app.ts @@ -1,14 +1,14 @@ -import {validate} from "../../src/index"; -import {Post} from "./Post"; +import { validate } from '../../src/index'; +import { Post } from './Post'; // Sample1. simple validation let post1 = new Post(); -post1.title = "Hello world"; // should pass -post1.text = "this is a great post about hello world"; // should pass +post1.title = 'Hello world'; // should pass +post1.text = 'this is a great post about hello world'; // should pass post1.rating = 10; // should pass -post1.email = "@google.com"; // should not pass +post1.email = '@google.com'; // should not pass validate(post1).then(result => { - console.log("1. should not pass: ", result); // should not pass completely + console.log('1. should not pass: ', result); // should not pass completely }); diff --git a/sample/sample8-es6-maps/Post.ts b/sample/sample8-es6-maps/Post.ts new file mode 100644 index 0000000000..0148b1f4ad --- /dev/null +++ b/sample/sample8-es6-maps/Post.ts @@ -0,0 +1,12 @@ +import { Contains, IsInt, Length, IsEmail, IsFQDN, IsDate, ValidateNested } from '../../src/decorator/decorators'; +import { Tag } from './Tag'; + +export class Post { + @Length(10, 20, { + message: 'Incorrect length!', + }) + title: string; + + @ValidateNested() + tags: Map; +} diff --git a/sample/sample8-es6-maps/Tag.ts b/sample/sample8-es6-maps/Tag.ts new file mode 100644 index 0000000000..0d19e8893c --- /dev/null +++ b/sample/sample8-es6-maps/Tag.ts @@ -0,0 +1,8 @@ +import { Contains, IsInt, Length, IsEmail, IsFQDN, IsDate } from '../../src/decorator/decorators'; + +export class Tag { + @Length(10, 20, { + message: 'Tag value is too short or long', + }) + value: string; +} diff --git a/sample/sample8-es6-maps/app.ts b/sample/sample8-es6-maps/app.ts new file mode 100644 index 0000000000..3e893047c1 --- /dev/null +++ b/sample/sample8-es6-maps/app.ts @@ -0,0 +1,21 @@ +import { Validator } from '../../src/validation/Validator'; +import { Post } from './Post'; +import { Tag } from './Tag'; + +let validator = new Validator(); + +let tag1 = new Tag(); +tag1.value = 'ja'; + +let tag2 = new Tag(); +tag2.value = 'node.js'; + +let post1 = new Post(); +post1.title = 'Hello world'; +post1.tags = new Map(); +post1.tags.set('tag1', tag1); +post1.tags.set('tag2', tag2); + +validator.validate(post1).then(result => { + console.log('1. should not pass: ', result); +}); diff --git a/sample/sample9-es6-sets/Post.ts b/sample/sample9-es6-sets/Post.ts new file mode 100644 index 0000000000..d14f5de039 --- /dev/null +++ b/sample/sample9-es6-sets/Post.ts @@ -0,0 +1,12 @@ +import { Contains, IsInt, Length, IsEmail, IsFQDN, IsDate, ValidateNested } from '../../src/decorator/decorators'; +import { Tag } from './Tag'; + +export class Post { + @Length(10, 20, { + message: 'Incorrect length!', + }) + title: string; + + @ValidateNested() + tags: Set; +} diff --git a/sample/sample9-es6-sets/Tag.ts b/sample/sample9-es6-sets/Tag.ts new file mode 100644 index 0000000000..0d19e8893c --- /dev/null +++ b/sample/sample9-es6-sets/Tag.ts @@ -0,0 +1,8 @@ +import { Contains, IsInt, Length, IsEmail, IsFQDN, IsDate } from '../../src/decorator/decorators'; + +export class Tag { + @Length(10, 20, { + message: 'Tag value is too short or long', + }) + value: string; +} diff --git a/sample/sample9-es6-sets/app.ts b/sample/sample9-es6-sets/app.ts new file mode 100644 index 0000000000..58686cf42d --- /dev/null +++ b/sample/sample9-es6-sets/app.ts @@ -0,0 +1,21 @@ +import { Validator } from '../../src/validation/Validator'; +import { Post } from './Post'; +import { Tag } from './Tag'; + +let validator = new Validator(); + +let tag1 = new Tag(); +tag1.value = 'ja'; + +let tag2 = new Tag(); +tag2.value = 'node.js'; + +let post1 = new Post(); +post1.title = 'Hello world'; +post1.tags = new Set(); +post1.tags.add(tag1); +post1.tags.add(tag2); + +validator.validate(post1).then(result => { + console.log('1. should not pass: ', result); +}); diff --git a/src/container.ts b/src/container.ts index cc18963daf..2a288bde81 100644 --- a/src/container.ts +++ b/src/container.ts @@ -1,66 +1,59 @@ - /** * Container options. */ export interface UseContainerOptions { - - /** - * If set to true, then default container will be used in the case if given container haven't returned anything. - */ - fallback?: boolean; - - /** - * If set to true, then default container will be used in the case if given container thrown an exception. - */ - fallbackOnErrors?: boolean; - + /** + * If set to true, then default container will be used in the case if given container haven't returned anything. + */ + fallback?: boolean; + + /** + * If set to true, then default container will be used in the case if given container thrown an exception. + */ + fallbackOnErrors?: boolean; } /** * Container to be used by this library for inversion control. If container was not implicitly set then by default * container simply creates a new instance of the given class. */ -const defaultContainer: { get(someClass: { new (...args: any[]): T }|Function): T } = new (class { - private instances: { type: Function, object: any }[] = []; - get(someClass: { new (...args: any[]): T }): T { - let instance = this.instances.find(instance => instance.type === someClass); - if (!instance) { - instance = { type: someClass, object: new someClass() }; - this.instances.push(instance); - } - - return instance.object; +const defaultContainer: { get(someClass: { new (...args: any[]): T } | Function): T } = new (class { + private instances: { type: Function; object: any }[] = []; + get(someClass: { new (...args: any[]): T }): T { + let instance = this.instances.find(instance => instance.type === someClass); + if (!instance) { + instance = { type: someClass, object: new someClass() }; + this.instances.push(instance); } + + return instance.object; + } })(); -let userContainer: { get(someClass: { new (...args: any[]): T }|Function): T }; +let userContainer: { get(someClass: { new (...args: any[]): T } | Function): T }; let userContainerOptions: UseContainerOptions; /** * Sets container to be used by this library. */ -export function useContainer(iocContainer: { get(someClass: any): any }, options?: UseContainerOptions) { - userContainer = iocContainer; - userContainerOptions = options; +export function useContainer(iocContainer: { get(someClass: any): any }, options?: UseContainerOptions): void { + userContainer = iocContainer; + userContainerOptions = options; } /** * Gets the IOC container used by this library. */ -export function getFromContainer(someClass: { new (...args: any[]): T }|Function): T { - if (userContainer) { - try { - const instance = userContainer.get(someClass); - if (instance) - return instance; - - if (!userContainerOptions || !userContainerOptions.fallback) - return instance; - - } catch (error) { - if (!userContainerOptions || !userContainerOptions.fallbackOnErrors) - throw error; - } +export function getFromContainer(someClass: { new (...args: any[]): T } | Function): T { + if (userContainer) { + try { + const instance = userContainer.get(someClass); + if (instance) return instance; + + if (!userContainerOptions || !userContainerOptions.fallback) return instance; + } catch (error) { + if (!userContainerOptions || !userContainerOptions.fallbackOnErrors) throw error; } - return defaultContainer.get(someClass); + } + return defaultContainer.get(someClass); } diff --git a/src/decorator/ValidationOptions.ts b/src/decorator/ValidationOptions.ts index 4370f0a0d5..60059a5fa6 100644 --- a/src/decorator/ValidationOptions.ts +++ b/src/decorator/ValidationOptions.ts @@ -1,35 +1,39 @@ +import { ValidationArguments } from '../validation/ValidationArguments'; + /** * Options used to pass to validation decorators. */ export interface ValidationOptions { + /** + * Specifies if validated value is an array and each of its items must be validated. + */ + each?: boolean; - /** - * Specifies if validated value is an array and each of its item must be validated. - */ - each?: boolean; + /** + * Error message to be used on validation fail. + * Message can be either string or a function that returns a string. + */ + message?: string | ((validationArguments: ValidationArguments) => string); - /** - * Error message used to be used on validation fail. - * You can use "$value" to use value that was failed by validation. - * You can use "$constraint1" and "$constraint2" keys in the message string, - * and they will be replaced with constraint values if they exist. - * Message can be either string, either a function that returns a string. - * Second option allows to use values and custom messages depend of them. - */ - message?: string|((value?: any, constraint1?: any, constraint2?: any) => string); + /** + * Validation groups used for this validation. + */ + groups?: string[]; - /** - * Validation groups used for this validation. - */ - groups?: string[]; + /** + * Indicates if validation must be performed always, no matter of validation groups used. + */ + always?: boolean; - /** - * Indicates if validation must be performed always, no matter of validation groups used. - */ - always?: boolean; + /* + * A transient set of data passed through to the validation result for response mapping + */ + context?: any; +} - /* - * A transient set of data passed through to the validation result for response mapping - */ - context?: any; -} \ No newline at end of file +export function isValidationOptions(val: any): val is ValidationOptions { + if (!val) { + return false; + } + return 'each' in val || 'message' in val || 'groups' in val || 'always' in val || 'context' in val; +} diff --git a/src/decorator/array/ArrayContains.ts b/src/decorator/array/ArrayContains.ts new file mode 100644 index 0000000000..b2242c15d2 --- /dev/null +++ b/src/decorator/array/ArrayContains.ts @@ -0,0 +1,35 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; + +export const ARRAY_CONTAINS = 'arrayContains'; + +/** + * Checks if array contains all values from the given array of values. + * If null or undefined is given then this function returns false. + */ +export function arrayContains(array: unknown, values: any[]): boolean { + if (!Array.isArray(array)) return false; + + return values.every(value => array.indexOf(value) !== -1); +} + +/** + * Checks if array contains all values from the given array of values. + * If null or undefined is given then this function returns false. + */ +export function ArrayContains(values: any[], validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: ARRAY_CONTAINS, + constraints: [values], + validator: { + validate: (value, args): boolean => arrayContains(value, args?.constraints[0]), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must contain $constraint1 values', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/array/ArrayMaxSize.ts b/src/decorator/array/ArrayMaxSize.ts new file mode 100644 index 0000000000..726ebd6b5e --- /dev/null +++ b/src/decorator/array/ArrayMaxSize.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; + +export const ARRAY_MAX_SIZE = 'arrayMaxSize'; + +/** + * Checks if the array's length is less or equal to the specified number. + * If null or undefined is given then this function returns false. + */ +export function arrayMaxSize(array: unknown, max: number): boolean { + return Array.isArray(array) && array.length <= max; +} + +/** + * Checks if the array's length is less or equal to the specified number. + * If null or undefined is given then this function returns false. + */ +export function ArrayMaxSize(max: number, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: ARRAY_MAX_SIZE, + constraints: [max], + validator: { + validate: (value, args): boolean => arrayMaxSize(value, args?.constraints[0]), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must contain no more than $constraint1 elements', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/array/ArrayMinSize.ts b/src/decorator/array/ArrayMinSize.ts new file mode 100644 index 0000000000..750b297e90 --- /dev/null +++ b/src/decorator/array/ArrayMinSize.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; + +export const ARRAY_MIN_SIZE = 'arrayMinSize'; + +/** + * Checks if the array's length is greater than or equal to the specified number. + * If null or undefined is given then this function returns false. + */ +export function arrayMinSize(array: unknown, min: number): boolean { + return Array.isArray(array) && array.length >= min; +} + +/** + * Checks if the array's length is greater than or equal to the specified number. + * If null or undefined is given then this function returns false. + */ +export function ArrayMinSize(min: number, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: ARRAY_MIN_SIZE, + constraints: [min], + validator: { + validate: (value, args): boolean => arrayMinSize(value, args?.constraints[0]), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must contain at least $constraint1 elements', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/array/ArrayNotContains.ts b/src/decorator/array/ArrayNotContains.ts new file mode 100644 index 0000000000..fca6c4ccb5 --- /dev/null +++ b/src/decorator/array/ArrayNotContains.ts @@ -0,0 +1,35 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; + +export const ARRAY_NOT_CONTAINS = 'arrayNotContains'; + +/** + * Checks if array does not contain any of the given values. + * If null or undefined is given then this function returns false. + */ +export function arrayNotContains(array: unknown, values: any[]): boolean { + if (!Array.isArray(array)) return false; + + return values.every(value => array.indexOf(value) === -1); +} + +/** + * Checks if array does not contain any of the given values. + * If null or undefined is given then this function returns false. + */ +export function ArrayNotContains(values: any[], validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: ARRAY_NOT_CONTAINS, + constraints: [values], + validator: { + validate: (value, args): boolean => arrayNotContains(value, args?.constraints[0]), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property should not contain $constraint1 values', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/array/ArrayNotEmpty.ts b/src/decorator/array/ArrayNotEmpty.ts new file mode 100644 index 0000000000..432d4c5248 --- /dev/null +++ b/src/decorator/array/ArrayNotEmpty.ts @@ -0,0 +1,29 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; + +export const ARRAY_NOT_EMPTY = 'arrayNotEmpty'; + +/** + * Checks if given array is not empty. + * If null or undefined is given then this function returns false. + */ +export function arrayNotEmpty(array: unknown): boolean { + return Array.isArray(array) && array.length > 0; +} + +/** + * Checks if given array is not empty. + * If null or undefined is given then this function returns false. + */ +export function ArrayNotEmpty(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: ARRAY_NOT_EMPTY, + validator: { + validate: (value, args): boolean => arrayNotEmpty(value), + defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property should not be empty', validationOptions), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/array/ArrayUnique.ts b/src/decorator/array/ArrayUnique.ts new file mode 100644 index 0000000000..0979aeefc0 --- /dev/null +++ b/src/decorator/array/ArrayUnique.ts @@ -0,0 +1,43 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; + +export const ARRAY_UNIQUE = 'arrayUnique'; +export type ArrayUniqueIdentifier = (o: T) => any; + +/** + * Checks if all array's values are unique. Comparison for objects is reference-based. + * If null or undefined is given then this function returns false. + */ +export function arrayUnique(array: unknown[], identifier?: ArrayUniqueIdentifier): boolean { + if (!Array.isArray(array)) return false; + + if (identifier) { + array = array.map(o => (o != null ? identifier(o) : o)); + } + + const uniqueItems = array.filter((a, b, c) => c.indexOf(a) === b); + return array.length === uniqueItems.length; +} + +/** + * Checks if all array's values are unique. Comparison for objects is reference-based. + * If null or undefined is given then this function returns false. + */ +export function ArrayUnique( + identifierOrOptions?: ArrayUniqueIdentifier | ValidationOptions, + validationOptions?: ValidationOptions +): PropertyDecorator { + const identifier = typeof identifierOrOptions === 'function' ? identifierOrOptions : undefined; + const options = typeof identifierOrOptions !== 'function' ? identifierOrOptions : validationOptions; + + return ValidateBy( + { + name: ARRAY_UNIQUE, + validator: { + validate: (value, args): boolean => arrayUnique(value, identifier), + defaultMessage: buildMessage(eachPrefix => eachPrefix + "All $property's elements must be unique", options), + }, + }, + options + ); +} diff --git a/src/decorator/common/Allow.ts b/src/decorator/common/Allow.ts new file mode 100644 index 0000000000..943722ec8c --- /dev/null +++ b/src/decorator/common/Allow.ts @@ -0,0 +1,20 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { ValidationMetadataArgs } from '../../metadata/ValidationMetadataArgs'; +import { ValidationTypes } from '../../validation/ValidationTypes'; +import { ValidationMetadata } from '../../metadata/ValidationMetadata'; +import { getMetadataStorage } from '../../metadata/MetadataStorage'; + +/** + * If object has both allowed and not allowed properties a validation error will be thrown. + */ +export function Allow(validationOptions?: ValidationOptions): PropertyDecorator { + return function (object: object, propertyName: string): void { + const args: ValidationMetadataArgs = { + type: ValidationTypes.WHITELIST, + target: object.constructor, + propertyName: propertyName, + validationOptions: validationOptions, + }; + getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); + }; +} diff --git a/src/decorator/common/Equals.ts b/src/decorator/common/Equals.ts new file mode 100644 index 0000000000..91ef9e65d7 --- /dev/null +++ b/src/decorator/common/Equals.ts @@ -0,0 +1,31 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; + +export const EQUALS = 'equals'; + +/** + * Checks if value matches ("===") the comparison. + */ +export function equals(value: unknown, comparison: unknown): boolean { + return value === comparison; +} + +/** + * Checks if value matches ("===") the comparison. + */ +export function Equals(comparison: any, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: EQUALS, + constraints: [comparison], + validator: { + validate: (value, args): boolean => equals(value, args?.constraints[0]), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be equal to $constraint1', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/common/IsDefined.ts b/src/decorator/common/IsDefined.ts new file mode 100644 index 0000000000..69c08901a3 --- /dev/null +++ b/src/decorator/common/IsDefined.ts @@ -0,0 +1,32 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from './ValidateBy'; +import { ValidationTypes } from '../../validation/ValidationTypes'; + +// isDefined is (yet) a special case +export const IS_DEFINED = ValidationTypes.IS_DEFINED; + +/** + * Checks if value is defined (!== undefined, !== null). + */ +export function isDefined(value: any): boolean { + return value !== undefined && value !== null; +} + +/** + * Checks if value is defined (!== undefined, !== null). + */ +export function IsDefined(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_DEFINED, + validator: { + validate: (value): boolean => isDefined(value), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property should not be null or undefined', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/common/IsEmpty.ts b/src/decorator/common/IsEmpty.ts new file mode 100644 index 0000000000..1447811b9e --- /dev/null +++ b/src/decorator/common/IsEmpty.ts @@ -0,0 +1,27 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; + +export const IS_EMPTY = 'isEmpty'; + +/** + * Checks if given value is empty (=== '', === null, === undefined). + */ +export function isEmpty(value: unknown): boolean { + return value === '' || value === null || value === undefined; +} + +/** + * Checks if given value is empty (=== '', === null, === undefined). + */ +export function IsEmpty(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_EMPTY, + validator: { + validate: (value, args): boolean => isEmpty(value), + defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be empty', validationOptions), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/common/IsIn.ts b/src/decorator/common/IsIn.ts new file mode 100644 index 0000000000..cd9499e5e8 --- /dev/null +++ b/src/decorator/common/IsIn.ts @@ -0,0 +1,31 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; + +export const IS_IN = 'isIn'; + +/** + * Checks if given value is in a array of allowed values. + */ +export function isIn(value: unknown, possibleValues: readonly unknown[]): boolean { + return !Array.isArray(possibleValues) || possibleValues.some(possibleValue => possibleValue === value); +} + +/** + * Checks if given value is in a array of allowed values. + */ +export function IsIn(values: readonly any[], validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_IN, + constraints: [values], + validator: { + validate: (value, args): boolean => isIn(value, args?.constraints[0]), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be one of the following values: $constraint1', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/common/IsLatLong.ts b/src/decorator/common/IsLatLong.ts new file mode 100644 index 0000000000..becbf29c33 --- /dev/null +++ b/src/decorator/common/IsLatLong.ts @@ -0,0 +1,31 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from './ValidateBy'; +import isLatLongValidator from 'validator/lib/isLatLong'; + +export const IS_LATLONG = 'isLatLong'; + +/** + * Checks if a value is string in format a "latitude,longitude". + */ +export function isLatLong(value: string): boolean { + return typeof value === 'string' && isLatLongValidator(value); +} + +/** + * Checks if a value is string in format a "latitude,longitude". + */ +export function IsLatLong(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_LATLONG, + validator: { + validate: (value, args): boolean => isLatLong(value), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be a latitude,longitude string', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/common/IsLatitude.ts b/src/decorator/common/IsLatitude.ts new file mode 100644 index 0000000000..1be12e130e --- /dev/null +++ b/src/decorator/common/IsLatitude.ts @@ -0,0 +1,31 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from './ValidateBy'; +import { isLatLong } from './IsLatLong'; + +export const IS_LATITUDE = 'isLatitude'; + +/** + * Checks if a given value is a latitude. + */ +export function isLatitude(value: string): boolean { + return (typeof value === 'number' || typeof value === 'string') && isLatLong(`${value},0`); +} + +/** + * Checks if a given value is a latitude. + */ +export function IsLatitude(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_LATITUDE, + validator: { + validate: (value, args): boolean => isLatitude(value), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be a latitude string or number', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/common/IsLongitude.ts b/src/decorator/common/IsLongitude.ts new file mode 100644 index 0000000000..013f5387af --- /dev/null +++ b/src/decorator/common/IsLongitude.ts @@ -0,0 +1,31 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from './ValidateBy'; +import { isLatLong } from './IsLatLong'; + +export const IS_LONGITUDE = 'isLongitude'; + +/** + * Checks if a given value is a longitude. + */ +export function isLongitude(value: string): boolean { + return (typeof value === 'number' || typeof value === 'string') && isLatLong(`0,${value}`); +} + +/** + * Checks if a given value is a longitude. + */ +export function IsLongitude(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_LONGITUDE, + validator: { + validate: (value, args): boolean => isLongitude(value), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be a longitude string or number', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/common/IsNotEmpty.ts b/src/decorator/common/IsNotEmpty.ts new file mode 100644 index 0000000000..605da09edc --- /dev/null +++ b/src/decorator/common/IsNotEmpty.ts @@ -0,0 +1,27 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; + +export const IS_NOT_EMPTY = 'isNotEmpty'; + +/** + * Checks if given value is not empty (!== '', !== null, !== undefined). + */ +export function isNotEmpty(value: unknown): boolean { + return value !== '' && value !== null && value !== undefined; +} + +/** + * Checks if given value is not empty (!== '', !== null, !== undefined). + */ +export function IsNotEmpty(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_NOT_EMPTY, + validator: { + validate: (value, args): boolean => isNotEmpty(value), + defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property should not be empty', validationOptions), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/common/IsNotIn.ts b/src/decorator/common/IsNotIn.ts new file mode 100644 index 0000000000..187e91d96a --- /dev/null +++ b/src/decorator/common/IsNotIn.ts @@ -0,0 +1,31 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; + +export const IS_NOT_IN = 'isNotIn'; + +/** + * Checks if given value not in a array of allowed values. + */ +export function isNotIn(value: unknown, possibleValues: readonly unknown[]): boolean { + return !Array.isArray(possibleValues) || !possibleValues.some(possibleValue => possibleValue === value); +} + +/** + * Checks if given value not in a array of allowed values. + */ +export function IsNotIn(values: readonly any[], validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_NOT_IN, + constraints: [values], + validator: { + validate: (value, args): boolean => isNotIn(value, args?.constraints[0]), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property should not be one of the following values: $constraint1', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/common/IsOptional.ts b/src/decorator/common/IsOptional.ts new file mode 100644 index 0000000000..25ef08915c --- /dev/null +++ b/src/decorator/common/IsOptional.ts @@ -0,0 +1,25 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { ValidationMetadataArgs } from '../../metadata/ValidationMetadataArgs'; +import { ValidationTypes } from '../../validation/ValidationTypes'; +import { ValidationMetadata } from '../../metadata/ValidationMetadata'; +import { getMetadataStorage } from '../../metadata/MetadataStorage'; + +/** + * Checks if value is missing and if so, ignores all validators. + */ +export function IsOptional(validationOptions?: ValidationOptions): PropertyDecorator { + return function (object: object, propertyName: string): void { + const args: ValidationMetadataArgs = { + type: ValidationTypes.CONDITIONAL_VALIDATION, + target: object.constructor, + propertyName: propertyName, + constraints: [ + (object: any, value: any): boolean => { + return object[propertyName] !== null && object[propertyName] !== undefined; + }, + ], + validationOptions: validationOptions, + }; + getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); + }; +} diff --git a/src/decorator/common/NotEquals.ts b/src/decorator/common/NotEquals.ts new file mode 100644 index 0000000000..ec5ecdefc0 --- /dev/null +++ b/src/decorator/common/NotEquals.ts @@ -0,0 +1,31 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; + +export const NOT_EQUALS = 'notEquals'; + +/** + * Checks if value does not match ("!==") the comparison. + */ +export function notEquals(value: unknown, comparison: unknown): boolean { + return value !== comparison; +} + +/** + * Checks if value does not match ("!==") the comparison. + */ +export function NotEquals(comparison: any, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: NOT_EQUALS, + constraints: [comparison], + validator: { + validate: (value, args): boolean => notEquals(value, args?.constraints[0]), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property should not be equal to $constraint1', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/common/Validate.ts b/src/decorator/common/Validate.ts new file mode 100644 index 0000000000..59c80cdd08 --- /dev/null +++ b/src/decorator/common/Validate.ts @@ -0,0 +1,54 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { ValidationMetadataArgs } from '../../metadata/ValidationMetadataArgs'; +import { ValidationMetadata } from '../../metadata/ValidationMetadata'; +import { getMetadataStorage } from '../../metadata/MetadataStorage'; +import { ValidationTypes } from '../../validation/ValidationTypes'; +import { ConstraintMetadata } from '../../metadata/ConstraintMetadata'; + +/** + * Registers custom validator class. + */ +export function ValidatorConstraint(options?: { name?: string; async?: boolean }) { + return function (target: Function): void { + const isAsync = options && options.async; + let name = options && options.name ? options.name : ''; + if (!name) { + name = (target as any).name; + if (!name) + // generate name if it was not given + name = name.replace(/\.?([A-Z]+)/g, (x, y) => '_' + (y as string).toLowerCase()).replace(/^_/, ''); + } + const metadata = new ConstraintMetadata(target, name, isAsync); + getMetadataStorage().addConstraintMetadata(metadata); + }; +} + +/** + * Performs validation based on the given custom validation class. + * Validation class must be decorated with ValidatorConstraint decorator. + */ +export function Validate(constraintClass: Function, validationOptions?: ValidationOptions): PropertyDecorator; +export function Validate( + constraintClass: Function, + constraints?: any[], + validationOptions?: ValidationOptions +): PropertyDecorator; +export function Validate( + constraintClass: Function, + constraintsOrValidationOptions?: any[] | ValidationOptions, + maybeValidationOptions?: ValidationOptions +): PropertyDecorator { + return function (object: object, propertyName: string): void { + const args: ValidationMetadataArgs = { + type: ValidationTypes.CUSTOM_VALIDATION, + target: object.constructor, + propertyName: propertyName, + constraintCls: constraintClass, + constraints: Array.isArray(constraintsOrValidationOptions) ? constraintsOrValidationOptions : undefined, + validationOptions: !Array.isArray(constraintsOrValidationOptions) + ? constraintsOrValidationOptions + : maybeValidationOptions, + }; + getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); + }; +} diff --git a/src/decorator/common/ValidateBy.ts b/src/decorator/common/ValidateBy.ts new file mode 100644 index 0000000000..641cb00a06 --- /dev/null +++ b/src/decorator/common/ValidateBy.ts @@ -0,0 +1,34 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { registerDecorator } from '../../register-decorator'; +import { ValidationArguments } from '../../validation/ValidationArguments'; +import { ValidatorConstraintInterface } from '../../validation/ValidatorConstraintInterface'; + +export interface ValidateByOptions { + name: string; + constraints?: any[]; + validator: ValidatorConstraintInterface | Function; + async?: boolean; +} + +export function buildMessage( + impl: (eachPrefix: string, args?: ValidationArguments) => string, + validationOptions?: ValidationOptions +): (validationArguments?: ValidationArguments) => string { + return (validationArguments?: ValidationArguments): string => { + const eachPrefix = validationOptions && validationOptions.each ? 'each value in ' : ''; + return impl(eachPrefix, validationArguments); + }; +} + +export function ValidateBy(options: ValidateByOptions, validationOptions?: ValidationOptions): PropertyDecorator { + return function (object: object, propertyName: string): void { + registerDecorator({ + name: options.name, + target: object.constructor, + propertyName: propertyName, + options: validationOptions, + constraints: options.constraints, + validator: options.validator, + }); + }; +} diff --git a/src/decorator/common/ValidateIf.ts b/src/decorator/common/ValidateIf.ts new file mode 100644 index 0000000000..14f1deeb77 --- /dev/null +++ b/src/decorator/common/ValidateIf.ts @@ -0,0 +1,24 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { ValidationMetadataArgs } from '../../metadata/ValidationMetadataArgs'; +import { ValidationTypes } from '../../validation/ValidationTypes'; +import { ValidationMetadata } from '../../metadata/ValidationMetadata'; +import { getMetadataStorage } from '../../metadata/MetadataStorage'; + +/** + * Ignores the other validators on a property when the provided condition function returns false. + */ +export function ValidateIf( + condition: (object: any, value: any) => boolean, + validationOptions?: ValidationOptions +): PropertyDecorator { + return function (object: object, propertyName: string): void { + const args: ValidationMetadataArgs = { + type: ValidationTypes.CONDITIONAL_VALIDATION, + target: object.constructor, + propertyName: propertyName, + constraints: [condition], + validationOptions: validationOptions, + }; + getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); + }; +} diff --git a/src/decorator/common/ValidateNested.ts b/src/decorator/common/ValidateNested.ts new file mode 100644 index 0000000000..da56eaefa4 --- /dev/null +++ b/src/decorator/common/ValidateNested.ts @@ -0,0 +1,24 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { ValidationMetadataArgs } from '../../metadata/ValidationMetadataArgs'; +import { ValidationTypes } from '../../validation/ValidationTypes'; +import { ValidationMetadata } from '../../metadata/ValidationMetadata'; +import { getMetadataStorage } from '../../metadata/MetadataStorage'; + +/** + * Objects / object arrays marked with this decorator will also be validated. + */ +export function ValidateNested(validationOptions?: ValidationOptions): PropertyDecorator { + const opts: ValidationOptions = { ...validationOptions }; + const eachPrefix = opts.each ? 'each value in ' : ''; + opts.message = opts.message || eachPrefix + 'nested property $property must be either object or array'; + + return function (object: object, propertyName: string): void { + const args: ValidationMetadataArgs = { + type: ValidationTypes.NESTED_VALIDATION, + target: object.constructor, + propertyName: propertyName, + validationOptions: opts, + }; + getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); + }; +} diff --git a/src/decorator/common/ValidatePromise.ts b/src/decorator/common/ValidatePromise.ts new file mode 100644 index 0000000000..bd90519e86 --- /dev/null +++ b/src/decorator/common/ValidatePromise.ts @@ -0,0 +1,20 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { ValidationMetadataArgs } from '../../metadata/ValidationMetadataArgs'; +import { ValidationTypes } from '../../validation/ValidationTypes'; +import { ValidationMetadata } from '../../metadata/ValidationMetadata'; +import { getMetadataStorage } from '../../metadata/MetadataStorage'; + +/** + * Resolve promise before validation + */ +export function ValidatePromise(validationOptions?: ValidationOptions): PropertyDecorator { + return function (object: object, propertyName: string): void { + const args: ValidationMetadataArgs = { + type: ValidationTypes.PROMISE_VALIDATION, + target: object.constructor, + propertyName: propertyName, + validationOptions: validationOptions, + }; + getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); + }; +} diff --git a/src/decorator/date/MaxDate.ts b/src/decorator/date/MaxDate.ts new file mode 100644 index 0000000000..2bd062539f --- /dev/null +++ b/src/decorator/date/MaxDate.ts @@ -0,0 +1,31 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; + +export const MAX_DATE = 'maxDate'; + +/** + * Checks if the value is a date that's before the specified date. + */ +export function maxDate(date: unknown, maxDate: Date | (() => Date)): boolean { + return date instanceof Date && date.getTime() <= (maxDate instanceof Date ? maxDate : maxDate()).getTime(); +} + +/** + * Checks if the value is a date that's after the specified date. + */ +export function MaxDate(date: Date | (() => Date), validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: MAX_DATE, + constraints: [date], + validator: { + validate: (value, args): boolean => maxDate(value, args?.constraints[0]), + defaultMessage: buildMessage( + eachPrefix => 'maximal allowed date for ' + eachPrefix + '$property is $constraint1', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/date/MinDate.ts b/src/decorator/date/MinDate.ts new file mode 100644 index 0000000000..a0855e798e --- /dev/null +++ b/src/decorator/date/MinDate.ts @@ -0,0 +1,31 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; + +export const MIN_DATE = 'minDate'; + +/** + * Checks if the value is a date that's after the specified date. + */ +export function minDate(date: unknown, minDate: Date | (() => Date)): boolean { + return date instanceof Date && date.getTime() >= (minDate instanceof Date ? minDate : minDate()).getTime(); +} + +/** + * Checks if the value is a date that's after the specified date. + */ +export function MinDate(date: Date | (() => Date), validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: MIN_DATE, + constraints: [date], + validator: { + validate: (value, args): boolean => minDate(value, args?.constraints[0]), + defaultMessage: buildMessage( + eachPrefix => 'minimal allowed date for ' + eachPrefix + '$property is $constraint1', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/decorators.ts b/src/decorator/decorators.ts index 72024ddf43..d449e9301a 100644 --- a/src/decorator/decorators.ts +++ b/src/decorator/decorators.ts @@ -1,1174 +1,149 @@ -import {ValidationTypes} from "../validation/ValidationTypes"; -import {IsEmailOptions, IsFQDNOptions, IsURLOptions, IsCurrencyOptions, IsNumberOptions} from "../validation/ValidationTypeOptions"; -import {ValidationOptions} from "./ValidationOptions"; -import {ValidationMetadata} from "../metadata/ValidationMetadata"; -import {ValidationMetadataArgs} from "../metadata/ValidationMetadataArgs"; -import {ConstraintMetadata} from "../metadata/ConstraintMetadata"; -import {getFromContainer} from "../container"; -import {MetadataStorage} from "../metadata/MetadataStorage"; - // ------------------------------------------------------------------------- // System // ------------------------------------------------------------------------- -/** - * Registers custom validator class. - */ -export function ValidatorConstraint(options?: { name?: string, async?: boolean }) { - return function(target: Function) { - const isAsync = options && options.async ? true : false; - let name = options && options.name ? options.name : ""; - if (!name) { - name = (target as any).name; - if (!name) // generate name if it was not given - name = name.replace(/\.?([A-Z]+)/g, (x, y) => "_" + y.toLowerCase()).replace(/^_/, ""); - } - const metadata = new ConstraintMetadata(target, name, isAsync); - getFromContainer(MetadataStorage).addConstraintMetadata(metadata); - }; -} - -/** - * Performs validation based on the given custom validation class. - * Validation class must be decorated with ValidatorConstraint decorator. - */ -export function Validate(constraintClass: Function, validationOptions?: ValidationOptions): Function; -export function Validate(constraintClass: Function, constraints?: any[], validationOptions?: ValidationOptions): Function; -export function Validate(constraintClass: Function, constraintsOrValidationOptions?: any[]|ValidationOptions, maybeValidationOptions?: ValidationOptions): Function { - return function(object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.CUSTOM_VALIDATION, - target: object.constructor, - propertyName: propertyName, - constraintCls: constraintClass, - constraints: constraintsOrValidationOptions instanceof Array ? constraintsOrValidationOptions as any[] : undefined, - validationOptions: !(constraintsOrValidationOptions instanceof Array) ? constraintsOrValidationOptions as ValidationOptions : maybeValidationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Objects / object arrays marked with this decorator will also be validated. - */ -export function ValidateNested(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.NESTED_VALIDATION, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * If object has both allowed and not allowed properties a validation error will be thrown. - */ -export function Allow(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.WHITELIST, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - - -/** - * Objects / object arrays marked with this decorator will also be validated. - */ -export function ValidateIf(condition: (object: any, value: any) => boolean, validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.CONDITIONAL_VALIDATION, - target: object.constructor, - propertyName: propertyName, - constraints: [condition], - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - // ------------------------------------------------------------------------- // Common checkers // ------------------------------------------------------------------------- -/** - * Checks if given value is defined (!== undefined, !== null). - */ -export function IsDefined(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_DEFINED, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the value match ("===") the comparison. - */ -export function Equals(comparison: any, validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.EQUALS, - target: object.constructor, - propertyName: propertyName, - constraints: [comparison], - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the value does not match ("!==") the comparison. - */ -export function NotEquals(comparison: any, validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.NOT_EQUALS, - target: object.constructor, - propertyName: propertyName, - constraints: [comparison], - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if given value is empty (=== '', === null, === undefined). - */ -export function IsEmpty(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_EMPTY, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if given value is not empty (!== '', !== null, !== undefined). - */ -export function IsNotEmpty(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_NOT_EMPTY, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if value is in a array of allowed values. - */ -export function IsIn(values: any[], validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_IN, - target: object.constructor, - propertyName: propertyName, - constraints: [values], - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if value is not in a array of disallowed values. - */ -export function IsNotIn(values: any[], validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_NOT_IN, - target: object.constructor, - propertyName: propertyName, - constraints: [values], - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if value is missing and if so, ignores all validators. - */ -export function IsOptional(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.CONDITIONAL_VALIDATION, - target: object.constructor, - propertyName: propertyName, - constraints: [(object: any, value: any) => { - return object[propertyName] !== null && object[propertyName] !== undefined; - }], - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -// ------------------------------------------------------------------------- -// Type checkers -// ------------------------------------------------------------------------- - -/** - * Checks if a value is a boolean. - */ -export function IsBoolean(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_BOOLEAN, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if a value is a date. - */ -export function IsDate(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_DATE, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if a value is a number. - */ -export function IsNumber(options: IsNumberOptions = {}, validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_NUMBER, - target: object.constructor, - propertyName: propertyName, - constraints: [options], - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the value is an integer number. - */ -export function IsInt(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_INT, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if a value is a string. - */ -export function IsString(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_STRING, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -export function IsDateString(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_DATE_STRING, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if a value is an array. - */ -export function IsArray(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_ARRAY, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if a value is a number enum. - */ -export function IsEnum(entity: Object, validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_ENUM, - target: object.constructor, - propertyName: propertyName, - constraints: [entity], - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - +export * from './common/Allow'; +export * from './common/IsDefined'; +export * from './common/IsOptional'; +export * from './common/Validate'; +export * from './common/ValidateBy'; +export * from './common/ValidateIf'; +export * from './common/ValidateNested'; +export * from './common/ValidatePromise'; +export * from './common/IsLatLong'; +export * from './common/IsLatitude'; +export * from './common/IsLongitude'; +export * from './common/Equals'; +export * from './common/NotEquals'; +export * from './common/IsEmpty'; +export * from './common/IsNotEmpty'; +export * from './common/IsIn'; +export * from './common/IsNotIn'; // ------------------------------------------------------------------------- // Number checkers // ------------------------------------------------------------------------- -/** - * Checks if the value is a number that's divisible by another. - */ -export function IsDivisibleBy(num: number, validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_DIVISIBLE_BY, - target: object.constructor, - propertyName: propertyName, - constraints: [num], - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the value is a positive number. - */ -export function IsPositive(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_POSITIVE, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the value is a negative number. - */ -export function IsNegative(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_NEGATIVE, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} -/** - * Checks if the given number is greater than or equal to given number. - */ -export function Min(min: number, validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.MIN, - target: object.constructor, - propertyName: propertyName, - constraints: [min], - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the given number is less than or equal to given number. - */ -export function Max(max: number, validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.MAX, - target: object.constructor, - propertyName: propertyName, - constraints: [max], - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} +export * from './number/IsDivisibleBy'; +export * from './number/IsPositive'; +export * from './number/IsNegative'; +export * from './number/Max'; +export * from './number/Min'; // ------------------------------------------------------------------------- // Date checkers // ------------------------------------------------------------------------- -/** - * Checks if the value is a date that's after the specified date. - */ -export function MinDate(date: Date, validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.MIN_DATE, - target: object.constructor, - propertyName: propertyName, - constraints: [date], - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the value is a date that's before the specified date. - */ -export function MaxDate(date: Date, validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.MAX_DATE, - target: object.constructor, - propertyName: propertyName, - constraints: [date], - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} +export * from './date/MinDate'; +export * from './date/MaxDate'; // ------------------------------------------------------------------------- -// String-as-types checkers +// String checkers // ------------------------------------------------------------------------- -/** - * Checks if a string is a boolean. - */ -export function IsBooleanString(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_BOOLEAN_STRING, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string is a number. - */ -export function IsNumberString(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_NUMBER_STRING, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} +export * from './string/Contains'; +export * from './string/NotContains'; +export * from './string/IsAlpha'; +export * from './string/IsAlphanumeric'; +export * from './string/IsDecimal'; +export * from './string/IsAscii'; +export * from './string/IsBase64'; +export * from './string/IsByteLength'; +export * from './string/IsCreditCard'; +export * from './string/IsCurrency'; +export * from './string/IsEmail'; +export * from './string/IsFQDN'; +export * from './string/IsFullWidth'; +export * from './string/IsHalfWidth'; +export * from './string/IsVariableWidth'; +export * from './string/IsHexColor'; +export * from './string/IsHexadecimal'; +export * from './string/IsMacAddress'; +export * from './string/IsIP'; +export * from './string/IsPort'; +export * from './string/IsISBN'; +export * from './string/IsISIN'; +export * from './string/IsISO8601'; +export * from './string/IsJSON'; +export * from './string/IsJWT'; +export * from './string/IsLowercase'; +export * from './string/IsMobilePhone'; +export * from './string/IsISO31661Alpha2'; +export * from './string/IsISO31661Alpha3'; +export * from './string/IsMongoId'; +export * from './string/IsMultibyte'; +export * from './string/IsSurrogatePair'; +export * from './string/IsUrl'; +export * from './string/IsUUID'; +export * from './string/IsFirebasePushId'; +export * from './string/IsUppercase'; +export * from './string/Length'; +export * from './string/MaxLength'; +export * from './string/MinLength'; +export * from './string/Matches'; +export * from './string/IsPhoneNumber'; +export * from './string/IsMilitaryTime'; +export * from './string/IsHash'; +export * from './string/IsISSN'; +export * from './string/IsDateString'; +export * from './string/IsBooleanString'; +export * from './string/IsNumberString'; +export * from './string/IsBase32'; +export * from './string/IsBIC'; +export * from './string/IsBtcAddress'; +export * from './string/IsDataURI'; +export * from './string/IsEAN'; +export * from './string/IsEthereumAddress'; +export * from './string/IsHSL'; +export * from './string/IsIBAN'; +export * from './string/IsIdentityCard'; +export * from './string/IsISRC'; +export * from './string/IsLocale'; +export * from './string/IsMagnetURI'; +export * from './string/IsMimeType'; +export * from './string/IsOctal'; +export * from './string/IsPassportNumber'; +export * from './string/IsPostalCode'; +export * from './string/IsRFC3339'; +export * from './string/IsRgbColor'; +export * from './string/IsSemVer'; +export * from './string/IsStrongPassword'; +export * from './string/IsTimeZone'; +export * from './string/IsBase58'; +export * from './string/is-tax-id'; +export * from './string/is-iso4217-currency-code'; // ------------------------------------------------------------------------- -// String checkers +// Type checkers // ------------------------------------------------------------------------- -/** - * Checks if the string contains the seed. - */ -export function Contains(seed: string, validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.CONTAINS, - target: object.constructor, - propertyName: propertyName, - constraints: [seed], - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string does not contain the seed. - */ -export function NotContains(seed: string, validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.NOT_CONTAINS, - target: object.constructor, - propertyName: propertyName, - constraints: [seed], - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string contains only letters (a-zA-Z). - */ -export function IsAlpha(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_ALPHA, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string contains only letters and numbers. - */ -export function IsAlphanumeric(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_ALPHANUMERIC, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string contains ASCII chars only. - */ -export function IsAscii(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_ASCII, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if a string is base64 encoded. - */ -export function IsBase64(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_BASE64, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string's length (in bytes) falls in a range. - */ -export function IsByteLength(min: number, max?: number, validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_BYTE_LENGTH, - target: object.constructor, - propertyName: propertyName, - constraints: [min, max], - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string is a credit card. - */ -export function IsCreditCard(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_CREDIT_CARD, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string is a valid currency amount. - */ -export function IsCurrency(options?: IsCurrencyOptions, validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_CURRENCY, - target: object.constructor, - propertyName: propertyName, - constraints: [options], - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string is an email. - */ -export function IsEmail(options?: IsEmailOptions, validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_EMAIL, - target: object.constructor, - propertyName: propertyName, - constraints: [options], - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string is a fully qualified domain name (e.g. domain.com). - */ -export function IsFQDN(options?: IsFQDNOptions, validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_FQDN, - target: object.constructor, - propertyName: propertyName, - constraints: [options], - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string contains any full-width chars. - */ -export function IsFullWidth(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_FULL_WIDTH, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string contains any half-width chars. - */ -export function IsHalfWidth(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_HALF_WIDTH, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string contains a mixture of full and half-width chars. - */ -export function IsVariableWidth(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_VARIABLE_WIDTH, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string is a hexadecimal color. - */ -export function IsHexColor(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_HEX_COLOR, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string is a hexadecimal number. - */ -export function IsHexadecimal(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_HEXADECIMAL, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string is an IP (version 4 or 6). - */ -export function IsIP(version?: "4"|"6", validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_IP, - target: object.constructor, - propertyName: propertyName, - constraints: [version], - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string is an ISBN (version 10 or 13). - */ -export function IsISBN(version?: "10"|"13", validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_ISBN, - target: object.constructor, - propertyName: propertyName, - constraints: [version], - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string is an ISIN (stock/security identifier). - */ -export function IsISIN(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_ISIN, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string is a valid ISO 8601 date. - */ -export function IsISO8601(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_ISO8601, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string is valid JSON (note: uses JSON.parse). - */ -export function IsJSON(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_JSON, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string is lowercase. - */ -export function IsLowercase(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_LOWERCASE, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string is a mobile phone number (locale is one of ['zh-CN', 'zh-TW', 'en-ZA', 'en-AU', 'en-HK', - * 'pt-PT', 'fr-FR', 'el-GR', 'en-GB', 'en-US', 'en-ZM', 'ru-RU', 'nb-NO', 'nn-NO', 'vi-VN', 'en-NZ']). - */ -export function IsMobilePhone(locale: string, validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_MOBILE_PHONE, - target: object.constructor, - propertyName: propertyName, - constraints: [locale], - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string is a valid phone number. - * @param {string} region 2 characters uppercase country code (e.g. DE, US, CH). - * If users must enter the intl. prefix (e.g. +41), then you may pass "ZZ" or null as region. - * See [google-libphonenumber, metadata.js:countryCodeToRegionCodeMap on github]{@link https://github.com/ruimarinho/google-libphonenumber/blob/1e46138878cff479aafe2ce62175c6c49cb58720/src/metadata.js#L33} - */ -export function IsPhoneNumber(region: string, validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_PHONE_NUMBER, - target: object.constructor, - propertyName: propertyName, - constraints: [region], - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string is a valid hex-encoded representation of a MongoDB ObjectId. - */ -export function IsMongoId(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_MONGO_ID, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string contains one or more multibyte chars. - */ -export function IsMultibyte(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_MULTIBYTE, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string contains any surrogate pairs chars. - */ -export function IsSurrogatePair(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_SURROGATE_PAIR, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string is an url. - */ -export function IsUrl(options?: IsURLOptions, validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_URL, - target: object.constructor, - propertyName: propertyName, - constraints: [options], - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string is a UUID (version 3, 4 or 5). - */ -export function IsUUID(version?: "3"|"4"|"5", validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_UUID, - target: object.constructor, - propertyName: propertyName, - constraints: [version], - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string is uppercase. - */ -export function IsUppercase(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_UPPERCASE, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string's length falls in a range. Note: this function takes into account surrogate pairs. - */ -export function Length(min: number, max?: number, validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.LENGTH, - target: object.constructor, - propertyName: propertyName, - constraints: [min, max], - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string's length is not less than given number. Note: this function takes into account surrogate pairs. - */ -export function MinLength(min: number, validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.MIN_LENGTH, - target: object.constructor, - propertyName: propertyName, - constraints: [min], - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string's length is not more than given number. Note: this function takes into account surrogate pairs. - */ -export function MaxLength(max: number, validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.MAX_LENGTH, - target: object.constructor, - propertyName: propertyName, - constraints: [max], - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if string matches the pattern. Either matches('foo', /foo/i) or matches('foo', 'foo', 'i'). - */ -export function Matches(pattern: RegExp, validationOptions?: ValidationOptions): Function; -export function Matches(pattern: RegExp, modifiers?: string, validationOptions?: ValidationOptions): Function; -export function Matches(pattern: RegExp, modifiersOrAnnotationOptions?: string|ValidationOptions, validationOptions?: ValidationOptions): Function { - let modifiers: string; - if (modifiersOrAnnotationOptions && modifiersOrAnnotationOptions instanceof Object && !validationOptions) { - validationOptions = modifiersOrAnnotationOptions as ValidationOptions; - } else { - modifiers = modifiersOrAnnotationOptions as string; - } - - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.MATCHES, - target: object.constructor, - propertyName: propertyName, - constraints: [pattern, modifiers], - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if the string correctly represents a time in the format HH:MM - */ -export function IsMilitaryTime(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_MILITARY_TIME, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} +export * from './typechecker/IsBoolean'; +export * from './typechecker/IsDate'; +export * from './typechecker/IsNumber'; +export * from './typechecker/IsEnum'; +export * from './typechecker/IsInt'; +export * from './typechecker/IsString'; +export * from './typechecker/IsArray'; +export * from './typechecker/IsObject'; // ------------------------------------------------------------------------- // Array checkers // ------------------------------------------------------------------------- -/** - * Checks if array contains all values from the given array of values. - */ -export function ArrayContains(values: any[], validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.ARRAY_CONTAINS, - target: object.constructor, - propertyName: propertyName, - constraints: [values], - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if array does not contain any of the given values. - */ -export function ArrayNotContains(values: any[], validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.ARRAY_NOT_CONTAINS, - target: object.constructor, - propertyName: propertyName, - constraints: [values], - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if given array is not empty. - */ -export function ArrayNotEmpty(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.ARRAY_NOT_EMPTY, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} +export * from './array/ArrayContains'; +export * from './array/ArrayNotContains'; +export * from './array/ArrayNotEmpty'; +export * from './array/ArrayMinSize'; +export * from './array/ArrayMaxSize'; +export * from './array/ArrayUnique'; -/** - * Checks if array's length is as minimal this number. - */ -export function ArrayMinSize(min: number, validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.ARRAY_MIN_SIZE, - target: object.constructor, - propertyName: propertyName, - constraints: [min], - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if array's length is as maximal this number. - */ -export function ArrayMaxSize(max: number, validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.ARRAY_MAX_SIZE, - target: object.constructor, - propertyName: propertyName, - constraints: [max], - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} - -/** - * Checks if all array's values are unique. Comparison for objects is reference-based. - */ -export function ArrayUnique(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.ARRAY_UNIQUE, - target: object.constructor, - propertyName: propertyName, - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} +// ------------------------------------------------------------------------- +// Object checkers +// ------------------------------------------------------------------------- -/** - * Checks if all array's values are unique. Comparison for objects is reference-based. - */ -export function IsInstance(targetType: new (...args: any[]) => any, validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - const args: ValidationMetadataArgs = { - type: ValidationTypes.IS_INSTANCE, - target: object.constructor, - propertyName: propertyName, - constraints: [targetType], - validationOptions: validationOptions - }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args)); - }; -} +export * from './object/IsNotEmptyObject'; +export * from './object/IsInstance'; diff --git a/src/decorator/number/IsDivisibleBy.ts b/src/decorator/number/IsDivisibleBy.ts new file mode 100644 index 0000000000..439948c403 --- /dev/null +++ b/src/decorator/number/IsDivisibleBy.ts @@ -0,0 +1,32 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isDivisibleByValidator from 'validator/lib/isDivisibleBy'; + +export const IS_DIVISIBLE_BY = 'isDivisibleBy'; + +/** + * Checks if value is a number that's divisible by another. + */ +export function isDivisibleBy(value: unknown, num: number): boolean { + return typeof value === 'number' && typeof num === 'number' && isDivisibleByValidator(String(value), num); +} + +/** + * Checks if value is a number that's divisible by another. + */ +export function IsDivisibleBy(num: number, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_DIVISIBLE_BY, + constraints: [num], + validator: { + validate: (value, args): boolean => isDivisibleBy(value, args?.constraints[0]), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be divisible by $constraint1', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/number/IsNegative.ts b/src/decorator/number/IsNegative.ts new file mode 100644 index 0000000000..85463760fa --- /dev/null +++ b/src/decorator/number/IsNegative.ts @@ -0,0 +1,30 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; + +export const IS_NEGATIVE = 'isNegative'; + +/** + * Checks if the value is a negative number smaller than zero. + */ +export function isNegative(value: unknown): boolean { + return typeof value === 'number' && value < 0; +} + +/** + * Checks if the value is a negative number smaller than zero. + */ +export function IsNegative(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_NEGATIVE, + validator: { + validate: (value, args): boolean => isNegative(value), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be a negative number', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/number/IsPositive.ts b/src/decorator/number/IsPositive.ts new file mode 100644 index 0000000000..41c888d678 --- /dev/null +++ b/src/decorator/number/IsPositive.ts @@ -0,0 +1,30 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; + +export const IS_POSITIVE = 'isPositive'; + +/** + * Checks if the value is a positive number greater than zero. + */ +export function isPositive(value: unknown): boolean { + return typeof value === 'number' && value > 0; +} + +/** + * Checks if the value is a positive number greater than zero. + */ +export function IsPositive(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_POSITIVE, + validator: { + validate: (value, args): boolean => isPositive(value), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be a positive number', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/number/Max.ts b/src/decorator/number/Max.ts new file mode 100644 index 0000000000..d495af1893 --- /dev/null +++ b/src/decorator/number/Max.ts @@ -0,0 +1,31 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; + +export const MAX = 'max'; + +/** + * Checks if the first number is less than or equal to the second. + */ +export function max(num: unknown, max: number): boolean { + return typeof num === 'number' && typeof max === 'number' && num <= max; +} + +/** + * Checks if the value is less than or equal to the allowed maximum value. + */ +export function Max(maxValue: number, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: MAX, + constraints: [maxValue], + validator: { + validate: (value, args): boolean => max(value, args?.constraints[0]), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must not be greater than $constraint1', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/number/Min.ts b/src/decorator/number/Min.ts new file mode 100644 index 0000000000..22d665d0a3 --- /dev/null +++ b/src/decorator/number/Min.ts @@ -0,0 +1,31 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; + +export const MIN = 'min'; + +/** + * Checks if the first number is greater than or equal to the second. + */ +export function min(num: unknown, min: number): boolean { + return typeof num === 'number' && typeof min === 'number' && num >= min; +} + +/** + * Checks if the value is greater than or equal to the allowed minimum value. + */ +export function Min(minValue: number, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: MIN, + constraints: [minValue], + validator: { + validate: (value, args): boolean => min(value, args?.constraints[0]), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must not be less than $constraint1', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/object/IsInstance.ts b/src/decorator/object/IsInstance.ts new file mode 100644 index 0000000000..e265223ca7 --- /dev/null +++ b/src/decorator/object/IsInstance.ts @@ -0,0 +1,39 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; + +export const IS_INSTANCE = 'isInstance'; + +/** + * Checks if the value is an instance of the specified object. + */ +export function isInstance(object: unknown, targetTypeConstructor: new (...args: any[]) => any): boolean { + return ( + targetTypeConstructor && typeof targetTypeConstructor === 'function' && object instanceof targetTypeConstructor + ); +} + +/** + * Checks if the value is an instance of the specified object. + */ +export function IsInstance( + targetType: new (...args: any[]) => any, + validationOptions?: ValidationOptions +): PropertyDecorator { + return ValidateBy( + { + name: IS_INSTANCE, + constraints: [targetType], + validator: { + validate: (value, args): boolean => isInstance(value, args?.constraints[0]), + defaultMessage: buildMessage((eachPrefix, args) => { + if (args?.constraints[0]) { + return eachPrefix + `$property must be an instance of ${args?.constraints[0].name as string}`; + } else { + return eachPrefix + `${IS_INSTANCE} decorator expects and object as value, but got falsy value.`; + } + }, validationOptions), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/object/IsNotEmptyObject.ts b/src/decorator/object/IsNotEmptyObject.ts new file mode 100644 index 0000000000..7eada8548a --- /dev/null +++ b/src/decorator/object/IsNotEmptyObject.ts @@ -0,0 +1,51 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import { isObject } from '../typechecker/IsObject'; + +export const IS_NOT_EMPTY_OBJECT = 'isNotEmptyObject'; + +/** + * Checks if the value is valid Object & not empty. + * Returns false if the value is not an object or an empty valid object. + */ +export function isNotEmptyObject(value: unknown, options?: { nullable?: boolean }): boolean { + if (!isObject(value)) { + return false; + } + + if (options?.nullable === true) { + return !Object.values(value).every(propertyValue => propertyValue === null || propertyValue === undefined); + } + + for (const key in value) { + if (value.hasOwnProperty(key)) { + return true; + } + } + + return false; +} + +/** + * Checks if the value is valid Object & not empty. + * Returns false if the value is not an object or an empty valid object. + */ +export function IsNotEmptyObject( + options?: { nullable?: boolean }, + validationOptions?: ValidationOptions +): PropertyDecorator { + return ValidateBy( + { + name: IS_NOT_EMPTY_OBJECT, + constraints: [options], + validator: { + validate: (value, args): boolean => isNotEmptyObject(value, args?.constraints[0]), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be a non-empty object', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/Contains.ts b/src/decorator/string/Contains.ts new file mode 100644 index 0000000000..da2cc335c8 --- /dev/null +++ b/src/decorator/string/Contains.ts @@ -0,0 +1,34 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import containsValidator from 'validator/lib/contains'; + +export const CONTAINS = 'contains'; + +/** + * Checks if the string contains the seed. + * If given value is not a string, then it returns false. + */ +export function contains(value: unknown, seed: string): boolean { + return typeof value === 'string' && containsValidator(value, seed); +} + +/** + * Checks if the string contains the seed. + * If given value is not a string, then it returns false. + */ +export function Contains(seed: string, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: CONTAINS, + constraints: [seed], + validator: { + validate: (value, args): boolean => contains(value, args?.constraints[0]), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must contain a $constraint1 string', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsAlpha.ts b/src/decorator/string/IsAlpha.ts new file mode 100644 index 0000000000..de5c3fbb59 --- /dev/null +++ b/src/decorator/string/IsAlpha.ts @@ -0,0 +1,35 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isAlphaValidator from 'validator/lib/isAlpha'; +import ValidatorJS from 'validator'; + +export const IS_ALPHA = 'isAlpha'; + +/** + * Checks if the string contains only letters (a-zA-Z). + * If given value is not a string, then it returns false. + */ +export function isAlpha(value: unknown, locale?: ValidatorJS.AlphaLocale): boolean { + return typeof value === 'string' && isAlphaValidator(value, locale); +} + +/** + * Checks if the string contains only letters (a-zA-Z). + * If given value is not a string, then it returns false. + */ +export function IsAlpha(locale?: ValidatorJS.AlphaLocale, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_ALPHA, + constraints: [locale], + validator: { + validate: (value, args): boolean => isAlpha(value, args?.constraints[0]), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must contain only letters (a-zA-Z)', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsAlphanumeric.ts b/src/decorator/string/IsAlphanumeric.ts new file mode 100644 index 0000000000..d90cbf8848 --- /dev/null +++ b/src/decorator/string/IsAlphanumeric.ts @@ -0,0 +1,38 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isAlphanumericValidator from 'validator/lib/isAlphanumeric'; +import ValidatorJS from 'validator'; + +export const IS_ALPHANUMERIC = 'isAlphanumeric'; + +/** + * Checks if the string contains only letters and numbers. + * If given value is not a string, then it returns false. + */ +export function isAlphanumeric(value: unknown, locale?: ValidatorJS.AlphanumericLocale): boolean { + return typeof value === 'string' && isAlphanumericValidator(value, locale); +} + +/** + * Checks if the string contains only letters and numbers. + * If given value is not a string, then it returns false. + */ +export function IsAlphanumeric( + locale?: ValidatorJS.AlphanumericLocale, + validationOptions?: ValidationOptions +): PropertyDecorator { + return ValidateBy( + { + name: IS_ALPHANUMERIC, + constraints: [locale], + validator: { + validate: (value, args): boolean => isAlphanumeric(value, args?.constraints[0]), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must contain only letters and numbers', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsAscii.ts b/src/decorator/string/IsAscii.ts new file mode 100644 index 0000000000..05f74725dd --- /dev/null +++ b/src/decorator/string/IsAscii.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isAsciiValidator from 'validator/lib/isAscii'; + +export const IS_ASCII = 'isAscii'; + +/** + * Checks if the string contains ASCII chars only. + * If given value is not a string, then it returns false. + */ +export function isAscii(value: unknown): boolean { + return typeof value === 'string' && isAsciiValidator(value); +} + +/** + * Checks if the string contains ASCII chars only. + * If given value is not a string, then it returns false. + */ +export function IsAscii(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_ASCII, + validator: { + validate: (value, args): boolean => isAscii(value), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must contain only ASCII characters', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsBIC.ts b/src/decorator/string/IsBIC.ts new file mode 100644 index 0000000000..b530e67758 --- /dev/null +++ b/src/decorator/string/IsBIC.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isBICValidator from 'validator/lib/isBIC'; + +export const IS_BIC = 'isBIC'; + +/** + * Check if a string is a BIC (Bank Identification Code) or SWIFT code. + * If given value is not a string, then it returns false. + */ +export function isBIC(value: unknown): boolean { + return typeof value === 'string' && isBICValidator(value); +} + +/** + * Check if a string is a BIC (Bank Identification Code) or SWIFT code. + * If given value is not a string, then it returns false. + */ +export function IsBIC(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_BIC, + validator: { + validate: (value, args): boolean => isBIC(value), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be a BIC or SWIFT code', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsBase32.ts b/src/decorator/string/IsBase32.ts new file mode 100644 index 0000000000..99958d0d3e --- /dev/null +++ b/src/decorator/string/IsBase32.ts @@ -0,0 +1,30 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isBase32Validator from 'validator/lib/isBase32'; + +export const IS_BASE32 = 'isBase32'; + +/** + * Checks if a string is base32 encoded. + * If given value is not a string, then it returns false. + */ +export function isBase32(value: unknown): boolean { + return typeof value === 'string' && isBase32Validator(value); +} + +/** + * Check if a string is base32 encoded. + * If given value is not a string, then it returns false. + */ +export function IsBase32(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_BASE32, + validator: { + validate: (value, args): boolean => isBase32(value), + defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be base32 encoded', validationOptions), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsBase58.ts b/src/decorator/string/IsBase58.ts new file mode 100644 index 0000000000..cd474a0c2c --- /dev/null +++ b/src/decorator/string/IsBase58.ts @@ -0,0 +1,30 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isBase58Validator from 'validator/lib/isBase58'; + +export const IS_BASE58 = 'isBase58'; + +/** + * Checks if a string is base58 encoded. + * If given value is not a string, then it returns false. + */ +export function isBase58(value: unknown): boolean { + return typeof value === 'string' && isBase58Validator(value); +} + +/** + * Checks if a string is base58 encoded. + * If given value is not a string, then it returns false. + */ +export function IsBase58(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_BASE58, + validator: { + validate: (value, args): boolean => isBase58(value), + defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be base58 encoded', validationOptions), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsBase64.ts b/src/decorator/string/IsBase64.ts new file mode 100644 index 0000000000..75600c6982 --- /dev/null +++ b/src/decorator/string/IsBase64.ts @@ -0,0 +1,30 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isBase64Validator from 'validator/lib/isBase64'; + +export const IS_BASE64 = 'isBase64'; + +/** + * Checks if a string is base64 encoded. + * If given value is not a string, then it returns false. + */ +export function isBase64(value: unknown): boolean { + return typeof value === 'string' && isBase64Validator(value); +} + +/** + * Checks if a string is base64 encoded. + * If given value is not a string, then it returns false. + */ +export function IsBase64(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_BASE64, + validator: { + validate: (value, args): boolean => isBase64(value), + defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be base64 encoded', validationOptions), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsBooleanString.ts b/src/decorator/string/IsBooleanString.ts new file mode 100644 index 0000000000..e53d71b37b --- /dev/null +++ b/src/decorator/string/IsBooleanString.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isBooleanValidator from 'validator/lib/isBoolean'; + +export const IS_BOOLEAN_STRING = 'isBooleanString'; + +/** + * Checks if a string is a boolean. + * If given value is not a string, then it returns false. + */ +export function isBooleanString(value: unknown): boolean { + return typeof value === 'string' && isBooleanValidator(value); +} + +/** + * Checks if a string is a boolean. + * If given value is not a string, then it returns false. + */ +export function IsBooleanString(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_BOOLEAN_STRING, + validator: { + validate: (value, args): boolean => isBooleanString(value), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be a boolean string', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsBtcAddress.ts b/src/decorator/string/IsBtcAddress.ts new file mode 100644 index 0000000000..f9162adcec --- /dev/null +++ b/src/decorator/string/IsBtcAddress.ts @@ -0,0 +1,30 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isBtcAddressValidator from 'validator/lib/isBtcAddress'; + +export const IS_BTC_ADDRESS = 'isBtcAddress'; + +/** + * Check if the string is a valid BTC address. + * If given value is not a string, then it returns false. + */ +export function isBtcAddress(value: unknown): boolean { + return typeof value === 'string' && isBtcAddressValidator(value); +} + +/** + * Check if the string is a valid BTC address. + * If given value is not a string, then it returns false. + */ +export function IsBtcAddress(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_BTC_ADDRESS, + validator: { + validate: (value, args): boolean => isBtcAddress(value), + defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be a BTC address', validationOptions), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsByteLength.ts b/src/decorator/string/IsByteLength.ts new file mode 100644 index 0000000000..334e209733 --- /dev/null +++ b/src/decorator/string/IsByteLength.ts @@ -0,0 +1,34 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isByteLengthValidator from 'validator/lib/isByteLength'; + +export const IS_BYTE_LENGTH = 'isByteLength'; + +/** + * Checks if the string's length (in bytes) falls in a range. + * If given value is not a string, then it returns false. + */ +export function isByteLength(value: unknown, min: number, max?: number): boolean { + return typeof value === 'string' && isByteLengthValidator(value, { min, max }); +} + +/** + * Checks if the string's length (in bytes) falls in a range. + * If given value is not a string, then it returns false. + */ +export function IsByteLength(min: number, max?: number, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_BYTE_LENGTH, + constraints: [min, max], + validator: { + validate: (value, args): boolean => isByteLength(value, args?.constraints[0], args?.constraints[1]), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + "$property's byte length must fall into ($constraint1, $constraint2) range", + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsCreditCard.ts b/src/decorator/string/IsCreditCard.ts new file mode 100644 index 0000000000..2511d1930c --- /dev/null +++ b/src/decorator/string/IsCreditCard.ts @@ -0,0 +1,30 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isCreditCardValidator from 'validator/lib/isCreditCard'; + +export const IS_CREDIT_CARD = 'isCreditCard'; + +/** + * Checks if the string is a credit card. + * If given value is not a string, then it returns false. + */ +export function isCreditCard(value: unknown): boolean { + return typeof value === 'string' && isCreditCardValidator(value); +} + +/** + * Checks if the string is a credit card. + * If given value is not a string, then it returns false. + */ +export function IsCreditCard(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_CREDIT_CARD, + validator: { + validate: (value, args): boolean => isCreditCard(value), + defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be a credit card', validationOptions), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsCurrency.ts b/src/decorator/string/IsCurrency.ts new file mode 100644 index 0000000000..741eeadaa4 --- /dev/null +++ b/src/decorator/string/IsCurrency.ts @@ -0,0 +1,35 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isCurrencyValidator from 'validator/lib/isCurrency'; +import ValidatorJS from 'validator'; + +export const IS_CURRENCY = 'isCurrency'; + +/** + * Checks if the string is a valid currency amount. + * If given value is not a string, then it returns false. + */ +export function isCurrency(value: unknown, options?: ValidatorJS.IsCurrencyOptions): boolean { + return typeof value === 'string' && isCurrencyValidator(value, options); +} + +/** + * Checks if the string is a valid currency amount. + * If given value is not a string, then it returns false. + */ +export function IsCurrency( + options?: ValidatorJS.IsCurrencyOptions, + validationOptions?: ValidationOptions +): PropertyDecorator { + return ValidateBy( + { + name: IS_CURRENCY, + constraints: [options], + validator: { + validate: (value, args): boolean => isCurrency(value, args?.constraints[0]), + defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be a currency', validationOptions), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsDataURI.ts b/src/decorator/string/IsDataURI.ts new file mode 100644 index 0000000000..f07e5fefae --- /dev/null +++ b/src/decorator/string/IsDataURI.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isDataURIValidator from 'validator/lib/isDataURI'; + +export const IS_DATA_URI = 'isDataURI'; + +/** + * Check if the string is a data uri format. + * If given value is not a string, then it returns false. + */ +export function isDataURI(value: unknown): boolean { + return typeof value === 'string' && isDataURIValidator(value); +} + +/** + * Check if the string is a data uri format. + * If given value is not a string, then it returns false. + */ +export function IsDataURI(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_DATA_URI, + validator: { + validate: (value, args): boolean => isDataURI(value), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be a data uri format', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsDateString.ts b/src/decorator/string/IsDateString.ts new file mode 100644 index 0000000000..f7be9f6636 --- /dev/null +++ b/src/decorator/string/IsDateString.ts @@ -0,0 +1,36 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import ValidatorJS from 'validator'; +import { isISO8601 } from './IsISO8601'; + +export const IS_DATE_STRING = 'isDateString'; + +/** + * Alias for IsISO8601 validator + */ +export function isDateString(value: unknown, options?: ValidatorJS.IsISO8601Options): boolean { + return isISO8601(value, options); +} + +/** + * Alias for IsISO8601 validator + */ +export function IsDateString( + options?: ValidatorJS.IsISO8601Options, + validationOptions?: ValidationOptions +): PropertyDecorator { + return ValidateBy( + { + name: IS_DATE_STRING, + constraints: [options], + validator: { + validate: (value): boolean => isDateString(value, options), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be a valid ISO 8601 date string', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsDecimal.ts b/src/decorator/string/IsDecimal.ts new file mode 100644 index 0000000000..f6c1fa49ab --- /dev/null +++ b/src/decorator/string/IsDecimal.ts @@ -0,0 +1,38 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isDecimalValidator from 'validator/lib/isDecimal'; +import ValidatorJS from 'validator'; + +export const IS_DECIMAL = 'isDecimal'; + +/** + * Checks if the string is a valid decimal. + * If given value is not a string, then it returns false. + */ +export function isDecimal(value: unknown, options?: ValidatorJS.IsDecimalOptions): boolean { + return typeof value === 'string' && isDecimalValidator(value, options); +} + +/** + * Checks if the string is a valid decimal. + * If given value is not a string, then it returns false. + */ +export function IsDecimal( + options?: ValidatorJS.IsDecimalOptions, + validationOptions?: ValidationOptions +): PropertyDecorator { + return ValidateBy( + { + name: IS_DECIMAL, + constraints: [options], + validator: { + validate: (value, args): boolean => isDecimal(value, args?.constraints[0]), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property is not a valid decimal number.', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsEAN.ts b/src/decorator/string/IsEAN.ts new file mode 100644 index 0000000000..73669e969a --- /dev/null +++ b/src/decorator/string/IsEAN.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isEANValidator from 'validator/lib/isEAN'; + +export const IS_EAN = 'isEAN'; + +/** + * Check if the string is an EAN (European Article Number). + * If given value is not a string, then it returns false. + */ +export function isEAN(value: unknown): boolean { + return typeof value === 'string' && isEANValidator(value); +} + +/** + * Check if the string is an EAN (European Article Number). + * If given value is not a string, then it returns false. + */ +export function IsEAN(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_EAN, + validator: { + validate: (value, args): boolean => isEAN(value), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be an EAN (European Article Number)', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsEmail.ts b/src/decorator/string/IsEmail.ts new file mode 100644 index 0000000000..1728f84f51 --- /dev/null +++ b/src/decorator/string/IsEmail.ts @@ -0,0 +1,35 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isEmailValidator from 'validator/lib/isEmail'; +import ValidatorJS from 'validator'; + +export const IS_EMAIL = 'isEmail'; + +/** + * Checks if the string is an email. + * If given value is not a string, then it returns false. + */ +export function isEmail(value: unknown, options?: ValidatorJS.IsEmailOptions): boolean { + return typeof value === 'string' && isEmailValidator(value, options); +} + +/** + * Checks if the string is an email. + * If given value is not a string, then it returns false. + */ +export function IsEmail( + options?: ValidatorJS.IsEmailOptions, + validationOptions?: ValidationOptions +): PropertyDecorator { + return ValidateBy( + { + name: IS_EMAIL, + constraints: [options], + validator: { + validate: (value, args): boolean => isEmail(value, args?.constraints[0]), + defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be an email', validationOptions), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsEthereumAddress.ts b/src/decorator/string/IsEthereumAddress.ts new file mode 100644 index 0000000000..cf8dbdbb1b --- /dev/null +++ b/src/decorator/string/IsEthereumAddress.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isEthereumAddressValidator from 'validator/lib/isEthereumAddress'; + +export const IS_ETHEREUM_ADDRESS = 'isEthereumAddress'; + +/** + * Check if the string is an Ethereum address using basic regex. Does not validate address checksums. + * If given value is not a string, then it returns false. + */ +export function isEthereumAddress(value: unknown): boolean { + return typeof value === 'string' && isEthereumAddressValidator(value); +} + +/** + * Check if the string is an Ethereum address using basic regex. Does not validate address checksums. + * If given value is not a string, then it returns false. + */ +export function IsEthereumAddress(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_ETHEREUM_ADDRESS, + validator: { + validate: (value, args): boolean => isEthereumAddress(value), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be an Ethereum address', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsFQDN.ts b/src/decorator/string/IsFQDN.ts new file mode 100644 index 0000000000..ab6c635cf8 --- /dev/null +++ b/src/decorator/string/IsFQDN.ts @@ -0,0 +1,35 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isFqdnValidator from 'validator/lib/isFQDN'; +import ValidatorJS from 'validator'; + +export const IS_FQDN = 'isFqdn'; + +/** + * Checks if the string is a fully qualified domain name (e.g. domain.com). + * If given value is not a string, then it returns false. + */ +export function isFQDN(value: unknown, options?: ValidatorJS.IsFQDNOptions): boolean { + return typeof value === 'string' && isFqdnValidator(value, options); +} + +/** + * Checks if the string is a fully qualified domain name (e.g. domain.com). + * If given value is not a string, then it returns false. + */ +export function IsFQDN(options?: ValidatorJS.IsFQDNOptions, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_FQDN, + constraints: [options], + validator: { + validate: (value, args): boolean => isFQDN(value, args?.constraints[0]), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be a valid domain name', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsFirebasePushId.ts b/src/decorator/string/IsFirebasePushId.ts new file mode 100644 index 0000000000..1d81230c7b --- /dev/null +++ b/src/decorator/string/IsFirebasePushId.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; + +export const IS_FIREBASE_PUSH_ID = 'IsFirebasePushId'; + +/** + * Checks if the string is a Firebase Push Id + * If given value is not a Firebase Push Id, it returns false + */ +export function isFirebasePushId(value: unknown): boolean { + const webSafeRegex = /^[a-zA-Z0-9_-]*$/; + return typeof value === 'string' && value.length === 20 && webSafeRegex.test(value); +} + +/** + * Checks if the string is a Firebase Push Id + * If given value is not a Firebase Push Id, it returns false + */ +export function IsFirebasePushId(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_FIREBASE_PUSH_ID, + validator: { + validate: (value, args): boolean => isFirebasePushId(value), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be a Firebase Push Id', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsFullWidth.ts b/src/decorator/string/IsFullWidth.ts new file mode 100644 index 0000000000..cb9a7cc3a5 --- /dev/null +++ b/src/decorator/string/IsFullWidth.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isFullWidthValidator from 'validator/lib/isFullWidth'; + +export const IS_FULL_WIDTH = 'isFullWidth'; + +/** + * Checks if the string contains any full-width chars. + * If given value is not a string, then it returns false. + */ +export function isFullWidth(value: unknown): boolean { + return typeof value === 'string' && isFullWidthValidator(value); +} + +/** + * Checks if the string contains any full-width chars. + * If given value is not a string, then it returns false. + */ +export function IsFullWidth(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_FULL_WIDTH, + validator: { + validate: (value, args): boolean => isFullWidth(value), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must contain a full-width characters', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsHSL.ts b/src/decorator/string/IsHSL.ts new file mode 100644 index 0000000000..401cbc6fb1 --- /dev/null +++ b/src/decorator/string/IsHSL.ts @@ -0,0 +1,32 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isHSLValidator from 'validator/lib/isHSL'; + +export const IS_HSL = 'isHSL'; + +/** + * Check if the string is an HSL (hue, saturation, lightness, optional alpha) color based on CSS Colors Level 4 specification. + * Comma-separated format supported. Space-separated format supported with the exception of a few edge cases (ex: hsl(200grad+.1%62%/1)). + * If given value is not a string, then it returns false. + */ +export function isHSL(value: unknown): boolean { + return typeof value === 'string' && isHSLValidator(value); +} + +/** + * Check if the string is an HSL (hue, saturation, lightness, optional alpha) color based on CSS Colors Level 4 specification. + * Comma-separated format supported. Space-separated format supported with the exception of a few edge cases (ex: hsl(200grad+.1%62%/1)). + * If given value is not a string, then it returns false. + */ +export function IsHSL(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_HSL, + validator: { + validate: (value, args): boolean => isHSL(value), + defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be a HSL color', validationOptions), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsHalfWidth.ts b/src/decorator/string/IsHalfWidth.ts new file mode 100644 index 0000000000..0f04c94348 --- /dev/null +++ b/src/decorator/string/IsHalfWidth.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isHalfWidthValidator from 'validator/lib/isHalfWidth'; + +export const IS_HALF_WIDTH = 'isHalfWidth'; + +/** + * Checks if the string contains any half-width chars. + * If given value is not a string, then it returns false. + */ +export function isHalfWidth(value: unknown): boolean { + return typeof value === 'string' && isHalfWidthValidator(value); +} + +/** + * Checks if the string contains any half-width chars. + * If given value is not a string, then it returns false. + */ +export function IsHalfWidth(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_HALF_WIDTH, + validator: { + validate: (value, args): boolean => isHalfWidth(value), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must contain a half-width characters', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsHash.ts b/src/decorator/string/IsHash.ts new file mode 100644 index 0000000000..2c38828454 --- /dev/null +++ b/src/decorator/string/IsHash.ts @@ -0,0 +1,37 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isHashValidator from 'validator/lib/isHash'; +import ValidatorJS from 'validator'; + +export const IS_HASH = 'isHash'; + +/** + * Check if the string is a hash of type algorithm. + * Algorithm is one of ['md4', 'md5', 'sha1', 'sha256', 'sha384', 'sha512', 'ripemd128', 'ripemd160', 'tiger128', + * 'tiger160', 'tiger192', 'crc32', 'crc32b'] + */ +export function isHash(value: unknown, algorithm: ValidatorJS.HashAlgorithm): boolean { + return typeof value === 'string' && isHashValidator(value, algorithm); +} + +/** + * Check if the string is a hash of type algorithm. + * Algorithm is one of ['md4', 'md5', 'sha1', 'sha256', 'sha384', 'sha512', 'ripemd128', 'ripemd160', 'tiger128', + * 'tiger160', 'tiger192', 'crc32', 'crc32b'] + */ +export function IsHash(algorithm: string, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_HASH, + constraints: [algorithm], + validator: { + validate: (value, args): boolean => isHash(value, args?.constraints[0]), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be a hash of type $constraint1', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsHexColor.ts b/src/decorator/string/IsHexColor.ts new file mode 100644 index 0000000000..c72c471135 --- /dev/null +++ b/src/decorator/string/IsHexColor.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isHexColorValidator from 'validator/lib/isHexColor'; + +export const IS_HEX_COLOR = 'isHexColor'; + +/** + * Checks if the string is a hexadecimal color. + * If given value is not a string, then it returns false. + */ +export function isHexColor(value: unknown): boolean { + return typeof value === 'string' && isHexColorValidator(value); +} + +/** + * Checks if the string is a hexadecimal color. + * If given value is not a string, then it returns false. + */ +export function IsHexColor(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_HEX_COLOR, + validator: { + validate: (value, args): boolean => isHexColor(value), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be a hexadecimal color', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsHexadecimal.ts b/src/decorator/string/IsHexadecimal.ts new file mode 100644 index 0000000000..26d3eb3e34 --- /dev/null +++ b/src/decorator/string/IsHexadecimal.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isHexadecimalValidator from 'validator/lib/isHexadecimal'; + +export const IS_HEXADECIMAL = 'isHexadecimal'; + +/** + * Checks if the string is a hexadecimal number. + * If given value is not a string, then it returns false. + */ +export function isHexadecimal(value: unknown): boolean { + return typeof value === 'string' && isHexadecimalValidator(value); +} + +/** + * Checks if the string is a hexadecimal number. + * If given value is not a string, then it returns false. + */ +export function IsHexadecimal(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_HEXADECIMAL, + validator: { + validate: (value, args): boolean => isHexadecimal(value), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be a hexadecimal number', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsIBAN.ts b/src/decorator/string/IsIBAN.ts new file mode 100644 index 0000000000..d0a159fc83 --- /dev/null +++ b/src/decorator/string/IsIBAN.ts @@ -0,0 +1,30 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isIBANValidator from 'validator/lib/isIBAN'; + +export const IS_IBAN = 'isIBAN'; + +/** + * Check if a string is a IBAN (International Bank Account Number). + * If given value is not a string, then it returns false. + */ +export function isIBAN(value: unknown): boolean { + return typeof value === 'string' && isIBANValidator(value); +} + +/** + * Check if a string is a IBAN (International Bank Account Number). + * If given value is not a string, then it returns false. + */ +export function IsIBAN(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_IBAN, + validator: { + validate: (value, args): boolean => isIBAN(value), + defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be an IBAN', validationOptions), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsIP.ts b/src/decorator/string/IsIP.ts new file mode 100644 index 0000000000..01fc55d2aa --- /dev/null +++ b/src/decorator/string/IsIP.ts @@ -0,0 +1,35 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isIPValidator from 'validator/lib/isIP'; + +export type IsIpVersion = '4' | '6' | 4 | 6; + +export const IS_IP = 'isIp'; + +/** + * Checks if the string is an IP (version 4 or 6). + * If given value is not a string, then it returns false. + */ +export function isIP(value: unknown, version?: IsIpVersion): boolean { + /* eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion */ + const versionStr = version ? (`${version}` as '4' | '6') : undefined; + return typeof value === 'string' && isIPValidator(value, versionStr); +} + +/** + * Checks if the string is an IP (version 4 or 6). + * If given value is not a string, then it returns false. + */ +export function IsIP(version?: IsIpVersion, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_IP, + constraints: [version], + validator: { + validate: (value, args): boolean => isIP(value, args?.constraints[0]), + defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be an ip address', validationOptions), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsISBN.ts b/src/decorator/string/IsISBN.ts new file mode 100644 index 0000000000..336d62c61d --- /dev/null +++ b/src/decorator/string/IsISBN.ts @@ -0,0 +1,35 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isIsbnValidator from 'validator/lib/isISBN'; + +export type IsISBNVersion = '10' | '13' | 10 | 13; + +export const IS_ISBN = 'isIsbn'; + +/** + * Checks if the string is an ISBN (version 10 or 13). + * If given value is not a string, then it returns false. + */ +export function isISBN(value: unknown, version?: IsISBNVersion): boolean { + /* eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion */ + const versionStr = version ? (`${version}` as '10' | '13') : undefined; + return typeof value === 'string' && isIsbnValidator(value, versionStr); +} + +/** + * Checks if the string is an ISBN (version 10 or 13). + * If given value is not a string, then it returns false. + */ +export function IsISBN(version?: IsISBNVersion, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_ISBN, + constraints: [version], + validator: { + validate: (value, args): boolean => isISBN(value, args?.constraints[0]), + defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be an ISBN', validationOptions), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsISIN.ts b/src/decorator/string/IsISIN.ts new file mode 100644 index 0000000000..2f9b143b1d --- /dev/null +++ b/src/decorator/string/IsISIN.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isIsinValidator from 'validator/lib/isISIN'; + +export const IS_ISIN = 'isIsin'; + +/** + * Checks if the string is an ISIN (stock/security identifier). + * If given value is not a string, then it returns false. + */ +export function isISIN(value: unknown): boolean { + return typeof value === 'string' && isIsinValidator(value); +} + +/** + * Checks if the string is an ISIN (stock/security identifier). + * If given value is not a string, then it returns false. + */ +export function IsISIN(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_ISIN, + validator: { + validate: (value, args): boolean => isISIN(value), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be an ISIN (stock/security identifier)', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsISO31661Alpha2.ts b/src/decorator/string/IsISO31661Alpha2.ts new file mode 100644 index 0000000000..87b19551ae --- /dev/null +++ b/src/decorator/string/IsISO31661Alpha2.ts @@ -0,0 +1,31 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isISO31661Alpha2Validator from 'validator/lib/isISO31661Alpha2'; + +export const IS_ISO31661_ALPHA_2 = 'isISO31661Alpha2'; + +/** + * Check if the string is a valid [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) officially assigned country code. + */ +export function isISO31661Alpha2(value: unknown): boolean { + return typeof value === 'string' && isISO31661Alpha2Validator(value); +} + +/** + * Check if the string is a valid [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) officially assigned country code. + */ +export function IsISO31661Alpha2(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_ISO31661_ALPHA_2, + validator: { + validate: (value, args): boolean => isISO31661Alpha2(value), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be a valid ISO31661 Alpha2 code', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsISO31661Alpha3.ts b/src/decorator/string/IsISO31661Alpha3.ts new file mode 100644 index 0000000000..bf43ff519b --- /dev/null +++ b/src/decorator/string/IsISO31661Alpha3.ts @@ -0,0 +1,31 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isISO31661Alpha3Validator from 'validator/lib/isISO31661Alpha3'; + +export const IS_ISO31661_ALPHA_3 = 'isISO31661Alpha3'; + +/** + * Check if the string is a valid [ISO 3166-1 alpha-3](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-3) officially assigned country code. + */ +export function isISO31661Alpha3(value: unknown): boolean { + return typeof value === 'string' && isISO31661Alpha3Validator(value); +} + +/** + * Check if the string is a valid [ISO 3166-1 alpha-3](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-3) officially assigned country code. + */ +export function IsISO31661Alpha3(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_ISO31661_ALPHA_3, + validator: { + validate: (value, args): boolean => isISO31661Alpha3(value), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be a valid ISO31661 Alpha3 code', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsISO8601.ts b/src/decorator/string/IsISO8601.ts new file mode 100644 index 0000000000..9c87a8dfc9 --- /dev/null +++ b/src/decorator/string/IsISO8601.ts @@ -0,0 +1,40 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isIso8601Validator from 'validator/lib/isISO8601'; +import ValidatorJS from 'validator'; + +export const IS_ISO8601 = 'isIso8601'; + +/** + * Checks if the string is a valid ISO 8601 date. + * If given value is not a string, then it returns false. + * Use the option strict = true for additional checks for a valid date, e.g. invalidates dates like 2019-02-29. + */ +export function isISO8601(value: unknown, options?: ValidatorJS.IsISO8601Options): boolean { + return typeof value === 'string' && isIso8601Validator(value, options); +} + +/** + * Checks if the string is a valid ISO 8601 date. + * If given value is not a string, then it returns false. + * Use the option strict = true for additional checks for a valid date, e.g. invalidates dates like 2019-02-29. + */ +export function IsISO8601( + options?: ValidatorJS.IsISO8601Options, + validationOptions?: ValidationOptions +): PropertyDecorator { + return ValidateBy( + { + name: IS_ISO8601, + constraints: [options], + validator: { + validate: (value, args): boolean => isISO8601(value, args?.constraints[0]), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be a valid ISO 8601 date string', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsISRC.ts b/src/decorator/string/IsISRC.ts new file mode 100644 index 0000000000..f41b3c3cdc --- /dev/null +++ b/src/decorator/string/IsISRC.ts @@ -0,0 +1,30 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isISRCValidator from 'validator/lib/isISRC'; + +export const IS_ISRC = 'isISRC'; + +/** + * Check if the string is a ISRC. + * If given value is not a string, then it returns false. + */ +export function isISRC(value: unknown): boolean { + return typeof value === 'string' && isISRCValidator(value); +} + +/** + * Check if the string is a ISRC. + * If given value is not a string, then it returns false. + */ +export function IsISRC(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_ISRC, + validator: { + validate: (value, args): boolean => isISRC(value), + defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be an ISRC', validationOptions), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsISSN.ts b/src/decorator/string/IsISSN.ts new file mode 100644 index 0000000000..25ad003400 --- /dev/null +++ b/src/decorator/string/IsISSN.ts @@ -0,0 +1,32 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isISSNValidator from 'validator/lib/isISSN'; +import ValidatorJS from 'validator'; + +export const IS_ISSN = 'isISSN'; + +/** + * Checks if the string is a ISSN. + * If given value is not a string, then it returns false. + */ +export function isISSN(value: unknown, options?: ValidatorJS.IsISSNOptions): boolean { + return typeof value === 'string' && isISSNValidator(value, options); +} + +/** + * Checks if the string is a ISSN. + * If given value is not a string, then it returns false. + */ +export function IsISSN(options?: ValidatorJS.IsISSNOptions, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_ISSN, + constraints: [options], + validator: { + validate: (value, args): boolean => isISSN(value, args?.constraints[0]), + defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be a ISSN', validationOptions), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsIdentityCard.ts b/src/decorator/string/IsIdentityCard.ts new file mode 100644 index 0000000000..8114fe577d --- /dev/null +++ b/src/decorator/string/IsIdentityCard.ts @@ -0,0 +1,42 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isIdentityCardValidator from 'validator/lib/isIdentityCard'; +import ValidatorJS from 'validator'; + +export const IS_IDENTITY_CARD = 'isIdentityCard'; + +/** + * Check if the string is a valid identity card code. + * locale is one of ['ES', 'zh-TW', 'he-IL', 'ar-TN'] OR 'any'. If 'any' is used, function will check if any of the locals match. + * Defaults to 'any'. + * If given value is not a string, then it returns false. + */ +export function isIdentityCard(value: unknown, locale: ValidatorJS.IdentityCardLocale): boolean { + return typeof value === 'string' && isIdentityCardValidator(value, locale); +} + +/** + * Check if the string is a valid identity card code. + * locale is one of ['ES', 'zh-TW', 'he-IL', 'ar-TN'] OR 'any'. If 'any' is used, function will check if any of the locals match. + * Defaults to 'any'. + * If given value is not a string, then it returns false. + */ +export function IsIdentityCard( + locale?: ValidatorJS.IdentityCardLocale, + validationOptions?: ValidationOptions +): PropertyDecorator { + return ValidateBy( + { + name: IS_IDENTITY_CARD, + constraints: [locale], + validator: { + validate: (value, args): boolean => isIdentityCard(value, args?.constraints[0]), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be a identity card number', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsJSON.ts b/src/decorator/string/IsJSON.ts new file mode 100644 index 0000000000..2bdf8f04b9 --- /dev/null +++ b/src/decorator/string/IsJSON.ts @@ -0,0 +1,30 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isJSONValidator from 'validator/lib/isJSON'; + +export const IS_JSON = 'isJson'; + +/** + * Checks if the string is valid JSON (note: uses JSON.parse). + * If given value is not a string, then it returns false. + */ +export function isJSON(value: unknown): boolean { + return typeof value === 'string' && isJSONValidator(value); +} + +/** + * Checks if the string is valid JSON (note: uses JSON.parse). + * If given value is not a string, then it returns false. + */ +export function IsJSON(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_JSON, + validator: { + validate: (value, args): boolean => isJSON(value), + defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be a json string', validationOptions), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsJWT.ts b/src/decorator/string/IsJWT.ts new file mode 100644 index 0000000000..69ecc900a7 --- /dev/null +++ b/src/decorator/string/IsJWT.ts @@ -0,0 +1,30 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isJwtValidator from 'validator/lib/isJWT'; + +export const IS_JWT = 'isJwt'; + +/** + * Checks if the string is valid JWT token. + * If given value is not a string, then it returns false. + */ +export function isJWT(value: unknown): boolean { + return typeof value === 'string' && isJwtValidator(value); +} + +/** + * Checks if the string is valid JWT token. + * If given value is not a string, then it returns false. + */ +export function IsJWT(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_JWT, + validator: { + validate: (value, args): boolean => isJWT(value), + defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be a jwt string', validationOptions), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsLocale.ts b/src/decorator/string/IsLocale.ts new file mode 100644 index 0000000000..043ddf2510 --- /dev/null +++ b/src/decorator/string/IsLocale.ts @@ -0,0 +1,30 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isLocaleValidator from 'validator/lib/isLocale'; + +export const IS_LOCALE = 'isLocale'; + +/** + * Check if the string is a locale. + * If given value is not a string, then it returns false. + */ +export function isLocale(value: unknown): boolean { + return typeof value === 'string' && isLocaleValidator(value); +} + +/** + * Check if the string is a locale. + * If given value is not a string, then it returns false. + */ +export function IsLocale(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_LOCALE, + validator: { + validate: (value, args): boolean => isLocale(value), + defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be locale', validationOptions), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsLowercase.ts b/src/decorator/string/IsLowercase.ts new file mode 100644 index 0000000000..1042dadcfd --- /dev/null +++ b/src/decorator/string/IsLowercase.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isLowercaseValidator from 'validator/lib/isLowercase'; + +export const IS_LOWERCASE = 'isLowercase'; + +/** + * Checks if the string is lowercase. + * If given value is not a string, then it returns false. + */ +export function isLowercase(value: unknown): boolean { + return typeof value === 'string' && isLowercaseValidator(value); +} + +/** + * Checks if the string is lowercase. + * If given value is not a string, then it returns false. + */ +export function IsLowercase(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_LOWERCASE, + validator: { + validate: (value, args): boolean => isLowercase(value), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be a lowercase string', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsMacAddress.ts b/src/decorator/string/IsMacAddress.ts new file mode 100644 index 0000000000..ed91b32a6c --- /dev/null +++ b/src/decorator/string/IsMacAddress.ts @@ -0,0 +1,45 @@ +import { ValidationOptions, isValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isMacAddressValidator from 'validator/lib/isMACAddress'; +import ValidatorJS from 'validator'; + +export const IS_MAC_ADDRESS = 'isMacAddress'; + +/** + * Check if the string is a MAC address. + * If given value is not a string, then it returns false. + */ +export function isMACAddress(value: unknown, options?: ValidatorJS.IsMACAddressOptions): boolean { + return typeof value === 'string' && isMacAddressValidator(value, options); +} + +/** + * Check if the string is a MAC address. + * If given value is not a string, then it returns false. + */ +export function IsMACAddress( + optionsArg?: ValidatorJS.IsMACAddressOptions, + validationOptionsArg?: ValidationOptions +): PropertyDecorator; +export function IsMACAddress(validationOptionsArg?: ValidationOptions): PropertyDecorator; +export function IsMACAddress( + optionsOrValidationOptionsArg?: ValidatorJS.IsMACAddressOptions | ValidationOptions, + validationOptionsArg?: ValidationOptions +): PropertyDecorator { + const options = !isValidationOptions(optionsOrValidationOptionsArg) ? optionsOrValidationOptionsArg : undefined; + const validationOptions = isValidationOptions(optionsOrValidationOptionsArg) + ? optionsOrValidationOptionsArg + : validationOptionsArg; + + return ValidateBy( + { + name: IS_MAC_ADDRESS, + constraints: [options], + validator: { + validate: (value, args): boolean => isMACAddress(value, options), + defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be a MAC Address', validationOptions), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsMagnetURI.ts b/src/decorator/string/IsMagnetURI.ts new file mode 100644 index 0000000000..a4758dca65 --- /dev/null +++ b/src/decorator/string/IsMagnetURI.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isMagnetURIValidator from 'validator/lib/isMagnetURI'; + +export const IS_MAGNET_URI = 'isMagnetURI'; + +/** + * Check if the string is a magnet uri format. + * If given value is not a string, then it returns false. + */ +export function isMagnetURI(value: unknown): boolean { + return typeof value === 'string' && isMagnetURIValidator(value); +} + +/** + * Check if the string is a magnet uri format. + * If given value is not a string, then it returns false. + */ +export function IsMagnetURI(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_MAGNET_URI, + validator: { + validate: (value, args): boolean => isMagnetURI(value), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be magnet uri format', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsMilitaryTime.ts b/src/decorator/string/IsMilitaryTime.ts new file mode 100644 index 0000000000..6d209b2ac1 --- /dev/null +++ b/src/decorator/string/IsMilitaryTime.ts @@ -0,0 +1,34 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import matchesValidator from 'validator/lib/matches'; + +export const IS_MILITARY_TIME = 'isMilitaryTime'; + +/** + * Checks if the string represents a time without a given timezone in the format HH:MM (military) + * If the given value does not match the pattern HH:MM, then it returns false. + */ +export function isMilitaryTime(value: unknown): boolean { + const militaryTimeRegex = /^([01]\d|2[0-3]):?([0-5]\d)$/; + return typeof value === 'string' && matchesValidator(value, militaryTimeRegex); +} + +/** + * Checks if the string represents a time without a given timezone in the format HH:MM (military) + * If the given value does not match the pattern HH:MM, then it returns false. + */ +export function IsMilitaryTime(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_MILITARY_TIME, + validator: { + validate: (value, args): boolean => isMilitaryTime(value), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be a valid representation of military time in the format HH:MM', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsMimeType.ts b/src/decorator/string/IsMimeType.ts new file mode 100644 index 0000000000..edc5136953 --- /dev/null +++ b/src/decorator/string/IsMimeType.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isMimeTypeValidator from 'validator/lib/isMimeType'; + +export const IS_MIME_TYPE = 'isMimeType'; + +/** + * Check if the string matches to a valid MIME type format + * If given value is not a string, then it returns false. + */ +export function isMimeType(value: unknown): boolean { + return typeof value === 'string' && isMimeTypeValidator(value); +} + +/** + * Check if the string matches to a valid MIME type format + * If given value is not a string, then it returns false. + */ +export function IsMimeType(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_MIME_TYPE, + validator: { + validate: (value, args): boolean => isMimeType(value), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be MIME type format', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsMobilePhone.ts b/src/decorator/string/IsMobilePhone.ts new file mode 100644 index 0000000000..8754bc54d7 --- /dev/null +++ b/src/decorator/string/IsMobilePhone.ts @@ -0,0 +1,56 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isMobilePhoneValidator from 'validator/lib/isMobilePhone'; +import ValidatorJS from 'validator'; + +export const IS_MOBILE_PHONE = 'isMobilePhone'; + +/** + * Checks if the string is a mobile phone number (locale is either an array of locales (e.g ['sk-SK', 'sr-RS']) + * OR one of ['am-Am', 'ar-AE', 'ar-BH', 'ar-DZ', 'ar-EG', 'ar-IQ', ar-JO', 'ar-KW', 'ar-SA', 'ar-SY', 'ar-TN', 'be-BY', + * 'bg-BG', 'bn-BD', 'cs-CZ', 'da-DK', 'de-DE', 'de-AT', 'el-GR', 'en-AU', 'en-CA', 'en-GB', 'en-GG', 'en-GH', 'en-HK', + * 'en-MO', 'en-IE', 'en-IN', 'en-KE', 'en-MT', 'en-MU', 'en-NG', 'en-NZ', 'en-PK', 'en-RW', 'en-SG', 'en-SL', 'en-UG', + * 'en-US', 'en-TZ', 'en-ZA', 'en-ZM', 'es-CL', 'es-CR', 'es-EC', 'es-ES', 'es-MX', 'es-PA', 'es-PY', 'es-UY', 'et-EE', + * 'fa-IR', 'fi-FI', 'fj-FJ', 'fo-FO', 'fr-BE', 'fr-FR', 'fr-GF', 'fr-GP', 'fr-MQ', 'fr-RE', 'he-IL', 'hu-HU', 'id-ID', + * 'it-IT', 'ja-JP', 'kk-KZ', 'kl-GL', 'ko-KR', 'lt-LT', 'ms-MY', 'nb-NO', 'ne-NP', 'nl-BE', 'nl-NL', 'nn-NO', 'pl-PL', + * 'pt-BR', 'pt-PT', 'ro-RO', 'ru-RU', 'sl-SI', 'sk-SK', 'sr-RS', 'sv-SE', 'th-TH', 'tr-TR', 'uk-UA', 'vi-VN', 'zh-CN', + * 'zh-HK', 'zh-MO', 'zh-TW'] + * If given value is not a string, then it returns false. + */ +export function isMobilePhone( + value: unknown, + locale?: ValidatorJS.MobilePhoneLocale, + options?: ValidatorJS.IsMobilePhoneOptions +): boolean { + return typeof value === 'string' && isMobilePhoneValidator(value, locale, options); +} + +/** + * Checks if the string is a mobile phone number (locale is either an array of locales (e.g ['sk-SK', 'sr-RS']) + * OR one of ['am-Am', 'ar-AE', 'ar-BH', 'ar-DZ', 'ar-EG', 'ar-IQ', ar-JO', 'ar-KW', 'ar-SA', 'ar-SY', 'ar-TN', 'be-BY', + * 'bg-BG', 'bn-BD', 'cs-CZ', 'da-DK', 'de-DE', 'de-AT', 'el-GR', 'en-AU', 'en-CA', 'en-GB', 'en-GG', 'en-GH', 'en-HK', + * 'en-MO', 'en-IE', 'en-IN', 'en-KE', 'en-MT', 'en-MU', 'en-NG', 'en-NZ', 'en-PK', 'en-RW', 'en-SG', 'en-SL', 'en-UG', + * 'en-US', 'en-TZ', 'en-ZA', 'en-ZM', 'es-CL', 'es-CR', 'es-EC', 'es-ES', 'es-MX', 'es-PA', 'es-PY', 'es-UY', 'et-EE', + * 'fa-IR', 'fi-FI', 'fj-FJ', 'fo-FO', 'fr-BE', 'fr-FR', 'fr-GF', 'fr-GP', 'fr-MQ', 'fr-RE', 'he-IL', 'hu-HU', 'id-ID', + * 'it-IT', 'ja-JP', 'kk-KZ', 'kl-GL', 'ko-KR', 'lt-LT', 'ms-MY', 'nb-NO', 'ne-NP', 'nl-BE', 'nl-NL', 'nn-NO', 'pl-PL', + * 'pt-BR', 'pt-PT', 'ro-RO', 'ru-RU', 'sl-SI', 'sk-SK', 'sr-RS', 'sv-SE', 'th-TH', 'tr-TR', 'uk-UA', 'vi-VN', 'zh-CN', + * 'zh-HK', 'zh-MO', 'zh-TW'] + * If given value is not a string, then it returns false. + */ +export function IsMobilePhone( + locale?: ValidatorJS.MobilePhoneLocale, + options?: ValidatorJS.IsMobilePhoneOptions, + validationOptions?: ValidationOptions +): PropertyDecorator { + return ValidateBy( + { + name: IS_MOBILE_PHONE, + constraints: [locale, options], + validator: { + validate: (value, args): boolean => isMobilePhone(value, args?.constraints[0], args?.constraints[1]), + defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be a phone number', validationOptions), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsMongoId.ts b/src/decorator/string/IsMongoId.ts new file mode 100644 index 0000000000..fa8507fb66 --- /dev/null +++ b/src/decorator/string/IsMongoId.ts @@ -0,0 +1,30 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isMongoIdValidator from 'validator/lib/isMongoId'; + +export const IS_MONGO_ID = 'isMongoId'; + +/** + * Checks if the string is a valid hex-encoded representation of a MongoDB ObjectId. + * If given value is not a string, then it returns false. + */ +export function isMongoId(value: unknown): boolean { + return typeof value === 'string' && isMongoIdValidator(value); +} + +/** + * Checks if the string is a valid hex-encoded representation of a MongoDB ObjectId. + * If given value is not a string, then it returns false. + */ +export function IsMongoId(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_MONGO_ID, + validator: { + validate: (value, args): boolean => isMongoId(value), + defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be a mongodb id', validationOptions), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsMultibyte.ts b/src/decorator/string/IsMultibyte.ts new file mode 100644 index 0000000000..c295b640c9 --- /dev/null +++ b/src/decorator/string/IsMultibyte.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isMultibyteValidator from 'validator/lib/isMultibyte'; + +export const IS_MULTIBYTE = 'isMultibyte'; + +/** + * Checks if the string contains one or more multibyte chars. + * If given value is not a string, then it returns false. + */ +export function isMultibyte(value: unknown): boolean { + return typeof value === 'string' && isMultibyteValidator(value); +} + +/** + * Checks if the string contains one or more multibyte chars. + * If given value is not a string, then it returns false. + */ +export function IsMultibyte(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_MULTIBYTE, + validator: { + validate: (value, args): boolean => isMultibyte(value), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must contain one or more multibyte chars', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsNumberString.ts b/src/decorator/string/IsNumberString.ts new file mode 100644 index 0000000000..283f2a52e4 --- /dev/null +++ b/src/decorator/string/IsNumberString.ts @@ -0,0 +1,35 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isNumericValidator from 'validator/lib/isNumeric'; +import ValidatorJS from 'validator'; + +export const IS_NUMBER_STRING = 'isNumberString'; + +/** + * Checks if the string is numeric. + * If given value is not a string, then it returns false. + */ +export function isNumberString(value: unknown, options?: ValidatorJS.IsNumericOptions): boolean { + return typeof value === 'string' && isNumericValidator(value, options); +} + +/** + * Checks if the string is numeric. + * If given value is not a string, then it returns false. + */ +export function IsNumberString( + options?: ValidatorJS.IsNumericOptions, + validationOptions?: ValidationOptions +): PropertyDecorator { + return ValidateBy( + { + name: IS_NUMBER_STRING, + constraints: [options], + validator: { + validate: (value, args): boolean => isNumberString(value, args?.constraints[0]), + defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be a number string', validationOptions), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsOctal.ts b/src/decorator/string/IsOctal.ts new file mode 100644 index 0000000000..4427926455 --- /dev/null +++ b/src/decorator/string/IsOctal.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isOctalValidator from 'validator/lib/isOctal'; + +export const IS_OCTAL = 'isOctal'; + +/** + * Check if the string is a valid octal number. + * If given value is not a string, then it returns false. + */ +export function isOctal(value: unknown): boolean { + return typeof value === 'string' && isOctalValidator(value); +} + +/** + * Check if the string is a valid octal number. + * If given value is not a string, then it returns false. + */ +export function IsOctal(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_OCTAL, + validator: { + validate: (value, args): boolean => isOctal(value), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be valid octal number', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsPassportNumber.ts b/src/decorator/string/IsPassportNumber.ts new file mode 100644 index 0000000000..e900aeed2e --- /dev/null +++ b/src/decorator/string/IsPassportNumber.ts @@ -0,0 +1,34 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isPassportNumberValidator from 'validator/lib/isPassportNumber'; + +export const IS_PASSPORT_NUMBER = 'isPassportNumber'; + +/** + * Check if the string is a valid passport number relative to a specific country code. + * If given value is not a string, then it returns false. + */ +export function isPassportNumber(value: unknown, countryCode: string): boolean { + return typeof value === 'string' && isPassportNumberValidator(value, countryCode); +} + +/** + * Check if the string is a valid passport number relative to a specific country code. + * If given value is not a string, then it returns false. + */ +export function IsPassportNumber(countryCode: string, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_PASSPORT_NUMBER, + constraints: [countryCode], + validator: { + validate: (value, args): boolean => isPassportNumber(value, args?.constraints[0]), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be valid passport number', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsPhoneNumber.ts b/src/decorator/string/IsPhoneNumber.ts new file mode 100644 index 0000000000..2a359138ae --- /dev/null +++ b/src/decorator/string/IsPhoneNumber.ts @@ -0,0 +1,48 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import { parsePhoneNumberFromString, CountryCode } from 'libphonenumber-js'; + +export const IS_PHONE_NUMBER = 'isPhoneNumber'; + +/** + * Checks if the string is a valid phone number. To successfully validate any phone number the text must include + * the intl. calling code, if the calling code wont be provided then the region must be set. + * + * @param value the potential phone number string to test + * @param region 2 characters uppercase country code (e.g. DE, US, CH) for country specific validation. + * If text doesn't start with the international calling code (e.g. +41), then you must set this parameter. + */ +export function isPhoneNumber(value: string, region?: CountryCode): boolean { + try { + const phoneNum = parsePhoneNumberFromString(value, region); + const result = phoneNum?.isValid(); + return !!result; + } catch (error) { + // logging? + return false; + } +} + +/** + * Checks if the string is a valid phone number. To successfully validate any phone number the text must include + * the intl. calling code, if the calling code wont be provided then the region must be set. + * + * @param region 2 characters uppercase country code (e.g. DE, US, CH) for country specific validation. + * If text doesn't start with the international calling code (e.g. +41), then you must set this parameter. + */ +export function IsPhoneNumber(region?: CountryCode, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_PHONE_NUMBER, + constraints: [region], + validator: { + validate: (value, args): boolean => isPhoneNumber(value, args?.constraints[0]), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be a valid phone number', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsPort.ts b/src/decorator/string/IsPort.ts new file mode 100644 index 0000000000..d7809e3c42 --- /dev/null +++ b/src/decorator/string/IsPort.ts @@ -0,0 +1,28 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isPortValidator from 'validator/lib/isPort'; + +export const IS_PORT = 'isPort'; + +/** + * Check if the string is a valid port number. + */ +export function isPort(value: unknown): boolean { + return typeof value === 'string' && isPortValidator(value); +} + +/** + * Check if the string is a valid port number. + */ +export function IsPort(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_PORT, + validator: { + validate: (value, args): boolean => isPort(value), + defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be a port', validationOptions), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsPostalCode.ts b/src/decorator/string/IsPostalCode.ts new file mode 100644 index 0000000000..296d3122ec --- /dev/null +++ b/src/decorator/string/IsPostalCode.ts @@ -0,0 +1,35 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isPostalCodeValidator from 'validator/lib/isPostalCode'; +import ValidatorJS from 'validator'; + +export const IS_POSTAL_CODE = 'isPostalCode'; + +/** + * Check if the string is a postal code, in the specified locale. + * If given value is not a string, then it returns false. + */ +export function isPostalCode(value: unknown, locale: 'any' | ValidatorJS.PostalCodeLocale): boolean { + return typeof value === 'string' && isPostalCodeValidator(value, locale); +} + +/** + * Check if the string is a postal code, in the specified locale. + * If given value is not a string, then it returns false. + */ +export function IsPostalCode( + locale?: 'any' | ValidatorJS.PostalCodeLocale, + validationOptions?: ValidationOptions +): PropertyDecorator { + return ValidateBy( + { + name: IS_POSTAL_CODE, + constraints: [locale], + validator: { + validate: (value, args): boolean => isPostalCode(value, args?.constraints[0]), + defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be a postal code', validationOptions), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsRFC3339.ts b/src/decorator/string/IsRFC3339.ts new file mode 100644 index 0000000000..88262b79a5 --- /dev/null +++ b/src/decorator/string/IsRFC3339.ts @@ -0,0 +1,30 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isRFC3339Validator from 'validator/lib/isRFC3339'; + +export const IS_RFC_3339 = 'isRFC3339'; + +/** + * Check if the string is a valid RFC 3339 date. + * If given value is not a string, then it returns false. + */ +export function isRFC3339(value: unknown): boolean { + return typeof value === 'string' && isRFC3339Validator(value); +} + +/** + * Check if the string is a valid RFC 3339 date. + * If given value is not a string, then it returns false. + */ +export function IsRFC3339(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_RFC_3339, + validator: { + validate: (value, args): boolean => isRFC3339(value), + defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be RFC 3339 date', validationOptions), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsRgbColor.ts b/src/decorator/string/IsRgbColor.ts new file mode 100644 index 0000000000..0fc56cc5dd --- /dev/null +++ b/src/decorator/string/IsRgbColor.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isRgbColorValidator from 'validator/lib/isRgbColor'; + +export const IS_RGB_COLOR = 'isRgbColor'; + +/** + * Check if the string is a rgb or rgba color. + * `includePercentValues` defaults to true. If you don't want to allow to set rgb or rgba values with percents, like rgb(5%,5%,5%), or rgba(90%,90%,90%,.3), then set it to false. + * If given value is not a string, then it returns false. + */ +export function isRgbColor(value: unknown, includePercentValues?: boolean): boolean { + return typeof value === 'string' && isRgbColorValidator(value, includePercentValues); +} + +/** + * Check if the string is a rgb or rgba color. + * `includePercentValues` defaults to true. If you don't want to allow to set rgb or rgba values with percents, like rgb(5%,5%,5%), or rgba(90%,90%,90%,.3), then set it to false. + * If given value is not a string, then it returns false. + */ +export function IsRgbColor(includePercentValues?: boolean, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_RGB_COLOR, + constraints: [includePercentValues], + validator: { + validate: (value, args): boolean => isRgbColor(value, args?.constraints[0]), + defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be RGB color', validationOptions), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsSemVer.ts b/src/decorator/string/IsSemVer.ts new file mode 100644 index 0000000000..e599655085 --- /dev/null +++ b/src/decorator/string/IsSemVer.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isSemVerValidator from 'validator/lib/isSemVer'; + +export const IS_SEM_VER = 'isSemVer'; + +/** + * Check if the string is a Semantic Versioning Specification (SemVer). + * If given value is not a string, then it returns false. + */ +export function isSemVer(value: unknown): boolean { + return typeof value === 'string' && isSemVerValidator(value); +} + +/** + * Check if the string is a Semantic Versioning Specification (SemVer). + * If given value is not a string, then it returns false. + */ +export function IsSemVer(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_SEM_VER, + validator: { + validate: (value, args): boolean => isSemVer(value), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be a Semantic Versioning Specification', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsStrongPassword.ts b/src/decorator/string/IsStrongPassword.ts new file mode 100644 index 0000000000..9d17e69b31 --- /dev/null +++ b/src/decorator/string/IsStrongPassword.ts @@ -0,0 +1,42 @@ +import validator from 'validator'; +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; + +export const IS_STRONG_PASSWORD = 'isStrongPassword'; + +/** + * Options to be passed to IsStrongPassword decorator. + */ +export type IsStrongPasswordOptions = Pick< + validator.StrongPasswordOptions, + 'minLength' | 'minLowercase' | 'minUppercase' | 'minNumbers' | 'minSymbols' +>; + +/** + * Checks if the string is a strong password. + * If given value is not a string, then it returns false. + */ +export function isStrongPassword(value: unknown, options?: IsStrongPasswordOptions): boolean { + return typeof value === 'string' && validator.isStrongPassword(value, options); +} + +/** + * Checks if the string is a strong password. + * If given value is not a string, then it returns false. + */ +export function IsStrongPassword( + options?: IsStrongPasswordOptions, + validationOptions?: ValidationOptions +): PropertyDecorator { + return ValidateBy( + { + name: IS_STRONG_PASSWORD, + constraints: [options], + validator: { + validate: (value, args): boolean => isStrongPassword(value, args.constraints[0]), + defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property is not strong enough', validationOptions), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsSurrogatePair.ts b/src/decorator/string/IsSurrogatePair.ts new file mode 100644 index 0000000000..8cd7e2a8d0 --- /dev/null +++ b/src/decorator/string/IsSurrogatePair.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isSurrogatePairValidator from 'validator/lib/isSurrogatePair'; + +export const IS_SURROGATE_PAIR = 'isSurrogatePair'; + +/** + * Checks if the string contains any surrogate pairs chars. + * If given value is not a string, then it returns false. + */ +export function isSurrogatePair(value: unknown): boolean { + return typeof value === 'string' && isSurrogatePairValidator(value); +} + +/** + * Checks if the string contains any surrogate pairs chars. + * If given value is not a string, then it returns false. + */ +export function IsSurrogatePair(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_SURROGATE_PAIR, + validator: { + validate: (value, args): boolean => isSurrogatePair(value), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must contain any surrogate pairs chars', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsTimeZone.ts b/src/decorator/string/IsTimeZone.ts new file mode 100644 index 0000000000..3504d0aa6b --- /dev/null +++ b/src/decorator/string/IsTimeZone.ts @@ -0,0 +1,43 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; + +export const IS_TIMEZONE = 'isTimeZone'; + +/** + * Checks if the string represents a valid IANA timezone + * If the given value is not a valid IANA timezone, then it returns false. + */ +export function isTimeZone(value: unknown): boolean { + try { + if (typeof value !== 'string') { + return false; + } + + /** Specifying an invalid time-zone will raise a `RangeError: Invalid time zone specified` error. */ + Intl.DateTimeFormat(undefined, { timeZone: value }); + + return true; + } catch (exception) { + return false; + } +} + +/** + * Checks if the string represents a valid IANA timezone + * If the given value is not a valid IANA timezone, then it returns false. + */ +export function IsTimeZone(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_TIMEZONE, + validator: { + validate: (value, args): boolean => isTimeZone(value), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be a valid IANA time-zone', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsUUID.ts b/src/decorator/string/IsUUID.ts new file mode 100644 index 0000000000..a767b8f924 --- /dev/null +++ b/src/decorator/string/IsUUID.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isUuidValidator from 'validator/lib/isUUID'; + +export type UUIDVersion = '3' | '4' | '5' | 'all' | 3 | 4 | 5; + +export const IS_UUID = 'isUuid'; + +/** + * Checks if the string is a UUID (version 3, 4 or 5). + * If given value is not a string, then it returns false. + */ +export function isUUID(value: unknown, version?: UUIDVersion): boolean { + return typeof value === 'string' && isUuidValidator(value, version); +} + +/** + * Checks if the string is a UUID (version 3, 4 or 5). + * If given value is not a string, then it returns false. + */ +export function IsUUID(version?: UUIDVersion, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_UUID, + constraints: [version], + validator: { + validate: (value, args): boolean => isUUID(value, args?.constraints[0]), + defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be a UUID', validationOptions), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsUppercase.ts b/src/decorator/string/IsUppercase.ts new file mode 100644 index 0000000000..2e22354082 --- /dev/null +++ b/src/decorator/string/IsUppercase.ts @@ -0,0 +1,30 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isUppercaseValidator from 'validator/lib/isUppercase'; + +export const IS_UPPERCASE = 'isUppercase'; + +/** + * Checks if the string is uppercase. + * If given value is not a string, then it returns false. + */ +export function isUppercase(value: unknown): boolean { + return typeof value === 'string' && isUppercaseValidator(value); +} + +/** + * Checks if the string is uppercase. + * If given value is not a string, then it returns false. + */ +export function IsUppercase(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_UPPERCASE, + validator: { + validate: (value, args): boolean => isUppercase(value), + defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be uppercase', validationOptions), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsUrl.ts b/src/decorator/string/IsUrl.ts new file mode 100644 index 0000000000..42b5c98ae2 --- /dev/null +++ b/src/decorator/string/IsUrl.ts @@ -0,0 +1,32 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isUrlValidator from 'validator/lib/isURL'; +import ValidatorJS from 'validator'; + +export const IS_URL = 'isUrl'; + +/** + * Checks if the string is a url. + * If given value is not a string, then it returns false. + */ +export function isURL(value: string, options?: ValidatorJS.IsURLOptions): boolean { + return typeof value === 'string' && isUrlValidator(value, options); +} + +/** + * Checks if the string is a url. + * If given value is not a string, then it returns false. + */ +export function IsUrl(options?: ValidatorJS.IsURLOptions, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_URL, + constraints: [options], + validator: { + validate: (value, args): boolean => isURL(value, args?.constraints[0]), + defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be a URL address', validationOptions), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/IsVariableWidth.ts b/src/decorator/string/IsVariableWidth.ts new file mode 100644 index 0000000000..0eb4d312d3 --- /dev/null +++ b/src/decorator/string/IsVariableWidth.ts @@ -0,0 +1,33 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isVariableWidthValidator from 'validator/lib/isVariableWidth'; + +export const IS_VARIABLE_WIDTH = 'isVariableWidth'; + +/** + * Checks if the string contains variable-width chars. + * If given value is not a string, then it returns false. + */ +export function isVariableWidth(value: unknown): boolean { + return typeof value === 'string' && isVariableWidthValidator(value); +} + +/** + * Checks if the string contains variable-width chars. + * If given value is not a string, then it returns false. + */ +export function IsVariableWidth(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_VARIABLE_WIDTH, + validator: { + validate: (value, args): boolean => isVariableWidth(value), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must contain a full-width and half-width characters', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/Length.ts b/src/decorator/string/Length.ts new file mode 100644 index 0000000000..04427bdfb7 --- /dev/null +++ b/src/decorator/string/Length.ts @@ -0,0 +1,43 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isLengthValidator from 'validator/lib/isLength'; + +export const IS_LENGTH = 'isLength'; + +/** + * Checks if the string's length falls in a range. Note: this function takes into account surrogate pairs. + * If given value is not a string, then it returns false. + */ +export function length(value: unknown, min: number, max?: number): boolean { + return typeof value === 'string' && isLengthValidator(value, { min, max }); +} + +/** + * Checks if the string's length falls in a range. Note: this function takes into account surrogate pairs. + * If given value is not a string, then it returns false. + */ +export function Length(min: number, max?: number, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_LENGTH, + constraints: [min, max], + validator: { + validate: (value, args): boolean => length(value, args?.constraints[0], args?.constraints[1]), + defaultMessage: buildMessage((eachPrefix, args) => { + const isMinLength = args?.constraints[0] !== null && args?.constraints[0] !== undefined; + const isMaxLength = args?.constraints[1] !== null && args?.constraints[1] !== undefined; + if (isMinLength && (!args.value || args.value.length < args?.constraints[0])) { + return eachPrefix + '$property must be longer than or equal to $constraint1 characters'; + } else if (isMaxLength && args.value.length > args?.constraints[1]) { + return eachPrefix + '$property must be shorter than or equal to $constraint2 characters'; + } + return ( + eachPrefix + + '$property must be longer than or equal to $constraint1 and shorter than or equal to $constraint2 characters' + ); + }, validationOptions), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/Matches.ts b/src/decorator/string/Matches.ts new file mode 100644 index 0000000000..403d5a6f15 --- /dev/null +++ b/src/decorator/string/Matches.ts @@ -0,0 +1,49 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import matchesValidator from 'validator/lib/matches'; + +export const MATCHES = 'matches'; + +/** + * Checks if string matches the pattern. Either matches('foo', /foo/i). + * If given value is not a string, then it returns false. + */ +export function matches(value: string, pattern: RegExp): boolean; +export function matches(value: string, pattern: string, modifiers: string): boolean; +export function matches(value: string, pattern: RegExp | string, modifiers?: string): boolean { + return typeof value === 'string' && matchesValidator(value, pattern as unknown as any, modifiers); +} + +/** + * Checks if string matches the pattern. Either matches('foo', /foo/i) + * If given value is not a string, then it returns false. + */ +export function Matches(pattern: RegExp, validationOptions?: ValidationOptions): PropertyDecorator; +export function Matches(pattern: string, modifiers?: string, validationOptions?: ValidationOptions): PropertyDecorator; +export function Matches( + pattern: RegExp | string, + modifiersOrAnnotationOptions?: string | ValidationOptions, + validationOptions?: ValidationOptions +): PropertyDecorator { + let modifiers: string; + if (modifiersOrAnnotationOptions && modifiersOrAnnotationOptions instanceof Object && !validationOptions) { + validationOptions = modifiersOrAnnotationOptions; + } else { + modifiers = modifiersOrAnnotationOptions as string; + } + + return ValidateBy( + { + name: MATCHES, + constraints: [pattern, modifiers], + validator: { + validate: (value, args): boolean => matches(value, args?.constraints[0], args?.constraints[1]), + defaultMessage: buildMessage( + (eachPrefix, args) => eachPrefix + '$property must match $constraint1 regular expression', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/MaxLength.ts b/src/decorator/string/MaxLength.ts new file mode 100644 index 0000000000..78dc194ccf --- /dev/null +++ b/src/decorator/string/MaxLength.ts @@ -0,0 +1,34 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isLengthValidator from 'validator/lib/isLength'; + +export const MAX_LENGTH = 'maxLength'; + +/** + * Checks if the string's length is not more than given number. Note: this function takes into account surrogate pairs. + * If given value is not a string, then it returns false. + */ +export function maxLength(value: unknown, max: number): boolean { + return typeof value === 'string' && isLengthValidator(value, { min: 0, max }); +} + +/** + * Checks if the string's length is not more than given number. Note: this function takes into account surrogate pairs. + * If given value is not a string, then it returns false. + */ +export function MaxLength(max: number, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: MAX_LENGTH, + constraints: [max], + validator: { + validate: (value, args): boolean => maxLength(value, args?.constraints[0]), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be shorter than or equal to $constraint1 characters', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/MinLength.ts b/src/decorator/string/MinLength.ts new file mode 100644 index 0000000000..050a51be26 --- /dev/null +++ b/src/decorator/string/MinLength.ts @@ -0,0 +1,34 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isLengthValidator from 'validator/lib/isLength'; + +export const MIN_LENGTH = 'minLength'; + +/** + * Checks if the string's length is not less than given number. Note: this function takes into account surrogate pairs. + * If given value is not a string, then it returns false. + */ +export function minLength(value: unknown, min: number): boolean { + return typeof value === 'string' && isLengthValidator(value, { min }); +} + +/** + * Checks if the string's length is not less than given number. Note: this function takes into account surrogate pairs. + * If given value is not a string, then it returns false. + */ +export function MinLength(min: number, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: MIN_LENGTH, + constraints: [min], + validator: { + validate: (value, args): boolean => minLength(value, args?.constraints[0]), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be longer than or equal to $constraint1 characters', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/NotContains.ts b/src/decorator/string/NotContains.ts new file mode 100644 index 0000000000..5db77b9de0 --- /dev/null +++ b/src/decorator/string/NotContains.ts @@ -0,0 +1,34 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import containsValidator from 'validator/lib/contains'; + +export const NOT_CONTAINS = 'notContains'; + +/** + * Checks if the string does not contain the seed. + * If given value is not a string, then it returns false. + */ +export function notContains(value: unknown, seed: string): boolean { + return typeof value === 'string' && !containsValidator(value, seed); +} + +/** + * Checks if the string does not contain the seed. + * If given value is not a string, then it returns false. + */ +export function NotContains(seed: string, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: NOT_CONTAINS, + constraints: [seed], + validator: { + validate: (value, args): boolean => notContains(value, args?.constraints[0]), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property should not contain a $constraint1 string', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/is-iso4217-currency-code.ts b/src/decorator/string/is-iso4217-currency-code.ts new file mode 100644 index 0000000000..903620a146 --- /dev/null +++ b/src/decorator/string/is-iso4217-currency-code.ts @@ -0,0 +1,31 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isISO4217Validator from 'validator/lib/isISO4217'; + +export const IS_ISO4217_CURRENCY_CODE = 'isISO4217CurrencyCode'; + +/** + * Check if the string is a valid [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) officially assigned currency code. + */ +export function isISO4217CurrencyCode(value: unknown): boolean { + return typeof value === 'string' && isISO4217Validator(value); +} + +/** + * Check if the string is a valid [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) officially assigned currency code. + */ +export function IsISO4217CurrencyCode(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_ISO4217_CURRENCY_CODE, + validator: { + validate: (value, args): boolean => isISO4217CurrencyCode(value), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be a valid ISO4217 currency code', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/string/is-tax-id.ts b/src/decorator/string/is-tax-id.ts new file mode 100644 index 0000000000..950852f190 --- /dev/null +++ b/src/decorator/string/is-tax-id.ts @@ -0,0 +1,42 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isTaxIDValidator from 'validator/lib/isTaxID'; + +export const IS_TAX_ID = 'isTaxId'; + +/** + * Checks if the string is a valid tax ID. Default locale is `en-US`. + * If given value is not a string, then it returns false. + * + * Supported locales: bg-BG, cs-CZ, de-AT, de-DE, dk-DK, el-CY, el-GR, en-CA, + * en-IE, en-US, es-ES, et-EE, fi-FI, fr-BE, fr-FR, fr-LU, hr-HR, hu-HU, it-IT, + * lv-LV, mt-MT, nl-NL, pl-PL, pt-BR, pt-PT, ro-RO, sk-SK, sl-SI, sv-SE. + */ +export function isTaxId(value: unknown, locale?: string): boolean { + return typeof value === 'string' && isTaxIDValidator(value, locale || 'en-US'); +} + +/** + * Checks if the string is a valid tax ID. Default locale is `en-US`. + * If given value is not a string, then it returns false. + * + * Supported locales: bg-BG, cs-CZ, de-AT, de-DE, dk-DK, el-CY, el-GR, en-CA, + * en-IE, en-US, es-ES, et-EE, fi-FI, fr-BE, fr-FR, fr-LU, hr-HR, hu-HU, it-IT, + * lv-LV, mt-MT, nl-NL, pl-PL, pt-BR, pt-PT, ro-RO, sk-SK, sl-SI, sv-SE. + */ +export function IsTaxId(locale?: string, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_TAX_ID, + constraints: [locale], + validator: { + validate: (value, args): boolean => isTaxId(value, args?.constraints[0]), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be a Tax Identification Number', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/typechecker/IsArray.ts b/src/decorator/typechecker/IsArray.ts new file mode 100644 index 0000000000..7e5f43d7de --- /dev/null +++ b/src/decorator/typechecker/IsArray.ts @@ -0,0 +1,27 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; + +export const IS_ARRAY = 'isArray'; + +/** + * Checks if a given value is an array + */ +export function isArray(value: unknown): value is Array { + return Array.isArray(value); +} + +/** + * Checks if a given value is an array + */ +export function IsArray(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_ARRAY, + validator: { + validate: (value, args): boolean => isArray(value), + defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be an array', validationOptions), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/typechecker/IsBoolean.ts b/src/decorator/typechecker/IsBoolean.ts new file mode 100644 index 0000000000..187638de22 --- /dev/null +++ b/src/decorator/typechecker/IsBoolean.ts @@ -0,0 +1,27 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; + +export const IS_BOOLEAN = 'isBoolean'; + +/** + * Checks if a given value is a boolean. + */ +export function isBoolean(value: unknown): value is boolean { + return value instanceof Boolean || typeof value === 'boolean'; +} + +/** + * Checks if a value is a boolean. + */ +export function IsBoolean(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_BOOLEAN, + validator: { + validate: (value, args): boolean => isBoolean(value), + defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be a boolean value', validationOptions), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/typechecker/IsDate.ts b/src/decorator/typechecker/IsDate.ts new file mode 100644 index 0000000000..ea12e927f8 --- /dev/null +++ b/src/decorator/typechecker/IsDate.ts @@ -0,0 +1,27 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; + +export const IS_DATE = 'isDate'; + +/** + * Checks if a given value is a date. + */ +export function isDate(value: unknown): value is Date { + return value instanceof Date && !isNaN(value.getTime()); +} + +/** + * Checks if a value is a date. + */ +export function IsDate(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_DATE, + validator: { + validate: (value, args): boolean => isDate(value), + defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be a Date instance', validationOptions), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/typechecker/IsEnum.ts b/src/decorator/typechecker/IsEnum.ts new file mode 100644 index 0000000000..eb3d6b064b --- /dev/null +++ b/src/decorator/typechecker/IsEnum.ts @@ -0,0 +1,41 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; + +export const IS_ENUM = 'isEnum'; + +/** + * Checks if a given value is the member of the provided enum. + */ +export function isEnum(value: unknown, entity: any): boolean { + const enumValues = Object.keys(entity).map(k => entity[k]); + return enumValues.includes(value); +} + +/** + * Returns the possible values from an enum (both simple number indexed and string indexed enums). + */ +function validEnumValues(entity: any): string[] { + return Object.entries(entity) + .filter(([key, value]) => isNaN(parseInt(key))) + .map(([key, value]) => value as string); +} + +/** + * Checks if a given value is the member of the provided enum. + */ +export function IsEnum(entity: object, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_ENUM, + constraints: [entity, validEnumValues(entity)], + validator: { + validate: (value, args): boolean => isEnum(value, args?.constraints[0]), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be one of the following values: $constraint2', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/typechecker/IsInt.ts b/src/decorator/typechecker/IsInt.ts new file mode 100644 index 0000000000..50f822841d --- /dev/null +++ b/src/decorator/typechecker/IsInt.ts @@ -0,0 +1,30 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; + +export const IS_INT = 'isInt'; + +/** + * Checks if value is an integer. + */ +export function isInt(val: unknown): val is Number { + return typeof val === 'number' && Number.isInteger(val); +} + +/** + * Checks if value is an integer. + */ +export function IsInt(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_INT, + validator: { + validate: (value, args): boolean => isInt(value), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be an integer number', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/typechecker/IsNumber.ts b/src/decorator/typechecker/IsNumber.ts new file mode 100644 index 0000000000..5b6e67c13c --- /dev/null +++ b/src/decorator/typechecker/IsNumber.ts @@ -0,0 +1,62 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; + +export const IS_NUMBER = 'isNumber'; + +/** + * Options to be passed to IsNumber decorator. + */ +export interface IsNumberOptions { + allowNaN?: boolean; + allowInfinity?: boolean; + maxDecimalPlaces?: number; +} + +/** + * Checks if a given value is a number. + */ +export function isNumber(value: unknown, options: IsNumberOptions = {}): value is number { + if (typeof value !== 'number') { + return false; + } + + if (value === Infinity || value === -Infinity) { + return !!options.allowInfinity; + } + + if (Number.isNaN(value)) { + return !!options.allowNaN; + } + + if (options.maxDecimalPlaces !== undefined) { + let decimalPlaces = 0; + if (value % 1 !== 0) { + decimalPlaces = value.toString().split('.')[1].length; + } + if (decimalPlaces > options.maxDecimalPlaces) { + return false; + } + } + + return Number.isFinite(value); +} + +/** + * Checks if a value is a number. + */ +export function IsNumber(options: IsNumberOptions = {}, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_NUMBER, + constraints: [options], + validator: { + validate: (value, args): boolean => isNumber(value, args?.constraints[0]), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be a number conforming to the specified constraints', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/typechecker/IsObject.ts b/src/decorator/typechecker/IsObject.ts new file mode 100644 index 0000000000..5cbd415b67 --- /dev/null +++ b/src/decorator/typechecker/IsObject.ts @@ -0,0 +1,29 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; + +export const IS_OBJECT = 'isObject'; + +/** + * Checks if the value is valid Object. + * Returns false if the value is not an object. + */ +export function isObject(value: unknown): value is T { + return value != null && (typeof value === 'object' || typeof value === 'function') && !Array.isArray(value); +} + +/** + * Checks if the value is valid Object. + * Returns false if the value is not an object. + */ +export function IsObject(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_OBJECT, + validator: { + validate: (value, args): boolean => isObject(value), + defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be an object', validationOptions), + }, + }, + validationOptions + ); +} diff --git a/src/decorator/typechecker/IsString.ts b/src/decorator/typechecker/IsString.ts new file mode 100644 index 0000000000..4c309cd622 --- /dev/null +++ b/src/decorator/typechecker/IsString.ts @@ -0,0 +1,27 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; + +export const IS_STRING = 'isString'; + +/** + * Checks if a given value is a real string. + */ +export function isString(value: unknown): value is string { + return value instanceof String || typeof value === 'string'; +} + +/** + * Checks if a given value is a real string. + */ +export function IsString(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_STRING, + validator: { + validate: (value, args): boolean => isString(value), + defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be a string', validationOptions), + }, + }, + validationOptions + ); +} diff --git a/src/index.ts b/src/index.ts index 07001fc2a1..34aa0f38b5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,27 +1,26 @@ -import {ValidationError} from "./validation/ValidationError"; -import {ValidatorOptions} from "./validation/ValidatorOptions"; -import {ValidationSchema} from "./validation-schema/ValidationSchema"; -import {MetadataStorage} from "./metadata/MetadataStorage"; -import {Validator} from "./validation/Validator"; -import {getFromContainer} from "./container"; +import { ValidationError } from './validation/ValidationError'; +import { ValidatorOptions } from './validation/ValidatorOptions'; +import { ValidationSchema } from './validation-schema/ValidationSchema'; +import { getMetadataStorage } from './metadata/MetadataStorage'; +import { Validator } from './validation/Validator'; +import { getFromContainer } from './container'; // ------------------------------------------------------------------------- // Export everything api users needs // ------------------------------------------------------------------------- -export * from "./container"; -export * from "./decorator/decorators"; -export * from "./decorator/ValidationOptions"; -export * from "./validation/ValidatorConstraintInterface"; -export * from "./validation/ValidationError"; -export * from "./validation/ValidationTypeOptions"; -export * from "./validation/ValidatorOptions"; -export * from "./validation/ValidationArguments"; -export * from "./validation/ValidationTypes"; -export * from "./validation/Validator"; -export * from "./validation-schema/ValidationSchema"; -export * from "./register-decorator"; -export * from "./metadata/MetadataStorage"; +export * from './container'; +export * from './decorator/decorators'; +export * from './decorator/ValidationOptions'; +export * from './validation/ValidatorConstraintInterface'; +export * from './validation/ValidationError'; +export * from './validation/ValidatorOptions'; +export * from './validation/ValidationArguments'; +export * from './validation/ValidationTypes'; +export * from './validation/Validator'; +export * from './validation-schema/ValidationSchema'; +export * from './register-decorator'; +export * from './metadata/MetadataStorage'; // ------------------------------------------------------------------------- // Shortcut methods for api users @@ -30,47 +29,70 @@ export * from "./metadata/MetadataStorage"; /** * Validates given object. */ -export function validate(object: Object, validatorOptions?: ValidatorOptions): Promise; +export function validate(object: object, validatorOptions?: ValidatorOptions): Promise; /** * Validates given object by a given validation schema. */ -export function validate(schemaName: string, object: Object, validatorOptions?: ValidatorOptions): Promise; +export function validate( + schemaName: string, + object: object, + validatorOptions?: ValidatorOptions +): Promise; /** * Validates given object by object's decorators or given validation schema. */ -export function validate(schemaNameOrObject: Object|string, - objectOrValidationOptions?: Object|ValidatorOptions, - maybeValidatorOptions?: ValidatorOptions): Promise { - if (typeof schemaNameOrObject === "string") { - return getFromContainer(Validator).validate(schemaNameOrObject as string, objectOrValidationOptions as Object, maybeValidatorOptions); - } else { - return getFromContainer(Validator).validate(schemaNameOrObject as Object, objectOrValidationOptions as ValidatorOptions); - } +export function validate( + schemaNameOrObject: object | string, + objectOrValidationOptions?: object | ValidatorOptions, + maybeValidatorOptions?: ValidatorOptions +): Promise { + if (typeof schemaNameOrObject === 'string') { + return getFromContainer(Validator).validate( + schemaNameOrObject, + objectOrValidationOptions as object, + maybeValidatorOptions + ); + } else { + return getFromContainer(Validator).validate(schemaNameOrObject, objectOrValidationOptions as ValidatorOptions); + } } /** * Validates given object and reject on error. */ -export function validateOrReject(object: Object, validatorOptions?: ValidatorOptions): Promise; +export function validateOrReject(object: object, validatorOptions?: ValidatorOptions): Promise; /** * Validates given object by a given validation schema and reject on error. */ -export function validateOrReject(schemaName: string, object: Object, validatorOptions?: ValidatorOptions): Promise; +export function validateOrReject( + schemaName: string, + object: object, + validatorOptions?: ValidatorOptions +): Promise; /** * Validates given object by object's decorators or given validation schema and reject on error. */ -export function validateOrReject(schemaNameOrObject: Object|string, - objectOrValidationOptions?: Object|ValidatorOptions, - maybeValidatorOptions?: ValidatorOptions): Promise { - if (typeof schemaNameOrObject === "string") { - return getFromContainer(Validator).validateOrReject(schemaNameOrObject as string, objectOrValidationOptions as Object, maybeValidatorOptions); - } else { - return getFromContainer(Validator).validateOrReject(schemaNameOrObject as Object, objectOrValidationOptions as ValidatorOptions); - } +export function validateOrReject( + schemaNameOrObject: object | string, + objectOrValidationOptions?: object | ValidatorOptions, + maybeValidatorOptions?: ValidatorOptions +): Promise { + if (typeof schemaNameOrObject === 'string') { + return getFromContainer(Validator).validateOrReject( + schemaNameOrObject, + objectOrValidationOptions as object, + maybeValidatorOptions + ); + } else { + return getFromContainer(Validator).validateOrReject( + schemaNameOrObject, + objectOrValidationOptions as ValidatorOptions + ); + } } /** @@ -78,33 +100,43 @@ export function validateOrReject(schemaNameOrObject: Object|string, * Note that this method completely ignores async validations. * If you want to properly perform validation you need to call validate method instead. */ -export function validateSync(object: Object, validatorOptions?: ValidatorOptions): ValidationError[]; +export function validateSync(object: object, validatorOptions?: ValidatorOptions): ValidationError[]; /** * Validates given object by a given validation schema. * Note that this method completely ignores async validations. * If you want to properly perform validation you need to call validate method instead. */ -export function validateSync(schemaName: string, object: Object, validatorOptions?: ValidatorOptions): ValidationError[]; +export function validateSync( + schemaName: string, + object: object, + validatorOptions?: ValidatorOptions +): ValidationError[]; /** * Validates given object by object's decorators or given validation schema. * Note that this method completely ignores async validations. * If you want to properly perform validation you need to call validate method instead. */ -export function validateSync(schemaNameOrObject: Object|string, - objectOrValidationOptions?: Object|ValidatorOptions, - maybeValidatorOptions?: ValidatorOptions): ValidationError[] { - if (typeof schemaNameOrObject === "string") { - return getFromContainer(Validator).validateSync(schemaNameOrObject as string, objectOrValidationOptions as Object, maybeValidatorOptions); - } else { - return getFromContainer(Validator).validateSync(schemaNameOrObject as Object, objectOrValidationOptions as ValidatorOptions); - } +export function validateSync( + schemaNameOrObject: object | string, + objectOrValidationOptions?: object | ValidatorOptions, + maybeValidatorOptions?: ValidatorOptions +): ValidationError[] { + if (typeof schemaNameOrObject === 'string') { + return getFromContainer(Validator).validateSync( + schemaNameOrObject, + objectOrValidationOptions as object, + maybeValidatorOptions + ); + } else { + return getFromContainer(Validator).validateSync(schemaNameOrObject, objectOrValidationOptions as ValidatorOptions); + } } /** * Registers a new validation schema. */ export function registerSchema(schema: ValidationSchema): void { - getFromContainer(MetadataStorage).addValidationSchema(schema); + getMetadataStorage().addValidationSchema(schema); } diff --git a/src/metadata/ConstraintMetadata.ts b/src/metadata/ConstraintMetadata.ts index 0045024fb1..61ffcecc13 100644 --- a/src/metadata/ConstraintMetadata.ts +++ b/src/metadata/ConstraintMetadata.ts @@ -1,49 +1,47 @@ -import {ValidatorConstraintInterface} from "../validation/ValidatorConstraintInterface"; -import {getFromContainer} from "../container"; +import { ValidatorConstraintInterface } from '../validation/ValidatorConstraintInterface'; +import { getFromContainer } from '../container'; /** * This metadata interface contains information for custom validators. */ export class ConstraintMetadata { - - // ------------------------------------------------------------------------- - // Properties - // ------------------------------------------------------------------------- - - /** - * Target class which performs validation. - */ - target: Function; - - /** - * Custom validation's name, that will be used as validation error type. - */ - name: string; - - /** - * Indicates if this validation is asynchronous or not. - */ - async: boolean; - - // ------------------------------------------------------------------------- - // Constructor - // ------------------------------------------------------------------------- - - constructor(target: Function, name?: string, async: boolean = false) { - this.target = target; - this.name = name; - this.async = async; - } - - // ------------------------------------------------------------------------- - // Accessors - // ------------------------------------------------------------------------- - - /** - * Instance of the target custom validation class which performs validation. - */ - get instance(): ValidatorConstraintInterface { - return getFromContainer(this.target); - } - + // ------------------------------------------------------------------------- + // Properties + // ------------------------------------------------------------------------- + + /** + * Target class which performs validation. + */ + target: Function; + + /** + * Custom validation's name, that will be used as validation error type. + */ + name: string; + + /** + * Indicates if this validation is asynchronous or not. + */ + async: boolean; + + // ------------------------------------------------------------------------- + // Constructor + // ------------------------------------------------------------------------- + + constructor(target: Function, name?: string, async: boolean = false) { + this.target = target; + this.name = name; + this.async = async; + } + + // ------------------------------------------------------------------------- + // Accessors + // ------------------------------------------------------------------------- + + /** + * Instance of the target custom validation class which performs validation. + */ + get instance(): ValidatorConstraintInterface { + return getFromContainer(this.target); + } } diff --git a/src/metadata/MetadataStorage.ts b/src/metadata/MetadataStorage.ts index 4e2bd4c8a7..286346ac90 100644 --- a/src/metadata/MetadataStorage.ts +++ b/src/metadata/MetadataStorage.ts @@ -1,114 +1,171 @@ -import {ValidationMetadata} from "./ValidationMetadata"; -import {ConstraintMetadata} from "./ConstraintMetadata"; -import {ValidationSchema} from "../validation-schema/ValidationSchema"; -import {ValidationSchemaToMetadataTransformer} from "../validation-schema/ValidationSchemaToMetadataTransformer"; +import { ValidationMetadata } from './ValidationMetadata'; +import { ConstraintMetadata } from './ConstraintMetadata'; +import { ValidationSchema } from '../validation-schema/ValidationSchema'; +import { ValidationSchemaToMetadataTransformer } from '../validation-schema/ValidationSchemaToMetadataTransformer'; +import { getGlobal } from '../utils'; /** * Storage all metadatas. */ export class MetadataStorage { + // ------------------------------------------------------------------------- + // Private properties + // ------------------------------------------------------------------------- - // ------------------------------------------------------------------------- - // Private properties - // ------------------------------------------------------------------------- + private validationMetadatas: Map = new Map(); + private constraintMetadatas: Map = new Map(); - private validationMetadatas: ValidationMetadata[] = []; - private constraintMetadatas: ConstraintMetadata[] = []; + get hasValidationMetaData(): boolean { + return !!this.validationMetadatas.size; + } - get hasValidationMetaData() { - return !!this.validationMetadatas.length; - } + // ------------------------------------------------------------------------- + // Public Methods + // ------------------------------------------------------------------------- - // ------------------------------------------------------------------------- - // Public Methods - // ------------------------------------------------------------------------- + /** + * Adds a new validation metadata. + */ + addValidationSchema(schema: ValidationSchema): void { + const validationMetadatas = new ValidationSchemaToMetadataTransformer().transform(schema); + validationMetadatas.forEach(validationMetadata => this.addValidationMetadata(validationMetadata)); + } - /** - * Adds a new validation metadata. - */ - addValidationSchema(schema: ValidationSchema) { - const validationMetadatas = new ValidationSchemaToMetadataTransformer().transform(schema); - validationMetadatas.forEach(validationMetadata => this.addValidationMetadata(validationMetadata)); - } - - /** - * Adds a new validation metadata. - */ - addValidationMetadata(metadata: ValidationMetadata) { - this.validationMetadatas.push(metadata); - } + /** + * Adds a new validation metadata. + */ + addValidationMetadata(metadata: ValidationMetadata): void { + const existingMetadata = this.validationMetadatas.get(metadata.target); - /** - * Adds a new constraint metadata. - */ - addConstraintMetadata(metadata: ConstraintMetadata) { - this.constraintMetadatas.push(metadata); + if (existingMetadata) { + existingMetadata.push(metadata); + } else { + this.validationMetadatas.set(metadata.target, [metadata]); } + } - /** - * Groups metadata by their property names. - */ - groupByPropertyName(metadata: ValidationMetadata[]): { [propertyName: string]: ValidationMetadata[] } { - const grouped: { [propertyName: string]: ValidationMetadata[] } = {}; - metadata.forEach(metadata => { - if (!grouped[metadata.propertyName]) - grouped[metadata.propertyName] = []; - grouped[metadata.propertyName].push(metadata); - }); - return grouped; - } + /** + * Adds a new constraint metadata. + */ + addConstraintMetadata(metadata: ConstraintMetadata): void { + const existingMetadata = this.constraintMetadatas.get(metadata.target); - /** - * Gets all validation metadatas for the given object with the given groups. - */ - getTargetValidationMetadatas(targetConstructor: Function, targetSchema: string, groups?: string[]): ValidationMetadata[] { - - // get directly related to a target metadatas - const originalMetadatas = this.validationMetadatas.filter(metadata => { - if (metadata.target !== targetConstructor && metadata.target !== targetSchema) - return false; - if (metadata.always) - return true; - if (groups && groups.length > 0) - return metadata.groups && !!metadata.groups.find(group => groups.indexOf(group) !== -1); - - return true; - }); - - // get metadatas for inherited classes - const inheritedMetadatas = this.validationMetadatas.filter(metadata => { - // if target is a string it's means we validate agains a schema, and there is no inheritance support for schemas - if (typeof metadata.target === "string") - return false; - if (metadata.target === targetConstructor) - return false; - if (metadata.target instanceof Function && - !(targetConstructor.prototype instanceof (metadata.target as Function))) - return false; - if (metadata.always) - return true; - if (groups && groups.length > 0) - return metadata.groups && !!metadata.groups.find(group => groups.indexOf(group) !== -1); - - return true; - }); - - // filter out duplicate metadatas, prefer original metadatas instead of inherited metadatas - const uniqueInheritedMetadatas = inheritedMetadatas.filter(inheritedMetadata => { - return !originalMetadatas.find(originalMetadata => { - return originalMetadata.propertyName === inheritedMetadata.propertyName && - originalMetadata.type === inheritedMetadata.type; - }); - }); - - return originalMetadatas.concat(uniqueInheritedMetadatas); + if (existingMetadata) { + existingMetadata.push(metadata); + } else { + this.constraintMetadatas.set(metadata.target, [metadata]); } + } + + /** + * Groups metadata by their property names. + */ + groupByPropertyName(metadata: ValidationMetadata[]): { [propertyName: string]: ValidationMetadata[] } { + const grouped: { [propertyName: string]: ValidationMetadata[] } = {}; + metadata.forEach(metadata => { + if (!grouped[metadata.propertyName]) grouped[metadata.propertyName] = []; + grouped[metadata.propertyName].push(metadata); + }); + return grouped; + } + + /** + * Gets all validation metadatas for the given object with the given groups. + */ + getTargetValidationMetadatas( + targetConstructor: Function, + targetSchema: string, + always: boolean, + strictGroups: boolean, + groups?: string[] + ): ValidationMetadata[] { + const includeMetadataBecauseOfAlwaysOption = (metadata: ValidationMetadata): boolean => { + // `metadata.always` overrides global default. + if (typeof metadata.always !== 'undefined') return metadata.always; + + // `metadata.groups` overrides global default. + if (metadata.groups && metadata.groups.length) return false; + + // Use global default. + return always; + }; + + const excludeMetadataBecauseOfStrictGroupsOption = (metadata: ValidationMetadata): boolean => { + if (strictGroups) { + // Validation is not using groups. + if (!groups || !groups.length) { + // `metadata.groups` has at least one group. + if (metadata.groups && metadata.groups.length) return true; + } + } + + return false; + }; - /** - * Gets all validator constraints for the given object. - */ - getTargetValidatorConstraints(target: Function): ConstraintMetadata[] { - return this.constraintMetadatas.filter(metadata => metadata.target === target); + // get directly related to a target metadatas + const filteredForOriginalMetadatasSearch = this.validationMetadatas.get(targetConstructor) || []; + const originalMetadatas = filteredForOriginalMetadatasSearch.filter(metadata => { + if (metadata.target !== targetConstructor && metadata.target !== targetSchema) return false; + if (includeMetadataBecauseOfAlwaysOption(metadata)) return true; + if (excludeMetadataBecauseOfStrictGroupsOption(metadata)) return false; + if (groups && groups.length > 0) + return metadata.groups && !!metadata.groups.find(group => groups.indexOf(group) !== -1); + + return true; + }); + + // get metadatas for inherited classes + const filteredForInheritedMetadatasSearch = []; + for (const [key, value] of this.validationMetadatas.entries()) { + if (targetConstructor.prototype instanceof key) { + filteredForInheritedMetadatasSearch.push(...value); + } } + const inheritedMetadatas = filteredForInheritedMetadatasSearch.filter(metadata => { + // if target is a string it's means we validate against a schema, and there is no inheritance support for schemas + if (typeof metadata.target === 'string') return false; + if (metadata.target === targetConstructor) return false; + if (metadata.target instanceof Function && !(targetConstructor.prototype instanceof metadata.target)) + return false; + if (includeMetadataBecauseOfAlwaysOption(metadata)) return true; + if (excludeMetadataBecauseOfStrictGroupsOption(metadata)) return false; + if (groups && groups.length > 0) + return metadata.groups && !!metadata.groups.find(group => groups.indexOf(group) !== -1); + + return true; + }); + + // filter out duplicate metadatas, prefer original metadatas instead of inherited metadatas + const uniqueInheritedMetadatas = inheritedMetadatas.filter(inheritedMetadata => { + return !originalMetadatas.find(originalMetadata => { + return ( + originalMetadata.propertyName === inheritedMetadata.propertyName && + originalMetadata.type === inheritedMetadata.type + ); + }); + }); + + return originalMetadatas.concat(uniqueInheritedMetadatas); + } + + /** + * Gets all validator constraints for the given object. + */ + getTargetValidatorConstraints(target: Function): ConstraintMetadata[] { + return this.constraintMetadatas.get(target) || []; + } +} + +/** + * Gets metadata storage. + * Metadata storage follows the best practices and stores metadata in a global variable. + */ +export function getMetadataStorage(): MetadataStorage { + const global = getGlobal(); + + if (!global.classValidatorMetadataStorage) { + global.classValidatorMetadataStorage = new MetadataStorage(); + } -} \ No newline at end of file + return global.classValidatorMetadataStorage; +} diff --git a/src/metadata/ValidationMetadata.ts b/src/metadata/ValidationMetadata.ts index 70e433c9b9..c1b1acce82 100644 --- a/src/metadata/ValidationMetadata.ts +++ b/src/metadata/ValidationMetadata.ts @@ -1,88 +1,92 @@ -import {ValidationMetadataArgs} from "./ValidationMetadataArgs"; -import {ValidationArguments} from "../validation/ValidationArguments"; +import { ValidationMetadataArgs } from './ValidationMetadataArgs'; +import { ValidationArguments } from '../validation/ValidationArguments'; /** * This metadata contains validation rules. */ export class ValidationMetadata { - - // ------------------------------------------------------------------------- - // Properties - // ------------------------------------------------------------------------- - - /** - * Validation type. - */ - type: string; - - /** - * Target class to which this validation is applied. - */ - target: Function|string; - - /** - * Property of the object to be validated. - */ - propertyName: string; - - /** - * Constraint class that performs validation. Used only for custom validations. - */ - constraintCls: Function; - - /** - * Array of constraints of this validation. - */ - constraints: any[]; - - /** - * Validation message to be shown in the case of error. - */ - message: string|((args: ValidationArguments) => string); - - /** - * Validation groups used for this validation. - */ - groups: string[] = []; - - /** - * Indicates if validation must be performed always, no matter of validation groups used. - */ - always: boolean = false; - - /** - * Specifies if validated value is an array and each of its item must be validated. - */ - each: boolean = false; - - /* - * A transient set of data passed through to the validation result for response mapping - */ - context?: any = undefined; - - /** - * Extra options specific to validation type. - */ - validationTypeOptions: any; - - // ------------------------------------------------------------------------- - // Constructor - // ------------------------------------------------------------------------- - - constructor(args: ValidationMetadataArgs) { - this.type = args.type; - this.target = args.target; - this.propertyName = args.propertyName; - this.constraints = args.constraints; - this.constraintCls = args.constraintCls; - this.validationTypeOptions = args.validationTypeOptions; - if (args.validationOptions) { - this.message = args.validationOptions.message; - this.groups = args.validationOptions.groups; - this.always = args.validationOptions.always; - this.each = args.validationOptions.each; - this.context = args.validationOptions.context; - } + // ------------------------------------------------------------------------- + // Properties + // ------------------------------------------------------------------------- + + /** + * Validation type. + */ + type: string; + + /** + * Validator name. + */ + name?: string; + + /** + * Target class to which this validation is applied. + */ + target: Function | string; + + /** + * Property of the object to be validated. + */ + propertyName: string; + + /** + * Constraint class that performs validation. Used only for custom validations. + */ + constraintCls: Function; + + /** + * Array of constraints of this validation. + */ + constraints: any[]; + + /** + * Validation message to be shown in the case of error. + */ + message: string | ((args: ValidationArguments) => string); + + /** + * Validation groups used for this validation. + */ + groups: string[] = []; + + /** + * Indicates if validation must be performed always, no matter of validation groups used. + */ + always?: boolean; + + /** + * Specifies if validated value is an array and each of its item must be validated. + */ + each: boolean = false; + + /* + * A transient set of data passed through to the validation result for response mapping + */ + context?: any = undefined; + + /** + * Extra options specific to validation type. + */ + validationTypeOptions: any; + + // ------------------------------------------------------------------------- + // Constructor + // ------------------------------------------------------------------------- + + constructor(args: ValidationMetadataArgs) { + this.type = args.type; + this.name = args.name; + this.target = args.target; + this.propertyName = args.propertyName; + this.constraints = args?.constraints; + this.constraintCls = args.constraintCls; + this.validationTypeOptions = args.validationTypeOptions; + if (args.validationOptions) { + this.message = args.validationOptions.message; + this.groups = args.validationOptions.groups; + this.always = args.validationOptions.always; + this.each = args.validationOptions.each; + this.context = args.validationOptions.context; } - + } } diff --git a/src/metadata/ValidationMetadataArgs.ts b/src/metadata/ValidationMetadataArgs.ts index 8dcdb904ba..ff28b3e0af 100644 --- a/src/metadata/ValidationMetadataArgs.ts +++ b/src/metadata/ValidationMetadataArgs.ts @@ -1,42 +1,46 @@ -import {ValidationOptions} from "../decorator/ValidationOptions"; +import { ValidationOptions } from '../decorator/ValidationOptions'; /** * Constructor arguments for ValidationMetadata class. */ export interface ValidationMetadataArgs { - - /** - * Validation type. - */ - type: string; - - /** - * Object that is used to be validated. - */ - target: Function|string; - - /** - * Property of the object to be validated. - */ - propertyName: string; - - /** - * Constraint class that performs validation. Used only for custom validations. - */ - constraintCls?: Function; - - /** - * Array of constraints of this validation. - */ - constraints?: any[]; - - /** - * Validation options. - */ - validationOptions?: ValidationOptions; - - /** - * Extra options specific to validation type. - */ - validationTypeOptions?: any; -} \ No newline at end of file + /** + * Validation type. + */ + type: string; + + /** + * Validator name. + */ + name?: string; + + /** + * Object that is used to be validated. + */ + target: Function | string; + + /** + * Property of the object to be validated. + */ + propertyName: string; + + /** + * Constraint class that performs validation. Used only for custom validations. + */ + constraintCls?: Function; + + /** + * Array of constraints of this validation. + */ + constraints?: any[]; + + /** + * Validation options. + */ + validationOptions?: ValidationOptions; + + /** + * Extra options specific to validation type. + */ + validationTypeOptions?: any; +} diff --git a/src/register-decorator.ts b/src/register-decorator.ts index cdf6ca38ff..ffe4bf86a5 100644 --- a/src/register-decorator.ts +++ b/src/register-decorator.ts @@ -1,84 +1,87 @@ -import {ValidatorOptions} from "./validation/ValidatorOptions"; -import {MetadataStorage} from "./metadata/MetadataStorage"; -import {ConstraintMetadata} from "./metadata/ConstraintMetadata"; -import {ValidatorConstraintInterface} from "./validation/ValidatorConstraintInterface"; -import {ValidationMetadata} from "./metadata/ValidationMetadata"; -import {ValidationMetadataArgs} from "./metadata/ValidationMetadataArgs"; -import {ValidationTypes} from "./validation/ValidationTypes"; -import {ValidationArguments} from "./validation/ValidationArguments"; -import {getFromContainer} from "./container"; +import { ConstraintMetadata } from './metadata/ConstraintMetadata'; +import { ValidatorConstraintInterface } from './validation/ValidatorConstraintInterface'; +import { ValidationMetadata } from './metadata/ValidationMetadata'; +import { ValidationMetadataArgs } from './metadata/ValidationMetadataArgs'; +import { ValidationTypes } from './validation/ValidationTypes'; +import { ValidationArguments } from './validation/ValidationArguments'; +import { getFromContainer } from './container'; +import { MetadataStorage, getMetadataStorage } from './metadata/MetadataStorage'; +import { ValidationOptions } from './decorator/ValidationOptions'; export interface ValidationDecoratorOptions { + /** + * Target object to be validated. + */ + target: Function; - /** - * Target object to be validated. - */ - target: Function; + /** + * Target object's property name to be validated. + */ + propertyName: string; - /** - * Target object's property name to be validated. - */ - propertyName: string; + /** + * Name of the validation that is being registered. + */ + name?: string; - /** - * Name of the validation that is being registered. - */ - name?: string; + /** + * Indicates if this decorator will perform async validation. + */ + async?: boolean; - /** - * Indicates if this decorator will perform async validation. - */ - async?: boolean; + /** + * Validator options. + */ + options?: ValidationOptions; - /** - * Validator options. - */ - options?: ValidatorOptions; + /** + * Array of validation constraints. + */ + constraints?: any[]; - /** - * Array of validation constraints. - */ - constraints?: any[]; - - /** - * Validator that performs validation. - */ - validator: ValidatorConstraintInterface|Function; + /** + * Validator that performs validation. + */ + validator: ValidatorConstraintInterface | Function; } /** * Registers a custom validation decorator. */ export function registerDecorator(options: ValidationDecoratorOptions): void { - - let constraintCls: Function; - if (options.validator instanceof Function) { - constraintCls = options.validator as Function; - } else { - const validator = options.validator as ValidatorConstraintInterface; - constraintCls = class CustomConstraint implements ValidatorConstraintInterface { - validate(value: any, validationArguments?: ValidationArguments): Promise|boolean { - return validator.validate(value, validationArguments); - } - - defaultMessage(validationArguments?: ValidationArguments) { - if (validator.defaultMessage) { - return validator.defaultMessage(validationArguments); - } - - return ""; - } - }; - getFromContainer(MetadataStorage).addConstraintMetadata(new ConstraintMetadata(constraintCls, options.name, options.async)); + let constraintCls: Function; + if (options.validator instanceof Function) { + constraintCls = options.validator; + const constraintClasses = getFromContainer(MetadataStorage).getTargetValidatorConstraints(options.validator); + if (constraintClasses.length > 1) { + throw `More than one implementation of ValidatorConstraintInterface found for validator on: ${options.target.name}:${options.propertyName}`; } + } else { + const validator = options.validator; + constraintCls = class CustomConstraint implements ValidatorConstraintInterface { + validate(value: any, validationArguments?: ValidationArguments): Promise | boolean { + return validator.validate(value, validationArguments); + } + + defaultMessage(validationArguments?: ValidationArguments): string { + if (validator.defaultMessage) { + return validator.defaultMessage(validationArguments); + } - const validationMetadataArgs: ValidationMetadataArgs = { - type: ValidationTypes.CUSTOM_VALIDATION, - target: options.target, - propertyName: options.propertyName, - validationOptions: options.options, - constraintCls: constraintCls, - constraints: options.constraints + return ''; + } }; - getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(validationMetadataArgs)); + getMetadataStorage().addConstraintMetadata(new ConstraintMetadata(constraintCls, options.name, options.async)); + } + + const validationMetadataArgs: ValidationMetadataArgs = { + type: options.name && ValidationTypes.isValid(options.name) ? options.name : ValidationTypes.CUSTOM_VALIDATION, + name: options.name, + target: options.target, + propertyName: options.propertyName, + validationOptions: options.options, + constraintCls: constraintCls, + constraints: options.constraints, + }; + getMetadataStorage().addValidationMetadata(new ValidationMetadata(validationMetadataArgs)); } diff --git a/src/types.d.ts b/src/types.d.ts deleted file mode 100644 index 67607af4eb..0000000000 --- a/src/types.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -declare var window: any; - -declare module "ansicolor"; \ No newline at end of file diff --git a/src/utils/convert-to-array.util.ts b/src/utils/convert-to-array.util.ts new file mode 100644 index 0000000000..4f38179aaf --- /dev/null +++ b/src/utils/convert-to-array.util.ts @@ -0,0 +1,9 @@ +/** + * Convert Map, Set to Array + */ +export function convertToArray(val: Array | Set | Map): Array { + if (val instanceof Map) { + return Array.from(val.values()); + } + return Array.isArray(val) ? val : Array.from(val); +} diff --git a/src/utils/get-global.util.ts b/src/utils/get-global.util.ts new file mode 100644 index 0000000000..1fac64cbf0 --- /dev/null +++ b/src/utils/get-global.util.ts @@ -0,0 +1,31 @@ +/** + * This function returns the global object across Node and browsers. + * + * Note: `globalThis` is the standardized approach however it has been added to + * Node.js in version 12. We need to include this snippet until Node 12 EOL. + */ +export function getGlobal() { + if (typeof globalThis !== 'undefined') { + return globalThis; + } + + if (typeof global !== 'undefined') { + return global; + } + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore: Cannot find name 'window'. + if (typeof window !== 'undefined') { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore: Cannot find name 'window'. + return window; + } + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore: Cannot find name 'self'. + if (typeof self !== 'undefined') { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore: Cannot find name 'self'. + return self; + } +} diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 0000000000..0094adfff7 --- /dev/null +++ b/src/utils/index.ts @@ -0,0 +1,3 @@ +export * from './convert-to-array.util'; +export * from './get-global.util'; +export * from './is-promise.util'; diff --git a/src/utils/is-promise.util.ts b/src/utils/is-promise.util.ts new file mode 100644 index 0000000000..f4d00a4a35 --- /dev/null +++ b/src/utils/is-promise.util.ts @@ -0,0 +1,5 @@ +// https://github.com/TylorS/typed-is-promise/blob/abf1514e1b6961adfc75765476b0debb96b2c3ae/src/index.ts + +export function isPromise(p: any): p is Promise { + return p !== null && typeof p === 'object' && typeof p.then === 'function'; +} diff --git a/src/validation-schema/ValidationSchema.ts b/src/validation-schema/ValidationSchema.ts index 7b4a57fa95..f76fec6807 100644 --- a/src/validation-schema/ValidationSchema.ts +++ b/src/validation-schema/ValidationSchema.ts @@ -3,61 +3,63 @@ * Also using validation schemas makes this library to be easily used with es6/es5. */ export interface ValidationSchema { + /** + * Schema name. This is required, because we tell validator to validate by this schema using its name. + */ + name: string; + /** + * Validated properties. + */ + properties: { /** - * Schema name. This is required, because we tell validator to validate by this schema using its name. + * Name of the object's property to be validated which holds an array of validation constraints. */ - name: string; + [propertyName: string]: { + /** + * Validation type. Should be one of the ValidationTypes value. + */ + type: string; - /** - * Validated properties. - */ - properties: { - - /** - * Name of the object's property to be validated which holds an array of validation constraints. - */ - [propertyName: string]: { - - /** - * Validation type. Should be one of the ValidationTypes value. - */ - type: string; - - /** - * Constraints set by validation type. - */ - constraints?: any[]; - - /** - * Error message used to be used on validation fail. - * You can use "$value" to use value that was failed by validation. - * You can use "$constraint1" and "$constraint2" keys in the message string, - * and they will be replaced with constraint values if they exist. - * Message can be either string, either a function that returns a string. - * Second option allows to use values and custom messages depend of them. - */ - message?: string|((value?: any, constraint1?: any, constraint2?: any) => string); - - /** - * Specifies if validated value is an array and each of its item must be validated. - */ - each?: boolean; - - /** - * Indicates if validation must be performed always, no matter of validation groups used. - */ - always?: boolean; - - /** - * Validation groups used for this validation. - */ - groups?: string[]; - - /** - * Specific validation type options. - */ - options?: any; - }[]; - }; + /** + * Validator name. + */ + name?: string; + + /** + * Constraints set by validation type. + */ + constraints?: any[]; + + /** + * Error message used to be used on validation fail. + * You can use "$value" to use value that was failed by validation. + * You can use "$constraint1" and "$constraint2" keys in the message string, + * and they will be replaced with constraint values if they exist. + * Message can be either string, either a function that returns a string. + * Second option allows to use values and custom messages depend of them. + */ + message?: string | ((value?: any, constraint1?: any, constraint2?: any) => string); + + /** + * Specifies if validated value is an array and each of its item must be validated. + */ + each?: boolean; + + /** + * Indicates if validation must be performed always, no matter of validation groups used. + */ + always?: boolean; + + /** + * Validation groups used for this validation. + */ + groups?: string[]; + + /** + * Specific validation type options. + */ + options?: any; + }[]; + }; } diff --git a/src/validation-schema/ValidationSchemaToMetadataTransformer.ts b/src/validation-schema/ValidationSchemaToMetadataTransformer.ts index fcc9eed5ef..7739b6da92 100644 --- a/src/validation-schema/ValidationSchemaToMetadataTransformer.ts +++ b/src/validation-schema/ValidationSchemaToMetadataTransformer.ts @@ -1,39 +1,34 @@ -import {ValidationSchema} from "./ValidationSchema"; -import {ValidationMetadata} from "../metadata/ValidationMetadata"; -import {ValidationMetadataArgs} from "../metadata/ValidationMetadataArgs"; -import {ValidationOptions} from "../decorator/ValidationOptions"; -import {ValidationTypes} from "../validation/ValidationTypes"; +import { ValidationSchema } from './ValidationSchema'; +import { ValidationMetadata } from '../metadata/ValidationMetadata'; +import { ValidationMetadataArgs } from '../metadata/ValidationMetadataArgs'; +import { ValidationOptions } from '../decorator/ValidationOptions'; /** * Used to transform validation schemas to validation metadatas. */ export class ValidationSchemaToMetadataTransformer { - - transform(schema: ValidationSchema): ValidationMetadata[] { - const metadatas: ValidationMetadata[] = []; - Object.keys(schema.properties).forEach(property => { - schema.properties[property].forEach(validation => { - if (!ValidationTypes.isValid(validation.type)) - throw new Error(`Validation schema ${schema.name}#${property} as incorrect type ${validation.type}`); - - const validationOptions: ValidationOptions = { - message: validation.message, - groups: validation.groups, - always: validation.always, - each: validation.each - }; - const args: ValidationMetadataArgs = { - type: validation.type, - target: schema.name, - propertyName: property, - constraints: validation.constraints, - validationTypeOptions: validation.options, - validationOptions: validationOptions - }; - metadatas.push(new ValidationMetadata(args)); - }); - }); - return metadatas; - } - -} \ No newline at end of file + transform(schema: ValidationSchema): ValidationMetadata[] { + const metadatas: ValidationMetadata[] = []; + Object.keys(schema.properties).forEach(property => { + schema.properties[property].forEach(validation => { + const validationOptions: ValidationOptions = { + message: validation.message, + groups: validation.groups, + always: validation.always, + each: validation.each, + }; + const args: ValidationMetadataArgs = { + type: validation.type, + name: validation.name, + target: schema.name, + propertyName: property, + constraints: validation.constraints, + validationTypeOptions: validation.options, + validationOptions: validationOptions, + }; + metadatas.push(new ValidationMetadata(args)); + }); + }); + return metadatas; + } +} diff --git a/src/validation/ValidationArguments.ts b/src/validation/ValidationArguments.ts index 2df2ab7518..5e760187f2 100644 --- a/src/validation/ValidationArguments.ts +++ b/src/validation/ValidationArguments.ts @@ -3,30 +3,28 @@ * either by returning a function that accepts MessageArguments and returns a message string built based on these arguments. */ export interface ValidationArguments { + /** + * Validating value. + */ + value: any; - /** - * Validating value. - */ - value: any; + /** + * Constraints set by this validation type. + */ + constraints: any[]; - /** - * Constraints set by this validation type. - */ - constraints: any[]; + /** + * Name of the target that is being validated. + */ + targetName: string; - /** - * Name of the target that is being validated. - */ - targetName: string; + /** + * Object that is being validated. + */ + object: object; - /** - * Object that is being validated. - */ - object: Object; - - /** - * Name of the object's property being validated. - */ - property: string; - -} \ No newline at end of file + /** + * Name of the object's property being validated. + */ + property: string; +} diff --git a/src/validation/ValidationError.ts b/src/validation/ValidationError.ts index f895c0e8ae..daa3ff923d 100644 --- a/src/validation/ValidationError.ts +++ b/src/validation/ValidationError.ts @@ -2,74 +2,93 @@ * Validation error description. */ export class ValidationError { + /** + * Object that was validated. + * + * OPTIONAL - configurable via the ValidatorOptions.validationError.target option + */ + target?: object; - /** - * Object that was validated. - * - * OPTIONAL - configurable via the ValidatorOptions.validationError.target option - */ - target?: Object; + /** + * Object's property that haven't pass validation. + */ + property: string; - /** - * Object's property that haven't pass validation. - */ - property: string; + /** + * Value that haven't pass a validation. + * + * OPTIONAL - configurable via the ValidatorOptions.validationError.value option + */ + value?: any; - /** - * Value that haven't pass a validation. - * - * OPTIONAL - configurable via the ValidatorOptions.validationError.value option - */ - value?: any; + /** + * Constraints that failed validation with error messages. + */ + constraints?: { + [type: string]: string; + }; - /** - * Constraints that failed validation with error messages. - */ - constraints: { - [type: string]: string - }; + /** + * Contains all nested validation errors of the property. + */ + children?: ValidationError[]; - /** - * Contains all nested validation errors of the property. - */ - children: ValidationError[]; + /* + * A transient set of data passed through to the validation result for response mapping + */ + contexts?: { + [type: string]: any; + }; + /** + * + * @param shouldDecorate decorate the message with ANSI formatter escape codes for better readability + * @param hasParent true when the error is a child of an another one + * @param parentPath path as string to the parent of this property + * @param showConstraintMessages show constraint messages instead of constraint names + */ + toString( + shouldDecorate: boolean = false, + hasParent: boolean = false, + parentPath: string = ``, + showConstraintMessages: boolean = false + ): string { + const boldStart = shouldDecorate ? `\x1b[1m` : ``; + const boldEnd = shouldDecorate ? `\x1b[22m` : ``; + const constraintsToString = () => + (showConstraintMessages ? Object.values : Object.keys)(this.constraints ?? {}).join(`, `); + const propConstraintFailed = (propertyName: string): string => + ` - property ${boldStart}${parentPath}${propertyName}${boldEnd} has failed the following constraints: ${boldStart}${constraintsToString()}${boldEnd} \n`; - /* - * A transient set of data passed through to the validation result for response mapping - */ - contexts?: { - [type: string]: any - }; + if (!hasParent) { + return ( + `An instance of ${boldStart}${ + this.target ? this.target.constructor.name : 'an object' + }${boldEnd} has failed the validation:\n` + + (this.constraints ? propConstraintFailed(this.property) : ``) + + (this.children + ? this.children + .map(childError => childError.toString(shouldDecorate, true, this.property, showConstraintMessages)) + .join(``) + : ``) + ); + } else { + // we format numbers as array indexes for better readability. + const formattedProperty = Number.isInteger(+this.property) + ? `[${this.property}]` + : `${parentPath ? `.` : ``}${this.property}`; - /** - * - * @param shouldDecorate decorate the message with ANSI formatter escape codes for better readability - * @param hasParent true when the error is a child of an another one - * @param parentPath path as string to the parent of this property - */ - toString(shouldDecorate: boolean = false, hasParent: boolean = false, parentPath: string = ``): string { - const boldStart = shouldDecorate ? `\x1b[1m` : ``; - const boldEnd = shouldDecorate ? `\x1b[22m` : ``; - const propConstraintFailed = (propertyName: string): string => ` - property ${boldStart}${parentPath}${propertyName}${boldEnd} has failed the following constraints: ${boldStart}${Object.keys(this.constraints).join(`, `)}${boldEnd} \n`; - - if (!hasParent) { - return `An instance of ${boldStart}${this.target ? this.target.constructor.name : "an object"}${boldEnd} has failed the validation:\n` + - (this.constraints ? propConstraintFailed(this.property) : ``) + - this.children - .map(childError => childError.toString(shouldDecorate, true, this.property)) - .join(``); - } else { - // we format numbers as array indexes for better readability. - const formattedProperty = Number.isInteger(+this.property) ? `[${this.property}]` : `${parentPath ? `.` : ``}${this.property}`; - - if (this.constraints) { - return propConstraintFailed(formattedProperty); - } else { - return this.children - .map(childError => childError.toString(shouldDecorate, true, `${parentPath}${formattedProperty}`, )) - .join(``); - } - } + if (this.constraints) { + return propConstraintFailed(formattedProperty); + } else { + return this.children + ? this.children + .map(childError => + childError.toString(shouldDecorate, true, `${parentPath}${formattedProperty}`, showConstraintMessages) + ) + .join(``) + : ``; + } } + } } diff --git a/src/validation/ValidationExecutor.ts b/src/validation/ValidationExecutor.ts index 4c4248acfc..9d3d312f14 100644 --- a/src/validation/ValidationExecutor.ts +++ b/src/validation/ValidationExecutor.ts @@ -1,346 +1,429 @@ -import {Validator} from "./Validator"; -import {ValidationError} from "./ValidationError"; -import {ValidationMetadata} from "../metadata/ValidationMetadata"; -import {MetadataStorage} from "../metadata/MetadataStorage"; -import {getFromContainer} from "../container"; -import {ValidatorOptions} from "./ValidatorOptions"; -import {ValidationTypes} from "./ValidationTypes"; -import {ConstraintMetadata} from "../metadata/ConstraintMetadata"; -import {ValidationArguments} from "./ValidationArguments"; -import {ValidationUtils} from "./ValidationUtils"; +import { Validator } from './Validator'; +import { ValidationError } from './ValidationError'; +import { ValidationMetadata } from '../metadata/ValidationMetadata'; +import { ValidatorOptions } from './ValidatorOptions'; +import { ValidationTypes } from './ValidationTypes'; +import { ConstraintMetadata } from '../metadata/ConstraintMetadata'; +import { ValidationArguments } from './ValidationArguments'; +import { ValidationUtils } from './ValidationUtils'; +import { isPromise, convertToArray } from '../utils'; +import { getMetadataStorage } from '../metadata/MetadataStorage'; /** * Executes validation over given object. */ export class ValidationExecutor { - - // ------------------------------------------------------------------------- - // Properties - // ------------------------------------------------------------------------- - - awaitingPromises: Promise[] = []; - ignoreAsyncValidations: boolean = false; - - // ------------------------------------------------------------------------- - // Private Properties - // ------------------------------------------------------------------------- - - private metadataStorage = getFromContainer(MetadataStorage); - - // ------------------------------------------------------------------------- - // Constructor - // ------------------------------------------------------------------------- - - constructor(private validator: Validator, - private validatorOptions?: ValidatorOptions) { + // ------------------------------------------------------------------------- + // Properties + // ------------------------------------------------------------------------- + + awaitingPromises: Promise[] = []; + ignoreAsyncValidations: boolean = false; + + // ------------------------------------------------------------------------- + // Private Properties + // ------------------------------------------------------------------------- + + private metadataStorage = getMetadataStorage(); + + // ------------------------------------------------------------------------- + // Constructor + // ------------------------------------------------------------------------- + + constructor(private validator: Validator, private validatorOptions?: ValidatorOptions) {} + + // ------------------------------------------------------------------------- + // Public Methods + // ------------------------------------------------------------------------- + + execute(object: object, targetSchema: string, validationErrors: ValidationError[]): void { + /** + * If there is no metadata registered it means possibly the dependencies are not flatterned and + * more than one instance is used. + * + * TODO: This needs proper handling, forcing to use the same container or some other proper solution. + */ + if (!this.metadataStorage.hasValidationMetaData && this.validatorOptions?.enableDebugMessages === true) { + console.warn( + `No validation metadata found. No validation will be performed. There are multiple possible reasons:\n` + + ` - There may be multiple class-validator versions installed. You will need to flatten your dependencies to fix the issue.\n` + + ` - This validation runs before any file with validation decorator was parsed by NodeJS.` + ); } - // ------------------------------------------------------------------------- - // Public Methods - // ------------------------------------------------------------------------- - - execute(object: Object, targetSchema: string, validationErrors: ValidationError[]) { - /** - * If there is no metadata registered it means possibly the dependencies are not flatterned and - * more than one instance is used. - * - * TODO: This needs proper handling, forcing to use the same container or some other proper solution. - */ - if (!this.metadataStorage.hasValidationMetaData) { - console.warn(`No metadata found. There is more than once class-validator version installed probably. You need to flatten your dependencies.`); - } - - const groups = this.validatorOptions ? this.validatorOptions.groups : undefined; - const targetMetadatas = this.metadataStorage.getTargetValidationMetadatas(object.constructor, targetSchema, groups); - const groupedMetadatas = this.metadataStorage.groupByPropertyName(targetMetadatas); - - if (this.validatorOptions && this.validatorOptions.forbidUnknownValues && !targetMetadatas.length) { - const validationError = new ValidationError(); - - if (!this.validatorOptions || - !this.validatorOptions.validationError || - this.validatorOptions.validationError.target === undefined || - this.validatorOptions.validationError.target === true) - validationError.target = object; - - validationError.value = undefined; - validationError.property = undefined; - validationError.children = []; - validationError.constraints = { unknownValue: "an unknown value was passed to the validate function" }; - - validationErrors.push(validationError); - - return; - } - - if (this.validatorOptions && this.validatorOptions.whitelist) - this.whitelist(object, groupedMetadatas, validationErrors); - - // General validation - Object.keys(groupedMetadatas).forEach(propertyName => { - const value = (object as any)[propertyName]; - const definedMetadatas = groupedMetadatas[propertyName].filter(metadata => metadata.type === ValidationTypes.IS_DEFINED); - const metadatas = groupedMetadatas[propertyName].filter( - metadata => metadata.type !== ValidationTypes.IS_DEFINED && metadata.type !== ValidationTypes.WHITELIST); - const customValidationMetadatas = metadatas.filter(metadata => metadata.type === ValidationTypes.CUSTOM_VALIDATION); - const nestedValidationMetadatas = metadatas.filter(metadata => metadata.type === ValidationTypes.NESTED_VALIDATION); - const conditionalValidationMetadatas = metadatas.filter(metadata => metadata.type === ValidationTypes.CONDITIONAL_VALIDATION); - - const validationError = this.generateValidationError(object, value, propertyName); - validationErrors.push(validationError); - - const canValidate = this.conditionalValidations(object, value, conditionalValidationMetadatas); - if (!canValidate) { - return; - } - - // handle IS_DEFINED validation type the special way - it should work no matter skipMissingProperties is set or not - this.defaultValidations(object, value, definedMetadatas, validationError.constraints); - - if ((value === null || value === undefined) && this.validatorOptions && this.validatorOptions.skipMissingProperties === true) { - return; - } - - this.defaultValidations(object, value, metadatas, validationError.constraints); - this.customValidations(object, value, customValidationMetadatas, validationError.constraints); - this.nestedValidations(value, nestedValidationMetadatas, validationError.children); - - this.mapContexts(object, value, metadatas, validationError); - }); + const groups = this.validatorOptions ? this.validatorOptions.groups : undefined; + const strictGroups = (this.validatorOptions && this.validatorOptions.strictGroups) || false; + const always = (this.validatorOptions && this.validatorOptions.always) || false; + /** Forbid unknown values are turned on by default and any other value than false will enable it. */ + const forbidUnknownValues = + this.validatorOptions?.forbidUnknownValues === undefined || this.validatorOptions.forbidUnknownValues !== false; + + const targetMetadatas = this.metadataStorage.getTargetValidationMetadatas( + object.constructor, + targetSchema, + always, + strictGroups, + groups + ); + const groupedMetadatas = this.metadataStorage.groupByPropertyName(targetMetadatas); + + if (this.validatorOptions && forbidUnknownValues && !targetMetadatas.length) { + const validationError = new ValidationError(); + + if ( + !this.validatorOptions || + !this.validatorOptions.validationError || + this.validatorOptions.validationError.target === undefined || + this.validatorOptions.validationError.target === true + ) + validationError.target = object; + + validationError.value = undefined; + validationError.property = undefined; + validationError.children = []; + validationError.constraints = { unknownValue: 'an unknown value was passed to the validate function' }; + + validationErrors.push(validationError); + + return; } - whitelist(object: any, - groupedMetadatas: { [propertyName: string]: ValidationMetadata[] }, - validationErrors: ValidationError[]) { - let notAllowedProperties: string[] = []; - - Object.keys(object).forEach(propertyName => { - // does this property have no metadata? - if (!groupedMetadatas[propertyName] || groupedMetadatas[propertyName].length === 0) - notAllowedProperties.push(propertyName); + if (this.validatorOptions && this.validatorOptions.whitelist) + this.whitelist(object, groupedMetadatas, validationErrors); + + // General validation + Object.keys(groupedMetadatas).forEach(propertyName => { + const value = (object as any)[propertyName]; + const definedMetadatas = groupedMetadatas[propertyName].filter( + metadata => metadata.type === ValidationTypes.IS_DEFINED + ); + const metadatas = groupedMetadatas[propertyName].filter( + metadata => metadata.type !== ValidationTypes.IS_DEFINED && metadata.type !== ValidationTypes.WHITELIST + ); + + if ( + value instanceof Promise && + metadatas.find(metadata => metadata.type === ValidationTypes.PROMISE_VALIDATION) + ) { + this.awaitingPromises.push( + value.then(resolvedValue => { + this.performValidations(object, resolvedValue, propertyName, definedMetadatas, metadatas, validationErrors); + }) + ); + } else { + this.performValidations(object, value, propertyName, definedMetadatas, metadatas, validationErrors); + } + }); + } + + whitelist( + object: any, + groupedMetadatas: { [propertyName: string]: ValidationMetadata[] }, + validationErrors: ValidationError[] + ): void { + const notAllowedProperties: string[] = []; + + Object.keys(object).forEach(propertyName => { + // does this property have no metadata? + if (!groupedMetadatas[propertyName] || groupedMetadatas[propertyName].length === 0) + notAllowedProperties.push(propertyName); + }); + + if (notAllowedProperties.length > 0) { + if (this.validatorOptions && this.validatorOptions.forbidNonWhitelisted) { + // throw errors + notAllowedProperties.forEach(property => { + const validationError: ValidationError = this.generateValidationError(object, object[property], property); + validationError.constraints = { [ValidationTypes.WHITELIST]: `property ${property} should not exist` }; + validationError.children = undefined; + validationErrors.push(validationError); }); - - if (notAllowedProperties.length > 0) { - - if (this.validatorOptions && this.validatorOptions.forbidNonWhitelisted) { - - // throw errors - notAllowedProperties.forEach(property => { - validationErrors.push({ - target: object, property, value: (object as any)[property], children: undefined, - constraints: { [ValidationTypes.WHITELIST]: `property ${property} should not exist` } - }); - }); - - } else { - - // strip non allowed properties - notAllowedProperties.forEach(property => delete (object as any)[property]); - - } + } else { + // strip non allowed properties + notAllowedProperties.forEach(property => delete object[property]); + } + } + } + + stripEmptyErrors(errors: ValidationError[]): ValidationError[] { + return errors.filter(error => { + if (error.children) { + error.children = this.stripEmptyErrors(error.children); + } + + if (Object.keys(error.constraints).length === 0) { + if (error.children.length === 0) { + return false; + } else { + delete error.constraints; } + } + + return true; + }); + } + + // ------------------------------------------------------------------------- + // Private Methods + // ------------------------------------------------------------------------- + + private performValidations( + object: any, + value: any, + propertyName: string, + definedMetadatas: ValidationMetadata[], + metadatas: ValidationMetadata[], + validationErrors: ValidationError[] + ): void { + const customValidationMetadatas = metadatas.filter(metadata => metadata.type === ValidationTypes.CUSTOM_VALIDATION); + const nestedValidationMetadatas = metadatas.filter(metadata => metadata.type === ValidationTypes.NESTED_VALIDATION); + const conditionalValidationMetadatas = metadatas.filter( + metadata => metadata.type === ValidationTypes.CONDITIONAL_VALIDATION + ); + + const validationError = this.generateValidationError(object, value, propertyName); + validationErrors.push(validationError); + + const canValidate = this.conditionalValidations(object, value, conditionalValidationMetadatas); + if (!canValidate) { + return; } - stripEmptyErrors(errors: ValidationError[]) { - return errors.filter(error => { - if (error.children) { - error.children = this.stripEmptyErrors(error.children); - } - - if (Object.keys(error.constraints).length === 0) { - if (error.children.length === 0) { - return false; - } else { - delete error.constraints; - } - } + // handle IS_DEFINED validation type the special way - it should work no matter skipUndefinedProperties/skipMissingProperties is set or not + this.customValidations(object, value, definedMetadatas, validationError); + this.mapContexts(object, value, definedMetadatas, validationError); - return true; - }); + if (value === undefined && this.validatorOptions && this.validatorOptions.skipUndefinedProperties === true) { + return; } - // ------------------------------------------------------------------------- - // Private Methods - // ------------------------------------------------------------------------- - - private generateValidationError(object: Object, value: any, propertyName: string) { - const validationError = new ValidationError(); - - if (!this.validatorOptions || - !this.validatorOptions.validationError || - this.validatorOptions.validationError.target === undefined || - this.validatorOptions.validationError.target === true) - validationError.target = object; - - if (!this.validatorOptions || - !this.validatorOptions.validationError || - this.validatorOptions.validationError.value === undefined || - this.validatorOptions.validationError.value === true) - validationError.value = value; - - validationError.property = propertyName; - validationError.children = []; - validationError.constraints = {}; - - return validationError; + if (value === null && this.validatorOptions && this.validatorOptions.skipNullProperties === true) { + return; } - private conditionalValidations(object: Object, - value: any, - metadatas: ValidationMetadata[]) { - return metadatas - .map(metadata => metadata.constraints[0](object, value)) - .reduce((resultA, resultB) => resultA && resultB, true); + if ( + (value === null || value === undefined) && + this.validatorOptions && + this.validatorOptions.skipMissingProperties === true + ) { + return; } - private defaultValidations(object: Object, - value: any, - metadatas: ValidationMetadata[], - errorMap: { [key: string]: string }) { - return metadatas - .filter(metadata => { - if (metadata.each) { - if (value instanceof Array) { - return !value.every((subValue: any) => this.validator.validateValueByMetadata(subValue, metadata)); - } - - } else { - return !this.validator.validateValueByMetadata(value, metadata); - } - }) - .forEach(metadata => { - const [key, message] = this.createValidationError(object, value, metadata); - errorMap[key] = message; - }); - } + this.customValidations(object, value, customValidationMetadatas, validationError); + this.nestedValidations(value, nestedValidationMetadatas, validationError); + + this.mapContexts(object, value, metadatas, validationError); + this.mapContexts(object, value, customValidationMetadatas, validationError); + } + + private generateValidationError(object: object, value: any, propertyName: string): ValidationError { + const validationError = new ValidationError(); + + if ( + !this.validatorOptions || + !this.validatorOptions.validationError || + this.validatorOptions.validationError.target === undefined || + this.validatorOptions.validationError.target === true + ) + validationError.target = object; + + if ( + !this.validatorOptions || + !this.validatorOptions.validationError || + this.validatorOptions.validationError.value === undefined || + this.validatorOptions.validationError.value === true + ) + validationError.value = value; + + validationError.property = propertyName; + validationError.children = []; + validationError.constraints = {}; + + return validationError; + } + + private conditionalValidations(object: object, value: any, metadatas: ValidationMetadata[]): ValidationMetadata[] { + return metadatas + .map(metadata => metadata.constraints[0](object, value)) + .reduce((resultA, resultB) => resultA && resultB, true); + } + + private customValidations(object: object, value: any, metadatas: ValidationMetadata[], error: ValidationError): void { + metadatas.forEach(metadata => { + this.metadataStorage.getTargetValidatorConstraints(metadata.constraintCls).forEach(customConstraintMetadata => { + if (customConstraintMetadata.async && this.ignoreAsyncValidations) return; + if ( + this.validatorOptions && + this.validatorOptions.stopAtFirstError && + Object.keys(error.constraints || {}).length > 0 + ) + return; - private customValidations(object: Object, - value: any, - metadatas: ValidationMetadata[], - errorMap: { [key: string]: string }) { - - metadatas.forEach(metadata => { - getFromContainer(MetadataStorage) - .getTargetValidatorConstraints(metadata.constraintCls) - .forEach(customConstraintMetadata => { - if (customConstraintMetadata.async && this.ignoreAsyncValidations) - return; - - const validationArguments: ValidationArguments = { - targetName: object.constructor ? (object.constructor as any).name : undefined, - property: metadata.propertyName, - object: object, - value: value, - constraints: metadata.constraints - }; - const validatedValue = customConstraintMetadata.instance.validate(value, validationArguments); - if (validatedValue instanceof Promise) { - const promise = validatedValue.then(isValid => { - if (!isValid) { - const [type, message] = this.createValidationError(object, value, metadata, customConstraintMetadata); - errorMap[type] = message; - } - }); - this.awaitingPromises.push(promise); - } else { - if (!validatedValue) { - const [type, message] = this.createValidationError(object, value, metadata, customConstraintMetadata); - errorMap[type] = message; - } - } - }); - }); - } + const validationArguments: ValidationArguments = { + targetName: object.constructor ? (object.constructor as any).name : undefined, + property: metadata.propertyName, + object: object, + value: value, + constraints: metadata.constraints, + }; - private nestedValidations(value: any, metadatas: ValidationMetadata[], errors: ValidationError[]) { + if (!metadata.each || !(Array.isArray(value) || value instanceof Set || value instanceof Map)) { + const validatedValue = customConstraintMetadata.instance.validate(value, validationArguments); + if (isPromise(validatedValue)) { + const promise = validatedValue.then(isValid => { + if (!isValid) { + const [type, message] = this.createValidationError(object, value, metadata, customConstraintMetadata); + error.constraints[type] = message; + if (metadata.context) { + if (!error.contexts) { + error.contexts = {}; + } + error.contexts[type] = Object.assign(error.contexts[type] || {}, metadata.context); + } + } + }); + this.awaitingPromises.push(promise); + } else { + if (!validatedValue) { + const [type, message] = this.createValidationError(object, value, metadata, customConstraintMetadata); + error.constraints[type] = message; + } + } - if (value === void 0) { - return; + return; } - metadatas.forEach(metadata => { - if (metadata.type !== ValidationTypes.NESTED_VALIDATION) return; - const targetSchema = typeof metadata.target === "string" ? metadata.target as string : undefined; - - if (value instanceof Array) { - value.forEach((subValue: any, index: number) => { - const validationError = this.generateValidationError(value, subValue, index.toString()); - errors.push(validationError); - - this.execute(subValue, targetSchema, validationError.children); - }); - - } else if (value instanceof Object) { - this.execute(value, targetSchema, errors); - - } else { - const error = new ValidationError(); - error.value = value; - error.property = metadata.propertyName; - error.target = metadata.target; - const [type, message] = this.createValidationError(metadata.target, value, metadata); - error.constraints = { - [type]: message - }; - errors.push(error); + // convert set and map into array + const arrayValue = convertToArray(value); + // Validation needs to be applied to each array item + const validatedSubValues = arrayValue.map((subValue: any) => + customConstraintMetadata.instance.validate(subValue, validationArguments) + ); + const validationIsAsync = validatedSubValues.some((validatedSubValue: boolean | Promise) => + isPromise(validatedSubValue) + ); + + if (validationIsAsync) { + // Wrap plain values (if any) in promises, so that all are async + const asyncValidatedSubValues = validatedSubValues.map((validatedSubValue: boolean | Promise) => + isPromise(validatedSubValue) ? validatedSubValue : Promise.resolve(validatedSubValue) + ); + const asyncValidationIsFinishedPromise = Promise.all(asyncValidatedSubValues).then( + (flatValidatedValues: boolean[]) => { + const validationResult = flatValidatedValues.every((isValid: boolean) => isValid); + if (!validationResult) { + const [type, message] = this.createValidationError(object, value, metadata, customConstraintMetadata); + error.constraints[type] = message; + if (metadata.context) { + if (!error.contexts) { + error.contexts = {}; + } + error.contexts[type] = Object.assign(error.contexts[type] || {}, metadata.context); + } + } } - }); - } + ); - private mapContexts(object: Object, - value: any, - metadatas: ValidationMetadata[], - error: ValidationError) { + this.awaitingPromises.push(asyncValidationIsFinishedPromise); - return metadatas - .forEach(metadata => { - if (metadata.context) { - const type = this.getConstraintType(metadata); + return; + } - if (error.constraints[type]) { - if (!error.contexts) { - error.contexts = {}; - } + const validationResult = validatedSubValues.every((isValid: boolean) => isValid); + if (!validationResult) { + const [type, message] = this.createValidationError(object, value, metadata, customConstraintMetadata); + error.constraints[type] = message; + } + }); + }); + } - error.contexts[type] = Object.assign((error.contexts[type] || {}), metadata.context); - } - } - }); + private nestedValidations(value: any, metadatas: ValidationMetadata[], error: ValidationError): void { + if (value === void 0) { + return; } - private createValidationError(object: Object, - value: any, - metadata: ValidationMetadata, - customValidatorMetadata?: ConstraintMetadata): [string, string] { + metadatas.forEach(metadata => { + if (metadata.type !== ValidationTypes.NESTED_VALIDATION && metadata.type !== ValidationTypes.PROMISE_VALIDATION) { + return; + } else if ( + this.validatorOptions && + this.validatorOptions.stopAtFirstError && + Object.keys(error.constraints || {}).length > 0 + ) { + return; + } + + if (Array.isArray(value) || value instanceof Set || value instanceof Map) { + // Treats Set as an array - as index of Set value is value itself and it is common case to have Object as value + const arrayLikeValue = value instanceof Set ? Array.from(value) : value; + arrayLikeValue.forEach((subValue: any, index: any) => { + this.performValidations(value, subValue, index.toString(), [], metadatas, error.children); + }); + } else if (value instanceof Object) { + const targetSchema = typeof metadata.target === 'string' ? metadata.target : metadata.target.name; + this.execute(value, targetSchema, error.children); + } else { + const [type, message] = this.createValidationError(metadata.target as object, value, metadata); + error.constraints[type] = message; + } + }); + } + + private mapContexts(object: object, value: any, metadatas: ValidationMetadata[], error: ValidationError): void { + return metadatas.forEach(metadata => { + if (metadata.context) { + let customConstraint; + if (metadata.type === ValidationTypes.CUSTOM_VALIDATION) { + const customConstraints = this.metadataStorage.getTargetValidatorConstraints(metadata.constraintCls); + customConstraint = customConstraints[0]; + } - const targetName = object.constructor ? (object.constructor as any).name : undefined; - const type = this.getConstraintType(metadata, customValidatorMetadata); - const validationArguments: ValidationArguments = { - targetName: targetName, - property: metadata.propertyName, - object: object, - value: value, - constraints: metadata.constraints - }; + const type = this.getConstraintType(metadata, customConstraint); - let message = metadata.message; - if (!metadata.message && - (!this.validatorOptions || (this.validatorOptions && !this.validatorOptions.dismissDefaultMessages))) { - if (customValidatorMetadata && customValidatorMetadata.instance.defaultMessage instanceof Function) { - message = customValidatorMetadata.instance.defaultMessage(validationArguments); - } + if (error.constraints[type]) { + if (!error.contexts) { + error.contexts = {}; + } - if (!message) - message = ValidationTypes.getMessage(type, metadata.each); + error.contexts[type] = Object.assign(error.contexts[type] || {}, metadata.context); } - - const messageString = ValidationUtils.replaceMessageSpecialTokens(message, validationArguments); - return [type, messageString]; + } + }); + } + + private createValidationError( + object: object, + value: any, + metadata: ValidationMetadata, + customValidatorMetadata?: ConstraintMetadata + ): [string, string] { + const targetName = object.constructor ? (object.constructor as any).name : undefined; + const type = this.getConstraintType(metadata, customValidatorMetadata); + const validationArguments: ValidationArguments = { + targetName: targetName, + property: metadata.propertyName, + object: object, + value: value, + constraints: metadata.constraints, + }; + + let message = metadata.message || ''; + if ( + !metadata.message && + (!this.validatorOptions || (this.validatorOptions && !this.validatorOptions.dismissDefaultMessages)) + ) { + if (customValidatorMetadata && customValidatorMetadata.instance.defaultMessage instanceof Function) { + message = customValidatorMetadata.instance.defaultMessage(validationArguments); + } } - private getConstraintType(metadata: ValidationMetadata, customValidatorMetadata?: ConstraintMetadata): string { - const type = customValidatorMetadata && customValidatorMetadata.name ? customValidatorMetadata.name : metadata.type; - return type; - } + const messageString = ValidationUtils.replaceMessageSpecialTokens(message, validationArguments); + return [type, messageString]; + } + private getConstraintType(metadata: ValidationMetadata, customValidatorMetadata?: ConstraintMetadata): string { + const type = customValidatorMetadata && customValidatorMetadata.name ? customValidatorMetadata.name : metadata.type; + return type; + } } diff --git a/src/validation/ValidationTypeOptions.ts b/src/validation/ValidationTypeOptions.ts deleted file mode 100644 index 44a3678d70..0000000000 --- a/src/validation/ValidationTypeOptions.ts +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Options to be passed to IsURL decorator. - */ -export interface IsNumberOptions { - allowNaN?: boolean; - allowInfinity?: boolean; -} - -/** - * Options to be passed to IsCurrency decorator. - */ -export interface IsCurrencyOptions { - symbol?: string; - require_symbol?: boolean; - allow_space_after_symbol?: boolean; - symbol_after_digits?: boolean; - allow_negatives?: boolean; - parens_for_negatives?: boolean; - negative_sign_before_digits?: boolean; - negative_sign_after_digits?: boolean; - allow_negative_sign_placeholder?: boolean; - thousands_separator?: string; - decimal_separator?: string; - allow_space_after_digits?: boolean; -} - -/** - * Options to be passed to IsURL decorator. - */ -export interface IsURLOptions { - protocols?: string[]; - require_tld?: boolean; - require_protocol?: boolean; - require_valid_protocol?: boolean; - allow_underscores?: boolean; - host_whitelist?: false | (string | RegExp)[]; - host_blacklist?: false | (string | RegExp)[]; - allow_trailing_dot?: boolean; - allow_protocol_relative_urls?: boolean; -} - -/** - * Options to be passed to isEmail decorator. - */ -export interface IsEmailOptions { - allow_display_name?: boolean; - allow_utf8_local_part?: boolean; - require_tld?: boolean; -} - -/** - * Options to be passed to IsFQDN decorator. - */ -export interface IsFQDNOptions { - require_tld?: boolean; - allow_underscores?: boolean; - allow_trailing_dot?: boolean; -} diff --git a/src/validation/ValidationTypes.ts b/src/validation/ValidationTypes.ts index eb128cdfe9..640e7f3ffe 100644 --- a/src/validation/ValidationTypes.ts +++ b/src/validation/ValidationTypes.ts @@ -1,279 +1,25 @@ -import {ValidationArguments} from "./ValidationArguments"; - /** * Validation types. */ export class ValidationTypes { - - /* system */ - static CUSTOM_VALIDATION = "customValidation"; - static NESTED_VALIDATION = "nestedValidation"; - static CONDITIONAL_VALIDATION = "conditionalValidation"; - static WHITELIST = "whitelistValidation"; - - /* common checkers */ - static IS_DEFINED = "isDefined"; - static EQUALS = "equals"; - static NOT_EQUALS = "notEquals"; - static IS_EMPTY = "isEmpty"; - static IS_NOT_EMPTY = "isNotEmpty"; - static IS_IN = "isIn"; - static IS_NOT_IN = "isNotIn"; - - /* type checkers */ - static IS_BOOLEAN = "isBoolean"; - static IS_DATE = "isDate"; - static IS_NUMBER = "isNumber"; - static IS_STRING = "isString"; - static IS_DATE_STRING = "isDateString"; - static IS_ARRAY = "isArray"; - static IS_INT = "isInt"; - static IS_ENUM = "isEnum"; - - /* number checkers */ - static IS_DIVISIBLE_BY = "isDivisibleBy"; - static IS_POSITIVE = "isPositive"; - static IS_NEGATIVE = "isNegative"; - static MIN = "min"; - static MAX = "max"; - - /* date checkers */ - static MIN_DATE = "minDate"; - static MAX_DATE = "maxDate"; - - /* string-as-type checkers */ - static IS_BOOLEAN_STRING = "isBooleanString"; - static IS_NUMBER_STRING = "isNumberString"; - - /* string checkers */ - static CONTAINS = "contains"; - static NOT_CONTAINS = "notContains"; - static IS_ALPHA = "isAlpha"; - static IS_ALPHANUMERIC = "isAlphanumeric"; - static IS_ASCII = "isAscii"; - static IS_BASE64 = "isBase64"; - static IS_BYTE_LENGTH = "isByteLength"; - static IS_CREDIT_CARD = "isCreditCard"; - static IS_CURRENCY = "isCurrency"; - static IS_EMAIL = "isEmail"; - static IS_FQDN = "isFqdn"; - static IS_FULL_WIDTH = "isFullWidth"; - static IS_HALF_WIDTH = "isHalfWidth"; - static IS_VARIABLE_WIDTH = "isVariableWidth"; - static IS_HEX_COLOR = "isHexColor"; - static IS_HEXADECIMAL = "isHexadecimal"; - static IS_IP = "isIp"; - static IS_ISBN = "isIsbn"; - static IS_ISIN = "isIsin"; - static IS_ISO8601 = "isIso8601"; - static IS_JSON = "isJson"; - static IS_LOWERCASE = "isLowercase"; - static IS_MOBILE_PHONE = "isMobilePhone"; - static IS_PHONE_NUMBER = "isPhoneNumber"; - static IS_MONGO_ID = "isMongoId"; - static IS_MULTIBYTE = "isMultibyte"; - static IS_SURROGATE_PAIR = "isSurrogatePair"; - static IS_URL = "isUrl"; - static IS_UUID = "isUuid"; - static LENGTH = "length"; - static IS_UPPERCASE = "isUppercase"; - static MIN_LENGTH = "minLength"; - static MAX_LENGTH = "maxLength"; - static MATCHES = "matches"; - static IS_MILITARY_TIME = "isMilitaryTime"; - - /* array checkers */ - static ARRAY_CONTAINS = "arrayContains"; - static ARRAY_NOT_CONTAINS = "arrayNotContains"; - static ARRAY_NOT_EMPTY = "arrayNotEmpty"; - static ARRAY_MIN_SIZE = "arrayMinSize"; - static ARRAY_MAX_SIZE = "arrayMaxSize"; - static ARRAY_UNIQUE = "arrayUnique"; - - /* object chekers */ - static IS_INSTANCE = "isInstance"; - - /** - * Checks if validation type is valid. - */ - static isValid(type: string) { - return type !== "isValid" && - type !== "getMessage" && - Object.keys(this).map(key => (this as any)[key]).indexOf(type) !== -1; - } - - /** - * Gets default validation error message for the given validation type. - */ - static getMessage(type: string, isEach: boolean): string|((args: ValidationArguments) => string) { - const eachPrefix = isEach ? "each value in " : ""; - switch (type) { - - /* system chceck */ - case this.NESTED_VALIDATION: - return eachPrefix + "nested property $property must be either object or array"; - /* common checkers */ - case this.IS_DEFINED: - return eachPrefix + "$property should not be null or undefined"; - case this.EQUALS: - return eachPrefix + "$property must be equal to $constraint1"; - case this.NOT_EQUALS: - return eachPrefix + "$property should not be equal to $constraint1"; - case this.IS_EMPTY: - return eachPrefix + "$property must be empty"; - case this.IS_NOT_EMPTY: - return eachPrefix + "$property should not be empty"; - case this.IS_IN: - return eachPrefix + "$property must be one of the following values: $constraint1"; - case this.IS_NOT_IN: - return eachPrefix + "$property should not be one of the following values: $constraint1"; - - /* type checkers */ - case this.IS_BOOLEAN: - return eachPrefix + "$property must be a boolean value"; - case this.IS_DATE: - return eachPrefix + "$property must be a Date instance"; - case this.IS_NUMBER: - return eachPrefix + "$property must be a number"; - case this.IS_INT: - return eachPrefix + "$property must be an integer number"; - case this.IS_STRING: - return eachPrefix + "$property must be a string"; - case this.IS_DATE_STRING: - return eachPrefix + "$property must be a ISOString"; - case this.IS_ARRAY: - return eachPrefix + "$property must be an array"; - case this.IS_ENUM: - return eachPrefix + "$property must be a valid enum value"; - - /* number checkers */ - case this.IS_DIVISIBLE_BY: - return eachPrefix + "$property must be divisible by $constraint1"; - case this.IS_POSITIVE: - return eachPrefix + "$property must be a positive number"; - case this.IS_NEGATIVE: - return eachPrefix + "$property must be a negative number"; - case this.MIN: - return eachPrefix + "$property must not be less than $constraint1"; - case this.MAX: - return eachPrefix + "$property must not be greater than $constraint1"; - - /* date checkers */ - case this.MIN_DATE: - return "minimal allowed date for " + eachPrefix + "$property is $constraint1"; - case this.MAX_DATE: - return "maximal allowed date for " + eachPrefix + "$property is $constraint1"; - - /* string-as-type checkers */ - case this.IS_BOOLEAN_STRING: - return eachPrefix + "$property must be a boolean string"; - case this.IS_NUMBER_STRING: - return eachPrefix + "$property must be a number string"; - - /* string checkers */ - case this.CONTAINS: - return eachPrefix + "$property must contain a $constraint1 string"; - case this.NOT_CONTAINS: - return eachPrefix + "$property should not contain a $constraint1 string"; - case this.IS_ALPHA: - return eachPrefix + "$property must contain only letters (a-zA-Z)"; - case this.IS_ALPHANUMERIC: - return eachPrefix + "$property must contain only letters and numbers"; - case this.IS_ASCII: - return eachPrefix + "$property must contain only ASCII characters"; - case this.IS_BASE64: - return eachPrefix + "$property must be base64 encoded"; - case this.IS_BYTE_LENGTH: - return eachPrefix + "$property's byte length must fall into ($constraint1, $constraint2) range"; - case this.IS_CREDIT_CARD: - return eachPrefix + "$property must be a credit card"; - case this.IS_CURRENCY: - return eachPrefix + "$property must be a currency"; - case this.IS_EMAIL: - return eachPrefix + "$property must be an email"; - case this.IS_FQDN: - return eachPrefix + "$property must be a valid domain name"; - case this.IS_FULL_WIDTH: - return eachPrefix + "$property must contain a full-width characters"; - case this.IS_HALF_WIDTH: - return eachPrefix + "$property must contain a half-width characters"; - case this.IS_VARIABLE_WIDTH: - return eachPrefix + "$property must contain a full-width and half-width characters"; - case this.IS_HEX_COLOR: - return eachPrefix + "$property must be a hexadecimal color"; - case this.IS_HEXADECIMAL: - return eachPrefix + "$property must be a hexadecimal number"; - case this.IS_IP: - return eachPrefix + "$property must be an ip address"; - case this.IS_ISBN: - return eachPrefix + "$property must be an ISBN"; - case this.IS_ISIN: - return eachPrefix + "$property must be an ISIN (stock/security identifier)"; - case this.IS_ISO8601: - return eachPrefix + "$property must be a valid ISO 8601 date string"; - case this.IS_JSON: - return eachPrefix + "$property must be a json string"; - case this.IS_LOWERCASE: - return eachPrefix + "$property must be a lowercase string"; - case this.IS_MOBILE_PHONE: - return eachPrefix + "$property must be a phone number"; - case this.IS_PHONE_NUMBER: - return eachPrefix + "$property must be a valid phone number"; - case this.IS_MONGO_ID: - return eachPrefix + "$property must be a mongodb id"; - case this.IS_MULTIBYTE: - return eachPrefix + "$property must contain one or more multibyte chars"; - case this.IS_SURROGATE_PAIR: - return eachPrefix + "$property must contain any surrogate pairs chars"; - case this.IS_URL: - return eachPrefix + "$property must be an URL address"; - case this.IS_UUID: - return eachPrefix + "$property must be an UUID"; - case this.IS_UPPERCASE: - return eachPrefix + "$property must be uppercase"; - case this.LENGTH: - return (args: ValidationArguments) => { - const isMinLength = args.constraints[0] !== null && args.constraints[0] !== undefined; - const isMaxLength = args.constraints[1] !== null && args.constraints[1] !== undefined; - if (isMinLength && (!args.value || args.value.length < args.constraints[0])) { - return eachPrefix + "$property must be longer than or equal to $constraint1 characters"; - } else if (isMaxLength && (args.value.length > args.constraints[1])) { - return eachPrefix + "$property must be shorter than or equal to $constraint2 characters"; - } - return eachPrefix + "$property must be longer than or equal to $constraint1 and shorter than or equal to $constraint2 characters"; - }; - case this.MIN_LENGTH: - return eachPrefix + "$property must be longer than or equal to $constraint1 characters"; - case this.MAX_LENGTH: - return eachPrefix + "$property must be shorter than or equal to $constraint1 characters"; - case this.MATCHES: - return eachPrefix + "$property must match $constraint1 regular expression"; - - /* array checkers */ - case this.ARRAY_CONTAINS: - return eachPrefix + "$property must contain $constraint1 values"; - case this.ARRAY_NOT_CONTAINS: - return eachPrefix + "$property should not contain $constraint1 values"; - case this.ARRAY_NOT_EMPTY: - return eachPrefix + "$property should not be empty"; - case this.ARRAY_MIN_SIZE: - return eachPrefix + "$property must contain at least $constraint1 elements"; - case this.ARRAY_MAX_SIZE: - return eachPrefix + "$property must contain not more than $constraint1 elements"; - case this.ARRAY_UNIQUE: - return eachPrefix + "All $property's elements must be unique"; - - case this.IS_INSTANCE: - return (args: ValidationArguments) => { - if (args.constraints[0]) { - return eachPrefix + `$property must be an instance of ${args.constraints[0].name}`; - } else { - return eachPrefix + `${this.IS_INSTANCE} decorator expects and object as value, but got falsy value.`; - } - }; - } - - return ""; - } - + /* system */ + static CUSTOM_VALIDATION = 'customValidation'; // done + static NESTED_VALIDATION = 'nestedValidation'; // done + static PROMISE_VALIDATION = 'promiseValidation'; // done + static CONDITIONAL_VALIDATION = 'conditionalValidation'; // done + static WHITELIST = 'whitelistValidation'; // done + static IS_DEFINED = 'isDefined'; // done + + /** + * Checks if validation type is valid. + */ + static isValid(type: string): boolean { + return ( + type !== 'isValid' && + type !== 'getMessage' && + Object.keys(this) + .map(key => (this as any)[key]) + .indexOf(type) !== -1 + ); + } } diff --git a/src/validation/ValidationUtils.ts b/src/validation/ValidationUtils.ts index 0cf8f4112a..7408031627 100644 --- a/src/validation/ValidationUtils.ts +++ b/src/validation/ValidationUtils.ts @@ -1,32 +1,51 @@ -import {ValidationArguments} from "./ValidationArguments"; +import { ValidationArguments } from './ValidationArguments'; -export class ValidationUtils { +/** + * Convert the constraint to a string to be shown in an error + */ +export function constraintToString(constraint: unknown): string { + if (Array.isArray(constraint)) { + return constraint.join(', '); + } - static replaceMessageSpecialTokens(message: string|((args: ValidationArguments) => string), - validationArguments: ValidationArguments): string { + if (typeof constraint === 'symbol') { + constraint = constraint.description; + } - let messageString: string; - if (message instanceof Function) { - messageString = (message as (args: ValidationArguments) => string)(validationArguments); + return `${constraint}`; +} - } else if (typeof message === "string") { - messageString = message as string; - } +export class ValidationUtils { + static replaceMessageSpecialTokens( + message: string | ((args: ValidationArguments) => string), + validationArguments: ValidationArguments + ): string { + let messageString: string; + if (message instanceof Function) { + messageString = (message as (args: ValidationArguments) => string)(validationArguments); + } else if (typeof message === 'string') { + messageString = message; + } - if (messageString && validationArguments.constraints instanceof Array) { - validationArguments.constraints.forEach((constraint, index) => { - messageString = messageString.replace(new RegExp(`\\$constraint${index + 1}`, "g"), constraint); - }); - } + if (messageString && Array.isArray(validationArguments.constraints)) { + validationArguments.constraints.forEach((constraint, index) => { + messageString = messageString.replace( + new RegExp(`\\$constraint${index + 1}`, 'g'), + constraintToString(constraint) + ); + }); + } - if (messageString && validationArguments.value !== undefined && validationArguments.value !== null && typeof validationArguments.value === "string") - messageString = messageString.replace(/\$value/g, validationArguments.value); - if (messageString) - messageString = messageString.replace(/\$property/g, validationArguments.property); - if (messageString) - messageString = messageString.replace(/\$target/g, validationArguments.targetName); + if ( + messageString && + validationArguments.value !== undefined && + validationArguments.value !== null && + typeof validationArguments.value === 'string' + ) + messageString = messageString.replace(/\$value/g, validationArguments.value); + if (messageString) messageString = messageString.replace(/\$property/g, validationArguments.property); + if (messageString) messageString = messageString.replace(/\$target/g, validationArguments.targetName); - return messageString; - } - -} \ No newline at end of file + return messageString; + } +} diff --git a/src/validation/Validator.ts b/src/validation/Validator.ts index b052b9a9a3..77e46ef2b0 100644 --- a/src/validation/Validator.ts +++ b/src/validation/Validator.ts @@ -1,839 +1,113 @@ -import {ValidationMetadata} from "../metadata/ValidationMetadata"; -import {ValidationTypes} from "./ValidationTypes"; -import {ValidationError} from "./ValidationError"; -import {IsEmailOptions, IsFQDNOptions, IsURLOptions, IsCurrencyOptions, IsNumberOptions} from "./ValidationTypeOptions"; -import {ValidatorOptions} from "./ValidatorOptions"; -import {ValidationExecutor} from "./ValidationExecutor"; -import {ValidationOptions} from "../decorator/ValidationOptions"; +import { ValidationError } from './ValidationError'; +import { ValidatorOptions } from './ValidatorOptions'; +import { ValidationExecutor } from './ValidationExecutor'; +import { ValidationOptions } from '../decorator/ValidationOptions'; /** * Validator performs validation of the given object based on its metadata. */ export class Validator { - - // ------------------------------------------------------------------------- - // Private Properties - // ------------------------------------------------------------------------- - - private validatorJs = require("validator"); - private libPhoneNumber = { - phoneUtil: require("google-libphonenumber").PhoneNumberUtil.getInstance(), - }; - - /** - * Performs validation of the given object based on decorators or validation schema. - * Common method for `validateOrReject` and `validate` methods. - */ - private coreValidate(objectOrSchemaName: Object|string, objectOrValidationOptions: Object|ValidationOptions, maybeValidatorOptions?: ValidatorOptions): Promise { - const object = typeof objectOrSchemaName === "string" ? objectOrValidationOptions as Object : objectOrSchemaName as Object; - const options = typeof objectOrSchemaName === "string" ? maybeValidatorOptions : objectOrValidationOptions as ValidationOptions; - const schema = typeof objectOrSchemaName === "string" ? objectOrSchemaName as string : undefined; - - const executor = new ValidationExecutor(this, options); - const validationErrors: ValidationError[] = []; - executor.execute(object, schema, validationErrors); - - return Promise.all(executor.awaitingPromises).then(() => { - return executor.stripEmptyErrors(validationErrors); - }); - } - - // ------------------------------------------------------------------------- - // Public Methods - // ------------------------------------------------------------------------- - - /** - * Performs validation of the given object based on decorators used in given object class. - */ - validate(object: Object, options?: ValidatorOptions): Promise; - - /** - * Performs validation of the given object based on validation schema. - */ - validate(schemaName: string, object: Object, options?: ValidatorOptions): Promise; - - /** - * Performs validation of the given object based on decorators or validation schema. - */ - validate(objectOrSchemaName: Object|string, objectOrValidationOptions: Object|ValidationOptions, maybeValidatorOptions?: ValidatorOptions): Promise { - return this.coreValidate(objectOrSchemaName, objectOrValidationOptions, maybeValidatorOptions); - } - - /** - * Performs validation of the given object based on decorators used in given object class and reject on error. - */ - validateOrReject(object: Object, options?: ValidatorOptions): Promise; - - /** - * Performs validation of the given object based on validation schema and reject on error. - */ - validateOrReject(schemaName: string, object: Object, options?: ValidatorOptions): Promise; - - /** - * Performs validation of the given object based on decorators or validation schema and reject on error. - */ - async validateOrReject(objectOrSchemaName: Object|string, objectOrValidationOptions: Object|ValidationOptions, maybeValidatorOptions?: ValidatorOptions): Promise { - const errors = await this.coreValidate(objectOrSchemaName, objectOrValidationOptions, maybeValidatorOptions); - if (errors.length) - return Promise.reject(errors); - } - - /** - * Performs validation of the given object based on decorators used in given object class. - * NOTE: This method completely ignores all async validations. - */ - validateSync(object: Object, options?: ValidatorOptions): ValidationError[]; - - /** - * Performs validation of the given object based on validation schema. - */ - validateSync(schemaName: string, object: Object, options?: ValidatorOptions): ValidationError[]; - - /** - * Performs validation of the given object based on decorators or validation schema. - */ - validateSync(objectOrSchemaName: Object|string, objectOrValidationOptions: Object|ValidationOptions, maybeValidatorOptions?: ValidatorOptions): ValidationError[] { - const object = typeof objectOrSchemaName === "string" ? objectOrValidationOptions as Object : objectOrSchemaName as Object; - const options = typeof objectOrSchemaName === "string" ? maybeValidatorOptions : objectOrValidationOptions as ValidationOptions; - const schema = typeof objectOrSchemaName === "string" ? objectOrSchemaName as string : undefined; - - const executor = new ValidationExecutor(this, options); - executor.ignoreAsyncValidations = true; - const validationErrors: ValidationError[] = []; - executor.execute(object, schema, validationErrors); - return executor.stripEmptyErrors(validationErrors); - } - - /** - * Performs validation of the given object based on the given ValidationMetadata object. - */ - validateValueByMetadata(value: any, metadata: ValidationMetadata): boolean { - switch (metadata.type) { - /* common checkers */ - case ValidationTypes.IS_DEFINED: - return this.isDefined(value); - case ValidationTypes.EQUALS: - return this.equals(value, metadata.constraints[0]); - case ValidationTypes.NOT_EQUALS: - return this.notEquals(value, metadata.constraints[0]); - case ValidationTypes.IS_EMPTY: - return this.isEmpty(value); - case ValidationTypes.IS_NOT_EMPTY: - return this.isNotEmpty(value); - case ValidationTypes.IS_IN: - return this.isIn(value, metadata.constraints[0]); - case ValidationTypes.IS_NOT_IN: - return this.isNotIn(value, metadata.constraints[0]); - - /* type checkers */ - case ValidationTypes.IS_BOOLEAN: - return this.isBoolean(value); - case ValidationTypes.IS_DATE: - return this.isDate(value); - case ValidationTypes.IS_STRING: - return this.isString(value); - case ValidationTypes.IS_DATE_STRING: - return this.isDateString(value); - case ValidationTypes.IS_ARRAY: - return this.isArray(value); - case ValidationTypes.IS_NUMBER: - return this.isNumber(value, metadata.constraints[0]); - case ValidationTypes.IS_INT: - return this.isInt(value); - case ValidationTypes.IS_ENUM: - return this.isEnum(value, metadata.constraints[0]); - - /* number checkers */ - case ValidationTypes.IS_DIVISIBLE_BY: - return this.isDivisibleBy(value, metadata.constraints[0]); - case ValidationTypes.IS_POSITIVE: - return this.isPositive(value); - case ValidationTypes.IS_NEGATIVE: - return this.isNegative(value); - case ValidationTypes.MIN: - return this.min(value, metadata.constraints[0]); - case ValidationTypes.MAX: - return this.max(value, metadata.constraints[0]); - - /* date checkers */ - case ValidationTypes.MIN_DATE: - return this.minDate(value, metadata.constraints[0]); - case ValidationTypes.MAX_DATE: - return this.maxDate(value, metadata.constraints[0]); - - /* string-as-type checkers */ - case ValidationTypes.IS_BOOLEAN_STRING: - return this.isBooleanString(value); - case ValidationTypes.IS_NUMBER_STRING: - return this.isNumberString(value); - - /* string checkers */ - case ValidationTypes.CONTAINS: - return this.contains(value, metadata.constraints[0]); - case ValidationTypes.NOT_CONTAINS: - return this.notContains(value, metadata.constraints[0]); - case ValidationTypes.IS_ALPHA: - return this.isAlpha(value); - case ValidationTypes.IS_ALPHANUMERIC: - return this.isAlphanumeric(value); - case ValidationTypes.IS_ASCII: - return this.isAscii(value); - case ValidationTypes.IS_BASE64: - return this.isBase64(value); - case ValidationTypes.IS_BYTE_LENGTH: - return this.isByteLength(value, metadata.constraints[0], metadata.constraints[1]); - case ValidationTypes.IS_CREDIT_CARD: - return this.isCreditCard(value); - case ValidationTypes.IS_CURRENCY: - return this.isCurrency(value, metadata.constraints[0]); - case ValidationTypes.IS_EMAIL: - return this.isEmail(value, metadata.constraints[0]); - case ValidationTypes.IS_FQDN: - return this.isFQDN(value, metadata.constraints[0]); - case ValidationTypes.IS_FULL_WIDTH: - return this.isFullWidth(value); - case ValidationTypes.IS_HALF_WIDTH: - return this.isHalfWidth(value); - case ValidationTypes.IS_VARIABLE_WIDTH: - return this.isVariableWidth(value); - case ValidationTypes.IS_HEX_COLOR: - return this.isHexColor(value); - case ValidationTypes.IS_HEXADECIMAL: - return this.isHexadecimal(value); - case ValidationTypes.IS_IP: - return this.isIP(value, metadata.constraints[0]); - case ValidationTypes.IS_ISBN: - return this.isISBN(value, metadata.constraints[0]); - case ValidationTypes.IS_ISIN: - return this.isISIN(value); - case ValidationTypes.IS_ISO8601: - return this.isISO8601(value); - case ValidationTypes.IS_JSON: - return this.isJSON(value); - case ValidationTypes.IS_LOWERCASE: - return this.isLowercase(value); - case ValidationTypes.IS_MOBILE_PHONE: - return this.isMobilePhone(value, metadata.constraints[0]); - case ValidationTypes.IS_PHONE_NUMBER: - return this.isPhoneNumber(value, metadata.constraints[0]); - case ValidationTypes.IS_MONGO_ID: - return this.isMongoId(value); - case ValidationTypes.IS_MULTIBYTE: - return this.isMultibyte(value); - case ValidationTypes.IS_SURROGATE_PAIR: - return this.isSurrogatePair(value); - case ValidationTypes.IS_URL: - return this.isURL(value, metadata.constraints[0]); - case ValidationTypes.IS_UUID: - return this.isUUID(value, metadata.constraints[0]); - case ValidationTypes.IS_UPPERCASE: - return this.isUppercase(value); - case ValidationTypes.LENGTH: - return this.length(value, metadata.constraints[0], metadata.constraints[1]); - case ValidationTypes.MIN_LENGTH: - return this.minLength(value, metadata.constraints[0]); - case ValidationTypes.MAX_LENGTH: - return this.maxLength(value, metadata.constraints[0]); - case ValidationTypes.MATCHES: - return this.matches(value, metadata.constraints[0], metadata.constraints[1]); - case ValidationTypes.IS_MILITARY_TIME: - return this.isMilitaryTime(value); - - /* array checkers */ - case ValidationTypes.ARRAY_CONTAINS: - return this.arrayContains(value, metadata.constraints[0]); - case ValidationTypes.ARRAY_NOT_CONTAINS: - return this.arrayNotContains(value, metadata.constraints[0]); - case ValidationTypes.ARRAY_NOT_EMPTY: - return this.arrayNotEmpty(value); - case ValidationTypes.ARRAY_MIN_SIZE: - return this.arrayMinSize(value, metadata.constraints[0]); - case ValidationTypes.ARRAY_MAX_SIZE: - return this.arrayMaxSize(value, metadata.constraints[0]); - case ValidationTypes.ARRAY_UNIQUE: - return this.arrayUnique(value); - - case ValidationTypes.IS_INSTANCE: - return this.isInstance(value, metadata.constraints[0]); - } - return true; - } - - // ------------------------------------------------------------------------- - // Validation Methods: common checkers - // ------------------------------------------------------------------------- - - /** - * Checks if value is defined (!== undefined, !== null). - */ - isDefined(value: any): boolean { - return value !== undefined && value !== null; - } - - /** - * Checks if value matches ("===") the comparison. - */ - equals(value: any, comparison: any): boolean { - return value === comparison; - } - - /** - * Checks if value does not match ("!==") the comparison. - */ - notEquals(value: any, comparison: any): boolean { - return value !== comparison; - } - - /** - * Checks if given value is empty (=== '', === null, === undefined). - */ - isEmpty(value: any): boolean { - return value === "" || value === null || value === undefined; - } - - /** - * Checks if given value is not empty (!== '', !== null, !== undefined). - */ - isNotEmpty(value: any): boolean { - return value !== "" && value !== null && value !== undefined; - } - - /** - * Checks if given value is in a array of allowed values. - */ - isIn(value: any, possibleValues: any[]): boolean { - return !(possibleValues instanceof Array) || possibleValues.some(possibleValue => possibleValue === value); - } - - /** - * Checks if given value not in a array of allowed values. - */ - isNotIn(value: any, possibleValues: any[]): boolean { - return !(possibleValues instanceof Array) || !possibleValues.some(possibleValue => possibleValue === value); - } - - // ------------------------------------------------------------------------- - // Validation Methods: type checkers - // ------------------------------------------------------------------------- - - /** - * Checks if a given value is a real boolean. - */ - isBoolean(value: any): boolean { - return value instanceof Boolean || typeof value === "boolean"; - } - - /** - * Checks if a given value is a real date. - */ - isDate(value: any): boolean { - return value instanceof Date && !isNaN(value.getTime()); - } - - /** - * Checks if a given value is a real string. - */ - isString(value: any): boolean { - return value instanceof String || typeof value === "string"; - } - - /** - * Checks if a given value is a ISOString date. - */ - isDateString(value: any): boolean { - const regex = /\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z|\+[0-2]\d(?:\:[0-5]\d)?)?/g; - return this.isString(value) && regex.test(value); - } - - /** - * Checks if a given value is an array - */ - isArray(value: any): boolean { - return value instanceof Array; - } - - /** - * Checks if a given value is an enum - */ - isEnum(value: any, entity: any): boolean { - const enumValues = Object.keys(entity) - .map(k => entity[k]); - return enumValues.indexOf(value) >= 0; - } - - /** - * Checks if a given value is a number. - */ - isNumber(value: any, options: IsNumberOptions = {}): boolean { - if (value === Infinity || value === -Infinity) { - return options.allowInfinity; - } - - if (Number.isNaN(value)) { - return options.allowNaN; - } - - return Number.isFinite(value); - } - - /** - * Checks if value is an integer. - */ - isInt(val: number): boolean { - return Number.isInteger(val); - } - - // ------------------------------------------------------------------------- - // Validation Methods: number checkers - // ------------------------------------------------------------------------- - - /** - * Checks if value is a number that's divisible by another. - */ - isDivisibleBy(value: number, num: number): boolean { - return typeof value === "number" && - typeof num === "number" && - this.validatorJs.isDivisibleBy(String(value), num); - } - - /** - * Checks if the value is a positive number. - */ - isPositive(value: number): boolean { - return typeof value === "number" && value > 0; - } - - /** - * Checks if the value is a negative number. - */ - isNegative(value: number): boolean { - return typeof value === "number" && value < 0; - } - - /** - * Checks if the first number is greater than or equal to the second. - */ - min(num: number, min: number): boolean { - return typeof num === "number" && typeof min === "number" && num >= min; - } - - /** - * Checks if the first number is less than or equal to the second. - */ - max(num: number, max: number): boolean { - return typeof num === "number" && typeof max === "number" && num <= max; - } - - // ------------------------------------------------------------------------- - // Validation Methods: date checkers - // ------------------------------------------------------------------------- - - /** - * Checks if the value is a date that's after the specified date. - */ - minDate(date: Date, minDate: Date): boolean { - return date && date.getTime() >= minDate.getTime(); - } - - /** - * Checks if the value is a date that's before the specified date. - */ - maxDate(date: Date, maxDate: Date): boolean { - return date && date.getTime() <= maxDate.getTime(); - } - - // ------------------------------------------------------------------------- - // Validation Methods: string-as-type checkers - // ------------------------------------------------------------------------- - - /** - * Checks if a string is a boolean. - * If given value is not a string, then it returns false. - */ - isBooleanString(value: string): boolean { - return typeof value === "string" && this.validatorJs.isBoolean(value); - } - - /** - * Checks if the string is numeric. - * If given value is not a string, then it returns false. - */ - isNumberString(value: string): boolean { - return typeof value === "string" && this.validatorJs.isNumeric(value); - } - - // ------------------------------------------------------------------------- - // Validation Methods: string checkers - // ------------------------------------------------------------------------- - - /** - * Checks if the string contains the seed. - * If given value is not a string, then it returns false. - */ - contains(value: string, seed: string): boolean { - return typeof value === "string" && this.validatorJs.contains(value, seed); - } - - /** - * Checks if the string does not contain the seed. - * If given value is not a string, then it returns false. - */ - notContains(value: string, seed: string): boolean { - return typeof value === "string" && !this.validatorJs.contains(value, seed); - } - - /** - * Checks if the string contains only letters (a-zA-Z). - * If given value is not a string, then it returns false. - */ - isAlpha(value: string): boolean { - return typeof value === "string" && this.validatorJs.isAlpha(value); - } - - /** - * Checks if the string contains only letters and numbers. - * If given value is not a string, then it returns false. - */ - isAlphanumeric(value: string): boolean { - return typeof value === "string" && this.validatorJs.isAlphanumeric(value); - } - - /** - * Checks if the string contains ASCII chars only. - * If given value is not a string, then it returns false. - */ - isAscii(value: string): boolean { - return typeof value === "string" && this.validatorJs.isAscii(value); - } - - /** - * Checks if a string is base64 encoded. - * If given value is not a string, then it returns false. - */ - isBase64(value: string): boolean { - return typeof value === "string" && this.validatorJs.isBase64(value); - } - - /** - * Checks if the string's length (in bytes) falls in a range. - * If given value is not a string, then it returns false. - */ - isByteLength(value: string, min: number, max?: number): boolean { - return typeof value === "string" && this.validatorJs.isByteLength(value, min, max); - } - - /** - * Checks if the string is a credit card. - * If given value is not a string, then it returns false. - */ - isCreditCard(value: string): boolean { - return typeof value === "string" && this.validatorJs.isCreditCard(value); - } - - /** - * Checks if the string is a valid currency amount. - * If given value is not a string, then it returns false. - */ - isCurrency(value: string, options?: IsCurrencyOptions): boolean { - return typeof value === "string" && this.validatorJs.isCurrency(value, options); - } - - /** - * Checks if the string is an email. - * If given value is not a string, then it returns false. - */ - isEmail(value: string, options?: IsEmailOptions): boolean { - return typeof value === "string" && this.validatorJs.isEmail(value, options); - } - - /** - * Checks if the string is a fully qualified domain name (e.g. domain.com). - * If given value is not a string, then it returns false. - */ - isFQDN(value: string, options?: IsFQDNOptions): boolean { - return typeof value === "string" && this.validatorJs.isFQDN(value, options); - } - - /** - * Checks if the string contains any full-width chars. - * If given value is not a string, then it returns false. - */ - isFullWidth(value: string): boolean { - return typeof value === "string" && this.validatorJs.isFullWidth(value); - } - - /** - * Checks if the string contains any half-width chars. - * If given value is not a string, then it returns false. - */ - isHalfWidth(value: string): boolean { - return typeof value === "string" && this.validatorJs.isHalfWidth(value); - } - - /** - * Checks if the string contains variable-width chars. - * If given value is not a string, then it returns false. - */ - isVariableWidth(value: string): boolean { - return typeof value === "string" && this.validatorJs.isVariableWidth(value); - } - - /** - * Checks if the string is a hexadecimal color. - * If given value is not a string, then it returns false. - */ - isHexColor(value: string): boolean { - return typeof value === "string" && this.validatorJs.isHexColor(value); - } - - /** - * Checks if the string is a hexadecimal number. - * If given value is not a string, then it returns false. - */ - isHexadecimal(value: string): boolean { - return typeof value === "string" && this.validatorJs.isHexadecimal(value); - } - - /** - * Checks if the string is an IP (version 4 or 6). - * If given value is not a string, then it returns false. - */ - isIP(value: string, version?: "4"|"6"): boolean { - return typeof value === "string" && this.validatorJs.isIP(value, version); - } - - /** - * Checks if the string is an ISBN (version 10 or 13). - * If given value is not a string, then it returns false. - */ - isISBN(value: string, version?: "10"|"13"): boolean { - return typeof value === "string" && this.validatorJs.isISBN(value, version); - } - - /** - * Checks if the string is an ISIN (stock/security identifier). - * If given value is not a string, then it returns false. - */ - isISIN(value: string): boolean { - return typeof value === "string" && this.validatorJs.isISIN(value); - } - - /** - * Checks if the string is a valid ISO 8601 date. - * If given value is not a string, then it returns false. - */ - isISO8601(value: string): boolean { - return typeof value === "string" && this.validatorJs.isISO8601(value); - } - - /** - * Checks if the string is valid JSON (note: uses JSON.parse). - * If given value is not a string, then it returns false. - */ - isJSON(value: string): boolean { - return typeof value === "string" && this.validatorJs.isJSON(value); - } - - /** - * Checks if the string is lowercase. - * If given value is not a string, then it returns false. - */ - isLowercase(value: string): boolean { - return typeof value === "string" && this.validatorJs.isLowercase(value); - } - - /** - * Checks if the string is a mobile phone number (locale is one of ['zh-CN', 'zh-TW', 'en-ZA', 'en-AU', 'en-HK', - * 'pt-PT', 'fr-FR', 'el-GR', 'en-GB', 'en-US', 'en-ZM', 'ru-RU', 'nb-NO', 'nn-NO', 'vi-VN', 'en-NZ']). - * If given value is not a string, then it returns false. - */ - isMobilePhone(value: string, locale: string): boolean { - return typeof value === "string" && this.validatorJs.isMobilePhone(value, locale); - } - - /** - * Checks if the string is a valid phone number. - * @param value the potential phone number string to test - * @param {string} region 2 characters uppercase country code (e.g. DE, US, CH). - * If users must enter the intl. prefix (e.g. +41), then you may pass "ZZ" or null as region. - * See [google-libphonenumber, metadata.js:countryCodeToRegionCodeMap on github]{@link https://github.com/ruimarinho/google-libphonenumber/blob/1e46138878cff479aafe2ce62175c6c49cb58720/src/metadata.js#L33} - */ - isPhoneNumber(value: string, region: string): boolean { - try { - const phoneNum = this.libPhoneNumber.phoneUtil.parseAndKeepRawInput(value, region); - return this.libPhoneNumber.phoneUtil.isValidNumber(phoneNum); - } catch (error) { - // logging? - return false; - } - } - - /** - * Checks if the string is a valid hex-encoded representation of a MongoDB ObjectId. - * If given value is not a string, then it returns false. - */ - isMongoId(value: string): boolean { - return typeof value === "string" && this.validatorJs.isMongoId(value); - } - - /** - * Checks if the string contains one or more multibyte chars. - * If given value is not a string, then it returns false. - */ - isMultibyte(value: string): boolean { - return typeof value === "string" && this.validatorJs.isMultibyte(value); - } - - /** - * Checks if the string contains any surrogate pairs chars. - * If given value is not a string, then it returns false. - */ - isSurrogatePair(value: string): boolean { - return typeof value === "string" && this.validatorJs.isSurrogatePair(value); - } - - /** - * Checks if the string is an url. - * If given value is not a string, then it returns false. - */ - isURL(value: string, options?: IsURLOptions): boolean { - return typeof value === "string" && this.validatorJs.isURL(value, options); - } - - /** - * Checks if the string is a UUID (version 3, 4 or 5). - * If given value is not a string, then it returns false. - */ - isUUID(value: string, version?: "3"|"4"|"5"): boolean { - return typeof value === "string" && this.validatorJs.isUUID(value, version); - } - - /** - * Checks if the string is uppercase. - * If given value is not a string, then it returns false. - */ - isUppercase(value: string): boolean { - return typeof value === "string" && this.validatorJs.isUppercase(value); - } - - /** - * Checks if the string's length falls in a range. Note: this function takes into account surrogate pairs. - * If given value is not a string, then it returns false. - */ - length(value: string, min: number, max?: number): boolean { - return typeof value === "string" && this.validatorJs.isLength(value, min, max); - } - - /** - * Checks if the string's length is not less than given number. Note: this function takes into account surrogate pairs. - * If given value is not a string, then it returns false. - */ - minLength(value: string, min: number) { - return typeof value === "string" && this.length(value, min); - } - - /** - * Checks if the string's length is not more than given number. Note: this function takes into account surrogate pairs. - * If given value is not a string, then it returns false. - */ - maxLength(value: string, max: number) { - return typeof value === "string" && this.length(value, 0, max); - } - - /** - * Checks if string matches the pattern. Either matches('foo', /foo/i) or matches('foo', 'foo', 'i'). - * If given value is not a string, then it returns false. - */ - matches(value: string, pattern: RegExp, modifiers?: string): boolean { - return typeof value === "string" && this.validatorJs.matches(value, pattern, modifiers); - } - - /** - * Checks if the string represents a time without a given timezone in the format HH:MM (military) - * If the given value does not match the pattern HH:MM, then it returns false. - */ - isMilitaryTime(value: string): boolean { - return this.matches(value, /^([01]\d|2[0-3]):?([0-5]\d)$/); - } - - // ------------------------------------------------------------------------- - // Validation Methods: array checkers - // ------------------------------------------------------------------------- - - /** - * Checks if array contains all values from the given array of values. - * If null or undefined is given then this function returns false. - */ - arrayContains(array: any[], values: any[]) { - if (!(array instanceof Array)) - return false; - - return !array || values.every(value => array.indexOf(value) !== -1); - } - - /** - * Checks if array does not contain any of the given values. - * If null or undefined is given then this function returns false. - */ - arrayNotContains(array: any[], values: any[]) { - if (!(array instanceof Array)) - return false; - - return !array || values.every(value => array.indexOf(value) === -1); - } - - /** - * Checks if given array is not empty. - * If null or undefined is given then this function returns false. - */ - arrayNotEmpty(array: any[]) { - if (!(array instanceof Array)) - return false; - - return array instanceof Array && array.length > 0; - } - - /** - * Checks if array's length is as minimal this number. - * If null or undefined is given then this function returns false. - */ - arrayMinSize(array: any[], min: number) { - if (!(array instanceof Array)) - return false; - - return array instanceof Array && array.length >= min; - } - - /** - * Checks if array's length is as maximal this number. - * If null or undefined is given then this function returns false. - */ - arrayMaxSize(array: any[], max: number) { - if (!(array instanceof Array)) - return false; - - return array instanceof Array && array.length <= max; - } - - /** - * Checks if all array's values are unique. Comparison for objects is reference-based. - * If null or undefined is given then this function returns false. - */ - arrayUnique(array: any[]) { - if (!(array instanceof Array)) - return false; - - const uniqueItems = array.filter((a, b, c) => c.indexOf(a) === b); - return array.length === uniqueItems.length; - } - - /** - * Checks if the value is an instance of the specified object. - */ - isInstance(object: any, targetTypeConstructor: new (...args: any[]) => any) { - return targetTypeConstructor - && typeof targetTypeConstructor === "function" - && object instanceof targetTypeConstructor; - } - + // ------------------------------------------------------------------------- + // Public Methods + // ------------------------------------------------------------------------- + + /** + * Performs validation of the given object based on decorators used in given object class. + */ + validate(object: object, options?: ValidatorOptions): Promise; + + /** + * Performs validation of the given object based on validation schema. + */ + validate(schemaName: string, object: object, options?: ValidatorOptions): Promise; + + /** + * Performs validation of the given object based on decorators or validation schema. + */ + validate( + objectOrSchemaName: object | string, + objectOrValidationOptions: object | ValidationOptions, + maybeValidatorOptions?: ValidatorOptions + ): Promise { + return this.coreValidate(objectOrSchemaName, objectOrValidationOptions, maybeValidatorOptions); + } + + /** + * Performs validation of the given object based on decorators used in given object class and reject on error. + */ + validateOrReject(object: object, options?: ValidatorOptions): Promise; + + /** + * Performs validation of the given object based on validation schema and reject on error. + */ + validateOrReject(schemaName: string, object: object, options?: ValidatorOptions): Promise; + + /** + * Performs validation of the given object based on decorators or validation schema and reject on error. + */ + async validateOrReject( + objectOrSchemaName: object | string, + objectOrValidationOptions: object | ValidationOptions, + maybeValidatorOptions?: ValidatorOptions + ): Promise { + const errors = await this.coreValidate(objectOrSchemaName, objectOrValidationOptions, maybeValidatorOptions); + if (errors.length) return Promise.reject(errors); + } + + /** + * Performs validation of the given object based on decorators used in given object class. + * NOTE: This method completely ignores all async validations. + */ + validateSync(object: object, options?: ValidatorOptions): ValidationError[]; + + /** + * Performs validation of the given object based on validation schema. + */ + validateSync(schemaName: string, object: object, options?: ValidatorOptions): ValidationError[]; + + /** + * Performs validation of the given object based on decorators or validation schema. + */ + validateSync( + objectOrSchemaName: object | string, + objectOrValidationOptions: object | ValidationOptions, + maybeValidatorOptions?: ValidatorOptions + ): ValidationError[] { + const object = typeof objectOrSchemaName === 'string' ? (objectOrValidationOptions as object) : objectOrSchemaName; + const options = + typeof objectOrSchemaName === 'string' ? maybeValidatorOptions : (objectOrValidationOptions as ValidationOptions); + const schema = typeof objectOrSchemaName === 'string' ? objectOrSchemaName : undefined; + + const executor = new ValidationExecutor(this, options); + executor.ignoreAsyncValidations = true; + const validationErrors: ValidationError[] = []; + executor.execute(object, schema, validationErrors); + return executor.stripEmptyErrors(validationErrors); + } + + // ------------------------------------------------------------------------- + // Private Properties + // ------------------------------------------------------------------------- + /** + * Performs validation of the given object based on decorators or validation schema. + * Common method for `validateOrReject` and `validate` methods. + */ + private coreValidate( + objectOrSchemaName: object | string, + objectOrValidationOptions: object | ValidationOptions, + maybeValidatorOptions?: ValidatorOptions + ): Promise { + const object = typeof objectOrSchemaName === 'string' ? (objectOrValidationOptions as object) : objectOrSchemaName; + const options = + typeof objectOrSchemaName === 'string' ? maybeValidatorOptions : (objectOrValidationOptions as ValidationOptions); + const schema = typeof objectOrSchemaName === 'string' ? objectOrSchemaName : undefined; + + const executor = new ValidationExecutor(this, options); + const validationErrors: ValidationError[] = []; + executor.execute(object, schema, validationErrors); + + return Promise.all(executor.awaitingPromises).then(() => { + return executor.stripEmptyErrors(validationErrors); + }); + } } diff --git a/src/validation/ValidatorConstraintInterface.ts b/src/validation/ValidatorConstraintInterface.ts index 5d485bd0df..f29ce2f6fe 100644 --- a/src/validation/ValidatorConstraintInterface.ts +++ b/src/validation/ValidatorConstraintInterface.ts @@ -1,17 +1,15 @@ -import {ValidationArguments} from "./ValidationArguments"; +import { ValidationArguments } from './ValidationArguments'; /** * Custom validators must implement this interface to provide custom validation logic. */ export interface ValidatorConstraintInterface { + /** + * Method to be called to perform custom validation over given value. + */ + validate(value: any, validationArguments?: ValidationArguments): Promise | boolean; - /** - * Method to be called to perform custom validation over given value. - */ - validate(value: any, validationArguments?: ValidationArguments): Promise|boolean; - - /** - * Gets default message when validation for this constraint fail. - */ - defaultMessage?(validationArguments?: ValidationArguments): string; - -} \ No newline at end of file + /** + * Gets default message when validation for this constraint fail. + */ + defaultMessage?(validationArguments?: ValidationArguments): string; +} diff --git a/src/validation/ValidatorOptions.ts b/src/validation/ValidatorOptions.ts index 67cc143038..46a0b5fb14 100644 --- a/src/validation/ValidatorOptions.ts +++ b/src/validation/ValidatorOptions.ts @@ -2,55 +2,87 @@ * Options passed to validator during validation. */ export interface ValidatorOptions { + /** + * If set to true then class-validator will print extra warning messages to the console when something is not right. + */ + enableDebugMessages?: boolean; - /** - * If set to true than validator will skip validation of all properties that are missing in the validating object. - */ - skipMissingProperties?: boolean; + /** + * If set to true then validator will skip validation of all properties that are undefined in the validating object. + */ + skipUndefinedProperties?: boolean; - /** - * If set to true validator will strip validated object of any properties that do not have any decorators. - * - * Tip: if no other decorator is suitable for your property use @Allow decorator. - */ - whitelist?: boolean; + /** + * If set to true then validator will skip validation of all properties that are null in the validating object. + */ + skipNullProperties?: boolean; - /** - * If set to true, instead of stripping non-whitelisted properties validator will throw an error - */ - forbidNonWhitelisted?: boolean; + /** + * If set to true then validator will skip validation of all properties that are null or undefined in the validating object. + */ + skipMissingProperties?: boolean; - /** - * Groups to be used during validation of the object. - */ - groups?: string[]; + /** + * If set to true validator will strip validated object of any properties that do not have any decorators. + * + * Tip: if no other decorator is suitable for your property use @Allow decorator. + */ + whitelist?: boolean; - /** - * If set to true, the validation will not use default messages. - * Error message always will be undefined if its not explicitly set. - */ - dismissDefaultMessages?: boolean; + /** + * If set to true, instead of stripping non-whitelisted properties validator will throw an error + */ + forbidNonWhitelisted?: boolean; - /** - * ValidationError special options. - */ - validationError?: { + /** + * Groups to be used during validation of the object. + */ + groups?: string[]; - /** - * Indicates if target should be exposed in ValidationError. - */ - target?: boolean; + /** + * Set default for `always` option of decorators. Default can be overridden in decorator options. + */ + always?: boolean; - /** - * Indicates if validated value should be exposed in ValidationError. - */ - value?: boolean; + /** + * If [groups]{@link ValidatorOptions#groups} is not given or is empty, + * ignore decorators with at least one group. + */ + strictGroups?: boolean; - }; + /** + * If set to true, the validation will not use default messages. + * Error message always will be undefined if its not explicitly set. + */ + dismissDefaultMessages?: boolean; + /** + * ValidationError special options. + */ + validationError?: { /** - * Settings true will cause fail validation of unknown objects. + * Indicates if target should be exposed in ValidationError. */ - forbidUnknownValues?: boolean; + target?: boolean; + + /** + * Indicates if validated value should be exposed in ValidationError. + */ + value?: boolean; + }; + + /** + * Fails validation for objects unknown to class-validator. Defaults to false. + * + * For instance, since a plain empty object has no annotations used for validation: + * - `validate({})` // passes + * - `validate({}, { forbidUnknownValues: true })` // fails. + * - `validate(new SomeAnnotatedEmptyClass(), { forbidUnknownValues: true })` // passes. + */ + forbidUnknownValues?: boolean; -} \ No newline at end of file + /** + * When set to true, validation of the given property will stop after encountering the first error. Defaults to false. + */ + stopAtFirstError?: boolean; +} diff --git a/test/functional/conditional-validation.spec.ts b/test/functional/conditional-validation.spec.ts index 92e8a32af2..e633763799 100644 --- a/test/functional/conditional-validation.spec.ts +++ b/test/functional/conditional-validation.spec.ts @@ -1,103 +1,95 @@ -import "es6-shim"; -import {IsNotEmpty, ValidateIf, IsOptional, Equals} from "../../src/decorator/decorators"; -import {Validator} from "../../src/validation/Validator"; -import {ValidatorOptions} from "../../src/validation/ValidatorOptions"; -import {expect, should, use } from "chai"; - -import * as chaiAsPromised from "chai-as-promised"; - -should(); -use(chaiAsPromised); - -// ------------------------------------------------------------------------- -// Setup -// ------------------------------------------------------------------------- +import { IsNotEmpty, ValidateIf, IsOptional, Equals } from '../../src/decorator/decorators'; +import { Validator } from '../../src/validation/Validator'; const validator = new Validator(); -// ------------------------------------------------------------------------- -// Specifications: common decorators -// ------------------------------------------------------------------------- +describe('conditional validation', () => { + it("shouldn't validate a property when the condition is false", () => { + expect.assertions(1); -describe("conditional validation", function() { - it("shouldn't validate a property when the condition is false", function() { - class MyClass { - @ValidateIf(o => false) - @IsNotEmpty() - title: string; - } + class MyClass { + @ValidateIf(o => false) + @IsNotEmpty() + title: string; + } - const model = new MyClass(); - return validator.validate(model).then(errors => { - errors.length.should.be.equal(0); - }); + const model = new MyClass(); + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(0); }); + }); - it("should validate a property when the condition is true", function() { - class MyClass { - @ValidateIf(o => true) - @IsNotEmpty() - title: string = ""; - } + it('should validate a property when the condition is true', () => { + expect.assertions(5); - const model = new MyClass(); - return validator.validate(model).then(errors => { - errors.length.should.be.equal(1); - errors[0].target.should.be.equal(model); - errors[0].property.should.be.equal("title"); - errors[0].constraints.should.be.eql({ isNotEmpty: "title should not be empty" }); - errors[0].value.should.be.equal(""); - }); + class MyClass { + @ValidateIf(o => true) + @IsNotEmpty() + title: string = ''; + } + + const model = new MyClass(); + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(1); + expect(errors[0].target).toEqual(model); + expect(errors[0].property).toEqual('title'); + expect(errors[0].constraints).toEqual({ isNotEmpty: 'title should not be empty' }); + expect(errors[0].value).toEqual(''); }); + }); + + it('should pass the object being validated to the condition function', () => { + expect.assertions(3); - it("should pass the object being validated to the condition function", function() { - class MyClass { - @ValidateIf(o => { - expect(o).to.be.instanceOf(MyClass); - expect(o.title).to.equal("title"); - return true; - }) - @IsNotEmpty() - title: string = "title"; - } + class MyClass { + @ValidateIf(o => { + expect(o).toBeInstanceOf(MyClass); + expect(o.title).toEqual('title'); + return true; + }) + @IsNotEmpty() + title: string = 'title'; + } - const model = new MyClass(); - return validator.validate(model).then(errors => { - errors.length.should.be.equal(0); - }); + const model = new MyClass(); + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(0); }); + }); + + it('should validate a property when value is empty', () => { + expect.assertions(5); - it("should validate a property when value is empty", function () { - class MyClass { - @IsOptional() - @Equals("test") - title: string = ""; - } + class MyClass { + @IsOptional() + @Equals('test') + title: string = ''; + } - const model = new MyClass(); - return validator.validate(model).then(errors => { - errors.length.should.be.equal(1); - errors[0].target.should.be.equal(model); - errors[0].property.should.be.equal("title"); - errors[0].constraints.should.be.eql({ equals: "title must be equal to test" }); - errors[0].value.should.be.equal(""); - }); + const model = new MyClass(); + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(1); + expect(errors[0].target).toEqual(model); + expect(errors[0].property).toEqual('title'); + expect(errors[0].constraints).toEqual({ equals: 'title must be equal to test' }); + expect(errors[0].value).toEqual(''); }); + }); - it("should validate a property when value is supplied", function () { - class MyClass { - @IsOptional() - @Equals("test") - title: string = "bad_value"; - } + it('should validate a property when value is supplied', () => { + class MyClass { + @IsOptional() + @Equals('test') + title: string = 'bad_value'; + } - const model = new MyClass(); - return validator.validate(model).then(errors => { - errors.length.should.be.equal(1); - errors[0].target.should.be.equal(model); - errors[0].property.should.be.equal("title"); - errors[0].constraints.should.be.eql({ equals: "title must be equal to test" }); - errors[0].value.should.be.equal("bad_value"); - }); + const model = new MyClass(); + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(1); + expect(errors[0].target).toEqual(model); + expect(errors[0].property).toEqual('title'); + expect(errors[0].constraints).toEqual({ equals: 'title must be equal to test' }); + expect(errors[0].value).toEqual('bad_value'); }); + }); }); diff --git a/test/functional/custom-decorators.spec.ts b/test/functional/custom-decorators.spec.ts index 6bcdc77d98..39204addb8 100644 --- a/test/functional/custom-decorators.spec.ts +++ b/test/functional/custom-decorators.spec.ts @@ -1,228 +1,276 @@ -import "es6-shim"; -import {Validator} from "../../src/validation/Validator"; -import {ValidationArguments} from "../../src/validation/ValidationArguments"; -import {registerDecorator} from "../../src/register-decorator"; -import {ValidationOptions} from "../../src/decorator/ValidationOptions"; -import {ValidatorConstraint} from "../../src/decorator/decorators"; -import {ValidatorConstraintInterface} from "../../src/validation/ValidatorConstraintInterface"; +import { Validator } from '../../src/validation/Validator'; +import { ValidationArguments } from '../../src/validation/ValidationArguments'; +import { registerDecorator } from '../../src/register-decorator'; +import { ValidationOptions } from '../../src/decorator/ValidationOptions'; +import { buildMessage, ValidatorConstraint } from '../../src/decorator/decorators'; +import { ValidatorConstraintInterface } from '../../src/validation/ValidatorConstraintInterface'; -import {should, use } from "chai"; +const validator = new Validator(); -import * as chaiAsPromised from "chai-as-promised"; +describe('decorator with inline validation', () => { + function IsLongerThan(property: string, validationOptions?: ValidationOptions) { + return function (object: object, propertyName: string): void { + registerDecorator({ + target: object.constructor, + propertyName: propertyName, + options: validationOptions, + constraints: [property], + name: 'isLongerThan', + validator: { + validate(value: any, args: ValidationArguments): Promise | boolean { + const [relatedPropertyName] = args.constraints; + const relatedValue = (args.object as any)[relatedPropertyName]; + if (relatedValue === undefined || relatedValue === null) { + return true; + } -should(); -use(chaiAsPromised); + const result = + typeof value === 'string' && typeof relatedValue === 'string' && value.length > relatedValue.length; -// ------------------------------------------------------------------------- -// Setup -// ------------------------------------------------------------------------- + const asPromise = validationOptions && validationOptions.context && validationOptions.context.promise; -const validator = new Validator(); + return asPromise ? Promise.resolve(result) : result; + }, + }, + }); + }; + } + + class MyClass { + @IsLongerThan('lastName', { + context: { foo: 'bar' }, + message: '$property must be longer then $constraint1. Given value: $value', + }) + firstName: string; + lastName: string; + } + + class MyClassWithAsyncValidator { + @IsLongerThan('lastName', { + context: { foo: 'bar', promise: true }, + message: '$property must be longer then $constraint1. Given value: $value', + }) + firstName: string; + lastName: string; + } -// ------------------------------------------------------------------------- -// Specifications: common decorators -// ------------------------------------------------------------------------- - -describe("custom decorators", function() { - - describe("decorator with inline validation", function() { - - function IsLongerThan(property: string, validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - registerDecorator({ - target: object.constructor, - propertyName: propertyName, - options: validationOptions, - constraints: [property], - name: "isLongerThan", - validator: { - validate(value: any, args: ValidationArguments) { - const [relatedPropertyName] = args.constraints; - const relatedValue = (args.object as any)[relatedPropertyName]; - if (relatedValue === undefined || relatedValue === null) - return true; - - return typeof value === "string" && - typeof relatedValue === "string" && - value.length > relatedValue.length; - } - } - }); - }; - } - - class MyClass { - @IsLongerThan("lastName", { - message: "$property must be longer then $constraint1. Given value: $value" - }) - firstName: string; - - lastName: string; - } - - it("if firstName is not empty and lastLame is empty then it should succeed", function() { - const model = new MyClass(); - model.firstName = "hell no world"; - return validator.validate(model).then(errors => { - errors.length.should.be.equal(0); - }); - }); - - it("if firstName is empty and lastLame is not empty then it should fail", function() { - const model = new MyClass(); - model.firstName = ""; - model.lastName = "Kim"; - return validator.validate(model).then(errors => { - errors.length.should.be.equal(1); - errors[0].constraints.should.be.eql({ isLongerThan: "firstName must be longer then lastName. Given value: " }); - }); - }); - - it("if firstName is shorter then lastLame then it should fail", function() { - const model = new MyClass(); - model.firstName = "Li"; - model.lastName = "Kim"; - return validator.validate(model).then(errors => { - errors.length.should.be.equal(1); - errors[0].constraints.should.be.eql({ isLongerThan: "firstName must be longer then lastName. Given value: Li" }); - }); - }); - + it('if firstName is not empty and lastLame is empty then it should succeed', () => { + expect.assertions(1); + const model = new MyClass(); + model.firstName = 'hell no world'; + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(0); }); - - describe("decorator with default message", function() { - - function IsLonger(property: string, validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - registerDecorator({ - target: object.constructor, - propertyName: propertyName, - options: validationOptions, - constraints: [property], - name: "isLonger", - validator: { - validate(value: any, args: ValidationArguments) { - const [relatedPropertyName] = args.constraints; - const relatedValue = (args.object as any)[relatedPropertyName]; - if (relatedValue === undefined || relatedValue === null) - return true; - - return typeof value === "string" && - typeof relatedValue === "string" && - value.length > relatedValue.length; - }, - defaultMessage(args: ValidationArguments) { - return args.property + " must be longer then " + args.constraints[0]; - } - } - }); - }; - } - - class SecondClass { - @IsLonger("lastName") - firstName: string; - - lastName: string; - } - - it("if firstName is not empty and lastLame is empty then it should succeed", function() { - const model = new SecondClass(); - model.firstName = "hell no world"; - return validator.validate(model).then(errors => { - errors.length.should.be.equal(0); - }); - }); - - it("if firstName is empty and lastLame is not empty then it should fail", function() { - const model = new SecondClass(); - model.firstName = ""; - model.lastName = "Kim"; - return validator.validate(model).then(errors => { - errors.length.should.be.equal(1); - errors[0].constraints.should.be.eql({ isLonger: "firstName must be longer then lastName" }); - }); - }); - - it("if firstName is shorter then lastLame then it should fail", function() { - const model = new SecondClass(); - model.firstName = "Li"; - model.lastName = "Kim"; - return validator.validate(model).then(errors => { - errors.length.should.be.equal(1); - errors[0].constraints.should.be.eql({ isLonger: "firstName must be longer then lastName" }); - }); - }); - + }); + + it('if firstName is empty and lastLame is not empty then it should fail', () => { + expect.assertions(2); + const model = new MyClass(); + model.firstName = ''; + model.lastName = 'Kim'; + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(1); + expect(errors[0].constraints).toEqual({ isLongerThan: 'firstName must be longer then lastName. Given value: ' }); }); + }); - describe("decorator with separate validation constraint class", function() { + it('if firstName is shorter then lastLame then it should fail', () => { + expect.assertions(2); + const model = new MyClass(); + model.firstName = 'Li'; + model.lastName = 'Kim'; + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(1); + expect(errors[0].constraints).toEqual({ + isLongerThan: 'firstName must be longer then lastName. Given value: Li', + }); + }); + }); - @ValidatorConstraint({ name: "isShortenThan" }) - class IsShortenThanConstraint implements ValidatorConstraintInterface { + it('should include context', () => { + expect.assertions(4); + const model = new MyClass(); + const asyncModel = new MyClassWithAsyncValidator(); + model.firstName = asyncModel.firstName = 'Paul'; + model.lastName = asyncModel.lastName = 'Walker'; - validate(value: any, args: ValidationArguments) { - const [relatedPropertyName] = args.constraints; - const relatedValue = (args.object as any)[relatedPropertyName]; - if (value === null || value === undefined) - return true; - - return typeof value === "string" && - typeof relatedValue === "string" && - value.length < relatedValue.length; - } + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(1); + expect(errors[0].contexts).toEqual({ isLongerThan: { foo: 'bar' } }); + return validator.validate(asyncModel).then(errors => { + expect(errors.length).toEqual(1); + expect(errors[0].contexts).toHaveProperty('isLongerThan.foo', 'bar'); + }); + }); + }); +}); + +describe('decorator with default message', () => { + function IsLonger(property: string, validationOptions?: ValidationOptions) { + return function (object: object, propertyName: string): void { + registerDecorator({ + target: object.constructor, + propertyName: propertyName, + options: validationOptions, + constraints: [property], + name: 'isLonger', + validator: { + validate(value: any, args: ValidationArguments): boolean { + const [relatedPropertyName] = args.constraints; + const relatedValue = (args.object as any)[relatedPropertyName]; + if (relatedValue === undefined || relatedValue === null) return true; + + return typeof value === 'string' && typeof relatedValue === 'string' && value.length > relatedValue.length; + }, + defaultMessage(args: ValidationArguments): string { + return args.property + ' must be longer then ' + args.constraints[0]; + }, + }, + }); + }; + } + + class SecondClass { + @IsLonger('lastName') + firstName: string; + lastName: string; + } - } - - function IsShortenThan(property: string, validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - registerDecorator({ - target: object.constructor, - propertyName: propertyName, - options: validationOptions, - constraints: [property], - validator: IsShortenThanConstraint - }); - }; - } - - class MyClass { - firstName: string; - - @IsShortenThan("firstName", { - message: "$property must be shorter then $constraint1. Given value: $value" - }) - lastName: string; - } - - it("if firstName is not empty and lastLame is empty then it should succeed", function() { - const model = new MyClass(); - model.firstName = "hell no world"; - return validator.validate(model).then(errors => { - errors.length.should.be.equal(0); - }); - }); - - it("if firstName is empty and lastLame is not empty then it should fail", function() { - const model = new MyClass(); - model.firstName = ""; - model.lastName = "Kim"; - return validator.validate(model).then(errors => { - errors.length.should.be.equal(1); - errors[0].constraints.should.be.eql({ isShortenThan: "lastName must be shorter then firstName. Given value: Kim" }); - }); - }); - - it("if firstName is shorter then lastLame then it should fail", function() { - const model = new MyClass(); - model.firstName = "Li"; - model.lastName = "Kim"; - return validator.validate(model).then(errors => { - errors.length.should.be.equal(1); - errors[0].constraints.should.be.eql({ isShortenThan: "lastName must be shorter then firstName. Given value: Kim" }); - }); - }); + it('if firstName is not empty and lastLame is empty then it should succeed', () => { + expect.assertions(1); + const model = new SecondClass(); + model.firstName = 'hell no world'; + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(0); + }); + }); + + it('if firstName is empty and lastLame is not empty then it should fail', () => { + expect.assertions(2); + const model = new SecondClass(); + model.firstName = ''; + model.lastName = 'Kim'; + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(1); + expect(errors[0].constraints).toEqual({ isLonger: 'firstName must be longer then lastName' }); + }); + }); + it('if firstName is shorter then lastLame then it should fail', () => { + expect.assertions(2); + const model = new SecondClass(); + model.firstName = 'Li'; + model.lastName = 'Kim'; + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(1); + expect(errors[0].constraints).toEqual({ isLonger: 'firstName must be longer then lastName' }); }); + }); +}); +describe('decorator with separate validation constraint class', () => { + @ValidatorConstraint({ name: 'isShortenThan' }) + class IsShortenThanConstraint implements ValidatorConstraintInterface { + validate(value: any, args: ValidationArguments): boolean { + const [relatedPropertyName] = args.constraints; + const relatedValue = (args.object as any)[relatedPropertyName]; + if (value === null || value === undefined) return true; + + return typeof value === 'string' && typeof relatedValue === 'string' && value.length < relatedValue.length; + } + } + + function IsShorterThan(property: string, validationOptions?: ValidationOptions) { + return function (object: object, propertyName: string): void { + registerDecorator({ + target: object.constructor, + propertyName: propertyName, + options: validationOptions, + constraints: [property], + validator: IsShortenThanConstraint, + }); + }; + } + + class MyClass { + firstName: string; + + @IsShorterThan('firstName', { + message: '$property must be shorter then $constraint1. Given value: $value', + }) + lastName: string; + } + + it('if firstName is not empty and lastLame is empty then it should succeed', () => { + expect.assertions(1); + const model = new MyClass(); + model.firstName = 'hell no world'; + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(0); + }); + }); + + it('if firstName is empty and lastLame is not empty then it should fail', () => { + expect.assertions(2); + const model = new MyClass(); + model.firstName = ''; + model.lastName = 'Kim'; + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(1); + expect(errors[0].constraints).toEqual({ + isShortenThan: 'lastName must be shorter then firstName. Given value: Kim', + }); + }); + }); + + it('if firstName is shorter then lastLame then it should fail', () => { + expect.assertions(2); + const model = new MyClass(); + model.firstName = 'Li'; + model.lastName = 'Kim'; + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(1); + expect(errors[0].constraints).toEqual({ + isShortenThan: 'lastName must be shorter then firstName. Given value: Kim', + }); + }); + }); +}); + +describe('decorator with symbol constraint', () => { + const mySymbol = Symbol('mySymbol'); + + function IsSameType(property: unknown, validationOptions?: ValidationOptions) { + return function (object: object, propertyName: string): void { + registerDecorator({ + target: object.constructor, + propertyName: propertyName, + options: validationOptions, + constraints: [property], + validator: { + validate(value: any, args: ValidationArguments) { + return typeof value === typeof args.constraints[0]; + }, + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be of type ' + typeof property, + validationOptions + ), + }, + }); + }; + } + + class MyClass { + @IsSameType(mySymbol) + property: symbol; + } + + it('if property is not a symbol then it should fail', () => { + expect.assertions(2); + const model = new MyClass(); + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(1); + expect(errors[0].constraints.customValidation).toEqual('property must be of type symbol'); + }); + }); }); diff --git a/test/functional/inherited-validation.spec.ts b/test/functional/inherited-validation.spec.ts index 23494b5c71..04254a9d3b 100644 --- a/test/functional/inherited-validation.spec.ts +++ b/test/functional/inherited-validation.spec.ts @@ -1,56 +1,37 @@ -import "es6-shim"; -import {Contains, MinLength} from "../../src/decorator/decorators"; -import {Validator} from "../../src/validation/Validator"; - -import {should, use } from "chai"; - -import * as chaiAsPromised from "chai-as-promised"; - -should(); -use(chaiAsPromised); - -// ------------------------------------------------------------------------- -// Setup -// ------------------------------------------------------------------------- +import { Contains, MinLength } from '../../src/decorator/decorators'; +import { Validator } from '../../src/validation/Validator'; const validator = new Validator(); -// ------------------------------------------------------------------------- -// Specifications: common decorators -// ------------------------------------------------------------------------- - -describe("inherited validation", function() { - - it("should validate inherited properties", function() { - - class MyClass { - @Contains("hello") - title: string; - } - - class MySubClass extends MyClass { - @MinLength(5) - name: string; - } - - const model = new MySubClass(); - model.title = "helo world"; - model.name = "my"; - return validator.validate(model).then(errors => { - errors.length.should.be.equal(2); - - // subclass own props are validated first - errors[0].target.should.be.equal(model); - errors[0].property.should.be.equal("name"); - errors[0].constraints.should.be.eql({ minLength: "name must be longer than or equal to 5 characters" }); - errors[0].value.should.be.equal("my"); - - // parent props are validated afterwards - errors[1].target.should.be.equal(model); - errors[1].property.should.be.equal("title"); - errors[1].constraints.should.be.eql({ contains: "title must contain a hello string" }); - errors[1].value.should.be.equal("helo world"); - }); +describe('inherited validation', () => { + it('should validate inherited properties', () => { + expect.assertions(9); + + class MyClass { + @Contains('hello') + title: string; + } + + class MySubClass extends MyClass { + @MinLength(5) + name: string; + } + + const model = new MySubClass(); + model.title = 'helo world'; + model.name = 'my'; + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(2); + // subclass own props are validated first + expect(errors[0].target).toEqual(model); + expect(errors[0].property).toEqual('name'); + expect(errors[0].constraints).toEqual({ minLength: 'name must be longer than or equal to 5 characters' }); + expect(errors[0].value).toEqual('my'); + // parent props are validated afterwards + expect(errors[1].target).toEqual(model); + expect(errors[1].property).toEqual('title'); + expect(errors[1].constraints).toEqual({ contains: 'title must contain a hello string' }); + expect(errors[1].value).toEqual('helo world'); }); - -}); \ No newline at end of file + }); +}); diff --git a/test/functional/nested-validation.spec.ts b/test/functional/nested-validation.spec.ts index 40b551e6e4..b7c3eb152b 100644 --- a/test/functional/nested-validation.spec.ts +++ b/test/functional/nested-validation.spec.ts @@ -1,141 +1,335 @@ -import "es6-shim"; -import {Contains, IsDefined, MinLength, ValidateNested} from "../../src/decorator/decorators"; -import {Validator} from "../../src/validation/Validator"; -import {expect} from "chai"; -import {inspect} from "util"; -import {ValidationTypes} from "../../src/validation/ValidationTypes"; - -import {should, use } from "chai"; - -import * as chaiAsPromised from "chai-as-promised"; - -should(); -use(chaiAsPromised); - -// ------------------------------------------------------------------------- -// Setup -// ------------------------------------------------------------------------- +import { Contains, IsDefined, MinLength, ValidateNested } from '../../src/decorator/decorators'; +import { Validator } from '../../src/validation/Validator'; +import { ValidationTypes } from '../../src/validation/ValidationTypes'; const validator = new Validator(); -// ------------------------------------------------------------------------- -// Specifications: common decorators -// ------------------------------------------------------------------------- - -describe("nested validation", function () { - - it("should not validate missing nested objects", function () { +describe('nested validation', () => { + it('should not validate missing nested objects', () => { + expect.assertions(4); - class MySubClass { - @MinLength(5) - name: string; - } + class MySubClass { + @MinLength(5) + name: string; + } - class MyClass { - @Contains("hello") - title: string; + class MyClass { + @Contains('hello') + title: string; - @ValidateNested() @IsDefined() - mySubClass: MySubClass; - } + @ValidateNested() + @IsDefined() + mySubClass: MySubClass; + } - const model: any = new MyClass(); + const model: MyClass = new MyClass(); + model.title = 'helo'; - model.title = "helo"; - return validator.validate(model).then(errors => { - errors[1].target.should.be.equal(model); - expect(errors[1].value).to.be.undefined; - errors[1].property.should.be.equal("mySubClass"); - errors[1].constraints.should.be.eql({isDefined: "mySubClass should not be null or undefined"}); - }); + return validator.validate(model).then(errors => { + expect(errors[1].target).toEqual(model); + expect(errors[1].value).toBeUndefined(); + expect(errors[1].property).toEqual('mySubClass'); + expect(errors[1].constraints).toEqual({ isDefined: 'mySubClass should not be null or undefined' }); }); - - - it("should validate nested objects", function () { - - class MySubClass { - @MinLength(5) - name: string; - } - - class MyClass { - @Contains("hello") - title: string; - - @ValidateNested() - mySubClass: MySubClass; - - @ValidateNested() - mySubClasses: MySubClass[]; - } - - const model = new MyClass(); - model.title = "helo world"; - model.mySubClass = new MySubClass(); - model.mySubClass.name = "my"; - model.mySubClasses = [new MySubClass(), new MySubClass()]; - model.mySubClasses[0].name = "my"; - model.mySubClasses[1].name = "not-short"; - return validator.validate(model).then(errors => { - errors.length.should.be.equal(3); - - errors[0].target.should.be.equal(model); - errors[0].property.should.be.equal("title"); - errors[0].constraints.should.be.eql({contains: "title must contain a hello string"}); - errors[0].value.should.be.equal("helo world"); - - errors[1].target.should.be.equal(model); - errors[1].property.should.be.equal("mySubClass"); - errors[1].value.should.be.equal(model.mySubClass); - expect(errors[1].constraints).to.be.undefined; - const subError1 = errors[1].children[0]; - subError1.target.should.be.equal(model.mySubClass); - subError1.property.should.be.equal("name"); - subError1.constraints.should.be.eql({minLength: "name must be longer than or equal to 5 characters"}); - subError1.value.should.be.equal("my"); - - errors[2].target.should.be.equal(model); - errors[2].property.should.be.equal("mySubClasses"); - errors[2].value.should.be.equal(model.mySubClasses); - expect(errors[2].constraints).to.be.undefined; - const subError2 = errors[2].children[0]; - subError2.target.should.be.equal(model.mySubClasses); - subError2.value.should.be.equal(model.mySubClasses[0]); - subError2.property.should.be.equal("0"); - const subSubError = subError2.children[0]; - subSubError.target.should.be.equal(model.mySubClasses[0]); - subSubError.property.should.be.equal("name"); - subSubError.constraints.should.be.eql({minLength: "name must be longer than or equal to 5 characters"}); - subSubError.value.should.be.equal("my"); - }); + }); + + it('should validate nested objects', () => { + expect.assertions(55); + + class MySubClass { + @MinLength(5) + name: string; + } + + class MyClass { + @Contains('hello') + title: string; + + @ValidateNested() + mySubClass: MySubClass; + + @ValidateNested() + mySubClasses: MySubClass[]; + + @ValidateNested() + mySubSubClasses: MySubClass[][]; + + @ValidateNested() + mySubSubSubClasses: MySubClass[][][]; + } + + const model = new MyClass(); + model.title = 'helo world'; + model.mySubClass = new MySubClass(); + model.mySubClass.name = 'my'; + model.mySubClasses = [new MySubClass(), new MySubClass()]; + model.mySubClasses[0].name = 'my'; + model.mySubClasses[1].name = 'not-short'; + model.mySubSubClasses = [[new MySubClass()]]; + model.mySubSubClasses[0][0].name = 'sub'; + model.mySubSubSubClasses = [[[new MySubClass()]]]; + model.mySubSubSubClasses[0][0][0].name = 'sub'; + + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(5); + + expect(errors[0].target).toEqual(model); + expect(errors[0].property).toEqual('title'); + expect(errors[0].constraints).toEqual({ contains: 'title must contain a hello string' }); + expect(errors[0].value).toEqual('helo world'); + + expect(errors[1].target).toEqual(model); + expect(errors[1].property).toEqual('mySubClass'); + expect(errors[1].value).toEqual(model.mySubClass); + expect(errors[1].constraints).toBeUndefined(); + const subError1 = errors[1].children[0]; + expect(subError1.target).toEqual(model.mySubClass); + expect(subError1.property).toEqual('name'); + expect(subError1.constraints).toEqual({ minLength: 'name must be longer than or equal to 5 characters' }); + expect(subError1.value).toEqual('my'); + + expect(errors[2].target).toEqual(model); + expect(errors[2].property).toEqual('mySubClasses'); + expect(errors[2].value).toEqual(model.mySubClasses); + expect(errors[2].constraints).toBeUndefined(); + const subError2 = errors[2].children[0]; + expect(subError2.target).toEqual(model.mySubClasses); + expect(subError2.value).toEqual(model.mySubClasses[0]); + expect(subError2.property).toEqual('0'); + const subSubError = subError2.children[0]; + expect(subSubError.target).toEqual(model.mySubClasses[0]); + expect(subSubError.property).toEqual('name'); + expect(subSubError.constraints).toEqual({ minLength: 'name must be longer than or equal to 5 characters' }); + expect(subSubError.value).toEqual('my'); + + expect(errors[3].target).toEqual(model); + expect(errors[3].property).toEqual('mySubSubClasses'); + expect(errors[3].value).toEqual(model.mySubSubClasses); + expect(errors[3].constraints).toBeUndefined(); + const subError3 = errors[3].children[0]; + expect(subError3.target).toEqual(model.mySubSubClasses); + expect(subError3.value).toEqual(model.mySubSubClasses[0]); + expect(subError3.property).toEqual('0'); + const subSubError3 = subError3.children[0]; + expect(subSubError3.target).toEqual(model.mySubSubClasses[0]); + expect(subSubError3.value).toEqual(model.mySubSubClasses[0][0]); + expect(subSubError3.property).toEqual('0'); + const subSubSubError3 = subSubError3.children[0]; + expect(subSubSubError3.target).toEqual(model.mySubSubClasses[0][0]); + expect(subSubSubError3.property).toEqual('name'); + expect(subSubSubError3.constraints).toEqual({ minLength: 'name must be longer than or equal to 5 characters' }); + expect(subSubSubError3.value).toEqual('sub'); + + expect(errors[4].target).toEqual(model); + expect(errors[4].property).toEqual('mySubSubSubClasses'); + expect(errors[4].value).toEqual(model.mySubSubSubClasses); + expect(errors[4].constraints).toBeUndefined(); + const subError4 = errors[4].children[0]; + expect(subError4.target).toEqual(model.mySubSubSubClasses); + expect(subError4.value).toEqual(model.mySubSubSubClasses[0]); + expect(subError4.property).toEqual('0'); + const subSubError4 = subError4.children[0]; + expect(subSubError4.target).toEqual(model.mySubSubSubClasses[0]); + expect(subSubError4.value).toEqual(model.mySubSubSubClasses[0][0]); + expect(subSubError4.property).toEqual('0'); + const subSubSubError4 = subSubError4.children[0]; + expect(subSubSubError4.target).toEqual(model.mySubSubSubClasses[0][0]); + expect(subSubSubError4.value).toEqual(model.mySubSubSubClasses[0][0][0]); + expect(subSubSubError4.property).toEqual('0'); + const subSubSubSubError4 = subSubSubError4.children[0]; + expect(subSubSubSubError4.target).toEqual(model.mySubSubSubClasses[0][0][0]); + expect(subSubSubSubError4.property).toEqual('name'); + expect(subSubSubSubError4.constraints).toEqual({ + minLength: 'name must be longer than or equal to 5 characters', + }); + expect(subSubSubSubError4.value).toEqual('sub'); }); - - it("should validate when nested is not object", () => { - - class MySubClass { - @MinLength(5) - name: string; - } - - class MyClass { - @ValidateNested() - mySubClass: MySubClass; - - } - - const model = new MyClass(); - model.mySubClass = "invalidnested object"; - - return validator.validate(model).then(errors => { - - expect(errors[0].target).to.equal(model); - expect(errors[0].property).to.equal("mySubClass"); - expect(errors[0].children.length).to.equal(1); - - const subError = errors[0].children[0]; - subError.constraints.should.be.eql({[ValidationTypes.NESTED_VALIDATION]: "nested property mySubClass must be either object or array"}); - }); - + }); + + it('should validate when nested is not object', () => { + expect.assertions(4); + + class MySubClass { + @MinLength(5) + name: string; + } + + class MyClass { + @ValidateNested() + mySubClass: MySubClass; + } + + const model = new MyClass(); + model.mySubClass = 'invalidnested object' as any; + + return validator.validate(model).then(errors => { + expect(errors[0].target).toEqual(model); + expect(errors[0].property).toEqual('mySubClass'); + expect(errors[0].children.length).toEqual(0); + expect(errors[0].constraints).toEqual({ + [ValidationTypes.NESTED_VALIDATION]: 'nested property mySubClass must be either object or array', + }); }); - + }); + + it('should validate nested set', () => { + expect.assertions(24); + + class MySubClass { + @MinLength(5) + name: string; + } + + class MyClass { + @Contains('hello') + title: string; + + @ValidateNested() + mySubClass: MySubClass; + + @ValidateNested() + mySubClasses: Set; + } + + const model = new MyClass(); + model.title = 'helo world'; + model.mySubClass = new MySubClass(); + model.mySubClass.name = 'my'; + model.mySubClasses = new Set(); + + const submodel1 = new MySubClass(); + submodel1.name = 'my'; + model.mySubClasses.add(submodel1); + + const submodel2 = new MySubClass(); + submodel2.name = 'not-short'; + model.mySubClasses.add(submodel2); + + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(3); + + expect(errors[0].target).toEqual(model); + expect(errors[0].property).toEqual('title'); + expect(errors[0].constraints).toEqual({ contains: 'title must contain a hello string' }); + expect(errors[0].value).toEqual('helo world'); + + expect(errors[1].target).toEqual(model); + expect(errors[1].property).toEqual('mySubClass'); + expect(errors[1].value).toEqual(model.mySubClass); + expect(errors[1].constraints).toBeUndefined(); + const subError1 = errors[1].children[0]; + expect(subError1.target).toEqual(model.mySubClass); + expect(subError1.property).toEqual('name'); + expect(subError1.constraints).toEqual({ minLength: 'name must be longer than or equal to 5 characters' }); + expect(subError1.value).toEqual('my'); + + expect(errors[2].target).toEqual(model); + expect(errors[2].property).toEqual('mySubClasses'); + expect(errors[2].value).toEqual(model.mySubClasses); + expect(errors[2].constraints).toBeUndefined(); + const subError2 = errors[2].children[0]; + expect(subError2.target).toEqual(model.mySubClasses); + expect(subError2.value).toEqual(submodel1); + expect(subError2.property).toEqual('0'); + const subSubError = subError2.children[0]; + expect(subSubError.target).toEqual(submodel1); + expect(subSubError.property).toEqual('name'); + expect(subSubError.constraints).toEqual({ minLength: 'name must be longer than or equal to 5 characters' }); + expect(subSubError.value).toEqual('my'); + }); + }); + + it('should validate nested map', () => { + expect.assertions(24); + + class MySubClass { + @MinLength(5) + name: string; + } + + class MyClass { + @Contains('hello') + title: string; + + @ValidateNested() + mySubClass: MySubClass; + + @ValidateNested() + mySubClasses: Map; + } + + const model = new MyClass(); + model.title = 'helo world'; + model.mySubClass = new MySubClass(); + model.mySubClass.name = 'my'; + model.mySubClasses = new Map(); + + const submodel1 = new MySubClass(); + submodel1.name = 'my'; + model.mySubClasses.set('key1', submodel1); + + const submodel2 = new MySubClass(); + submodel2.name = 'not-short'; + model.mySubClasses.set('key2', submodel2); + + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(3); + + expect(errors[0].target).toEqual(model); + expect(errors[0].property).toEqual('title'); + expect(errors[0].constraints).toEqual({ contains: 'title must contain a hello string' }); + expect(errors[0].value).toEqual('helo world'); + + expect(errors[1].target).toEqual(model); + expect(errors[1].property).toEqual('mySubClass'); + expect(errors[1].value).toEqual(model.mySubClass); + expect(errors[1].constraints).toBeUndefined(); + const subError1 = errors[1].children[0]; + expect(subError1.target).toEqual(model.mySubClass); + expect(subError1.property).toEqual('name'); + expect(subError1.constraints).toEqual({ minLength: 'name must be longer than or equal to 5 characters' }); + expect(subError1.value).toEqual('my'); + + expect(errors[2].target).toEqual(model); + expect(errors[2].property).toEqual('mySubClasses'); + expect(errors[2].value).toEqual(model.mySubClasses); + expect(errors[2].constraints).toBeUndefined(); + const subError2 = errors[2].children[0]; + expect(subError2.target).toEqual(model.mySubClasses); + expect(subError2.value).toEqual(submodel1); + expect(subError2.property).toEqual('key1'); + const subSubError = subError2.children[0]; + expect(subSubError.target).toEqual(submodel1); + expect(subSubError.property).toEqual('name'); + expect(subSubError.constraints).toEqual({ minLength: 'name must be longer than or equal to 5 characters' }); + expect(subSubError.value).toEqual('my'); + }); + }); + + it('nestedValidation should be defined as an error for the property specifying the decorator when validation fails.', () => { + class MySubClass { + @MinLength(5) + name: string; + } + + class MyClass { + @ValidateNested() + nestedWithClassValue: MySubClass; + + @ValidateNested() + nestedWithPrimitiveValue: MySubClass; + } + + const model = new MyClass(); + model.nestedWithClassValue = new MySubClass(); + model.nestedWithPrimitiveValue = 'invalid' as any; + + return validator.validate(model, { stopAtFirstError: true }).then(errors => { + expect(errors[0].property).toEqual('nestedWithClassValue'); + expect(errors[0].children.length).toEqual(1); + expect(errors[0].children[0].constraints).toHaveProperty('minLength'); + expect(errors[1].property).toEqual('nestedWithPrimitiveValue'); + expect(errors[1].constraints).toHaveProperty('nestedValidation'); + }); + }); }); diff --git a/test/functional/promise-validation.spec.ts b/test/functional/promise-validation.spec.ts new file mode 100644 index 0000000000..fdcb7469f5 --- /dev/null +++ b/test/functional/promise-validation.spec.ts @@ -0,0 +1,155 @@ +import { Contains, IsDefined, MinLength, ValidateNested, ValidatePromise } from '../../src/decorator/decorators'; +import { Validator } from '../../src/validation/Validator'; +import { ValidationTypes } from '../../src/validation/ValidationTypes'; + +const validator = new Validator(); + +describe('promise validation', () => { + it('should not validate missing nested objects', () => { + expect.assertions(4); + + class MySubClass { + @MinLength(5) + name: string; + } + + class MyClass { + @Contains('hello') + title: string; + + @ValidatePromise() + @ValidateNested() + @IsDefined() + mySubClass: Promise; + } + + const model: any = new MyClass(); + model.title = 'helo'; + + return validator.validate(model).then(errors => { + expect(errors[1].target).toEqual(model); + expect(errors[1].value).toBeUndefined(); + expect(errors[1].property).toEqual('mySubClass'); + expect(errors[1].constraints).toEqual({ isDefined: 'mySubClass should not be null or undefined' }); + }); + }); + + it('should validate nested objects', () => { + expect.assertions(24); + + class MySubClass { + @MinLength(5) + name: string; + } + + class MyClass { + @Contains('hello') + title: string; + + @ValidatePromise() + @ValidateNested() + mySubClass: Promise; + + @ValidatePromise() + @ValidateNested() + mySubClasses: Promise; + } + + const model = new MyClass(); + model.title = 'helo world'; + const mySubClass = new MySubClass(); + mySubClass.name = 'my'; + model.mySubClass = Promise.resolve(mySubClass); + const mySubClasses = [new MySubClass(), new MySubClass()]; + mySubClasses[0].name = 'my'; + mySubClasses[1].name = 'not-short'; + model.mySubClasses = Promise.resolve(mySubClasses); + return validator.validate(model).then(errors => { + return Promise.all([model.mySubClass, model.mySubClasses]).then(([modelMySubClass, modelMySubClasses]) => { + expect(errors.length).toEqual(3); + + expect(errors[0].target).toEqual(model); + expect(errors[0].property).toEqual('title'); + expect(errors[0].constraints).toEqual({ contains: 'title must contain a hello string' }); + expect(errors[0].value).toEqual('helo world'); + + expect(errors[1].target).toEqual(model); + expect(errors[1].property).toEqual('mySubClass'); + expect(errors[1].value).toEqual(modelMySubClass); + expect(errors[1].constraints).toBeUndefined(); + const subError1 = errors[1].children[0]; + expect(subError1.target).toEqual(modelMySubClass); + expect(subError1.property).toEqual('name'); + expect(subError1.constraints).toEqual({ minLength: 'name must be longer than or equal to 5 characters' }); + expect(subError1.value).toEqual('my'); + + expect(errors[2].target).toEqual(model); + expect(errors[2].property).toEqual('mySubClasses'); + expect(errors[2].value).toEqual(modelMySubClasses); + expect(errors[2].constraints).toBeUndefined(); + const subError2 = errors[2].children[0]; + expect(subError2.target).toEqual(modelMySubClasses); + expect(subError2.value).toEqual(modelMySubClasses[0]); + expect(subError2.property).toEqual('0'); + const subSubError = subError2.children[0]; + expect(subSubError.target).toEqual(modelMySubClasses[0]); + expect(subSubError.property).toEqual('name'); + expect(subSubError.constraints).toEqual({ minLength: 'name must be longer than or equal to 5 characters' }); + expect(subSubError.value).toEqual('my'); + }); + }); + }); + + it('should validate when nested is not object', () => { + expect.assertions(4); + + class MySubClass { + @MinLength(5) + name: string; + } + + class MyClass { + @ValidatePromise() + @ValidateNested() + mySubClass: MySubClass; + } + + const model = new MyClass(); + model.mySubClass = 'invalidnested object' as any; + + return validator.validate(model).then(errors => { + expect(errors[0].target).toEqual(model); + expect(errors[0].property).toEqual('mySubClass'); + expect(errors[0].children.length).toEqual(0); + expect(errors[0].constraints).toEqual({ + [ValidationTypes.NESTED_VALIDATION]: 'nested property mySubClass must be either object or array', + }); + }); + }); + + it('should validate array promise', () => { + expect.assertions(5); + + class MyClass { + @ValidatePromise() + @MinLength(2) + arrProperty: Promise; + } + + const model = new MyClass(); + model.arrProperty = Promise.resolve(['one']); + + return validator.validate(model).then(errors => { + return Promise.all([model.arrProperty]).then(([modelArrProperty]) => { + expect(errors.length).toEqual(1); + + expect(errors[0].target).toEqual(model); + expect(errors[0].property).toEqual('arrProperty'); + expect(errors[0].constraints).toEqual({ + minLength: 'arrProperty must be longer than or equal to 2 characters', + }); + expect(errors[0].value).toEqual(modelArrProperty); + }); + }); + }); +}); diff --git a/test/functional/reject-validation.spec.ts b/test/functional/reject-validation.spec.ts index b3d35a5a5d..3a51db74ac 100644 --- a/test/functional/reject-validation.spec.ts +++ b/test/functional/reject-validation.spec.ts @@ -1,55 +1,39 @@ -import "es6-shim"; - -import { ValidationError } from "./../../src/validation/ValidationError"; -import { Contains, MinLength } from "../../src/decorator/decorators"; -import { Validator } from "../../src/validation/Validator"; -import { expect } from "chai"; - -import {should, use } from "chai"; - -import * as chaiAsPromised from "chai-as-promised"; - -should(); -use(chaiAsPromised); +import { ValidationError } from './../../src/validation/ValidationError'; +import { Contains } from '../../src/decorator/decorators'; +import { Validator } from '../../src/validation/Validator'; class MyClass { - @Contains("hello", { - message: "$value is not valid. Your string must contain a hello word" - }) - someProperty: string; + @Contains('hello', { + message: '$value is not valid. Your string must contain a hello word', + }) + someProperty: string; } -describe("validateOrReject()", () => { - let validator: Validator; - let model: MyClass; +describe('validateOrReject()', () => { + let validator: Validator; + let model: MyClass; - beforeEach(() => { - validator = new Validator(); - model = new MyClass(); - }); + beforeEach(() => { + validator = new Validator(); + model = new MyClass(); + }); - it("should resolve promise when no error", (done) => { - model.someProperty = "hello world"; - validator.validateOrReject(model) - .then((args) => { - expect(args).to.not.exist; - done(); - }) - .catch((errors) => { - done("should resolve promise"); - }); + it('should resolve promise when no error', () => { + expect.assertions(1); + model.someProperty = 'hello world'; + return validator.validateOrReject(model).then(args => { + expect(args).toBeUndefined(); }); - - it("should reject promise on error", (done) => { - model.someProperty = "hell no world"; - validator.validateOrReject(model) - .then(() => { - done("should reject promise"); - }) - .catch((errors: ValidationError[]) => { - expect(errors).to.have.lengthOf(1); - expect(errors[0].constraints).to.deep.equal({ contains: "hell no world is not valid. Your string must contain a hello word" }); - done(); - }); + }); + + it('should reject promise on error', () => { + expect.assertions(2); + model.someProperty = 'hell no world'; + return validator.validateOrReject(model).catch((errors: ValidationError[]) => { + expect(errors.length).toEqual(1); + expect(errors[0].constraints).toEqual({ + contains: 'hell no world is not valid. Your string must contain a hello word', + }); }); + }); }); diff --git a/test/functional/sync-validation.spec.ts b/test/functional/sync-validation.spec.ts new file mode 100644 index 0000000000..39ae097587 --- /dev/null +++ b/test/functional/sync-validation.spec.ts @@ -0,0 +1,70 @@ +import { Validator } from '../../src/validation/Validator'; +import { ValidationArguments } from '../../src/validation/ValidationArguments'; +import { registerDecorator } from '../../src/register-decorator'; +import { ValidationOptions } from '../../src/decorator/ValidationOptions'; +import { ValidatorConstraint, Validate, IsNotEmpty } from '../../src/decorator/decorators'; +import { ValidatorConstraintInterface } from '../../src/validation/ValidatorConstraintInterface'; + +const validator = new Validator(); + +describe('sync validation should ignore async validation constraints', () => { + @ValidatorConstraint({ name: 'isShortenThan', async: true }) + class IsShortenThanConstraint implements ValidatorConstraintInterface { + validate(value: any, args: ValidationArguments): Promise { + return Promise.resolve(false); + } + } + + function IsLonger(property: string, validationOptions?: ValidationOptions) { + return function (object: object, propertyName: string): void { + registerDecorator({ + target: object.constructor, + propertyName: propertyName, + options: validationOptions, + constraints: [property], + async: true, + name: 'isLonger', + validator: { + validate(value: any, args: ValidationArguments): Promise { + return Promise.resolve(false); + }, + }, + }); + }; + } + + class SecondClass { + @IsLonger('lastName') + firstName: string; + + @Validate(IsShortenThanConstraint) + lastName: string; + + @IsNotEmpty({ message: 'name should not be empty' }) + name: string; + + @IsNotEmpty() + alwaysWithValue: string = 'this field always has a value'; + } + + it('should ignore async validations and validate only sync validation types', () => { + expect.assertions(1); + const model = new SecondClass(); + model.firstName = 'such validation may lead'; + model.firstName = 'to recursion'; + model.name = 'Umed'; + const errors = validator.validateSync(model); + expect(errors.length).toEqual(0); + }); + + it('should ignore async validations and validate only sync validation types', () => { + expect.assertions(2); + const model = new SecondClass(); + model.firstName = 'such validation may lead'; + model.firstName = 'to recursion'; + model.name = ''; + const errors = validator.validateSync(model); + expect(errors.length).toEqual(1); + expect(errors[0].constraints).toEqual({ isNotEmpty: 'name should not be empty' }); + }); +}); diff --git a/test/functional/sync-validation.ts b/test/functional/sync-validation.ts deleted file mode 100644 index 925d53d01e..0000000000 --- a/test/functional/sync-validation.ts +++ /dev/null @@ -1,93 +0,0 @@ -import "es6-shim"; -import {Validator} from "../../src/validation/Validator"; -import {ValidationArguments} from "../../src/validation/ValidationArguments"; -import {registerDecorator} from "../../src/register-decorator"; -import {ValidationOptions} from "../../src/decorator/ValidationOptions"; -import {ValidatorConstraint, Validate, IsNotEmpty} from "../../src/decorator/decorators"; -import {ValidatorConstraintInterface} from "../../src/validation/ValidatorConstraintInterface"; - -import {should, use } from "chai"; - -import * as chaiAsPromised from "chai-as-promised"; - -should(); -use(chaiAsPromised); - -// ------------------------------------------------------------------------- -// Setup -// ------------------------------------------------------------------------- - -const validator = new Validator(); - -// ------------------------------------------------------------------------- -// Specifications: common decorators -// ------------------------------------------------------------------------- - -describe("sync validation", function() { - - describe("sync validation should ignore async validation constraints", function() { - - @ValidatorConstraint({ name: "isShortenThan", async: true }) - class IsShortenThanConstraint implements ValidatorConstraintInterface { - - validate(value: any, args: ValidationArguments) { - return Promise.resolve(false); - } - - } - - function IsLonger(property: string, validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { - registerDecorator({ - target: object.constructor, - propertyName: propertyName, - options: validationOptions, - constraints: [property], - async: true, - name: "isLonger", - validator: { - validate(value: any, args: ValidationArguments) { - return Promise.resolve(false); - } - } - }); - }; - } - - class SecondClass { - @IsLonger("lastName") - firstName: string; - - @Validate(IsShortenThanConstraint) - lastName: string; - - @IsNotEmpty({ message: "name should not be empty" }) - name: string; - - @IsNotEmpty() - alwaysWithValue: string = "this field always has a value"; - } - - it("should ignore async validations and validate only sync validation types", function() { - const model = new SecondClass(); - model.firstName = "such validation may lead"; - model.firstName = "to recursion"; - model.name = "Umed"; - const errors = validator.validateSync(model); - console.log(errors); - errors.length.should.be.equal(0); - }); - - it("should ignore async validations and validate only sync validation types", function() { - const model = new SecondClass(); - model.firstName = "such validation may lead"; - model.firstName = "to recursion"; - model.name = ""; - const errors = validator.validateSync(model); - errors.length.should.be.equal(1); - errors[0].constraints.should.be.eql({ isNotEmpty: "name should not be empty" }); - }); - - }); - -}); diff --git a/test/functional/validation-error.spec.ts b/test/functional/validation-error.spec.ts index ab4520cb42..d2a7f8db99 100644 --- a/test/functional/validation-error.spec.ts +++ b/test/functional/validation-error.spec.ts @@ -1,18 +1,5 @@ -import "es6-shim"; -import { IsNotEmpty, IsString, IsUrl, IsOptional, ValidateNested, MinLength } from "../../src/decorator/decorators"; -import { Validator } from "../../src/validation/Validator"; -import { expect } from "chai"; - -import {should, use } from "chai"; - -import * as chaiAsPromised from "chai-as-promised"; - -should(); -use(chaiAsPromised); - -// ------------------------------------------------------------------------- -// Setup -// ------------------------------------------------------------------------- +import { IsString, IsUrl, IsOptional, ValidateNested, MinLength } from '../../src/decorator/decorators'; +import { Validator } from '../../src/validation/Validator'; const validator = new Validator(); @@ -23,10 +10,9 @@ const validator = new Validator(); * - testing arrays * - testing color codes? */ -describe("ValidationError", function () { - it("should correctly log error message without ANSI escape codes", async function () { +describe('ValidationError', () => { + it('should correctly log error message without ANSI escape codes', async () => { class NestedClass { - @IsString() public name: string; @@ -42,8 +28,8 @@ describe("ValidationError", function () { this.name = name; this.insideNested = insideNested; } - } + class RootClass { @IsString() @MinLength(15) @@ -56,28 +42,49 @@ describe("ValidationError", function () { public nestedArr: NestedClass[]; constructor() { - this.title = (5 as any); - this.nestedObj = new NestedClass("invalid-url", 5, new NestedClass("invalid-url", 5)); - this.nestedArr = [new NestedClass("invalid-url", 5), new NestedClass("invalid-url", 5)]; + this.title = 5 as any; + this.nestedObj = new NestedClass('invalid-url', 5, new NestedClass('invalid-url', 5)); + this.nestedArr = [new NestedClass('invalid-url', 5), new NestedClass('invalid-url', 5)]; } } const validationErrors = await validator.validate(new RootClass()); - - validationErrors[0].toString().should.be.equal("An instance of RootClass has failed the validation:\n" + - " - property title has failed the following constraints: minLength, isString \n"); - - validationErrors[1].toString().should.be.equal("An instance of RootClass has failed the validation:\n" + - " - property nestedObj.name has failed the following constraints: isString \n" + - " - property nestedObj.url has failed the following constraints: isUrl \n" + - " - property nestedObj.insideNested.name has failed the following constraints: isString \n" + - " - property nestedObj.insideNested.url has failed the following constraints: isUrl \n"); - - validationErrors[2].toString().should.be.equal("An instance of RootClass has failed the validation:\n" + - " - property nestedArr[0].name has failed the following constraints: isString \n" + - " - property nestedArr[0].url has failed the following constraints: isUrl \n" + - " - property nestedArr[1].name has failed the following constraints: isString \n" + - " - property nestedArr[1].url has failed the following constraints: isUrl \n"); + expect(validationErrors).toHaveLength(3); + expect(validationErrors[0].toString()).toEqual( + 'An instance of RootClass has failed the validation:\n' + + ' - property title has failed the following constraints: minLength, isString \n' + ); + expect(validationErrors[1].toString()).toEqual( + 'An instance of RootClass has failed the validation:\n' + + ' - property nestedObj.name has failed the following constraints: isString \n' + + ' - property nestedObj.url has failed the following constraints: isUrl \n' + + ' - property nestedObj.insideNested.name has failed the following constraints: isString \n' + + ' - property nestedObj.insideNested.url has failed the following constraints: isUrl \n' + ); + expect(validationErrors[2].toString()).toEqual( + 'An instance of RootClass has failed the validation:\n' + + ' - property nestedArr[0].name has failed the following constraints: isString \n' + + ' - property nestedArr[0].url has failed the following constraints: isUrl \n' + + ' - property nestedArr[1].name has failed the following constraints: isString \n' + + ' - property nestedArr[1].url has failed the following constraints: isUrl \n' + ); + expect(validationErrors[0].toString(undefined, undefined, undefined, true)).toEqual( + 'An instance of RootClass has failed the validation:\n' + + ' - property title has failed the following constraints: title must be longer than or equal to 15 characters, title must be a string \n' + ); + expect(validationErrors[1].toString(undefined, undefined, undefined, true)).toEqual( + 'An instance of RootClass has failed the validation:\n' + + ' - property nestedObj.name has failed the following constraints: name must be a string \n' + + ' - property nestedObj.url has failed the following constraints: url must be a URL address \n' + + ' - property nestedObj.insideNested.name has failed the following constraints: name must be a string \n' + + ' - property nestedObj.insideNested.url has failed the following constraints: url must be a URL address \n' + ); + expect(validationErrors[2].toString(undefined, undefined, undefined, true)).toEqual( + 'An instance of RootClass has failed the validation:\n' + + ' - property nestedArr[0].name has failed the following constraints: name must be a string \n' + + ' - property nestedArr[0].url has failed the following constraints: url must be a URL address \n' + + ' - property nestedArr[1].name has failed the following constraints: name must be a string \n' + + ' - property nestedArr[1].url has failed the following constraints: url must be a URL address \n' + ); }); - }); diff --git a/test/functional/validation-functions-and-decorators.spec.ts b/test/functional/validation-functions-and-decorators.spec.ts index a45d3a4fb4..a96f45b363 100644 --- a/test/functional/validation-functions-and-decorators.spec.ts +++ b/test/functional/validation-functions-and-decorators.spec.ts @@ -1,3150 +1,4766 @@ -import "es6-shim"; -import {expect} from "chai"; import { - IsBooleanString, - IsPositive, - IsNegative, - Contains, - Equals, - MinDate, - MaxDate, - IsAlpha, - IsAlphanumeric, - IsAscii, - IsBase64, - IsBoolean, - IsByteLength, - IsCreditCard, - IsCurrency, - IsDate, - IsDivisibleBy, - IsEmail, - IsEnum, - IsFQDN, - IsFullWidth, - IsHalfWidth, - IsVariableWidth, - IsHexColor, - IsHexadecimal, - IsIP, - IsISBN, - IsISO8601, - IsIn, - IsInt, - IsJSON, - Length, - IsLowercase, - IsMongoId, - IsMultibyte, - IsNumberString, - IsSurrogatePair, - IsUrl, - IsUUID, - IsUppercase, - Matches, - MinLength, - MaxLength, - Min, - Max, - IsNotEmpty, - IsMilitaryTime, - ArrayNotEmpty, - ArrayMinSize, - ArrayMaxSize, - NotEquals, - IsEmpty, - IsDefined, - IsNotIn, - IsNumber, - IsString, - NotContains, - ArrayContains, - ArrayNotContains, - ArrayUnique, - IsArray, - IsDateString, - IsInstance, - IsPhoneNumber -} from "../../src/decorator/decorators"; -import {Validator} from "../../src/validation/Validator"; -import {ValidatorOptions} from "../../src/validation/ValidatorOptions"; - -import {should, use } from "chai"; - -import * as chaiAsPromised from "chai-as-promised"; - -should(); -use(chaiAsPromised); - -// ------------------------------------------------------------------------- -// Helper functions -// ------------------------------------------------------------------------- - -export function checkValidValues(object: { someProperty: any }, values: any[], done: Function, validatorOptions?: ValidatorOptions) { - const validator = new Validator(); - const promises = values.map(value => { - object.someProperty = value; - return validator - .validate(object, validatorOptions) - .then(errors => errors.length.should.be.equal(0, `Unexpected errors: ${JSON.stringify(errors)}`)); - }); - Promise.all(promises).then(() => done(), err => done(err)); + IsBooleanString, + IsPositive, + IsLatLong, + IsLongitude, + IsLatitude, + IsNegative, + Contains, + Equals, + MinDate, + MaxDate, + IsAlpha, + IsAlphanumeric, + IsAscii, + IsDecimal, + IsBase64, + IsBoolean, + IsByteLength, + IsCreditCard, + IsCurrency, + IsDate, + IsDivisibleBy, + IsEmail, + IsEnum, + IsFQDN, + IsFullWidth, + IsHalfWidth, + IsVariableWidth, + IsHexColor, + IsHexadecimal, + IsIP, + IsISBN, + IsISO8601, + IsIn, + IsInt, + IsJSON, + IsJWT, + IsObject, + IsNotEmptyObject, + Length, + IsLowercase, + IsMongoId, + IsMultibyte, + IsNumberString, + IsSurrogatePair, + IsUrl, + IsUUID, + IsUppercase, + Matches, + MinLength, + MaxLength, + Min, + Max, + IsNotEmpty, + IsMilitaryTime, + ArrayNotEmpty, + ArrayMinSize, + ArrayMaxSize, + NotEquals, + IsEmpty, + IsDefined, + IsNotIn, + IsNumber, + IsString, + NotContains, + ArrayContains, + ArrayNotContains, + ArrayUnique, + IsArray, + IsDateString, + IsInstance, + IsPhoneNumber, + IsISO31661Alpha2, + IsISO31661Alpha3, + IsHash, + IsMACAddress, + IsISSN, + IsFirebasePushId, + isDefined, + isNumber, + isURL, + isBoolean, + isString, + isInt, + isArray, + isEnum, + contains, + isObject, + isNotEmptyObject, + isInstance, + notContains, + isAlpha, + isAlphanumeric, + isAscii, + isDecimal, + isBase64, + isByteLength, + isCreditCard, + isCurrency, + isEmail, + isFQDN, + isFullWidth, + isHalfWidth, + isVariableWidth, + isHexColor, + isHexadecimal, + isMACAddress, + isISBN, + isISO8601, + isIP, + isJSON, + isJWT, + isLowercase, + isMongoId, + isMultibyte, + isSurrogatePair, + isUUID, + isUppercase, + length, + minLength, + maxLength, + isFirebasePushId, + equals, + notEquals, + isEmpty, + isNotEmpty, + isIn, + isNotIn, + isDateString, + isDivisibleBy, + isPositive, + isNegative, + min, + max, + isBooleanString, + isNumberString, + matches, + isHash, + isISSN, + arrayContains, + arrayNotContains, + arrayMinSize, + arrayMaxSize, + arrayUnique, + arrayNotEmpty, + minDate, + maxDate, + isDate, + IsEAN, + isEAN, + IsEthereumAddress, + isEthereumAddress, + IsBtcAddress, + isBtcAddress, + IsDataURI, + isDataURI, + IsHSL, + isHSL, + IsRgbColor, + isRgbColor, + isIdentityCard, + IsIdentityCard, + IsBase32, + isBase32, + IsIBAN, + isIBAN, + IsBIC, + isBIC, + IsISRC, + isISRC, + IsRFC3339, + isRFC3339, + IsLocale, + isLocale, + IsMagnetURI, + isMagnetURI, + IsMimeType, + isMimeType, + isOctal, + IsOctal, + IsPassportNumber, + isPassportNumber, + IsPostalCode, + isPostalCode, + IsSemVer, + isSemVer, + IsStrongPassword, + isStrongPassword, + IsStrongPasswordOptions, + IsTimeZone, + IsBase58, + isBase58, + isTaxId, + IsTaxId, + IsISO4217CurrencyCode, +} from '../../src/decorator/decorators'; +import { Validator } from '../../src/validation/Validator'; +import { ValidatorOptions } from '../../src/validation/ValidatorOptions'; +import { constraintToString } from '../../src/validation/ValidationUtils'; +import { default as ValidatorJS } from 'validator'; + +function checkValidValues( + object: { someProperty: any }, + values: any[], + validatorOptions?: ValidatorOptions +): Promise { + const validator = new Validator(); + const promises = values.map(value => { + object.someProperty = value; + return validator.validate(object, validatorOptions).then(errors => { + expect(errors.length).toEqual(0); + if (errors.length !== 0) { + console.log(`Unexpected errors: ${JSON.stringify(errors)}`); + throw new Error('Unexpected validation errors'); + } + }); + }); + + return Promise.all(promises); } -export function checkInvalidValues(object: { someProperty: any }, values: any[], done: Function, validatorOptions?: ValidatorOptions) { - const validator = new Validator(); - const promises = values.map(value => { - object.someProperty = value; - return validator - .validate(object, validatorOptions) - .then(errors => errors.length.should.be.equal(1)); - }); - Promise.all(promises).then(() => done(), err => done(err)); -} +function checkInvalidValues( + object: { someProperty: any }, + values: any[], + validatorOptions?: ValidatorOptions +): Promise { + const validator = new Validator(); + const promises = values.map(value => { + object.someProperty = value; + return validator + .validate(object, validatorOptions) + .then(errors => { + expect(errors.length).toEqual(1); + if (errors.length !== 1) { + throw new Error('Missing validation errors'); + } + }) + .catch(error => { + console.log(error); + }); + }); -export function checkReturnedError(object: { someProperty: any }, - values: any[], - validationType: string, - message: string, - done: Function, - validatorOptions?: ValidatorOptions) { - - const validator = new Validator(); - const promises = values.map(value => { - object.someProperty = value; - return validator - .validate(object, validatorOptions) - .then(errors => { - errors.length.should.be.equal(1); - errors[0].target.should.be.equal(object); - errors[0].property.should.be.equal("someProperty"); - errors[0].constraints.should.be.eql({ [validationType]: message }); - expect(errors[0].value).to.be.equal(value); - }); - }); - Promise.all(promises).then(() => done(), err => done(err)); + return Promise.all(promises); } -// ------------------------------------------------------------------------- -// Setup -// ------------------------------------------------------------------------- +function checkReturnedError( + object: { someProperty: any }, + values: any[], + validationType: string, + message: string, + validatorOptions?: ValidatorOptions +): Promise { + const validator = new Validator(); + const promises = values.map(value => { + object.someProperty = value; + return validator.validate(object, validatorOptions).then(errors => { + expect(errors.length).toEqual(1); + expect(errors[0].target).toEqual(object); + expect(errors[0].property).toEqual('someProperty'); + expect(errors[0].constraints).toEqual({ [validationType]: message }); + expect(errors[0].value).toEqual(value); + }); + }); + + return Promise.all(promises); +} const validator = new Validator(); -// ------------------------------------------------------------------------- -// Specifications: common decorators -// ------------------------------------------------------------------------- +describe('IsDefined', () => { + const validValues = [0, 1, true, false, '', '0', '1234', -1]; + const invalidValues: any[] = [null, undefined]; -describe("IsDefined", function() { + class MyClass { + @IsDefined() + someProperty: string; + } - const validValues = [0, 1, true, false, "", "0", "1234", -1]; - const invalidValues: any[] = [null, undefined]; + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); - class MyClass { - @IsDefined() - someProperty: string; - } + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); + it('should not fail if validator.validate said that its valid with skipUndefinedProperties set to true', () => { + return checkValidValues(new MyClass(), validValues, { skipUndefinedProperties: true }); + }); - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); + it('should fail if validator.validate said that its invalid with skipUndefinedProperties set to true', () => { + return checkInvalidValues(new MyClass(), invalidValues, { skipUndefinedProperties: true }); + }); - it("should not fail if validator.validate said that its valid with skipMissingProperties set to true", function(done) { - checkValidValues(new MyClass(), validValues, done, { skipMissingProperties: true }); - }); + it('should not fail if validator.validate said that its valid with skipNullProperties set to true', () => { + return checkValidValues(new MyClass(), validValues, { skipNullProperties: true }); + }); - it("should fail if validator.validate said that its invalid with skipMissingProperties set to true", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done, { skipMissingProperties: true }); - }); + it('should fail if validator.validate said that its invalid with skipNullProperties set to true', () => { + return checkInvalidValues(new MyClass(), invalidValues, { skipNullProperties: true }); + }); - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isDefined(value).should.be.true); - }); + it('should not fail if validator.validate said that its valid with skipMissingProperties set to true', () => { + return checkValidValues(new MyClass(), validValues, { skipMissingProperties: true }); + }); - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isDefined(value).should.be.false); - }); + it('should fail if validator.validate said that its invalid with skipMissingProperties set to true', () => { + return checkInvalidValues(new MyClass(), invalidValues, { skipMissingProperties: true }); + }); - it("should return error object with proper data", function(done) { - const validationType = "isDefined"; - const message = "someProperty should not be null or undefined"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isDefined(value)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isDefined(value)).toBeFalsy()); + }); + it('should return error object with proper data', () => { + const validationType = 'isDefined'; + const message = 'someProperty should not be null or undefined'; + checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); }); -describe("Equals", function() { +describe('Equals', () => { + const constraint = 'Alex'; + const validValues = ['Alex']; + const invalidValues = ['Alexxx']; + + class MyClass { + @Equals(constraint) + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(equals(value, constraint)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(equals(value, constraint)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'equals'; + const message = 'someProperty must be equal to ' + constraintToString(constraint); + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - const constraint = "Alex"; - const validValues = ["Alex"]; - const invalidValues = ["Alexxx"]; +describe('NotEquals', () => { + const constraint = 'Alex'; + const validValues = ['Alexxx']; + const invalidValues = ['Alex']; + + class MyClass { + @NotEquals(constraint) + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(notEquals(value, constraint)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(notEquals(value, constraint)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'notEquals'; + const message = 'someProperty should not be equal to ' + constraintToString(constraint); + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - class MyClass { - @Equals(constraint) - someProperty: string; - } +describe('IsEmpty', () => { + const validValues = [null, undefined, '']; + const invalidValues = ['0', 0, 1, false, true]; - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); + class MyClass { + @IsEmpty() + someProperty: string; + } - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.equals(value, constraint).should.be.true); - }); + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.equals(value, constraint).should.be.false); - }); + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isEmpty(value)).toBeTruthy()); + }); - it("should return error object with proper data", function(done) { - const validationType = "equals"; - const message = "someProperty must be equal to " + constraint; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isEmpty(value)).toBeFalsy()); + }); + it('should return error object with proper data', () => { + const validationType = 'isEmpty'; + const message = 'someProperty must be empty'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); }); -describe("NotEquals", function() { - - const constraint = "Alex"; - const validValues = ["Alexxx"]; - const invalidValues = ["Alex"]; +describe('IsNotEmpty', () => { + const validValues = ['a', 'abc']; + const invalidValues = ['', undefined, null]; - class MyClass { - @NotEquals(constraint) - someProperty: string; - } + class MyClass { + @IsNotEmpty() + someProperty: string; + } - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.notEquals(value, constraint).should.be.true); - }); + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isNotEmpty(value)).toBeTruthy()); + }); - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.notEquals(value, constraint).should.be.false); - }); + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isNotEmpty(value)).toBeFalsy()); + }); - it("should return error object with proper data", function(done) { - const validationType = "notEquals"; - const message = "someProperty should not be equal to " + constraint; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); + it('should return error object with proper data', () => { + const validationType = 'isNotEmpty'; + const message = 'someProperty should not be empty'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); +describe('IsIn', () => { + const constraint = ['foo', 'bar'] as const; + const validValues = ['foo', 'bar']; + const invalidValues = ['foobar', 'barfoo', '']; + + class MyClass { + @IsIn(constraint) + someProperty: string[]; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isIn(value, constraint)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isIn(value, constraint)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isIn'; + const message = 'someProperty must be one of the following values: ' + constraintToString(constraint); + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); }); -describe("IsEmpty", function() { +describe('IsNotIn', () => { + const constraint = ['foo', 'bar'] as const; + const validValues = ['foobar', 'barfoo', '']; + const invalidValues = ['foo', 'bar']; + + class MyClass { + @IsNotIn(constraint) + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isNotIn(value, constraint)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isNotIn(value, constraint)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isNotIn'; + const message = 'someProperty should not be one of the following values: ' + constraintToString(constraint); + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - const validValues = [null, undefined, ""]; - const invalidValues = ["0", 0, 1, false, true]; +// ------------------------------------------------------------------------- +// Specifications: type check +// ------------------------------------------------------------------------- - class MyClass { - @IsEmpty() - someProperty: string; - } +describe('IsBoolean', () => { + const validValues = [true, false]; + const invalidValues = [0, 1, 'true', null, undefined]; - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); + class MyClass { + @IsBoolean() + someProperty: any; + } - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isEmpty(value).should.be.true); - }); + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isEmpty(value).should.be.false); - }); + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isBoolean(value)).toBeTruthy()); + }); - it("should return error object with proper data", function(done) { - const validationType = "isEmpty"; - const message = "someProperty must be empty"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isBoolean(value)).toBeFalsy()); + }); + it('should return error object with proper data', () => { + const validationType = 'isBoolean'; + const message = 'someProperty must be a boolean value'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); }); -describe("IsNotEmpty", function() { - - const validValues = ["a", "abc"]; - const invalidValues = ["", undefined, null]; +describe('IsLatLong', () => { + const validValues = ['27.6945311,85.3446311', '27.675509,85.2100893']; + const invalidValues = ['276945311,853446311', 'asas,as.as12']; - class MyClass { - @IsNotEmpty() - someProperty: string; - } + class MyClass { + @IsLatLong() + someProperty: any; + } - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); +}); - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isNotEmpty(value).should.be.true); - }); +describe('IsLatitude', () => { + const validValues = ['27.6945311', '27.675509', 27.675509]; + const invalidValues = ['276945311', 'asas', 1234222, 5678921]; - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isNotEmpty(value).should.be.false); - }); + class MyClass { + @IsLatitude() + someProperty: any; + } - it("should return error object with proper data", function(done) { - const validationType = "isNotEmpty"; - const message = "someProperty should not be empty"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); }); -describe("IsIn", function() { +describe('IsLongitude', () => { + const validValues = ['85.3446311', '85.2100893', 85.2100893]; + const invalidValues = ['853446311', 'as.as12', 12345, 737399]; - const constraint = ["foo", "bar"]; - const validValues = ["foo", "bar"]; - const invalidValues = ["foobar", "barfoo", ""]; - - class MyClass { - @IsIn(constraint) - someProperty: string; - } + class MyClass { + @IsLongitude() + someProperty: any; + } - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); +}); - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isIn(value, constraint).should.be.true); - }); +describe('IsDate', () => { + const validValues = [new Date()]; + const invalidValues = [1, true, false, 'Mon Aug 17 2015 00:24:56 GMT-0500 (CDT)', '2009-05-19 14:39:22-06:00']; - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isIn(value, constraint).should.be.false); - }); + class MyClass { + @IsDate() + someProperty: Date; + } - it("should return error object with proper data", function(done) { - const validationType = "isIn"; - const message = "someProperty must be one of the following values: " + constraint; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); -}); + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); -describe("IsNotIn", function() { + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isDate(value)).toBeTruthy()); + }); - const constraint = ["foo", "bar"]; - const validValues = ["foobar", "barfoo", ""]; - const invalidValues = ["foo", "bar"]; + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isDate(value)).toBeFalsy()); + }); - class MyClass { - @IsNotIn(constraint) - someProperty: string; - } + it('should return error object with proper data', () => { + const validationType = 'isDate'; + const message = 'someProperty must be a Date instance'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); +describe('IsNumber', () => { + const validValues = [0, 1, 2, 3, 4, 5.4, -10]; + const invalidValues = ['1', '0', true, false, '-100', 'abc', undefined, null]; + + class MyClass { + @IsNumber() + someProperty: number; + } + + class NaNTestClass { + @IsNumber({ allowNaN: true }) + someProperty: number; + } + + class InfinityTestClass { + @IsNumber({ allowInfinity: true }) + someProperty: number; + } + + class MaxDecimalPlacesTest { + @IsNumber({ maxDecimalPlaces: 3 }) + someProperty: number; + } + + class ZeroDecimalPlacesTest { + @IsNumber({ maxDecimalPlaces: 0 }) + someProperty: number; + } + + it('should fail if NaN passed without allowing NaN values', () => { + return checkInvalidValues(new MyClass(), [NaN]); + }); + + it('should fail if Infinity passed without allowing NaN values', () => { + return checkInvalidValues(new MyClass(), [Infinity, -Infinity]); + }); + + it('should not fail if NaN passed and NaN as value is allowed', () => { + return checkValidValues(new NaNTestClass(), [NaN]); + }); + + it('should not fail if Infinity passed and Infinity as value is allowed', () => { + return checkValidValues(new InfinityTestClass(), [Infinity, -Infinity]); + }); + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isNumber(value)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isNumber(value)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isNumber'; + const message = 'someProperty must be a number conforming to the specified constraints'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); + + it('should pass if number of decimal places within maxDecimalPlaces', () => { + return checkValidValues(new MaxDecimalPlacesTest(), [1.123]); + }); + + it('should fail if number of decimal places exceeds maxDecimalPlaces', () => { + return checkInvalidValues(new MaxDecimalPlacesTest(), [1.1234]); + }); + + it('should pass if number of decimal places is zero', () => { + return checkValidValues(new ZeroDecimalPlacesTest(), [-10, -1, 0, 1, 10]); + }); + + it('should fail if number of decimal places is not zero', () => { + return checkInvalidValues(new ZeroDecimalPlacesTest(), [-11.1, -2.2, -0.1, 0.1, 2.2, 11.1]); + }); +}); - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); +describe('IsInt', () => { + const validValues = [2, 4, 100, 1000]; + const invalidValues = ['01', '-01', '000', '100e10', '123.123', ' ', '', 2.5, -0.1]; - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isNotIn(value, constraint).should.be.true); - }); + class MyClass { + @IsInt() + someProperty: string; + } - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isNotIn(value, constraint).should.be.false); - }); + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); - it("should return error object with proper data", function(done) { - const validationType = "isNotIn"; - const message = "someProperty should not be one of the following values: " + constraint; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); -}); + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isInt(value)).toBeTruthy()); + }); -// ------------------------------------------------------------------------- -// Specifications: type check -// ------------------------------------------------------------------------- + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isInt(value as any)).toBeFalsy()); + }); -describe("IsBoolean", function() { + it('should return error object with proper data', () => { + const validationType = 'isInt'; + const message = 'someProperty must be an integer number'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - const validValues = [true, false]; - const invalidValues = [0, 1, "true", null, undefined]; +describe('IsString', () => { + const validValues = ['true', 'false', 'hello', '0', '', '1']; + const invalidValues = [true, false, 1, 2, null, undefined]; - class MyClass { - @IsBoolean() - someProperty: any; - } + class MyClass { + @IsString() + someProperty: string; + } - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isBoolean(value).should.be.true); - }); + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isString(value)).toBeTruthy()); + }); - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isBoolean(value).should.be.false); - }); + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isString(value as any)).toBeFalsy()); + }); - it("should return error object with proper data", function(done) { - const validationType = "isBoolean"; - const message = "someProperty must be a boolean value"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); + it('should return error object with proper data', () => { + const validationType = 'isString'; + const message = 'someProperty must be a string'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); +describe('IsDateString', () => { + const validValues = [ + '2017-06-06T17:04:42.081Z', + '2017-06-06T17:04:42.081', + '2018-01-04T08:15:30', + '2018-01-04T08:15:30Z', + '2018-01-04T08:15:30+04:00', + '2018-01-04T08:15:30+04', + '2020-03-26T11:00:01-03:00', + '2020-03-26T11:00:01-03', + '2019-09-03T20:16:24.12Z', + ]; + const invalidValues = [ + true, + false, + 1, + 2, + null, + undefined, + 'text', + 'text2018-01-04T08:15:30+04', + '2018-01-04T08:15:30Ztext', + '2009-02-29', // non-existent-day + '2019-18-13T22:14:14.761Z', // month greater than 12 + '2019-12-39T22:14:14.761Z', // day greater than 31 + '2019-12-31T29:14:14.761Z', // hour greater than 24 + '2019-00-31T29:14:14.761Z', // month of 0 + '2019-01-00T29:14:14.761Z', // day of 0 + '2019-09-03T20:16:24.12-5:00', // single digit hour in timezone offset + '2019-09-03T20:16:24.12+5:00', + '2019-09-03T20:16:24.12-05:0', // single digit minute in timezone offset + '2019-09-03T20:16:24.12+05:0', + ]; + + class MyClass { + @IsDateString({ strict: true }) + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isDateString(value, { strict: true })).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isDateString(value as any, { strict: true })).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isDateString'; + const message = 'someProperty must be a valid ISO 8601 date string'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); }); -describe("IsDate", function() { +describe('IsArray', () => { + const validValues = [[], [1, 2, 3], [0, 0, 0], [''], [0], [undefined], [{}], []]; + const invalidValues = [true, false, 1, {}, null, undefined]; - const validValues = [new Date()]; - const invalidValues = [1, true, false, "Mon Aug 17 2015 00:24:56 GMT-0500 (CDT)", "2009-05-19 14:39:22-06:00"]; + class MyClass { + @IsArray() + someProperty: string[]; + } - class MyClass { - @IsDate() - someProperty: Date; - } + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isArray(value)).toBeTruthy()); + }); - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isDate(value).should.be.true); - }); + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isArray(value as any)).toBeFalsy()); + }); - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isDate(value).should.be.false); - }); + it('should return error object with proper data', () => { + const validationType = 'isArray'; + const message = 'someProperty must be an array'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - it("should return error object with proper data", function(done) { - const validationType = "isDate"; - const message = "someProperty must be a Date instance"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); +describe('IsEnum', () => { + enum MyDefaultIndexedEnum { + First, + Second, + } + + enum MyCustomIndexedEnum { + First = 1, + Second = 999, + } + + enum MyStringEnum { + First = 'first', + Second = 'second', + } + + const validValues = [MyCustomIndexedEnum.First, MyCustomIndexedEnum.Second]; + const validStringValues = [MyStringEnum.First, MyStringEnum.Second]; + const invalidValues = [true, false, 42, {}, null, undefined, 'F2irst']; + + class MyClassOne { + @IsEnum(MyDefaultIndexedEnum) + someProperty: MyDefaultIndexedEnum; + } + + class MyClassTwo { + @IsEnum(MyCustomIndexedEnum) + someProperty: MyCustomIndexedEnum; + } + + class MyClassThree { + @IsEnum(MyStringEnum) + someProperty: MyStringEnum; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClassTwo(), validValues); + }); + + it('should not fail if validator.validate said that its valid (string enum)', () => { + return checkValidValues(new MyClassThree(), validStringValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClassTwo(), invalidValues); + }); + + it('should fail if validator.validate said that its invalid (string enum)', () => { + return checkInvalidValues(new MyClassThree(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isEnum(value, MyCustomIndexedEnum)).toBeTruthy()); + }); + + it('should not fail if method in validator said that its valid (string enum)', () => { + validStringValues.forEach(value => expect(isEnum(value, MyStringEnum)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isEnum(value, MyCustomIndexedEnum)).toBeFalsy()); + }); + + it('should fail if method in validator said that its invalid (string enum)', () => { + invalidValues.forEach(value => expect(isEnum(value, MyStringEnum)).toBeFalsy()); + }); + + it('should return error with proper message for default indexed enum', () => { + const validationType = 'isEnum'; + const message = 'someProperty must be one of the following values: 0, 1'; + return checkReturnedError(new MyClassOne(), invalidValues, validationType, message); + }); + + it('should return error with proper message for custom indexed enum', () => { + const validationType = 'isEnum'; + const message = 'someProperty must be one of the following values: 1, 999'; + return checkReturnedError(new MyClassTwo(), invalidValues, validationType, message); + }); + + it('should return error with proper message for string enum', () => { + const validationType = 'isEnum'; + const message = 'someProperty must be one of the following values: first, second'; + return checkReturnedError(new MyClassThree(), invalidValues, validationType, message); + }); +}); +describe('IsDivisibleBy', () => { + const constraint = 2; + const validValues = [2, 4, 100, 1000]; + const invalidValues = ['', undefined, null]; + + class MyClass { + @IsDivisibleBy(constraint) + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isDivisibleBy(value, constraint)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isDivisibleBy(value as any, constraint)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isDivisibleBy'; + const message = 'someProperty must be divisible by ' + constraintToString(constraint); + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); }); -describe("IsNumber", function() { +describe('IsPositive', () => { + const validValues = [3, 5000]; + const invalidValues = [ + '-1', + '-2', + '0', + '1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + '100000', + -500, + -123, + -1, + ' ', + '', + ]; + + class MyClass { + @IsPositive() + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isPositive(value)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isPositive(value as any)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isPositive'; + const message = 'someProperty must be a positive number'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - const validValues = [0, 1, 2, 3, 4, 5.4, -10]; - const invalidValues = ["1", "0", true, false, "-100", "abc", undefined, null]; +describe('IsNegative', () => { + const validValues = [-3, -5000, -0.1]; + const invalidValues = [ + '-1', + '-2', + '0', + '1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + '100000', + 500, + 123, + 1, + ' ', + '', + ]; + + class MyClass { + @IsNegative() + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isNegative(value)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isNegative(value as any)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isNegative'; + const message = 'someProperty must be a negative number'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - class MyClass { - @IsNumber() - someProperty: number; - } +describe('Min', () => { + const constraint = 10; + const validValues = [10, 11, 20, 30, 40]; + const invalidValues = [2, 3, 4, 5, 6, 7, 8, 9, -10]; + + class MyClass { + @Min(constraint) + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(min(value, constraint)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(min(value, constraint)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'min'; + const message = 'someProperty must not be less than ' + constraintToString(constraint); + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - class NaNTestClass { - @IsNumber({ allowNaN: true }) - someProperty: number; - } +describe('Max', () => { + const constraint = 10; + const validValues = [1, 2, 3, 4, 5, 6, 7, 8, 9, -10, 10]; + const invalidValues = [11, 20, 30, 40]; + + class MyClass { + @Max(constraint) + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(max(value, constraint)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(max(value, constraint)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'max'; + const message = 'someProperty must not be greater than ' + constraintToString(constraint); + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - class InfinityTestClass { - @IsNumber({ allowInfinity: true }) - someProperty: number; - } +describe('MinDate', () => { + const constraint = new Date(1995, 11, 17); + const validValues = [new Date()]; + const invalidValues = [new Date(1994, 11, 17)]; + + class MyClass { + @MinDate(constraint) + someProperty: Date; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(minDate(value, constraint)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(minDate(value, constraint)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'minDate'; + const message = 'minimal allowed date for someProperty is ' + constraintToString(constraint); + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - it("should fail if NaN passed without allowing NaN values", function (done) { - checkInvalidValues(new MyClass(), [NaN], done); - }); +describe('MaxDate', () => { + const constraint = new Date(1995, 11, 17); + const validValues = [new Date(1994, 11, 17)]; + const invalidValues = [new Date()]; + + class MyClass { + @MaxDate(constraint) + someProperty: Date; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(maxDate(value, constraint)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(maxDate(value, constraint)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'maxDate'; + const message = 'maximal allowed date for someProperty is ' + constraintToString(constraint); + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - it("should fail if Infinity passed without allowing NaN values", function (done) { - checkInvalidValues(new MyClass(), [Infinity, -Infinity], done); - }); +describe('MinDate function constraint', () => { + const constraint = () => new Date(1995, 11, 17); + const validValues = [new Date()]; + const invalidValues = [new Date(1994, 11, 17)]; + + class MyClass { + @MinDate(constraint) + someProperty: Date; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(minDate(value, constraint)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(minDate(value, constraint)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'minDate'; + const message = 'minimal allowed date for someProperty is ' + constraintToString(constraint); + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - it("should not fail if NaN passed and NaN as value is allowed", function (done) { - checkValidValues(new NaNTestClass(), [NaN], done); - }); +describe('MaxDate function constraint', () => { + const constraint = () => new Date(1995, 11, 17); + const validValues = [new Date(1994, 11, 17)]; + const invalidValues = [new Date()]; + + class MyClass { + @MaxDate(constraint) + someProperty: Date; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(maxDate(value, constraint)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(maxDate(value, constraint)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'maxDate'; + const message = 'maximal allowed date for someProperty is ' + constraintToString(constraint); + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - it("should not fail if Infinity passed and Infinity as value is allowed", function (done) { - checkValidValues(new InfinityTestClass(), [Infinity, -Infinity], done); - }); +describe('IsBooleanString', () => { + const validValues = ['1', '0', 'true', 'false']; + const invalidValues = ['2', '3', 'falze']; - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); + class MyClass { + @IsBooleanString() + someProperty: string; + } - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isNumber(value).should.be.true); - }); + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isNumber(value).should.be.false); - }); + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isBooleanString(value)).toBeTruthy()); + }); - it("should return error object with proper data", function(done) { - const validationType = "isNumber"; - const message = "someProperty must be a number"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isBooleanString(value)).toBeFalsy()); + }); + it('should return error object with proper data', () => { + const validationType = 'isBooleanString'; + const message = 'someProperty must be a boolean string'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); }); -describe("IsInt", function() { +describe('IsNumberString', () => { + const validValues = ['123', '123.123', '00123', '-00123', '0', '-0', '+123']; + const invalidValues = [' ', '.']; - const validValues = [2, 4, 100, 1000]; - const invalidValues = [ - "01", - "-01", - "000", - "100e10", - "123.123", - " ", - "", - 2.5, - -0.1 - ]; - - class MyClass { - @IsInt() - someProperty: string; - } + class MyClass { + @IsNumberString() + someProperty: string; + } - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isInt(value).should.be.true); - }); + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isNumberString(value)).toBeTruthy()); + }); - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isInt(value as any).should.be.false); - }); + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isNumberString(value)).toBeFalsy()); + }); - it("should return error object with proper data", function(done) { - const validationType = "isInt"; - const message = "someProperty must be an integer number"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); + it('should return error object with proper data', () => { + const validationType = 'isNumberString'; + const message = 'someProperty must be a number string'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); +describe('Contains', () => { + const constraint = 'hello'; + const validValues = ['hello world']; + const invalidValues = [null, undefined, 'bye world']; + + class MyClass { + @Contains(constraint) + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(contains(value, constraint)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(contains(value, constraint)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'contains'; + const message = 'someProperty must contain a ' + constraintToString(constraint) + ' string'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); }); -describe("IsString", function() { +describe('NotContains', () => { + const constraint = 'hello'; + const validValues = ['bye world']; + const invalidValues = [null, undefined, 'hello world']; + + class MyClass { + @NotContains(constraint) + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(notContains(value, constraint)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(notContains(value, constraint)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'notContains'; + const message = 'someProperty should not contain a ' + constraintToString(constraint) + ' string'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - const validValues = ["true", "false", "hello", "0", "", "1"]; - const invalidValues = [ - true, - false, - 1, - 2, - null, - undefined - ]; +describe('IsAlpha', () => { + const constraint = 'en-GB'; + const validValues = ['hellomynameisalex']; + const invalidValues = [null, undefined, 'hello1mynameisalex']; + + class MyClass { + @IsAlpha() + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isAlpha(value, constraint)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isAlpha(value, constraint)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isAlpha'; + const message = 'someProperty must contain only letters (a-zA-Z)'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - class MyClass { - @IsString() - someProperty: string; - } +describe('IsAlphanumeric', () => { + const constraint = ''; + const validValues = ['hellomyname1salex']; + const invalidValues = [null, undefined, 'hell*mynameisalex']; + + class MyClass { + @IsAlphanumeric() + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isAlphanumeric(value)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isAlphanumeric(value)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isAlphanumeric'; + const message = 'someProperty must contain only letters and numbers'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); +describe('IsAscii', () => { + const constraint = ''; + const validValues = ['hellomyname1salex']; + const invalidValues = [null, undefined, 'hell*mynameisлеха']; + + class MyClass { + @IsAscii() + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isAscii(value)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isAscii(value)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isAscii'; + const message = 'someProperty must contain only ASCII characters'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); +describe('IsDecimal', () => { + const validValues = [ + '100.0', + '100.1', + '100.3', + '100.4', + '100.5', + '100.6', + '100.7', + '100.8', + '100.9', + '1.9', + '-1.9', + '-124.1', + ]; + + const invalidValues = [ + null, + undefined, + 'hello', + '', + '1', + '1.', + '1,', + '-1', + '100', + '100,100', + '100.23', + '100.214141', + '100,23', + '100,2143192', + ]; + + const isDecimalOptions: ValidatorJS.IsDecimalOptions = { + // eslint-disable-next-line @typescript-eslint/camelcase + force_decimal: true, + // eslint-disable-next-line @typescript-eslint/camelcase + decimal_digits: '1', + locale: 'en-US', + }; + + class MyClass { + @IsDecimal(isDecimalOptions) + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isDecimal(value, isDecimalOptions)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isDecimal(value, isDecimalOptions)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isDecimal'; + const message = 'someProperty is not a valid decimal number.'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isString(value).should.be.true); - }); +describe('IsBase32', () => { + const constraint = ''; + const validValues = [ + 'ZG======', + 'JBSQ====', + 'JBSWY===', + 'JBSWY3A=', + 'JBSWY3DP', + 'JBSWY3DPEA======', + 'K5SWYY3PNVSSA5DPEBXG6ZA=', + 'K5SWYY3PNVSSA5DPEBXG6===', + ]; + const invalidValues = [ + null, + undefined, + '12345', + '', + 'JBSWY3DPtesting123', + 'ZG=====', + 'Z======', + 'Zm=8JBSWY3DP', + '=m9vYg==', + 'Zm9vYm/y====', + ]; + + class MyClass { + @IsBase32() + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isBase32(value)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isBase32(value)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isBase32'; + const message = 'someProperty must be base32 encoded'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isString(value as any).should.be.false); - }); +describe('IsBase64', () => { + const constraint = ''; + const validValues = ['aGVsbG8=']; + const invalidValues = [null, undefined, 'hell*mynameisalex']; + + class MyClass { + @IsBase64() + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isBase64(value)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isBase64(value)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isBase64'; + const message = 'someProperty must be base64 encoded'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - it("should return error object with proper data", function(done) { - const validationType = "isString"; - const message = "someProperty must be a string"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); +describe('IsIBAN', () => { + const constraint = ''; + const validValues = ['GR96 0810 0010 0000 0123 4567 890']; + const invalidValues = [null, undefined, 'XX22YYY1234567890123']; + + class MyClass { + @IsIBAN() + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isIBAN(value)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isIBAN(value)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isIBAN'; + const message = 'someProperty must be an IBAN'; + checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); +describe('IsBIC', () => { + const constraint = ''; + const validValues = ['SBICKEN1345']; + const invalidValues = [null, undefined, 'SBIC23NXXX']; + + class MyClass { + @IsBIC() + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isBIC(value)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isBIC(value)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isBIC'; + const message = 'someProperty must be a BIC or SWIFT code'; + checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); }); -describe("IsDateString", function() { +describe('IsEthereumAddress', () => { + const constraint = ''; + const validValues = ['0x683E07492fBDfDA84457C16546ac3f433BFaa128']; + const invalidValues = [null, undefined, '0xFCb5AFB808b5679b4911230Aa41FfCD0cd335b422222']; + + class MyClass { + @IsEthereumAddress() + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isEthereumAddress(value)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isEthereumAddress(value)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isEthereumAddress'; + const message = 'someProperty must be an Ethereum address'; + checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - const validValues = [ - "2017-06-06T17:04:42.081Z", - "2017-06-06T17:04:42.081", - "2018-01-04T08:15:30", - "2018-01-04T08:15:30Z", - "2018-01-04T08:15:30+04:00", - "2018-01-04T08:15:30+04", - ]; - const invalidValues = [ - true, - false, - 1, - 2, - null, - undefined, - "text" - ]; +describe('IsBtcAddress', () => { + const constraint = ''; + const validValues = ['bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq']; + const invalidValues = [null, undefined, 'pp8skudq3x5hzw8ew7vzsw8tn4k8wxsqsv0lt0mf3g']; + + class MyClass { + @IsBtcAddress() + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isBtcAddress(value)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isBtcAddress(value)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isBtcAddress'; + const message = 'someProperty must be a BTC address'; + checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - class MyClass { - @IsDateString() - someProperty: string; - } +describe('IsDataURI', () => { + const constraint = ''; + const validValues = ['data:text/html;charset=US-ASCII,%3Ch1%3EHello!%3C%2Fh1%3E']; + const invalidValues = [null, undefined, 'data:HelloWorld']; + + class MyClass { + @IsDataURI() + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isDataURI(value)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isDataURI(value)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isDataURI'; + const message = 'someProperty must be a data uri format'; + checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); +describe('IsHSL', () => { + const constraint = ''; + const validValues = ['hsl(-540, 03%, 4%)']; + const invalidValues = [null, undefined, 'hsl(-0160, 100%, 100a)']; + + class MyClass { + @IsHSL() + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isHSL(value)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isHSL(value)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isHSL'; + const message = 'someProperty must be a HSL color'; + checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); +describe('IsRgbColor', () => { + const constraint = ''; + const validValues = ['rgba(255,255,255,0.1)']; + const invalidValues = [null, undefined, 'rgba(0,0,0)']; + + class MyClass { + @IsRgbColor() + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isRgbColor(value)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isRgbColor(value)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isRgbColor'; + const message = 'someProperty must be RGB color'; + checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => expect(validator.isDateString(value)).be.true); - }); +describe('IsIdentityCard', () => { + const constraint = 'he-IL'; + const validValues = ['335240479']; + const invalidValues = [null, undefined, 'A1234567L']; + + class MyClass { + @IsIdentityCard(constraint) + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isIdentityCard(value, constraint)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isIdentityCard(value, constraint)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isIdentityCard'; + const message = 'someProperty must be a identity card number'; + checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => expect(validator.isDateString(value as any)).be.false); - }); +describe('IsEAN', () => { + const constraint = ''; + const validValues = ['9771234567003']; + const invalidValues = [null, undefined, '079777681629']; + + class MyClass { + @IsEAN() + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isEAN(value)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isEAN(value)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isEAN'; + const message = 'someProperty must be an EAN (European Article Number)'; + checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - it("should return error object with proper data", function(done) { - const validationType = "isDateString"; - // const message = "someProperty deve ser um texto de data"; - const message = "someProperty must be a ISOString"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); +describe('IsISRC', () => { + const constraint = ''; + const validValues = ['GBAYE6800011']; + const invalidValues = [null, undefined, 'SRC15705223']; + + class MyClass { + @IsISRC() + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isISRC(value)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isISRC(value)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isISRC'; + const message = 'someProperty must be an ISRC'; + checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); +describe('IsRFC3339', () => { + const constraint = ''; + const validValues = ['2010-02-18t00:23:23.33+06:00']; + const invalidValues = [null, undefined, '2009-05-31 14:60:55Z']; + + class MyClass { + @IsRFC3339() + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isRFC3339(value)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isRFC3339(value)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isRFC3339'; + const message = 'someProperty must be RFC 3339 date'; + checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); }); -describe("IsArray", function() { +describe('IsLocale', () => { + const constraint = ''; + const validValues = ['en_US_POSIX']; + const invalidValues = [null, undefined, 'lo_POP']; + + class MyClass { + @IsLocale() + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isLocale(value)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isLocale(value)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isLocale'; + const message = 'someProperty must be locale'; + checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - const validValues = [[], [1, 2, 3], [0, 0, 0], [""], [0], [undefined], [{}], new Array()]; - const invalidValues = [ - true, - false, - 1, - {}, - null, - undefined - ]; +describe('IsMagnetURI', () => { + const constraint = ''; + const validValues = ['magnet:?xt=urn:btih:1GSHJVBDVDVJFYEHKFHEFIO8573898434JBFEGHD&dn=foo&tr=udp://foo.com:1337']; + const invalidValues = [ + null, + undefined, + 'magnet:?xt=uarn:btih:MCJDCYUFHEUD6E2752T7UJNEKHSUGEJFGTFHVBJS&dn=bar&tr=udp://bar.com:1337', + ]; + + class MyClass { + @IsMagnetURI() + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isMagnetURI(value)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isMagnetURI(value)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isMagnetURI'; + const message = 'someProperty must be magnet uri format'; + checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - class MyClass { - @IsArray() - someProperty: string[]; - } +describe('IsMimeType', () => { + const constraint = ''; + const validValues = ['multipart/form-data; boundary=something; charset=utf-8']; + const invalidValues = [null, undefined, 'font/woff2; charset=utf-8']; + + class MyClass { + @IsMimeType() + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isMimeType(value)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isMimeType(value)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isMimeType'; + const message = 'someProperty must be MIME type format'; + checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); +describe('IsOctal', () => { + const constraint = ''; + const validValues = ['0o01234567']; + const invalidValues = [null, undefined, '00c12345670c']; + + class MyClass { + @IsOctal() + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isOctal(value)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isOctal(value)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isOctal'; + const message = 'someProperty must be valid octal number'; + checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); +describe('IsPassportNumber', () => { + const constraint = 'DE'; + const validValues = ['C26VMVVC3']; + const invalidValues = [null, undefined, 'AS0123456']; + + class MyClass { + @IsPassportNumber(constraint) + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isPassportNumber(value, constraint)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isPassportNumber(value, constraint)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isPassportNumber'; + const message = 'someProperty must be valid passport number'; + checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isArray(value).should.be.true); - }); +describe('IsPostalCode', () => { + const constraint = 'BR'; + const validValues = ['39100-000']; + const invalidValues = [null, undefined, '13165-00']; + + class MyClass { + @IsPostalCode(constraint) + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isPostalCode(value, constraint)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isPostalCode(value, constraint)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isPostalCode'; + const message = 'someProperty must be a postal code'; + checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isArray(value as any).should.be.false); - }); +describe('IsSemVer', () => { + const constraint = ''; + const validValues = ['1.1.2+meta-valid']; + const invalidValues = [null, undefined, '1.0.0-alpha_beta']; + + class MyClass { + @IsSemVer() + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isSemVer(value)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isSemVer(value)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isSemVer'; + const message = 'someProperty must be a Semantic Versioning Specification'; + checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - it("should return error object with proper data", function(done) { - const validationType = "isArray"; - const message = "someProperty must be an array"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); +describe('IsByteLength', () => { + const constraint1 = 2; + const constraint2 = 20; + const validValues = ['hellostring']; + const invalidValues = [null, undefined, 'helloveryveryveryverylongstring']; + + class MyClass { + @IsByteLength(constraint1, constraint2) + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isByteLength(value, constraint1, constraint2)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isByteLength(value, constraint1, constraint2)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isByteLength'; + const message = + "someProperty's byte length must fall into (" + + constraintToString(constraint1) + + ', ' + + constraintToString(constraint2) + + ') range'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); +describe('IsTaxId', () => { + const constraint = 'bg-BG'; + const validValues = ['7501010010', '0101010012', '0111010010', '7521010014', '7541010019']; + const invalidValues = [null, undefined, '750101001', '75010100101', '75-01010/01 0', '7521320010', '7501010019']; + + class MyClass { + @IsTaxId(constraint) + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isTaxId(value, constraint)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isTaxId(value, constraint)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isTaxId'; + const message = 'someProperty must be a Tax Identification Number'; + checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); }); +describe('IsCreditCard', () => { + const validValues = [ + '375556917985515', + '36050234196908', + '4716461583322103', + '4716-2210-5188-5662', + '4929 7226 5379 7141', + '5398228707871527', + ]; + const invalidValues = [null, undefined, 'foo', 'foo', '5398228707871528']; + + class MyClass { + @IsCreditCard() + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isCreditCard(value)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isCreditCard(value)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isCreditCard'; + const message = 'someProperty must be a credit card'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); -describe("IsEnum", function() { +describe('IsCurrency', () => { + const validValues = [ + '-$10,123.45', + '$10,123.45', + '$10123.45', + '10,123.45', + '10123.45', + '10,123', + '1,123,456', + '1123456', + '1.39', + '.03', + '0.10', + '$0.10', + '-$0.01', + '-$.99', + '$100,234,567.89', + '$10,123', + '10,123', + '-10123', + ]; + const invalidValues = [ + null, + undefined, + '1.234', + '$1.1', + '$ 32.50', + '500$', + '.0001', + '$.001', + '$0.001', + '12,34.56', + '123456,123,123456', + '123,4', + ',123', + '$-,123', + '$', + '.', + ',', + '00', + '$-', + '$-,.', + '-', + '-$', + '', + '- $', + ]; + + class MyClass { + @IsCurrency() + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isCurrency(value)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isCurrency(value)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isCurrency'; + const message = 'someProperty must be a currency'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - enum MyEnum { - First = 1, - Second = 999 - } +describe('IsEmail', () => { + const validValues = [ + 'foo@bar.com', + 'x@x.au', + 'foo@bar.com.au', + 'foo+bar@bar.com', + 'hans.m端ller@test.com', + 'hans@m端ller.com', + 'test|123@m端ller.com', + '"foobar"@example.com', + '" foo m端ller "@example.com', + '"foo\\@bar"@example.com', + ]; + const invalidValues = [ + null, + undefined, + 'invalidemail@', + 'invalid.com', + '@invalid.com', + 'foo@bar.com.', + 'somename@gmail.com', + 'foo@bar.co.uk.', + 'z@co.c', + 'gmail...ignores...dots...@gmail.com', + 'gmailgmailgmailgmailgmail@gmail.com', + ]; + + class MyClass { + @IsEmail() + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => { + expect(isEmail(value)).toBeTruthy(); + }); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isEmail(value)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isEmail'; + const message = 'someProperty must be an email'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - enum MyStringEnum { - First = "first", - Second = "second" - } +describe('IsFQDN', () => { + const validValues = [ + 'domain.com', + 'dom.plato', + 'a.domain.co', + 'foo--bar.com', + 'xn--froschgrn-x9a.com', + 'rebecca.blackfriday', + ]; + const invalidValues = [ + null, + undefined, + 'abc', + '256.0.0.0', + '_.com', + '*.some.com', + 's!ome.com', + 'domain.com/', + '/more.com', + ]; + + class MyClass { + @IsFQDN() + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isFQDN(value)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isFQDN(value)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isFqdn'; + const message = 'someProperty must be a valid domain name'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - const validValues = [MyEnum.First, MyEnum.Second]; - const validStringValues = [MyStringEnum.First, MyStringEnum.Second]; - const invalidValues = [ - true, - false, - 0, - {}, - null, - undefined, - "F2irst" - ]; +describe('IsFullWidth', () => { + const validValues = ['ひらがな・カタカナ、.漢字', '3ー0 a@com', 'Fカタカナ゙ᆲ', 'Good=Parts']; + const invalidValues = [null, undefined, 'abc', 'abc123']; - class MyClass { - @IsEnum(MyEnum) - someProperty: MyEnum; - } + class MyClass { + @IsFullWidth() + someProperty: string; + } - class MyClass2 { - @IsEnum(MyStringEnum) - someProperty: MyStringEnum; - } + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); - it("should not fail if validator.validate said that its valid (string enum)", function(done) { - checkValidValues(new MyClass2(), validStringValues, done); - }); + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isFullWidth(value)).toBeTruthy()); + }); - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isFullWidth(value)).toBeFalsy()); + }); - it("should fail if validator.validate said that its invalid (string enum)", function(done) { - checkInvalidValues(new MyClass2(), invalidValues, done); - }); + it('should return error object with proper data', () => { + const validationType = 'isFullWidth'; + const message = 'someProperty must contain a full-width characters'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isEnum(value, MyEnum).should.be.true); - }); +describe('IsHalfWidth', () => { + const validValues = ['l-btn_02--active', 'abc123い', 'カタカナ゙ᆲ←']; + const invalidValues = [null, undefined, 'あいうえお', '0011']; - it("should not fail if method in validator said that its valid (string enum)", function() { - validStringValues.forEach(value => validator.isEnum(value, MyStringEnum).should.be.true); - }); + class MyClass { + @IsHalfWidth() + someProperty: string; + } - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isEnum(value, MyEnum).should.be.false); - }); + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); - it("should fail if method in validator said that its invalid (string enum)", function() { - invalidValues.forEach(value => validator.isEnum(value, MyStringEnum).should.be.false); - }); + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); - it("should return error object with proper data", function(done) { - const validationType = "isEnum"; - const message = "someProperty must be a valid enum value"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isHalfWidth(value)).toBeTruthy()); + }); - it("should return error object with proper data (string enum)", function(done) { - const validationType = "isEnum"; - const message = "someProperty must be a valid enum value"; - checkReturnedError(new MyClass2(), invalidValues, validationType, message, done); - }); + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isHalfWidth(value)).toBeFalsy()); + }); + it('should return error object with proper data', () => { + const validationType = 'isHalfWidth'; + const message = 'someProperty must contain a half-width characters'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); }); +describe('IsVariableWidth', () => { + const validValues = ['ひらがなカタカナ漢字ABCDE', '3ー0123', 'Fカタカナ゙ᆲ', 'Good=Parts']; + const invalidValues = [ + null, + undefined, + 'abc', + 'abc123', + '!"#$%&()<>/+=-_? ~^|.,@`{}[]', + 'ひらがな・カタカナ、.漢字', + '123456', + 'カタカナ゙ᆲ', + ]; + + class MyClass { + @IsVariableWidth() + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isVariableWidth(value)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isVariableWidth(value)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isVariableWidth'; + const message = 'someProperty must contain a full-width and half-width characters'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); -// ------------------------------------------------------------------------- -// Specifications: number check -// ------------------------------------------------------------------------- +describe('IsHexColor', () => { + const validValues = ['#ff0034', '#CCCCCC', 'fff', '#f00']; + const invalidValues = [null, undefined, '#ff', '#xxxx', '#ff12FG']; -describe("IsDivisibleBy", function() { + class MyClass { + @IsHexColor() + someProperty: string; + } - const constraint = 2; - const validValues = [ 2, 4, 100, 1000]; - const invalidValues = ["", undefined, null]; + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); - class MyClass { - @IsDivisibleBy(constraint) - someProperty: string; - } + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); + it('should not fail if method in validator said that its valid', () => { + invalidValues.forEach(value => expect(isHexColor(value)).toBeFalsy()); + }); - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); + it('should fail if method in validator said that its invalid', () => { + validValues.forEach(value => expect(isHexColor(value)).toBeTruthy()); + }); - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isDivisibleBy(value, constraint).should.be.true); - }); + it('should return error object with proper data', () => { + const validationType = 'isHexColor'; + const message = 'someProperty must be a hexadecimal color'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isDivisibleBy(value as any, constraint).should.be.false); - }); +describe('IsHexadecimal', () => { + const validValues = ['deadBEEF', 'ff0044']; + const invalidValues = [null, undefined, 'abcdefg', '', '..']; - it("should return error object with proper data", function(done) { - const validationType = "isDivisibleBy"; - const message = "someProperty must be divisible by " + constraint; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); + class MyClass { + @IsHexadecimal() + someProperty: string; + } -}); + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); -describe("IsPositive", function() { + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); - const validValues = [ - , 3 - , 5000 - ]; - const invalidValues = [ - , "-1" - , "-2" - , "0" - , "1" - , "2" - , "3" - , "4" - , "5" - , "6" - , "7" - , "8" - , "9" - , "100000" - , -500 - , -123 - , -1 - , " " - , "" - ]; + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isHexadecimal(value)).toBeTruthy()); + }); - class MyClass { - @IsPositive() - someProperty: string; - } + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isHexadecimal(value)).toBeFalsy()); + }); - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); + it('should return error object with proper data', () => { + const validationType = 'isHexadecimal'; + const message = 'someProperty must be a hexadecimal number'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); +describe('IsMACAddress', () => { + const validValues = ['ab:ab:ab:ab:ab:ab', 'FF:FF:FF:FF:FF:FF', '01:02:03:04:05:ab', '01:AB:03:04:05:06']; + const invalidValues = [ + null, + undefined, + 'abc', + '01:02:03:04:05', + '01:02:03:04::ab', + '1:2:3:4:5:6', + 'AB:CD:EF:GH:01:02', + 'A9C5 D4 9F EB D3', + '01-02 03:04 05 ab', + ]; + + class MyClass { + @IsMACAddress() + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isMACAddress(value)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isMACAddress(value)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isMacAddress'; + const message = 'someProperty must be a MAC Address'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isPositive(value).should.be.true); - }); +describe('IsIP', () => { + const validValues = [ + '127.0.0.1', + '0.0.0.0', + '255.255.255.255', + '1.2.3.4', + '::1', + '2001:db8:0000:1:1:1:1:1', + '2001:41d0:2:a141::1', + '::ffff:127.0.0.1', + '::0000', + '0000::', + '1::', + '1111:1:1:1:1:1:1:1', + 'fe80::a6db:30ff:fe98:e946', + '::', + '::ffff:127.0.0.1', + '0:0:0:0:0:ffff:127.0.0.1', + ]; + const invalidValues = [ + null, + undefined, + 'abc', + '256.0.0.0', + '0.0.0.256', + '26.0.0.256', + '::banana', + 'banana::', + '::1banana', + '::1::', + '1:', + ':1', + ':1:1:1::2', + '1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1', + '::11111', + '11111:1:1:1:1:1:1:1', + '2001:db8:0000:1:1:1:1::1', + '0:0:0:0:0:0:ffff:127.0.0.1', + '0:0:0:0:ffff:127.0.0.1', + ]; + + class MyClass { + @IsIP() + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isIP(value)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isIP(value)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isIp'; + const message = 'someProperty must be an ip address'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isPositive(value as any).should.be.false); - }); +describe('IsISBN version 10', () => { + const validValues = [ + '3836221195', + '3-8362-2119-5', + '3 8362 2119 5', + '1617290858', + '1-61729-085-8', + '1 61729 085-8', + '0007269706', + '0-00-726970-6', + '0 00 726970 6', + '3423214120', + '3-423-21412-0', + '3 423 21412 0', + '340101319X', + '3-401-01319-X', + '3 401 01319 X', + ]; + const invalidValues = [ + null, + undefined, + '3423214121', + '3-423-21412-1', + '3 423 21412 1', + '978-3836221191', + '9783836221191', + '123456789a', + 'foo', + ]; + + class MyClass { + @IsISBN(10) + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isISBN(value, '10')).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isISBN(value, '10')).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isIsbn'; + const message = 'someProperty must be an ISBN'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - it("should return error object with proper data", function(done) { - const validationType = "isPositive"; - const message = "someProperty must be a positive number"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); +describe('IsISBN version 13', () => { + const validValues = [ + '9783836221191', + '978-3-8362-2119-1', + '978 3 8362 2119 1', + '9783401013190', + '978-3401013190', + '978 3401013190', + '9784873113685', + '978-4-87311-368-5', + '978 4 87311 368 5', + ]; + const invalidValues = [ + null, + undefined, + '9783836221190', + '978-3-8362-2119-0', + '978 3 8362 2119 0', + '3836221195', + '3-8362-2119-5', + '3 8362 2119 5', + '01234567890ab', + 'foo', + '', + ]; + + class MyClass { + @IsISBN(13) + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isISBN(value, '13')).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isISBN(value, '13')).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isIsbn'; + const message = 'someProperty must be an ISBN'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); +describe('IsISO8601', () => { + const validValues = [ + '2009-12T12:34', + '2009', + '2009-05-19', + '2009-05-19', + '20090519', + '2009123', + '2009-05', + '2009-123', + '2009-222', + '2009-001', + '2009-W01-1', + '2009-W51-1', + '2009-W511', + '2009-W33', + '2009W511', + '2009-05-19', + '2009-05-19 00:00', + '2009-05-19 14', + '2009-05-19 14:31', + '2009-05-19 14:39:22', + '2009-05-19T14:39Z', + '2009-W21-2', + '2009-W21-2T01:22', + '2009-139', + '2009-05-19 14:39:22-06:00', + '2009-05-19 14:39:22+0600', + '2009-05-19 14:39:22-01', + '20090621T0545Z', + '2007-04-06T00:00', + '2007-04-05T24:00', + '2010-02-18T16:23:48.5', + '2010-02-18T16:23:48,444', + '2010-02-18T16:23:48,3-06:00', + '2010-02-18T16:23.4', + '2010-02-18T16:23,25', + '2010-02-18T16:23.33+0600', + '2010-02-18T16.23334444', + '2010-02-18T16,2283', + '2009-05-19 143922.500', + '2009-05-19 1439,55', + ]; + const invalidValues = [ + null, + undefined, + '200905', + '2009367', + '2009-', + '2007-04-05T24:50', + '2009-000', + '2009-M511', + '2009M511', + '2009-05-19T14a39r', + '2009-05-19T14:3924', + '2009-0519', + '2009-05-1914:39', + '2009-05-19 14:', + '2009-05-19r14:39', + '2009-05-19 14a39a22', + '200912-01', + '2009-05-19 14:39:22+06a00', + '2009-05-19 146922.500', + '2010-02-18T16.5:23.35:48', + '2010-02-18T16:23.35:48', + '2010-02-18T16:23.35:48.45', + '2009-05-19 14.5.44', + '2010-02-18T16:23.33.600', + '2010-02-18T16,25:23:48,444', + '2009-02-29', + ]; + + class MyClass { + @IsISO8601({ strict: true }) + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isISO8601(value, { strict: true })).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isISO8601(value, { strict: true })).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isIso8601'; + const message = 'someProperty must be a valid ISO 8601 date string'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); }); -describe("IsNegative", function() { +describe('IsJSON', () => { + const validValues = ['{ "key": "value" }', '{}']; + const invalidValues = [null, undefined, '{ key: "value" }', "{ 'key': 'value' }", 'null', '1234', 'false', '"nope"']; - const validValues = [ - , -3 - , -5000 - , -0.1 - ]; - const invalidValues = [ - , "-1" - , "-2" - , "0" - , "1" - , "2" - , "3" - , "4" - , "5" - , "6" - , "7" - , "8" - , "9" - , "100000" - , 500 - , 123 - , 1 - , " " - , "" - ]; + class MyClass { + @IsJSON() + someProperty: string; + } - class MyClass { - @IsNegative() - someProperty: string; - } + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isJSON(value)).toBeTruthy()); + }); - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isNegative(value).should.be.true); - }); + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isJSON(value)).toBeFalsy()); + }); - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isNegative(value as any).should.be.false); - }); + it('should return error object with proper data', () => { + const validationType = 'isJson'; + const message = 'someProperty must be a json string'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - it("should return error object with proper data", function(done) { - const validationType = "isNegative"; - const message = "someProperty must be a negative number"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); +describe('IsJWT', () => { + const validValues = [ + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dnZWRJbkFzIjoiYWRtaW4iLCJpYXQiOjE0MjI3Nzk2Mzh9.gzSraSYS8EXBxLN_oWnFSRgCzcmJmMjLiuyu5CSpyHI', + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb3JlbSI6Imlwc3VtIn0.ymiJSsMJXR6tMSr8G9usjQ15_8hKPDv_CArLhxw28MI', + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkb2xvciI6InNpdCIsImFtZXQiOlsibG9yZW0iLCJpcHN1bSJdfQ.rRpe04zbWbbJjwM43VnHzAboDzszJtGrNsUxaqQ-GQ8', + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqb2huIjp7ImFnZSI6MjUsImhlaWdodCI6MTg1fSwiamFrZSI6eyJhZ2UiOjMwLCJoZWlnaHQiOjI3MH19.YRLPARDmhGMC3BBk_OhtwwK21PIkVCqQe8ncIRPKo-E', + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ', // No signature + ]; + const invalidValues = ['eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9', '$Zs.ewu.su84', 'ks64$S/9.dy$§kz.3sd73b']; + + class MyClass { + @IsJWT() + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isJWT(value)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isJWT(value)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isJwt'; + const message = 'someProperty must be a jwt string'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); +describe('IsObject', () => { + const validValues = [{ key: 'value' }, { key: 'value' }, {}]; + const invalidValues: any[] = [ + null, + undefined, + '{ key: "value" }', + "{ 'key': 'value' }", + 'string', + 1234, + false, + '[]', + [], + [{ key: 'value' }], + ]; + + class MyClass { + @IsObject() + someProperty: object; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isObject(value)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isObject(value)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isObject'; + const message = 'someProperty must be an object'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); }); -describe("Min", function() { +describe('IsNotEmptyObject', () => { + const validValues = [{ key: 'value' }, { key: 'value' }, { key: undefined }, { key: null }]; + const invalidValues = [ + null, + undefined, + '{ key: "value" }', + "{ 'key': 'value' }", + 'string', + 1234, + false, + {}, + [], + [{ key: 'value' }], + ]; + const nullableValidValues = [{ key: 'value' }, { key: 'value' }]; + const nullableInvalidValues = [ + null, + undefined, + '{ key: "value" }', + "{ 'key': 'value' }", + 'string', + 1234, + false, + {}, + { key: undefined }, + { key: null }, + [], + [{ key: 'value' }], + ]; + + class MyClass { + @IsNotEmptyObject() + someProperty: object; + } + + class NullableMyClass { + @IsNotEmptyObject({ nullable: true }) + someProperty: object; + } + + it.each([ + [new MyClass(), validValues], + [new NullableMyClass(), nullableValidValues], + ])('should not fail if validator.validate said that its valid', (validationObject, values) => { + return checkValidValues(validationObject, values); + }); + + it.each([ + [new MyClass(), invalidValues], + [new NullableMyClass(), nullableInvalidValues], + ])('should fail if validator.validate said that its invalid', (validationObject, values) => { + return checkInvalidValues(validationObject, values); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isNotEmptyObject(value)).toBeTruthy()); + nullableValidValues.forEach(value => expect(isNotEmptyObject(value, { nullable: true })).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isNotEmptyObject(value)).toBeFalsy()); + nullableInvalidValues.forEach(value => expect(isNotEmptyObject(value, { nullable: true })).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isNotEmptyObject'; + const message = 'someProperty must be a non-empty object'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - const constraint = 10; - const validValues = [10, 11, 20, 30, 40]; - const invalidValues = [2, 3, 4, 5, 6, 7, 8, 9, -10]; +describe('IsLowercase', () => { + const validValues = ['abc', 'abc123', 'this is lowercase.', 'tr竪s 端ber']; + const invalidValues = [null, undefined, 'fooBar', '123A']; - class MyClass { - @Min(constraint) - someProperty: string; - } + class MyClass { + @IsLowercase() + someProperty: string; + } - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.min(value, constraint).should.be.true); - }); - - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.min(value, constraint).should.be.false); - }); + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isLowercase(value)).toBeTruthy()); + }); - it("should return error object with proper data", function(done) { - const validationType = "min"; - const message = "someProperty must not be less than " + constraint; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isLowercase(value)).toBeFalsy()); + }); + it('should return error object with proper data', () => { + const validationType = 'isLowercase'; + const message = 'someProperty must be a lowercase string'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); }); -describe("Max", function() { +describe('IsMongoId', () => { + const validValues = ['507f1f77bcf86cd799439011']; + const invalidValues = [ + null, + undefined, + '507f1f77bcf86cd7994390', + '507f1f77bcf86cd79943901z', + '', + '507f1f77bcf86cd799439011 ', + ]; + + class MyClass { + @IsMongoId() + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isMongoId(value)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isMongoId(value)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isMongoId'; + const message = 'someProperty must be a mongodb id'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - const constraint = 10; - const validValues = [1, 2, 3, 4, 5, 6, 7, 8, 9, -10, 10]; - const invalidValues = [11, 20, 30, 40]; +describe('IsMultibyte', () => { + const validValues = [ + 'ひらがな・カタカナ、.漢字', + 'あいうえお foobar', + 'test@example.com', + '1234abcDExyz', + 'カタカナ', + '中文', + ]; + const invalidValues = [null, undefined, 'abc', 'abc123', '<>@" *.']; + + class MyClass { + @IsMultibyte() + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isMultibyte(value)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isMultibyte(value)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isMultibyte'; + const message = 'someProperty must contain one or more multibyte chars'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - class MyClass { - @Max(constraint) - someProperty: string; - } +describe('IsSurrogatePair', () => { + const validValues = ['𠮷野𠮷', '𩸽', 'ABC千𥧄1-2-3']; + const invalidValues = [null, undefined, '吉野竈', '鮪', 'ABC1-2-3']; - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); + class MyClass { + @IsSurrogatePair() + someProperty: string; + } - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.max(value, constraint).should.be.true); - }); + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.max(value, constraint).should.be.false); - }); + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isSurrogatePair(value)).toBeTruthy()); + }); - it("should return error object with proper data", function(done) { - const validationType = "max"; - const message = "someProperty must not be greater than " + constraint; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isSurrogatePair(value)).toBeFalsy()); + }); + it('should return error object with proper data', () => { + const validationType = 'isSurrogatePair'; + const message = 'someProperty must contain any surrogate pairs chars'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); }); -// ------------------------------------------------------------------------- -// Specifications: date check -// ------------------------------------------------------------------------- - -describe("MinDate", function() { +describe('IsUrl', () => { + const validValues = [ + 'foobar.com', + 'www.foobar.com', + 'foobar.com/', + 'valid.au', + 'http://www.foobar.com/', + 'http://www.foobar.com:23/', + 'http://www.foobar.com:65535/', + 'http://www.foobar.com:5/', + 'https://www.foobar.com/', + 'ftp://www.foobar.com/', + 'http://www.foobar.com/~foobar', + 'http://user:pass@www.foobar.com/', + 'http://user:@www.foobar.com/', + 'http://127.0.0.1/', + 'http://10.0.0.0/', + 'http://189.123.14.13/', + 'http://duckduckgo.com/?q=%2F', + 'http://foobar.com/t$-_.+!*"(),', + 'http://foobar.com/?foo=bar#baz=qux', + 'http://foobar.com?foo=bar', + 'http://foobar.com#baz=qux', + 'http://www.xn--froschgrn-x9a.net/', + 'http://xn--froschgrn-x9a.com/', + 'http://foo--bar.com', + 'http://høyfjellet.no', + 'http://xn--j1aac5a4g.xn--j1amh', + ]; + const invalidValues = [ + null, + undefined, + 'xyz://foobar.com', + 'invalid/', + 'invalid.x', + 'invalid.', + '.com', + 'http://com/', + 'http://300.0.0.1/', + 'mailto:foo@bar.com', + 'rtmp://foobar.com', + 'http://www.xn--.com/', + 'http://xn--.com/', + 'http://www.foobar.com:0/', + 'http://www.foobar.com:70000/', + 'http://www.foobar.com:99999/', + 'http://www.-foobar.com/', + 'http://www.foobar-.com/', + 'http://foobar/# lol', + 'http://foobar/? lol', + 'http://foobar/ lol/', + 'http://lol @foobar.com/', + 'http://lol:lol @foobar.com/', + 'http://lol:lol:lol@foobar.com/', + 'http://lol: @foobar.com/', + 'http://www.foo_bar.com/', + 'http://www.foobar.com/\t', + 'http://\n@www.foobar.com/', + '', + 'http://localhost:61500this is an invalid url!!!!', + 'http://foobar.com/' + new Array(2083).join('f'), + 'http://*.foo.com', + '*.foo.com', + '!.foo.com', + 'http://example.com.', + '////foobar.com', + 'http:////foobar.com', + ]; + + class MyClass { + @IsUrl() + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isURL(value)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isURL(value)).toBeFalsy()); + }); + + it('should fail on localhost without require_tld option', () => { + expect(isURL('http://localhost:3000/')).toBeFalsy(); + }); + + it('should pass on localhost with require_tld option', () => { + // eslint-disable-next-line @typescript-eslint/camelcase + expect(isURL('http://localhost:3000/', { require_tld: false })).toBeTruthy(); + }); + + it('should return error object with proper data', () => { + const validationType = 'isUrl'; + const message = 'someProperty must be a URL address'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - const constraint = new Date(1995, 11, 17); - const validValues = [new Date()]; - const invalidValues = [new Date(1994, 11, 17)]; +describe('IsUUID', () => { + const validValues = [ + 'A987FBC9-4BED-3078-CF07-9141BA07C9F3', + 'A987FBC9-4BED-4078-8F07-9141BA07C9F3', + 'A987FBC9-4BED-5078-AF07-9141BA07C9F3', + ]; + const invalidValues = [ + null, + undefined, + '', + 'xxxA987FBC9-4BED-3078-CF07-9141BA07C9F3', + 'A987FBC9-4BED-3078-CF07-9141BA07C9F3xxx', + 'A987FBC94BED3078CF079141BA07C9F3', + '934859', + '987FBC9-4BED-3078-CF07A-9141BA07C9F3', + 'AAAAAAAA-1111-1111-AAAG-111111111111', + ]; + + class MyClass { + @IsUUID() + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isUUID(value)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isUUID(value)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isUuid'; + const message = 'someProperty must be a UUID'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - class MyClass { - @MinDate(constraint) - someProperty: Date; - } +describe('IsUUID v3', () => { + const validValues = ['A987FBC9-4BED-3078-CF07-9141BA07C9F3']; + const invalidValues = [ + null, + undefined, + '', + 'xxxA987FBC9-4BED-3078-CF07-9141BA07C9F3', + '934859', + 'AAAAAAAA-1111-1111-AAAG-111111111111', + 'A987FBC9-4BED-4078-8F07-9141BA07C9F3', + 'A987FBC9-4BED-5078-AF07-9141BA07C9F3', + ]; + + class MyClass { + @IsUUID('3') + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isUUID(value, '3')).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isUUID(value, '3')).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isUuid'; + const message = 'someProperty must be a UUID'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); +describe('IsUUID v4', () => { + const validValues = [ + '713ae7e3-cb32-45f9-adcb-7c4fa86b90c1', + '625e63f3-58f5-40b7-83a1-a72ad31acffb', + '57b73598-8764-4ad0-a76a-679bb6640eb1', + '9c858901-8a57-4791-81fe-4c455b099bc9', + ]; + const invalidValues = [ + null, + undefined, + '', + 'xxxA987FBC9-4BED-3078-CF07-9141BA07C9F3', + '934859', + 'AAAAAAAA-1111-1111-AAAG-111111111111', + 'A987FBC9-4BED-5078-AF07-9141BA07C9F3', + 'A987FBC9-4BED-3078-CF07-9141BA07C9F3', + ]; + + class MyClass { + @IsUUID('4') + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isUUID(value, '4')).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isUUID(value, '4')).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isUuid'; + const message = 'someProperty must be a UUID'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); +describe('IsUUID v5', () => { + const validValues = [ + '987FBC97-4BED-5078-AF07-9141BA07C9F3', + '987FBC97-4BED-5078-BF07-9141BA07C9F3', + '987FBC97-4BED-5078-8F07-9141BA07C9F3', + '987FBC97-4BED-5078-9F07-9141BA07C9F3', + ]; + const invalidValues = [ + null, + undefined, + '', + 'xxxA987FBC9-4BED-3078-CF07-9141BA07C9F3', + '934859', + 'AAAAAAAA-1111-1111-AAAG-111111111111', + '9c858901-8a57-4791-81fe-4c455b099bc9', + 'A987FBC9-4BED-3078-CF07-9141BA07C9F3', + ]; + + class MyClass { + @IsUUID('5') + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isUUID(value, '5')).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isUUID(value, '5')).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isUuid'; + const message = 'someProperty must be a UUID'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.minDate(value, constraint).should.be.true); - }); +describe('IsFirebasePushId', () => { + const validValues = [ + '-M-Jh_1KAH5rYJF_7-kY', + '-M1yvu7FKe87rR_62NH7', + '-M1jVySxQQPktYyXA2qE', + '-JhLeOlGIEjaIOFHR0xd', + '-JhQ76OEK_848CkIFhAq', + '-JhQ7APk0UtyRTFO9-TS', + ]; + const invalidValues = [ + null, + undefined, + true, + false, + '', + '5584fa9e-6146-497a-85c9-dbb459ef7b74', + 'Steve', + 'dbfa63ea-2c1f-4cf8-b6b9-192b070b558c', + ]; + + class MyClass { + @IsFirebasePushId() + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isFirebasePushId(value)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isFirebasePushId(value)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'IsFirebasePushId'; + const message = 'someProperty must be a Firebase Push Id'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.minDate(value, constraint).should.be.false); - }); +describe('IsUppercase', () => { + const validValues = ['ABC', 'ABC123', 'ALL CAPS IS FUN.', ' .']; + const invalidValues = [null, undefined, 'fooBar', '123abc']; - it("should return error object with proper data", function(done) { - const validationType = "minDate"; - const message = "minimal allowed date for someProperty is " + constraint; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); + class MyClass { + @IsUppercase() + someProperty: string; + } -}); + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); -describe("MaxDate", function() { + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); - const constraint = new Date(1995, 11, 17); - const validValues = [new Date(1994, 11, 17)]; - const invalidValues = [new Date()]; + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isUppercase(value)).toBeTruthy()); + }); - class MyClass { - @MaxDate(constraint) - someProperty: Date; - } + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isUppercase(value)).toBeFalsy()); + }); - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); + it('should return error object with proper data', () => { + const validationType = 'isUppercase'; + const message = 'someProperty must be uppercase'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); +describe('Length', () => { + const constraint1 = 2; + const constraint2 = 3; + const validValues = ['abc', 'de']; + const invalidValues = [null, undefined, '', 'a', 'abcd']; + + class MyClass { + @Length(constraint1, constraint2) + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(length(value, constraint1, constraint2)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(length(value, constraint1, constraint2)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isLength'; + const message = 'someProperty must be longer than or equal to ' + constraintToString(constraint1) + ' characters'; + return checkReturnedError(new MyClass(), ['', 'a'], validationType, message); + }); + + it('should return error object with proper data', () => { + const validationType = 'isLength'; + const message = 'someProperty must be shorter than or equal to ' + constraintToString(constraint2) + ' characters'; + return checkReturnedError(new MyClass(), ['aaaa', 'azzazza'], validationType, message); + }); +}); - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.maxDate(value, constraint).should.be.true); - }); +describe('MinLength', () => { + const constraint1 = 10; + const validValues = ['helloworld', 'hello how are you']; + const invalidValues = [null, undefined, 'hellowar', 'howareyou']; + + class MyClass { + @MinLength(constraint1) + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(minLength(value, constraint1)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(minLength(value, constraint1)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'minLength'; + const message = 'someProperty must be longer than or equal to ' + constraintToString(constraint1) + ' characters'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.maxDate(value, constraint).should.be.false); - }); +describe('MaxLength', () => { + const constraint1 = 10; + const validValues = ['hellowar', 'howareyou', 'helloworld']; + const invalidValues = [null, undefined, 'helloworld!', 'hello how are you']; + + class MyClass { + @MaxLength(constraint1) + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(maxLength(value, constraint1)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(maxLength(value, constraint1)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'maxLength'; + const message = 'someProperty must be shorter than or equal to ' + constraintToString(constraint1) + ' characters'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - it("should return error object with proper data", function(done) { - const validationType = "maxDate"; - const message = "maximal allowed date for someProperty is " + constraint; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); +describe('Matches pattern RegExp', () => { + const constraint = /abc/; + const validValues = ['abc', 'abcdef', '123abc']; + const invalidValues = [null, undefined, 'acb', 'Abc']; + + class MyClass { + @Matches(constraint) + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(matches(value, constraint)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(matches(value, constraint)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'matches'; + const message = 'someProperty must match ' + constraintToString(constraint) + ' regular expression'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); +describe('Matches pattern string with modifier', () => { + const constraint = 'abc'; + const modifier = 'i'; + const validValues = ['abc', 'abcdef', '123abc', 'AbC']; + const invalidValues = [null, undefined, 'acb']; + + class MyClass { + @Matches(constraint, modifier) + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(matches(value, constraint, modifier)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(matches(value, constraint, modifier)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'matches'; + const message = 'someProperty must match ' + constraintToString(constraint) + ' regular expression'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); }); -// ------------------------------------------------------------------------- -// Specifications: string-as-type check -// ------------------------------------------------------------------------- +describe('IsMilitaryTime', () => { + class MyClass { + @IsMilitaryTime() + someProperty: string; + } + + it('should not fail for a valid time in the format HH:MM', () => { + const validValues = ['10:22', '12:03', '16:32', '23:59', '00:00']; + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail for invalid time format', () => { + const invalidValues = ['23:61', '25:00', '08:08 pm', '04:00am']; + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should fail for invalid values', () => { + const invalidValues = [undefined, null, '23:00 and invalid counterpart']; + return checkInvalidValues(new MyClass(), invalidValues); + }); +}); -describe("IsBooleanString", function() { +describe('IsTimeZone', () => { + class MyClass { + @IsTimeZone() + someProperty: string; + } + + it('should not fail for a valid IANA timezones', () => { + const validValues = ['Asia/Kathmandu', 'America/New_York', 'Europe/Paris', 'Europe/Berlin']; + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail for invalid timezone format', () => { + const invalidValues = ['Asia/Pokhara', 'America', 'New_York', '/Paris']; + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should fail for invalid values', () => { + const invalidValues = [undefined, null, 'Asia-Kathmandu']; + return checkInvalidValues(new MyClass(), invalidValues); + }); +}); +describe('isPhoneNumber', () => { + describe('with region', () => { const validValues = [ - "1", - "0", - "true", - "false" - ]; - const invalidValues = [ - "2", - "3", - "falze" + '0311111111', + '031 633 60 01', + '079 4 666 666', + '075 416 20 30', + '+41 311111111', + '+41 31 633 60 01', + '+41 79 4 666 666', + '+41 75 416 20 30', + '+41 (0)311111111', + '+41 (0)31 633 60 01', + '+41 (0)79 4 666 666', + '+41 (0)75 416 20 30', + '+49 9072 1111', ]; + const invalidValues = [undefined, null, 'asdf', '1']; class MyClass { - @IsBooleanString() - someProperty: string; + @IsPhoneNumber('CH') + someProperty: string; } - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); }); - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); }); + }); - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isBooleanString(value).should.be.true); - }); - - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isBooleanString(value).should.be.false); - }); - - it("should return error object with proper data", function(done) { - const validationType = "isBooleanString"; - const message = "someProperty must be a boolean string"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); - -}); - -describe("IsNumberString", function() { - + describe('no region', () => { const validValues = [ - "123" - , "123.123" - , "00123" - , "-00123" - , "0" - , "-0" - , "+123" + '+41 311111111', + '+41 31 633 60 01', + '+41 79 4 666 666', + '+41 75 416 20 30', + '+41 (0)311111111', + '+41 (0)31 633 60 01', + '+41 (0)79 4 666 666', + '+41 (0)75 416 20 30', + '+49 9072 1111', ]; const invalidValues = [ - " " - , "." + '0311111111', + '031 633 60 01', + '079 4 666 666', + '075 416 20 30', + undefined, + null, + 'asdf', + '1', ]; class MyClass { - @IsNumberString() - someProperty: string; - } - - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); - - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); - - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isNumberString(value).should.be.true); - }); - - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isNumberString(value).should.be.false); - }); - - it("should return error object with proper data", function(done) { - const validationType = "isNumberString"; - const message = "someProperty must be a number string"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); - -}); - -// ------------------------------------------------------------------------- -// Specifications: string check -// ------------------------------------------------------------------------- - -describe("Contains", function() { - - const constraint = "hello"; - const validValues = ["hello world"]; - const invalidValues = [null, undefined, "bye world"]; - - class MyClass { - @Contains(constraint) - someProperty: string; - } - - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); - - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); - - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.contains(value, constraint).should.be.true); - }); - - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.contains(value, constraint).should.be.false); - }); - - it("should return error object with proper data", function(done) { - const validationType = "contains"; - const message = "someProperty must contain a " + constraint + " string"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); - -}); - -describe("NotContains", function() { - - const constraint = "hello"; - const validValues = ["bye world"]; - const invalidValues = [null, undefined, "hello world"]; - - class MyClass { - @NotContains(constraint) - someProperty: string; - } - - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); - - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); - - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.notContains(value, constraint).should.be.true); - }); - - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.notContains(value, constraint).should.be.false); - }); - - it("should return error object with proper data", function(done) { - const validationType = "notContains"; - const message = "someProperty should not contain a " + constraint + " string"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); - -}); - -describe("IsAlpha", function() { - - const constraint = ""; - const validValues = ["hellomynameisalex"]; - const invalidValues = [null, undefined, "hello1mynameisalex"]; - - class MyClass { - @IsAlpha() - someProperty: string; - } - - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); - - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); - - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isAlpha(value).should.be.true); - }); - - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isAlpha(value).should.be.false); - }); - - it("should return error object with proper data", function(done) { - const validationType = "isAlpha"; - const message = "someProperty must contain only letters (a-zA-Z)"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); - -}); - -describe("IsAlphanumeric", function() { - - const constraint = ""; - const validValues = ["hellomyname1salex"]; - const invalidValues = [null, undefined, "hell*mynameisalex"]; - - class MyClass { - @IsAlphanumeric() - someProperty: string; + @IsPhoneNumber(null) + someProperty: string; } - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); }); - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); }); - - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isAlphanumeric(value).should.be.true); - }); - - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isAlphanumeric(value).should.be.false); - }); - - it("should return error object with proper data", function(done) { - const validationType = "isAlphanumeric"; - const message = "someProperty must contain only letters and numbers"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); - + }); }); -describe("IsAscii", function() { - - const constraint = ""; - const validValues = ["hellomyname1salex"]; - const invalidValues = [null, undefined, "hell*mynameisлеха"]; - - class MyClass { - @IsAscii() - someProperty: string; - } - - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); - - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); - - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isAscii(value).should.be.true); - }); - - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isAscii(value).should.be.false); - }); - - it("should return error object with proper data", function(done) { - const validationType = "isAscii"; - const message = "someProperty must contain only ASCII characters"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); - +describe('IsISO31661Alpha2', () => { + class MyClass { + @IsISO31661Alpha2() + someProperty: string; + } + + it('should not fail for a valid ISO31661 Alpha2 code', () => { + const validValues = ['AD', 'AE', 'AF', 'AG']; + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail for invalid values', () => { + const invalidValues = [undefined, null, '', 'AFR']; + return checkInvalidValues(new MyClass(), invalidValues); + }); }); -describe("IsBase64", function() { - - const constraint = ""; - const validValues = ["aGVsbG8="]; - const invalidValues = [null, undefined, "hell*mynameisalex"]; - - class MyClass { - @IsBase64() - someProperty: string; - } - - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); - - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); - - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isBase64(value).should.be.true); - }); - - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isBase64(value).should.be.false); - }); - - it("should return error object with proper data", function(done) { - const validationType = "isBase64"; - const message = "someProperty must be base64 encoded"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); - +describe('IsISO31661Alpha3', () => { + class MyClass { + @IsISO31661Alpha3() + someProperty: string; + } + + it('should not fail for a valid ISO31661 Alpha3 code', () => { + const validValues = ['ABW', 'HND', 'KHM', 'RWA']; + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail for invalid values', () => { + const invalidValues = [undefined, null, '', 'FR', 'fR', 'GB', 'PT', 'CM', 'JP', 'PM', 'ZW']; + return checkInvalidValues(new MyClass(), invalidValues); + }); }); -describe("IsByteLength", function() { - - const constraint1 = 2; - const constraint2 = 20; - const validValues = ["hellostring"]; - const invalidValues = [null, undefined, "helloveryveryveryverylongstring"]; - +describe('isHash', () => { + function testHash(algorithm: ValidatorJS.HashAlgorithm, validValues: any[], invalidValues: any[]): void { class MyClass { - @IsByteLength(constraint1, constraint2) - someProperty: string; + @IsHash(algorithm) + someProperty: string; } - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); }); - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); }); - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isByteLength(value, constraint1, constraint2).should.be.true); + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isHash(value, algorithm)).toBeTruthy()); }); - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isByteLength(value, constraint1, constraint2).should.be.false); + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isHash(value, algorithm)).toBeFalsy()); }); - it("should return error object with proper data", function(done) { - const validationType = "isByteLength"; - const message = "someProperty's byte length must fall into (" + constraint1 + ", " + constraint2 + ") range"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); + it('should return error object with proper data', () => { + const validationType = 'isHash'; + const message = `someProperty must be a hash of type ${algorithm}`; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); }); + } -}); - -describe("IsCreditCard", function() { - + for (const algorithm of ['md5', 'md4', 'ripemd128', 'tiger128']) { const validValues = [ - "375556917985515", - "36050234196908", - "4716461583322103", - "4716-2210-5188-5662", - "4929 7226 5379 7141", - "5398228707871527" + 'd94f3f016ae679c3008de268209132f2', + '751adbc511ccbe8edf23d486fa4581cd', + '88dae00e614d8f24cfd5a8b3f8002e93', + '0bf1c35032a71a14c2f719e5a14c1e96', + ]; + const invalidValues = [ + undefined, + null, + 'q94375dj93458w34', + '39485729348', + '%&FHKJFvk', + 'KYT0bf1c35032a71a14c2f719e5a1', ]; - const invalidValues = [null, undefined, "foo", "foo", "5398228707871528"]; - - class MyClass { - @IsCreditCard() - someProperty: string; - } - - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); - - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); - - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isCreditCard(value).should.be.true); - }); - - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isCreditCard(value).should.be.false); - }); - it("should return error object with proper data", function(done) { - const validationType = "isCreditCard"; - const message = "someProperty must be a credit card"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); + testHash(algorithm as ValidatorJS.HashAlgorithm, validValues, invalidValues); + } -}); + for (const algorithm of ['crc32', 'crc32b']) { + const validValues = ['d94f3f01', '751adbc5', '88dae00e', '0bf1c350']; + const invalidValues = [ + undefined, + null, + 'KYT0bf1c35032a71a14c2f719e5a14c1', + 'q94375dj93458w34', + 'q943', + '39485729348', + '%&FHKJFvk', + ]; -describe("IsCurrency", function() { + testHash(algorithm as ValidatorJS.HashAlgorithm, validValues, invalidValues); + } + for (const algorithm of ['sha1', 'tiger160', 'ripemd160']) { const validValues = [ - "-$10,123.45" - , "$10,123.45" - , "$10123.45" - , "10,123.45" - , "10123.45" - , "10,123" - , "1,123,456" - , "1123456" - , "1.39" - , ".03" - , "0.10" - , "$0.10" - , "-$0.01" - , "-$.99" - , "$100,234,567.89" - , "$10,123" - , "10,123" - , "-10123" + '3ca25ae354e192b26879f651a51d92aa8a34d8d3', + 'aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d', + 'beb8c3f30da46be179b8df5f5ecb5e4b10508230', + 'efd5d3b190e893ed317f38da2420d63b7ae0d5ed', ]; const invalidValues = [ - null - , undefined - , "1.234" - , "$1.1" - , "$ 32.50" - , "500$" - , ".0001" - , "$.001" - , "$0.001" - , "12,34.56" - , "123456,123,123456" - , "123,4" - , ",123" - , "$-,123" - , "$" - , "." - , "," - , "00" - , "$-" - , "$-,." - , "-" - , "-$" - , "" - , "- $" + undefined, + null, + 'KYT0bf1c35032a71a14c2f719e5a14c1', + 'KYT0bf1c35032a71a14c2f719e5a14c1dsjkjkjkjkkjk', + 'q94375dj93458w34', + '39485729348', + '%&FHKJFvk', ]; - class MyClass { - @IsCurrency() - someProperty: string; - } - - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); + testHash(algorithm as ValidatorJS.HashAlgorithm, validValues, invalidValues); + } - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); + for (const algorithm of ['sha256']) { + const validValues = [ + '2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824', + '1d996e033d612d9af2b44b70061ee0e868bfd14c2dd90b129e1edeb7953e7985', + '80f70bfeaed5886e33536bcfa8c05c60afef5a0e48f699a7912d5e399cdcc441', + '579282cfb65ca1f109b78536effaf621b853c9f7079664a3fbe2b519f435898c', + ]; + const invalidValues = [ + undefined, + null, + 'KYT0bf1c35032a71a14c2f719e5a14c1', + 'KYT0bf1c35032a71a14c2f719e5a14c1dsjkjkjkjkkjk', + 'q94375dj93458w34', + '39485729348', + '%&FHKJFvk', + ]; - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isCurrency(value).should.be.true); - }); + testHash(algorithm as ValidatorJS.HashAlgorithm, validValues, invalidValues); + } - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isCurrency(value).should.be.false); - }); + for (const algorithm of ['sha384']) { + const validValues = [ + '3fed1f814d28dc5d63e313f8a601ecc4836d1662a19365cbdcf6870f6b56388850b58043f7ebf2418abb8f39c3a42e31', + 'b330f4e575db6e73500bd3b805db1a84b5a034e5d21f0041d91eec85af1dfcb13e40bb1c4d36a72487e048ac6af74b58', + 'bf547c3fc5841a377eb1519c2890344dbab15c40ae4150b4b34443d2212e5b04aa9d58865bf03d8ae27840fef430b891', + 'fc09a3d11368386530f985dacddd026ae1e44e0e297c805c3429d50744e6237eb4417c20ffca8807b071823af13a3f65', + ]; + const invalidValues = [ + undefined, + null, + 'KYT0bf1c35032a71a14c2f719e5a14c1', + 'KYT0bf1c35032a71a14c2f719e5a14c1dsjkjkjkjkkjk', + 'q94375dj93458w34', + '39485729348', + '%&FHKJFvk', + ]; - it("should return error object with proper data", function(done) { - const validationType = "isCurrency"; - const message = "someProperty must be a currency"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); + testHash(algorithm as ValidatorJS.HashAlgorithm, validValues, invalidValues); + } -}); + for (const algorithm of ['sha512']) { + const validValues = [ + '9b71d224bd62f3785d96d46ad3ea3d73319bfbc2890caadae2dff72519673ca72323c3d99ba5c11d7c7acc6e14b8c5da0c4663475c2e5c3adef46f73bcdec043', + '83c586381bf5ba94c8d9ba8b6b92beb0997d76c257708742a6c26d1b7cbb9269af92d527419d5b8475f2bb6686d2f92a6649b7f174c1d8306eb335e585ab5049', + '45bc5fa8cb45ee408c04b6269e9f1e1c17090c5ce26ffeeda2af097735b29953ce547e40ff3ad0d120e5361cc5f9cee35ea91ecd4077f3f589b4d439168f91b9', + '432ac3d29e4f18c7f604f7c3c96369a6c5c61fc09bf77880548239baffd61636d42ed374f41c261e424d20d98e320e812a6d52865be059745fdb2cb20acff0ab', + ]; + const invalidValues = [ + undefined, + null, + 'KYT0bf1c35032a71a14c2f719e5a14c1', + 'KYT0bf1c35032a71a14c2f719e5a14c1dsjkjkjkjkkjk', + 'q94375dj93458w34', + '39485729348', + '%&FHKJFvk', + ]; -describe("IsEmail", function() { + testHash(algorithm as ValidatorJS.HashAlgorithm, validValues, invalidValues); + } + for (const algorithm of ['tiger192']) { const validValues = [ - "foo@bar.com" - , "x@x.au" - , "foo@bar.com.au" - , "foo+bar@bar.com" - , "hans.m端ller@test.com" - , "hans@m端ller.com" - , "test|123@m端ller.com" - , "\"foobar\"@example.com" - , "\" foo m端ller \"@example.com" - , "\"foo\\@bar\"@example.com" + '6281a1f098c5e7290927ed09150d43ff3990a0fe1a48267c', + '56268f7bc269cf1bc83d3ce42e07a85632394737918f4760', + '46fc0125a148788a3ac1d649566fc04eb84a746f1a6e4fa7', + '7731ea1621ae99ea3197b94583d034fdbaa4dce31a67404a', ]; const invalidValues = [ - null - , undefined - , "invalidemail@" - , "invalid.com" - , "@invalid.com" - , "foo@bar.com." - , "somename@gmail.com" - , "foo@bar.co.uk." - , "z@co.c" - , "test+ext@gmail.com" - , "some.name.midd.leNa.me.+extension@GoogleMail.com" - , "gmail...ignores...dots...@gmail.com" - , "gmailgmailgmailgmailgmail@gmail.com" + undefined, + null, + 'KYT0bf1c35032a71a14c2f719e5a14c1', + 'KYT0bf1c35032a71a14c2f719e5a14c1dsjkjkjkjkkjk', + 'q94375dj93458w34', + '39485729348', + '%&FHKJFvk', ]; - class MyClass { - @IsEmail() - someProperty: string; - } + testHash(algorithm as ValidatorJS.HashAlgorithm, validValues, invalidValues); + } +}); - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); +describe('IsISSN', () => { + const validValues = ['0378-5955', '0000-0000', '2434-561X', '2434-561x', '01896016', '20905076']; + const invalidValues = [ + null, + undefined, + '0378-5954', + '0000-0001', + '0378-123', + '037-1234', + '0', + '2434-561c', + '1684-5370', + '19960791', + '', + ]; + + class MyClass { + @IsISSN() + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isISSN(value)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isISSN(value)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isISSN'; + const message = 'someProperty must be a ISSN'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); +describe('IsISSN with options', () => { + // eslint-disable-next-line @typescript-eslint/camelcase + const options = { case_sensitive: true, require_hyphen: true }; + const validValues = ['2434-561X', '0378-5955']; + const invalidValues = [null, undefined, '2434-561x', '2434561X', '2434561x', '03785955']; + + class MyClass { + @IsISSN(options) + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isISSN(value, options)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isISSN(value, options)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isISSN'; + const message = 'someProperty must be a ISSN'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => { - console.log(value, validator.isEmail(value)); - return validator.isEmail(value).should.be.true; - }); - }); +describe('ArrayContains', () => { + const constraint = ['superman']; + const validValues = [ + ['world', 'hello', 'superman'], + ['world', 'superman', 'hello'], + ['superman', 'world', 'hello'], + ]; + const invalidValues = [null, undefined, ['world', 'hello']]; + + class MyClass { + @ArrayContains(constraint) + someProperty: string[]; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(arrayContains(value, constraint)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(arrayContains(value, constraint)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'arrayContains'; + const message = 'someProperty must contain ' + constraintToString(constraint) + ' values'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isEmail(value).should.be.false); - }); +describe('ArrayNotContains', () => { + const constraint = ['superman']; + const validValues = [['world', 'hello']]; + const invalidValues = [ + null, + undefined, + ['world', 'hello', 'superman'], + ['world', 'superman', 'hello'], + ['superman', 'world', 'hello'], + ]; + + class MyClass { + @ArrayNotContains(constraint) + someProperty: string[]; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(arrayNotContains(value, constraint)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(arrayNotContains(value, constraint)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'arrayNotContains'; + const message = 'someProperty should not contain ' + constraintToString(constraint) + ' values'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - it("should return error object with proper data", function(done) { - const validationType = "isEmail"; - const message = "someProperty must be an email"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); +describe('ArrayNotEmpty', () => { + const validValues = [ + [0], + [''], + [null], + [undefined], + [false], + ['world', 'hello', 'superman'], + ['world', 'superman', 'hello'], + ['superman', 'world', 'hello'], + ]; + const invalidValues: any[] = [null, undefined, []]; + + class MyClass { + @ArrayNotEmpty() + someProperty: string[]; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(arrayNotEmpty(value)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(arrayNotEmpty(value)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'arrayNotEmpty'; + const message = 'someProperty should not be empty'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); +describe('ArrayMinSize', () => { + const constraint = 2; + const validValues = [['world', 'hello']]; + const invalidValues = [null, undefined, ['hi']]; + + class MyClass { + @ArrayMinSize(constraint) + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(arrayMinSize(value, constraint)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(arrayMinSize(value, constraint)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'arrayMinSize'; + const message = 'someProperty must contain at least ' + constraintToString(constraint) + ' elements'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); }); -describe("IsFQDN", function() { +describe('ArrayMaxSize', () => { + const constraint = 2; + const validValues = [['world', 'hello']]; + const invalidValues = [null, undefined, ['hi', 'hello', 'javascript']]; + + class MyClass { + @ArrayMaxSize(constraint) + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(arrayMaxSize(value, constraint)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(arrayMaxSize(value, constraint)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'arrayMaxSize'; + const message = 'someProperty must contain no more than ' + constraintToString(constraint) + ' elements'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - const validValues = [ - "domain.com" - , "dom.plato" - , "a.domain.co" - , "foo--bar.com" - , "xn--froschgrn-x9a.com" - , "rebecca.blackfriday" - ]; - const invalidValues = [ - null - , undefined - , "abc" - , "256.0.0.0" - , "_.com" - , "*.some.com" - , "s!ome.com" - , "domain.com/" - , "/more.com" - ]; +describe('ArrayUnique', () => { + const validValues = [ + ['world', 'hello', 'superman'], + ['world', 'superman', 'hello'], + ['superman', 'world', 'hello'], + ['1', '2', null, undefined], + ]; + const invalidValues: any[] = [ + null, + undefined, + ['world', 'hello', 'hello'], + ['world', 'hello', 'world'], + ['1', '1', '1'], + ]; + + class MyClass { + @ArrayUnique() + someProperty: string[]; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(arrayUnique(value)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(arrayUnique(value)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'arrayUnique'; + const message = "All someProperty's elements must be unique"; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - class MyClass { - @IsFQDN() - someProperty: string; - } +describe('ArrayUnique with identifier', () => { + const identifier = o => o.name; + const validValues = [ + ['world', 'hello', 'superman'], + ['world', 'superman', 'hello'], + ['superman', 'world', 'hello'], + ['1', '2', null, undefined], + ].map(list => list.map(name => ({ name }))); + const invalidValues: any[] = [ + null, + undefined, + ['world', 'hello', 'hello'], + ['world', 'hello', 'world'], + ['1', '1', '1'], + ].map(list => list?.map(name => (name != null ? { name } : name))); + + class MyClass { + @ArrayUnique(identifier) + someProperty: { name: string }[]; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(arrayUnique(value, identifier)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(arrayUnique(value, identifier)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'arrayUnique'; + const message = "All someProperty's elements must be unique"; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); +describe('isInstance', () => { + class MySubClass { + // Empty + } - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); + class WrongSubClass { + // Empty + } - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isFQDN(value).should.be.true); - }); + class MyClass { + @IsInstance(MySubClass) + someProperty: MySubClass; + } - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isFQDN(value).should.be.false); - }); + const validValues = [new MySubClass()]; + const invalidValues = [null, undefined, 15, 'something', new WrongSubClass(), (): null => null]; - it("should return error object with proper data", function(done) { - const validationType = "isFqdn"; - const message = "someProperty must be a valid domain name"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); -}); + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); -describe("IsFullWidth", function() { + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isInstance(value, MySubClass)).toBeTruthy()); + }); - const validValues = [ - "ひらがな・カタカナ、.漢字" - , "3ー0 a@com" - , "Fカタカナ゙ᆲ" - , "Good=Parts" - ]; - const invalidValues = [ - null - , undefined - , "abc" - , "abc123" - ]; + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isInstance(value, MySubClass)).toBeFalsy()); + }); - class MyClass { - @IsFullWidth() - someProperty: string; - } + it('should return error object with proper data', () => { + const validationType = 'isInstance'; + const message = 'someProperty must be an instance of MySubClass'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); +describe('IsStrongPassword', () => { + class MyClass { + @IsStrongPassword() + someProperty: string; + } - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); + const validValues = ['Abcdef1!']; + const invalidValues = [null, undefined, 'Abcde1!', 'abcdef1!', 'ABCDEF1!', 'Abcdefg!', 'Abcdefg1']; - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isFullWidth(value).should.be.true); - }); + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isFullWidth(value).should.be.false); - }); + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); - it("should return error object with proper data", function(done) { - const validationType = "isFullWidth"; - const message = "someProperty must contain a full-width characters"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isStrongPassword(value)).toBeTruthy()); + }); -}); + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isStrongPassword(value)).toBeFalsy()); + }); -describe("IsHalfWidth", function() { + it('should return error object with proper data', () => { + const validationType = 'isStrongPassword'; + const message = 'someProperty is not strong enough'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); - const validValues = [ - , "l-btn_02--active" - , "abc123い" - , "カタカナ゙ᆲ←" - ]; - const invalidValues = [ - null - , undefined - , "あいうえお" - , "0011" - ]; - - class MyClass { - @IsHalfWidth() - someProperty: string; - } - - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); - - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); - - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isHalfWidth(value).should.be.true); - }); - - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isHalfWidth(value).should.be.false); - }); - - it("should return error object with proper data", function(done) { - const validationType = "isHalfWidth"; - const message = "someProperty must contain a half-width characters"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); - -}); - -describe("IsVariableWidth", function() { - - const validValues = [ - "ひらがなカタカナ漢字ABCDE" - , "3ー0123" - , "Fカタカナ゙ᆲ" - , "Good=Parts" - ]; - const invalidValues = [ - null - , undefined - , "abc" - , "abc123" - , "!\"#$%&()<>/+=-_? ~^|.,@`{}[]" - , "ひらがな・カタカナ、.漢字" - , "123456" - , "カタカナ゙ᆲ" - ]; - - class MyClass { - @IsVariableWidth() - someProperty: string; - } - - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); - - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); - - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isVariableWidth(value).should.be.true); - }); - - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isVariableWidth(value).should.be.false); - }); - - it("should return error object with proper data", function(done) { - const validationType = "isVariableWidth"; - const message = "someProperty must contain a full-width and half-width characters"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); - -}); - -describe("IsHexColor", function() { - - const validValues = [ - "#ff0034" - , "#CCCCCC" - , "fff" - , "#f00" - ]; - const invalidValues = [ - null - , undefined - , "#ff" - , "fff0" - , "#ff12FG" - ]; - - class MyClass { - @IsHexColor() - someProperty: string; - } - - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); - - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); - - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isHexColor(value).should.be.true); - }); - - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isHexColor(value).should.be.false); - }); - - it("should return error object with proper data", function(done) { - const validationType = "isHexColor"; - const message = "someProperty must be a hexadecimal color"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); - -}); - -describe("IsHexadecimal", function() { - - const validValues = [ - "deadBEEF" - , "ff0044" - ]; - const invalidValues = [ - null - , undefined - , "abcdefg" - , "" - , ".." - ]; - - class MyClass { - @IsHexadecimal() - someProperty: string; - } - - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); - - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); - - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isHexadecimal(value).should.be.true); - }); - - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isHexadecimal(value).should.be.false); - }); - - it("should return error object with proper data", function(done) { - const validationType = "isHexadecimal"; - const message = "someProperty must be a hexadecimal number"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); - -}); - -describe("IsIP", function() { - - const validValues = [ - "127.0.0.1" - , "0.0.0.0" - , "255.255.255.255" - , "1.2.3.4" - , "::1" - , "2001:db8:0000:1:1:1:1:1" - , "2001:41d0:2:a141::1" - , "::ffff:127.0.0.1" - , "::0000" - , "0000::" - , "1::" - , "1111:1:1:1:1:1:1:1" - , "fe80::a6db:30ff:fe98:e946" - , "::" - , "::ffff:127.0.0.1" - , "0:0:0:0:0:ffff:127.0.0.1" - ]; - const invalidValues = [ - null - , undefined - , "abc" - , "256.0.0.0" - , "0.0.0.256" - , "26.0.0.256" - , "::banana" - , "banana::" - , "::1banana" - , "::1::" - , "1:" - , ":1" - , ":1:1:1::2" - , "1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1" - , "::11111" - , "11111:1:1:1:1:1:1:1" - , "2001:db8:0000:1:1:1:1::1" - , "0:0:0:0:0:0:ffff:127.0.0.1" - , "0:0:0:0:ffff:127.0.0.1" - ]; - - class MyClass { - @IsIP() - someProperty: string; - } - - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); - - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); - - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isIP(value).should.be.true); - }); - - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isIP(value).should.be.false); - }); - - it("should return error object with proper data", function(done) { - const validationType = "isIp"; - const message = "someProperty must be an ip address"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); - -}); - -describe("IsISBN version 10", function() { - - const validValues = [ - "3836221195", "3-8362-2119-5", "3 8362 2119 5" - , "1617290858", "1-61729-085-8", "1 61729 085-8" - , "0007269706", "0-00-726970-6", "0 00 726970 6" - , "3423214120", "3-423-21412-0", "3 423 21412 0" - , "340101319X", "3-401-01319-X", "3 401 01319 X" - ]; - const invalidValues = [ - null, undefined, "3423214121", "3-423-21412-1", "3 423 21412 1" - , "978-3836221191", "9783836221191" - , "123456789a", "foo" - ]; - - class MyClass { - @IsISBN("10") - someProperty: string; - } - - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); - - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); - - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isISBN(value, "10").should.be.true); - }); - - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isISBN(value, "10").should.be.false); - }); - - it("should return error object with proper data", function(done) { - const validationType = "isIsbn"; - const message = "someProperty must be an ISBN"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); - -}); - -describe("IsISBN version 13", function() { - - const validValues = [ - "9783836221191", "978-3-8362-2119-1", "978 3 8362 2119 1" - , "9783401013190", "978-3401013190", "978 3401013190" - , "9784873113685", "978-4-87311-368-5", "978 4 87311 368 5" - ]; - const invalidValues = [ - null, undefined, "9783836221190", "978-3-8362-2119-0", "978 3 8362 2119 0" - , "3836221195", "3-8362-2119-5", "3 8362 2119 5" - , "01234567890ab", "foo", "" - ]; - - class MyClass { - @IsISBN("13") - someProperty: string; - } - - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); - - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); - - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isISBN(value, "13").should.be.true); - }); - - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isISBN(value, "13").should.be.false); - }); - - it("should return error object with proper data", function(done) { - const validationType = "isIsbn"; - const message = "someProperty must be an ISBN"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); - -}); - -describe("IsISO8601", function() { - - const validValues = [ - "2009-12T12:34" - , "2009" - , "2009-05-19" - , "2009-05-19" - , "20090519" - , "2009123" - , "2009-05" - , "2009-123" - , "2009-222" - , "2009-001" - , "2009-W01-1" - , "2009-W51-1" - , "2009-W511" - , "2009-W33" - , "2009W511" - , "2009-05-19" - , "2009-05-19 00:00" - , "2009-05-19 14" - , "2009-05-19 14:31" - , "2009-05-19 14:39:22" - , "2009-05-19T14:39Z" - , "2009-W21-2" - , "2009-W21-2T01:22" - , "2009-139" - , "2009-05-19 14:39:22-06:00" - , "2009-05-19 14:39:22+0600" - , "2009-05-19 14:39:22-01" - , "20090621T0545Z" - , "2007-04-06T00:00" - , "2007-04-05T24:00" - , "2010-02-18T16:23:48.5" - , "2010-02-18T16:23:48,444" - , "2010-02-18T16:23:48,3-06:00" - , "2010-02-18T16:23.4" - , "2010-02-18T16:23,25" - , "2010-02-18T16:23.33+0600" - , "2010-02-18T16.23334444" - , "2010-02-18T16,2283" - , "2009-05-19 143922.500" - , "2009-05-19 1439,55" - ]; - const invalidValues = [ - null - , undefined - , "200905" - , "2009367" - , "2009-" - , "2007-04-05T24:50" - , "2009-000" - , "2009-M511" - , "2009M511" - , "2009-05-19T14a39r" - , "2009-05-19T14:3924" - , "2009-0519" - , "2009-05-1914:39" - , "2009-05-19 14:" - , "2009-05-19r14:39" - , "2009-05-19 14a39a22" - , "200912-01" - , "2009-05-19 14:39:22+06a00" - , "2009-05-19 146922.500" - , "2010-02-18T16.5:23.35:48" - , "2010-02-18T16:23.35:48" - , "2010-02-18T16:23.35:48.45" - , "2009-05-19 14.5.44" - , "2010-02-18T16:23.33.600" - , "2010-02-18T16,25:23:48,444" - ]; - - class MyClass { - @IsISO8601() - someProperty: string; - } - - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); - - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); - - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isISO8601(value).should.be.true); - }); - - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isISO8601(value).should.be.false); - }); - - it("should return error object with proper data", function(done) { - const validationType = "isIso8601"; - const message = "someProperty must be a valid ISO 8601 date string"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); - -}); - -describe("IsJSON", function() { - - const validValues = ["{ \"key\": \"value\" }", "{}"]; - const invalidValues = [null, undefined, "{ key: \"value\" }", "{ 'key': 'value' }", "null", "1234", "false", "\"nope\""]; - - class MyClass { - @IsJSON() - someProperty: string; - } - - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); - - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); - - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isJSON(value).should.be.true); - }); - - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isJSON(value).should.be.false); - }); - - it("should return error object with proper data", function(done) { - const validationType = "isJson"; - const message = "someProperty must be a json string"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); - -}); - -describe("IsLowercase", function() { - - const validValues = [ - "abc" - , "abc123" - , "this is lowercase." - , "tr竪s 端ber" - ]; - const invalidValues = [ - null - , undefined - , "fooBar" - , "123A" - ]; - - class MyClass { - @IsLowercase() - someProperty: string; - } - - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); - - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); - - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isLowercase(value).should.be.true); - }); - - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isLowercase(value).should.be.false); - }); - - it("should return error object with proper data", function(done) { - const validationType = "isLowercase"; - const message = "someProperty must be a lowercase string"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); - -}); - -describe("IsMongoId", function() { - - const validValues = [ - "507f1f77bcf86cd799439011" - ]; - const invalidValues = [ - null - , undefined - , "507f1f77bcf86cd7994390" - , "507f1f77bcf86cd79943901z" - , "" - , "507f1f77bcf86cd799439011 " - ]; - - class MyClass { - @IsMongoId() - someProperty: string; - } - - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); - - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); - - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isMongoId(value).should.be.true); - }); - - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isMongoId(value).should.be.false); - }); - - it("should return error object with proper data", function(done) { - const validationType = "isMongoId"; - const message = "someProperty must be a mongodb id"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); - -}); - -describe("IsMultibyte", function() { - - const validValues = [ - "ひらがな・カタカナ、.漢字" - , "あいうえお foobar" - , "test@example.com" - , "1234abcDExyz" - , "カタカナ" - , "中文" - ]; - const invalidValues = [ - null - , undefined - , "abc" - , "abc123" - , "<>@\" *." - ]; - - class MyClass { - @IsMultibyte() - someProperty: string; - } - - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); - - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); - - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isMultibyte(value).should.be.true); - }); - - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isMultibyte(value).should.be.false); - }); - - it("should return error object with proper data", function(done) { - const validationType = "isMultibyte"; - const message = "someProperty must contain one or more multibyte chars"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); - -}); - -describe("IsSurrogatePair", function() { - - const validValues = [ - "𠮷野𠮷" - , "𩸽" - , "ABC千𥧄1-2-3" - ]; - const invalidValues = [ - null - , undefined - , "吉野竈" - , "鮪" - , "ABC1-2-3" - ]; - - class MyClass { - @IsSurrogatePair() - someProperty: string; - } - - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); - - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); - - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isSurrogatePair(value).should.be.true); - }); - - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isSurrogatePair(value).should.be.false); - }); - - it("should return error object with proper data", function(done) { - const validationType = "isSurrogatePair"; - const message = "someProperty must contain any surrogate pairs chars"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); - -}); - -describe("IsUrl", function() { - - const validValues = [ - "foobar.com" - , "www.foobar.com" - , "foobar.com/" - , "valid.au" - , "http://www.foobar.com/" - , "http://www.foobar.com:23/" - , "http://www.foobar.com:65535/" - , "http://www.foobar.com:5/" - , "https://www.foobar.com/" - , "ftp://www.foobar.com/" - , "http://www.foobar.com/~foobar" - , "http://user:pass@www.foobar.com/" - , "http://user:@www.foobar.com/" - , "http://127.0.0.1/" - , "http://10.0.0.0/" - , "http://189.123.14.13/" - , "http://duckduckgo.com/?q=%2F" - , "http://foobar.com/t$-_.+!*\"()," - , "http://foobar.com/?foo=bar#baz=qux" - , "http://foobar.com?foo=bar" - , "http://foobar.com#baz=qux" - , "http://www.xn--froschgrn-x9a.net/" - , "http://xn--froschgrn-x9a.com/" - , "http://foo--bar.com" - , "http://høyfjellet.no" - , "http://xn--j1aac5a4g.xn--j1amh" - ]; - const invalidValues = [ - null - , undefined - , "xyz://foobar.com" - , "invalid/" - , "invalid.x" - , "invalid." - , ".com" - , "http://com/" - , "http://300.0.0.1/" - , "mailto:foo@bar.com" - , "rtmp://foobar.com" - , "http://www.xn--.com/" - , "http://xn--.com/" - , "http://www.foobar.com:0/" - , "http://www.foobar.com:70000/" - , "http://www.foobar.com:99999/" - , "http://www.-foobar.com/" - , "http://www.foobar-.com/" - , "http://foobar/# lol" - , "http://foobar/? lol" - , "http://foobar/ lol/" - , "http://lol @foobar.com/" - , "http://lol:lol @foobar.com/" - , "http://lol:lol:lol@foobar.com/" - , "http://lol: @foobar.com/" - , "http://www.foo_bar.com/" - , "http://www.foobar.com/\t" - , "http://\n@www.foobar.com/" - , "" - , "http://localhost:61500this is an invalid url!!!!" - , "http://foobar.com/" + new Array(2083).join("f") - , "http://*.foo.com" - , "*.foo.com" - , "!.foo.com" - , "http://example.com." - , "////foobar.com" - , "http:////foobar.com" - ]; - - class MyClass { - @IsUrl() - someProperty: string; - } - - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); - - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); - - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isURL(value).should.be.true); - }); - - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isURL(value).should.be.false); - }); - - it("should fail on localhost without require_tld option", function () { - validator.isURL("http://localhost:3000/").should.be.false; - }); - - it("should pass on localhost with require_tld option", function () { - validator.isURL("http://localhost:3000/", { require_tld: false }).should.be.true; - }); - - it("should return error object with proper data", function(done) { - const validationType = "isUrl"; - const message = "someProperty must be an URL address"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); - -}); - -describe("IsUUID", function() { - - const validValues = [ - "A987FBC9-4BED-3078-CF07-9141BA07C9F3" - , "A987FBC9-4BED-4078-8F07-9141BA07C9F3" - , "A987FBC9-4BED-5078-AF07-9141BA07C9F3" - ]; - const invalidValues = [ - null - , undefined - , "" - , "xxxA987FBC9-4BED-3078-CF07-9141BA07C9F3" - , "A987FBC9-4BED-3078-CF07-9141BA07C9F3xxx" - , "A987FBC94BED3078CF079141BA07C9F3" - , "934859" - , "987FBC9-4BED-3078-CF07A-9141BA07C9F3" - , "AAAAAAAA-1111-1111-AAAG-111111111111" - ]; - - class MyClass { - @IsUUID() - someProperty: string; - } - - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); - - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); - - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isUUID(value).should.be.true); - }); - - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isUUID(value).should.be.false); - }); - - it("should return error object with proper data", function(done) { - const validationType = "isUuid"; - const message = "someProperty must be an UUID"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); - -}); - -describe("IsUUID v3", function() { - - const validValues = [ - "A987FBC9-4BED-3078-CF07-9141BA07C9F3" - ]; - const invalidValues = [ - null - , undefined - , "" - , "xxxA987FBC9-4BED-3078-CF07-9141BA07C9F3" - , "934859" - , "AAAAAAAA-1111-1111-AAAG-111111111111" - , "A987FBC9-4BED-4078-8F07-9141BA07C9F3" - , "A987FBC9-4BED-5078-AF07-9141BA07C9F3" - ]; - - class MyClass { - @IsUUID("3") - someProperty: string; - } - - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); - - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); - - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isUUID(value, "3").should.be.true); - }); - - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isUUID(value, "3").should.be.false); - }); - - it("should return error object with proper data", function(done) { - const validationType = "isUuid"; - const message = "someProperty must be an UUID"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); - -}); - -describe("IsUUID v4", function() { - - const validValues = [ - "713ae7e3-cb32-45f9-adcb-7c4fa86b90c1" - , "625e63f3-58f5-40b7-83a1-a72ad31acffb" - , "57b73598-8764-4ad0-a76a-679bb6640eb1" - , "9c858901-8a57-4791-81fe-4c455b099bc9" - ]; - const invalidValues = [ - null - , undefined - , "" - , "xxxA987FBC9-4BED-3078-CF07-9141BA07C9F3" - , "934859" - , "AAAAAAAA-1111-1111-AAAG-111111111111" - , "A987FBC9-4BED-5078-AF07-9141BA07C9F3" - , "A987FBC9-4BED-3078-CF07-9141BA07C9F3" - ]; - - class MyClass { - @IsUUID("4") - someProperty: string; - } - - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); - - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); - - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isUUID(value, "4").should.be.true); - }); - - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isUUID(value, "4").should.be.false); - }); - - it("should return error object with proper data", function(done) { - const validationType = "isUuid"; - const message = "someProperty must be an UUID"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); - -}); - -describe("IsUUID v5", function() { - - const validValues = [ - "987FBC97-4BED-5078-AF07-9141BA07C9F3" - , "987FBC97-4BED-5078-BF07-9141BA07C9F3" - , "987FBC97-4BED-5078-8F07-9141BA07C9F3" - , "987FBC97-4BED-5078-9F07-9141BA07C9F3" - ]; - const invalidValues = [ - null - , undefined - , "" - , "xxxA987FBC9-4BED-3078-CF07-9141BA07C9F3" - , "934859" - , "AAAAAAAA-1111-1111-AAAG-111111111111" - , "9c858901-8a57-4791-81fe-4c455b099bc9" - , "A987FBC9-4BED-3078-CF07-9141BA07C9F3", - ]; - - class MyClass { - @IsUUID("5") - someProperty: string; - } - - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); - - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); - - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isUUID(value, "5").should.be.true); - }); - - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isUUID(value, "5").should.be.false); - }); - - it("should return error object with proper data", function(done) { - const validationType = "isUuid"; - const message = "someProperty must be an UUID"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); - -}); - -describe("IsUppercase", function() { - - const validValues = [ - "ABC" - , "ABC123" - , "ALL CAPS IS FUN." - , " ." - ]; - const invalidValues = [ - null, - undefined, - "fooBar", - "123abc" - ]; - - class MyClass { - @IsUppercase() - someProperty: string; - } - - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); - - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); - - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isUppercase(value).should.be.true); - }); - - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isUppercase(value).should.be.false); - }); - - it("should return error object with proper data", function(done) { - const validationType = "isUppercase"; - const message = "someProperty must be uppercase"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); - -}); - -describe("Length", function() { - - const constraint1 = 2; - const constraint2 = 3; - const validValues = ["abc", "de"]; - const invalidValues = [null, undefined, "", "a", "abcd"]; - - class MyClass { - @Length(constraint1, constraint2) - someProperty: string; - } - - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); - - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); - - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.length(value, constraint1, constraint2).should.be.true); - }); - - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.length(value, constraint1, constraint2).should.be.false); - }); - - it("should return error object with proper data", function(done) { - const validationType = "length"; - const message = "someProperty must be longer than or equal to " + constraint1 + " characters"; - checkReturnedError(new MyClass(), ["", "a"], validationType, message, done); - }); - - it("should return error object with proper data", function(done) { - const validationType = "length"; - const message = "someProperty must be shorter than or equal to " + constraint2 + " characters"; - checkReturnedError(new MyClass(), ["aaaa", "azzazza"], validationType, message, done); - }); - -}); - -describe("MinLength", function() { - - const constraint1 = 10; - const validValues = ["helloworld", "hello how are you"]; - const invalidValues = [null, undefined, "hellowar", "howareyou"]; - - class MyClass { - @MinLength(constraint1) - someProperty: string; - } - - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); - - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); - - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.minLength(value, constraint1).should.be.true); - }); - - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.minLength(value, constraint1).should.be.false); - }); - - it("should return error object with proper data", function(done) { - const validationType = "minLength"; - const message = "someProperty must be longer than or equal to " + constraint1 + " characters"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); - -}); - -describe("MaxLength", function() { - - const constraint1 = 10; - const validValues = ["hellowar", "howareyou", "helloworld"]; - const invalidValues = [null, undefined, "helloworld!", "hello how are you"]; - - class MyClass { - @MaxLength(constraint1) - someProperty: string; - } - - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); - - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); - - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.maxLength(value, constraint1).should.be.true); - }); - - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.maxLength(value, constraint1).should.be.false); - }); - - it("should return error object with proper data", function(done) { - const validationType = "maxLength"; - const message = "someProperty must be shorter than or equal to " + constraint1 + " characters"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); - -}); - -describe("Matches", function() { - - const constraint = /abc/; - const validValues = ["abc", "abcdef", "123abc"]; - const invalidValues = [null, undefined, "acb", "Abc"]; - - class MyClass { - @Matches(constraint) - someProperty: string; - } - - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); - - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); - - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.matches(value, constraint).should.be.true); - }); - - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.matches(value, constraint).should.be.false); - }); - - it("should return error object with proper data", function(done) { - const validationType = "matches"; - const message = "someProperty must match " + constraint + " regular expression"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); - -}); - -describe("IsMilitaryTime", function() { - - class MyClass { - @IsMilitaryTime() - someProperty: string; - } - - it("should not fail for a valid time in the format HH:MM", function(done) { - const validValues = ["10:22", "12:03", "16:32", "23:59", "00:00"]; - checkValidValues(new MyClass(), validValues, done); - }); - - it("should fail for invalid time format", function(done) { - const invalidValues = ["23:61", "25:00", "08:08 pm", "04:00am"]; - checkInvalidValues(new MyClass(), invalidValues, done); - }); - - it("should fail for invalid values", function(done) { - const invalidValues = [undefined, null, "23:00 and invalid counterpart"]; - checkInvalidValues(new MyClass(), invalidValues, done); - }); - -}); - -describe("isPhoneNumber", function() { - describe("with region", function() { - const validValues = [ - "0311111111", "031 633 60 01", "079 4 666 666", "075 416 20 30", - "+41 311111111", "+41 31 633 60 01", "+41 79 4 666 666", "+41 75 416 20 30", - "+41 (0)311111111", "+41 (0)31 633 60 01", "+41 (0)79 4 666 666", "+41 (0)75 416 20 30", - "+49 9072 1111" - ]; - const invalidValues = [undefined, null, "asdf", "1"]; - - class MyClass { - @IsPhoneNumber("CH") - someProperty: string; - } - - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); - - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); - }); - - describe("no region", function() { - const validValues = [ - "+41 311111111", "+41 31 633 60 01", "+41 79 4 666 666", "+41 75 416 20 30", - "+41 (0)311111111", "+41 (0)31 633 60 01", "+41 (0)79 4 666 666", "+41 (0)75 416 20 30", - "+49 9072 1111" - ]; - const invalidValues = [ - "0311111111", "031 633 60 01", "079 4 666 666", "075 416 20 30", - undefined, null, "asdf", "1" - ]; - - class MyClass { - @IsPhoneNumber(null) - someProperty: string; - } - - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); - - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); - }); -}); - - -// ------------------------------------------------------------------------- -// Specifications: array check -// ------------------------------------------------------------------------- - -describe("ArrayContains", function() { - - const constraint = ["superman"]; - const validValues = [["world", "hello", "superman"], ["world", "superman", "hello"], ["superman", "world", "hello"]]; - const invalidValues = [null, undefined, ["world", "hello"]]; - - class MyClass { - @ArrayContains(constraint) - someProperty: string[]; - } - - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); - - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); - - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.arrayContains(value, constraint).should.be.true); - }); - - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.arrayContains(value, constraint).should.be.false); - }); - - it("should return error object with proper data", function(done) { - const validationType = "arrayContains"; - const message = "someProperty must contain " + constraint + " values"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); - -}); - -describe("ArrayNotContains", function() { - - const constraint = ["superman"]; - const validValues = [["world", "hello"]]; - const invalidValues = [null, undefined, ["world", "hello", "superman"], ["world", "superman", "hello"], ["superman", "world", "hello"]]; - - class MyClass { - @ArrayNotContains(constraint) - someProperty: string[]; - } - - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); - - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); - - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.arrayNotContains(value, constraint).should.be.true); - }); - - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.arrayNotContains(value, constraint).should.be.false); - }); - - it("should return error object with proper data", function(done) { - const validationType = "arrayNotContains"; - const message = "someProperty should not contain " + constraint + " values"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); - -}); - -describe("ArrayNotEmpty", function() { - - const validValues = [[0], [""], [null], [undefined], [false], ["world", "hello", "superman"], ["world", "superman", "hello"], ["superman", "world", "hello"]]; - const invalidValues: any[] = [null, undefined, []]; - - class MyClass { - @ArrayNotEmpty() - someProperty: string[]; - } - - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); - - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); - - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.arrayNotEmpty(value).should.be.true); - }); - - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.arrayNotEmpty(value).should.be.false); - }); - - it("should return error object with proper data", function(done) { - const validationType = "arrayNotEmpty"; - const message = "someProperty should not be empty"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); - -}); - -describe("ArrayMinSize", function() { - - const constraint = 2; - const validValues = [["world", "hello"]]; - const invalidValues = [null, undefined, ["hi"]]; - - class MyClass { - @ArrayMinSize(constraint) - someProperty: string; - } - - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); - - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); - - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.arrayMinSize(value, constraint).should.be.true); - }); - - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.arrayMinSize(value, constraint).should.be.false); - }); - - it("should return error object with proper data", function(done) { - const validationType = "arrayMinSize"; - const message = "someProperty must contain at least " + constraint + " elements"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); - -}); - -describe("ArrayMaxSize", function() { - - const constraint = 2; - const validValues = [["world", "hello"]]; - const invalidValues = [null, undefined, ["hi", "hello", "javascript"]]; - - class MyClass { - @ArrayMaxSize(constraint) - someProperty: string; - } - - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); - - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); - - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.arrayMaxSize(value, constraint).should.be.true); - }); - - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.arrayMaxSize(value, constraint).should.be.false); - }); - - it("should return error object with proper data", function(done) { - const validationType = "arrayMaxSize"; - const message = "someProperty must contain not more than " + constraint + " elements"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); - -}); - -describe("ArrayUnique", function () { - - const validValues = [["world", "hello", "superman"], ["world", "superman", "hello"], ["superman", "world", "hello"]]; - const invalidValues: any[] = [null, undefined, ["world", "hello", "hello"], ["world", "hello", "world"], ["1", "1", "1"]]; - - class MyClass { - @ArrayUnique() - someProperty: string[]; - } - - it("should not fail if validator.validate said that its valid", function (done) { - checkValidValues(new MyClass(), validValues, done); - }); - - it("should fail if validator.validate said that its invalid", function (done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); - - it("should not fail if method in validator said that its valid", function () { - validValues.forEach(value => validator.arrayUnique(value).should.be.true); - }); - - it("should fail if method in validator said that its invalid", function () { - invalidValues.forEach(value => validator.arrayUnique(value).should.be.false); - }); - - it("should return error object with proper data", function (done) { - const validationType = "arrayUnique"; - const message = "All someProperty's elements must be unique"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); +describe('IsStrongPassword with options', () => { + const options: IsStrongPasswordOptions = { + minLength: 12, + minLowercase: 2, + minUppercase: 2, + minNumbers: 2, + minSymbols: 2, + }; + + class MyClass { + @IsStrongPassword(options) + someProperty: string; + } + + const validValues = ['ABcdefgh12!#']; + const invalidValues = [ + null, + undefined, + 'ABcdefg12!#', + 'Abcdefgh12!#', + 'ABcDEFGH12!#', + 'ABcdefghi1!#', + 'ABcdefghi12!', + ]; + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isStrongPassword(value, options)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isStrongPassword(value, options)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isStrongPassword'; + const message = 'someProperty is not strong enough'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); +describe('IsBase58', () => { + const constraint = ''; + const validValues = ['4Fcj4ooZqEQiyH68xykKJFnwZbePBCxgTjwQVtce1VyS']; + const invalidValues = [null, undefined, 'my*name-isKinggodHoon']; + + class MyClass { + @IsBase58() + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isBase58(value)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isBase58(value)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isBase58'; + const message = 'someProperty must be base58 encoded'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); }); -describe("isInstance", function () { - - class MySubClass { } - class WrongSubClass {} - - class MyClass { - @IsInstance(MySubClass) - someProperty: MySubClass; - } - - const validValues = [new MySubClass()]; - const invalidValues = [null, undefined, 15, "something", new WrongSubClass(), () => null]; - - it("should not fail if validator.validate said that its valid", function(done) { - checkValidValues(new MyClass(), validValues, done); - }); - - it("should fail if validator.validate said that its invalid", function(done) { - checkInvalidValues(new MyClass(), invalidValues, done); - }); - - it("should not fail if method in validator said that its valid", function() { - validValues.forEach(value => validator.isInstance(value, MySubClass).should.be.true); - }); - - it("should fail if method in validator said that its invalid", function() { - invalidValues.forEach(value => validator.isInstance(value, MySubClass).should.be.false); - }); - - it("should return error object with proper data", function(done) { - const validationType = "isInstance"; - const message = "someProperty must be an instance of MySubClass"; - checkReturnedError(new MyClass(), invalidValues, validationType, message, done); - }); - +describe('IsISO4217', () => { + class MyClass { + @IsISO4217CurrencyCode() + someProperty: string; + } + + it('should not fail for a valid ISO4217 code', () => { + const validValues = ['EUR', 'USD', 'BDT', 'LRD']; + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail for invalid values', () => { + const invalidValues = [undefined, null, '', 'USS']; + return checkInvalidValues(new MyClass(), invalidValues); + }); }); diff --git a/test/functional/validation-options.spec.ts b/test/functional/validation-options.spec.ts index b496d92a3b..99db80aa76 100644 --- a/test/functional/validation-options.spec.ts +++ b/test/functional/validation-options.spec.ts @@ -1,390 +1,1253 @@ -import "es6-shim"; -import {Contains, MinLength} from "../../src/decorator/decorators"; -import {Validator} from "../../src/validation/Validator"; +import { + Contains, + IsDefined, + Matches, + MinLength, + IsArray, + Validate, + ValidateNested, + ValidatorConstraint, + IsOptional, + IsNotEmpty, + Allow, +} from '../../src/decorator/decorators'; +import { Validator } from '../../src/validation/Validator'; +import { + registerDecorator, + ValidationArguments, + ValidationError, + ValidationOptions, + ValidatorConstraintInterface, +} from '../../src'; -import {should, use } from "chai"; +const validator = new Validator(); -import * as chaiAsPromised from "chai-as-promised"; +describe('message', () => { + it('should contain a custom message', () => { + class MyClass { + @Contains('hello', { + message: 'String is not valid. You string must contain a hello word', + }) + someProperty: string; + } + + const model = new MyClass(); + // TODO: Why is this commented out? + // model.someProperty = "hell no world"; + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(1); + expect(errors[0].constraints).toEqual({ contains: 'String is not valid. You string must contain a hello word' }); + }); + }); + + it('$value token should be replaced in a custom message', () => { + class MyClass { + @Contains('hello', { + message: '$value is not valid. You string must contain a hello word', + }) + someProperty: string; + } + + const model = new MyClass(); + model.someProperty = 'hell no world'; + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(1); + expect(errors[0].constraints).toEqual({ + contains: 'hell no world is not valid. You string must contain a hello word', + }); + }); + }); + + it('$value token should be replaced in a custom message', () => { + class MyClass { + @MinLength(2, { + message: args => { + if (args.value.length < 2) { + return '$value is too short, minimum length is $constraint1 characters $property'; + } + }, + }) + name: string; + } + + const model = new MyClass(); + model.name = ''; + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(1); + expect(errors[0].constraints).toEqual({ minLength: ' is too short, minimum length is 2 characters name' }); + }); + }); + + it('$constraint1 token should be replaced in a custom message', () => { + class MyClass { + @Contains('hello', { + message: 'String is not valid. You string must contain a $constraint1 word', + }) + someProperty: string; + } + + const model = new MyClass(); + model.someProperty = 'hell no world'; + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(1); + expect(errors[0].constraints).toEqual({ contains: 'String is not valid. You string must contain a hello word' }); + }); + }); + + it('$target token should be replaced in a custom message', () => { + class MyClass { + @Contains('hello', { + message: '$target is not valid.', + }) + someProperty: string; + } + + const model = new MyClass(); + model.someProperty = 'hell no world'; + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(1); + expect(errors[0].constraints).toEqual({ contains: 'MyClass is not valid.' }); + }); + }); + + it('$property token should be replaced in a custom message', () => { + class MyClass { + @Contains('hello', { + message: '$property is not valid.', + }) + someProperty: string; + } + + const model = new MyClass(); + model.someProperty = 'hell no world'; + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(1); + expect(errors[0].constraints).toEqual({ contains: 'someProperty is not valid.' }); + }); + }); + + it('should replace all token', () => { + class MyClass { + @Contains('hello', { + message: '$target#$property is not valid: $value must contain a $constraint1 word', + }) + someProperty: string; + } + + const model = new MyClass(); + model.someProperty = 'hell no world'; + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(1); + expect(errors[0].constraints).toEqual({ + contains: 'MyClass#someProperty is not valid: hell no world must contain a hello word', + }); + }); + }); +}); -should(); -use(chaiAsPromised); +describe('each', () => { + describe('Array', () => { + it('should apply validation to each item in the array', () => { + class MyClass { + @Contains('hello', { + each: true, + }) + someProperty: string[]; + } + + const model = new MyClass(); + model.someProperty = ['hell no world', 'hello', 'helo world', 'hello world', 'hello dear friend']; + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(1); + expect(errors[0].constraints).toEqual({ contains: 'each value in someProperty must contain a hello string' }); + expect(errors[0].value).toEqual(model.someProperty); + expect(errors[0].target).toEqual(model); + expect(errors[0].property).toEqual('someProperty'); + }); + }); -// ------------------------------------------------------------------------- -// Setup -// ------------------------------------------------------------------------- + it('should apply validation via custom constraint class to array items (but not array itself)', () => { + @ValidatorConstraint({ name: 'customIsNotArrayConstraint', async: false }) + class CustomIsNotArrayConstraint implements ValidatorConstraintInterface { + validate(value: any): boolean { + return !Array.isArray(value); + } + } + + class MyClass { + @Validate(CustomIsNotArrayConstraint, { + each: true, + }) + someArrayOfNonArrayItems: string[]; + } + + const model = new MyClass(); + model.someArrayOfNonArrayItems = ['not array', 'also not array', 'not array at all']; + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(0); + }); + }); -const validator = new Validator(); + it('should apply validation via custom constraint class with synchronous logic to each item in the array', () => { + @ValidatorConstraint({ name: 'customContainsHelloConstraint', async: false }) + class CustomContainsHelloConstraint implements ValidatorConstraintInterface { + validate(value: any): boolean { + return !Array.isArray(value) && String(value).includes('hello'); + } + } + + class MyClass { + @Validate(CustomContainsHelloConstraint, { + each: true, + }) + someProperty: string[]; + } + + const model = new MyClass(); + model.someProperty = ['hell no world', 'hello', 'helo world', 'hello world', 'hello dear friend']; + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(1); + expect(errors[0].constraints).toEqual({ customContainsHelloConstraint: '' }); + expect(errors[0].value).toEqual(model.someProperty); + expect(errors[0].target).toEqual(model); + expect(errors[0].property).toEqual('someProperty'); + }); + }); -// ------------------------------------------------------------------------- -// Specifications: common decorators -// ------------------------------------------------------------------------- + it('should apply validation via custom constraint class with async logic to each item in the array', () => { + @ValidatorConstraint({ name: 'customAsyncContainsHelloConstraint', async: true }) + class CustomAsyncContainsHelloConstraint implements ValidatorConstraintInterface { + validate(value: any): Promise { + const isValid = !Array.isArray(value) && String(value).includes('hello'); + return Promise.resolve(isValid); + } + } + + class MyClass { + @Validate(CustomAsyncContainsHelloConstraint, { + each: true, + }) + someProperty: string[]; + } + + const model = new MyClass(); + model.someProperty = ['hell no world', 'hello', 'helo world', 'hello world', 'hello dear friend']; + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(1); + expect(errors[0].constraints).toEqual({ customAsyncContainsHelloConstraint: '' }); + expect(errors[0].value).toEqual(model.someProperty); + expect(errors[0].target).toEqual(model); + expect(errors[0].property).toEqual('someProperty'); + }); + }); -describe("validation options", function() { + it('should apply validation via custom constraint class with mixed (synchronous + async) logic to each item in the array', () => { + @ValidatorConstraint({ name: 'customMixedContainsHelloConstraint', async: true }) + class CustomMixedContainsHelloConstraint implements ValidatorConstraintInterface { + validate(value: any): boolean | Promise { + const isValid = !Array.isArray(value) && String(value).includes('hello'); + return isValid ? isValid : Promise.resolve(isValid); + } + } + + class MyClass { + @Validate(CustomMixedContainsHelloConstraint, { + each: true, + }) + someProperty: string[]; + } + + const model = new MyClass(); + model.someProperty = ['hell no world', 'hello', 'helo world', 'hello world', 'hello dear friend']; + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(1); + expect(errors[0].constraints).toEqual({ customMixedContainsHelloConstraint: '' }); + expect(errors[0].value).toEqual(model.someProperty); + expect(errors[0].target).toEqual(model); + expect(errors[0].property).toEqual('someProperty'); + }); + }); + }); + + describe('Set', () => { + it('should apply validation to each item in the Set', () => { + class MyClass { + @Contains('hello', { + each: true, + }) + someProperty: Set; + } + + const model = new MyClass(); + model.someProperty = new Set([ + 'hell no world', + 'hello', + 'helo world', + 'hello world', + 'hello dear friend', + ]); + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(1); + expect(errors[0].constraints).toEqual({ contains: 'each value in someProperty must contain a hello string' }); + expect(errors[0].value).toEqual(model.someProperty); + expect(errors[0].target).toEqual(model); + expect(errors[0].property).toEqual('someProperty'); + }); + }); - describe("message", function() { + it('should apply validation via custom constraint class to Set items (but not Set itself)', () => { + @ValidatorConstraint({ name: 'customIsNotSetConstraint', async: false }) + class CustomIsNotSetConstraint implements ValidatorConstraintInterface { + validate(value: any): boolean { + return !(value instanceof Set); + } + } + + class MyClass { + @Validate(CustomIsNotSetConstraint, { + each: true, + }) + someSetOfNonSetItems: Set; + } + + const model = new MyClass(); + model.someSetOfNonSetItems = new Set(['not array', 'also not array', 'not array at all']); + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(0); + }); + }); - it("should contain a custom message", function() { - class MyClass { - @Contains("hello", { - message: "String is not valid. You string must contain a hello word" - }) - someProperty: string; - } + it('should apply validation via custom constraint class with synchronous logic to each item in the Set', () => { + @ValidatorConstraint({ name: 'customContainsHelloConstraint', async: false }) + class CustomContainsHelloConstraint implements ValidatorConstraintInterface { + validate(value: any): boolean { + return !(value instanceof Set) && String(value).includes('hello'); + } + } + + class MyClass { + @Validate(CustomContainsHelloConstraint, { + each: true, + }) + someProperty: Set; + } + + const model = new MyClass(); + model.someProperty = new Set([ + 'hell no world', + 'hello', + 'helo world', + 'hello world', + 'hello dear friend', + ]); + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(1); + expect(errors[0].constraints).toEqual({ customContainsHelloConstraint: '' }); + expect(errors[0].value).toEqual(model.someProperty); + expect(errors[0].target).toEqual(model); + expect(errors[0].property).toEqual('someProperty'); + }); + }); - const model = new MyClass(); - // model.someProperty = "hell no world"; - return validator.validate(model).then(errors => { - errors.length.should.be.equal(1); - errors[0].constraints.should.be.eql({ contains: "String is not valid. You string must contain a hello word" }); - }); - }); + it('should apply validation via custom constraint class with async logic to each item in the Set', () => { + @ValidatorConstraint({ name: 'customAsyncContainsHelloConstraint', async: true }) + class CustomAsyncContainsHelloConstraint implements ValidatorConstraintInterface { + validate(value: any): Promise { + const isValid = !(value instanceof Set) && String(value).includes('hello'); + return Promise.resolve(isValid); + } + } + + class MyClass { + @Validate(CustomAsyncContainsHelloConstraint, { + each: true, + }) + someProperty: Set; + } + + const model = new MyClass(); + model.someProperty = new Set([ + 'hell no world', + 'hello', + 'helo world', + 'hello world', + 'hello dear friend', + ]); + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(1); + expect(errors[0].constraints).toEqual({ customAsyncContainsHelloConstraint: '' }); + expect(errors[0].value).toEqual(model.someProperty); + expect(errors[0].target).toEqual(model); + expect(errors[0].property).toEqual('someProperty'); + }); + }); - it("$value token should be replaced in a custom message", function() { - class MyClass { - @Contains("hello", { - message: "$value is not valid. You string must contain a hello word" - }) - someProperty: string; - } - - const model = new MyClass(); - model.someProperty = "hell no world"; - return validator.validate(model).then(errors => { - errors.length.should.be.equal(1); - errors[0].constraints.should.be.eql({ contains: "hell no world is not valid. You string must contain a hello word" }); - }); - }); + it('should apply validation via custom constraint class with mixed (synchronous + async) logic to each item in the Set', () => { + @ValidatorConstraint({ name: 'customMixedContainsHelloConstraint', async: true }) + class CustomMixedContainsHelloConstraint implements ValidatorConstraintInterface { + validate(value: any): boolean | Promise { + const isValid = !(value instanceof Set) && String(value).includes('hello'); + return isValid ? isValid : Promise.resolve(isValid); + } + } + + class MyClass { + @Validate(CustomMixedContainsHelloConstraint, { + each: true, + }) + someProperty: Set; + } + + const model = new MyClass(); + model.someProperty = new Set([ + 'hell no world', + 'hello', + 'helo world', + 'hello world', + 'hello dear friend', + ]); + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(1); + expect(errors[0].constraints).toEqual({ customMixedContainsHelloConstraint: '' }); + expect(errors[0].value).toEqual(model.someProperty); + expect(errors[0].target).toEqual(model); + expect(errors[0].property).toEqual('someProperty'); + }); + }); + }); + + describe('Map', () => { + it('should apply validation to each item in the Map', () => { + class MyClass { + @Contains('hello', { + each: true, + }) + someProperty: Map; + } + + const model = new MyClass(); + model.someProperty = new Map([ + ['key1', 'hell no world'], + ['key2', 'hello'], + ['key3', 'helo world'], + ['key4', 'hello world'], + ['key5', 'hello dear friend'], + ]); + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(1); + expect(errors[0].constraints).toEqual({ contains: 'each value in someProperty must contain a hello string' }); + expect(errors[0].value).toEqual(model.someProperty); + expect(errors[0].target).toEqual(model); + expect(errors[0].property).toEqual('someProperty'); + }); + }); - it("$value token should be replaced in a custom message", function() { - class MyClass { - @MinLength(2, { - message: args => { - if (args.value.length < 2) { - return "$value is too short, minimum length is $constraint1 characters $property"; - } - } - }) - name: string; - } - - const model = new MyClass(); - model.name = ""; - return validator.validate(model).then(errors => { - errors.length.should.be.equal(1); - errors[0].constraints.should.be.eql({ minLength: " is too short, minimum length is 2 characters name" }); - }); - }); + it('should apply validation via custom constraint class to Map items (but not Map itself)', () => { + @ValidatorConstraint({ name: 'customIsNotMapConstraint', async: false }) + class CustomIsNotMapConstraint implements ValidatorConstraintInterface { + validate(value: any): boolean { + return !(value instanceof Map); + } + } + + class MyClass { + @Validate(CustomIsNotMapConstraint, { + each: true, + }) + someArrayOfNonArrayItems: Map; + } + + const model = new MyClass(); + model.someArrayOfNonArrayItems = new Map([ + ['key1', 'not array'], + ['key2', 'also not array'], + ['key3', 'not array at all'], + ]); + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(0); + }); + }); - it("$constraint1 token should be replaced in a custom message", function() { - class MyClass { - @Contains("hello", { - message: "String is not valid. You string must contain a $constraint1 word" - }) - someProperty: string; - } - - const model = new MyClass(); - model.someProperty = "hell no world"; - return validator.validate(model).then(errors => { - errors.length.should.be.equal(1); - errors[0].constraints.should.be.eql({ contains: "String is not valid. You string must contain a hello word" }); - }); - }); + it('should apply validation via custom constraint class with synchronous logic to each item in the Map', () => { + @ValidatorConstraint({ name: 'customContainsHelloConstraint', async: false }) + class CustomContainsHelloConstraint implements ValidatorConstraintInterface { + validate(value: any): boolean { + return !(value instanceof Map) && String(value).includes('hello'); + } + } + + class MyClass { + @Validate(CustomContainsHelloConstraint, { + each: true, + }) + someProperty: Map; + } + + const model = new MyClass(); + model.someProperty = new Map([ + ['key1', 'hell no world'], + ['key2', 'hello'], + ['key3', 'helo world'], + ['key4', 'hello world'], + ['key5', 'hello dear friend'], + ]); + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(1); + expect(errors[0].constraints).toEqual({ customContainsHelloConstraint: '' }); + expect(errors[0].value).toEqual(model.someProperty); + expect(errors[0].target).toEqual(model); + expect(errors[0].property).toEqual('someProperty'); + }); + }); - it("$target token should be replaced in a custom message", function() { - class MyClass { - @Contains("hello", { - message: "$target is not valid." - }) - someProperty: string; - } - - const model = new MyClass(); - model.someProperty = "hell no world"; - return validator.validate(model).then(errors => { - errors.length.should.be.equal(1); - errors[0].constraints.should.be.eql({ contains: "MyClass is not valid." }); - }); - }); + it('should apply validation via custom constraint class with async logic to each item in the Map', () => { + @ValidatorConstraint({ name: 'customAsyncContainsHelloConstraint', async: true }) + class CustomAsyncContainsHelloConstraint implements ValidatorConstraintInterface { + validate(value: any): Promise { + const isValid = !(value instanceof Map) && String(value).includes('hello'); + return Promise.resolve(isValid); + } + } + + class MyClass { + @Validate(CustomAsyncContainsHelloConstraint, { + each: true, + }) + someProperty: Map; + } + + const model = new MyClass(); + model.someProperty = new Map([ + ['key1', 'hell no world'], + ['key2', 'hello'], + ['key3', 'helo world'], + ['key4', 'hello world'], + ['key5', 'hello dear friend'], + ]); + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(1); + expect(errors[0].constraints).toEqual({ customAsyncContainsHelloConstraint: '' }); + expect(errors[0].value).toEqual(model.someProperty); + expect(errors[0].target).toEqual(model); + expect(errors[0].property).toEqual('someProperty'); + }); + }); - it("$property token should be replaced in a custom message", function() { - class MyClass { - @Contains("hello", { - message: "$property is not valid." - }) - someProperty: string; - } - - const model = new MyClass(); - model.someProperty = "hell no world"; - return validator.validate(model).then(errors => { - errors.length.should.be.equal(1); - errors[0].constraints.should.be.eql({ contains: "someProperty is not valid." }); - }); - }); + it('should apply validation via custom constraint class with mixed (synchronous + async) logic to each item in the Map', () => { + @ValidatorConstraint({ name: 'customMixedContainsHelloConstraint', async: true }) + class CustomMixedContainsHelloConstraint implements ValidatorConstraintInterface { + validate(value: any): boolean | Promise { + const isValid = !(value instanceof Map) && String(value).includes('hello'); + return isValid ? isValid : Promise.resolve(isValid); + } + } + + class MyClass { + @Validate(CustomMixedContainsHelloConstraint, { + each: true, + }) + someProperty: Map; + } + + const model = new MyClass(); + model.someProperty = new Map([ + ['key1', 'hell no world'], + ['key2', 'hello'], + ['key3', 'helo world'], + ['key4', 'hello world'], + ['key5', 'hello dear friend'], + ]); + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(1); + expect(errors[0].constraints).toEqual({ customMixedContainsHelloConstraint: '' }); + expect(errors[0].value).toEqual(model.someProperty); + expect(errors[0].target).toEqual(model); + expect(errors[0].property).toEqual('someProperty'); + }); + }); + }); +}); - it("should replace all token", function() { - class MyClass { - @Contains("hello", { - message: "$target#$property is not valid: $value must contain a $constraint1 word" - }) - someProperty: string; - } - - const model = new MyClass(); - model.someProperty = "hell no world"; - return validator.validate(model).then(errors => { - errors.length.should.be.equal(1); - errors[0].constraints.should.be.eql({ contains: "MyClass#someProperty is not valid: hell no world must contain a hello word" }); - }); - }); +describe('groups', () => { + function expectTitleContains(error: ValidationError): void { + expect(error.constraints).toEqual({ contains: 'title must contain a hello string' }); + } + + function expectTextContains(error: ValidationError): void { + expect(error.constraints).toEqual({ contains: 'text must contain a bye string' }); + } + + class MyClass { + @Contains('hello', { + groups: ['title-validation'], + }) + title: string; + + @Contains('bye', { + groups: ['text-validation'], + }) + text: string; + } + + const validTitle = new MyClass(); + validTitle.title = 'hello world'; + validTitle.text = 'hello world'; + + const validText = new MyClass(); + validText.title = 'bye world'; + validText.text = 'bye world'; + + const validBoth = new MyClass(); + validBoth.title = 'hello world'; + validBoth.text = 'bye world'; + + const validNone = new MyClass(); + validNone.title = 'bye world'; + validNone.text = 'hello world'; + + describe('should validate only properties of the given group: title-validation', () => { + it('with valid title', () => { + return validator.validate(validTitle, { groups: ['title-validation'] }).then(errors => { + expect(errors.length).toEqual(0); + }); + }); + it('with valid text', () => { + return validator.validate(validText, { groups: ['title-validation'] }).then(errors => { + expect(errors.length).toEqual(1); + expectTitleContains(errors[0]); + }); }); - describe("each", function() { + it('with both valid', () => { + return validator.validate(validBoth, { groups: ['title-validation'] }).then(errors => { + expect(errors.length).toEqual(0); + }); + }); - it("should apply validation to each item in the array", function() { - class MyClass { - @Contains("hello", { - each: true - }) - someProperty: string[]; - } + it('with none valid', () => { + return validator.validate(validNone, { groups: ['title-validation'] }).then(errors => { + expect(errors.length).toEqual(1); + expectTitleContains(errors[0]); + }); + }); + }); + + describe('should validate only properties of the given group: text-validation', () => { + it('with valid title', () => { + return validator.validate(validTitle, { groups: ['text-validation'] }).then(errors => { + expect(errors.length).toEqual(1); + expectTextContains(errors[0]); + }); + }); - const model = new MyClass(); - model.someProperty = ["hell no world", "hello", "helo world", "hello world", "hello dear friend"]; - return validator.validate(model).then(errors => { - errors.length.should.be.equal(1); - errors[0].constraints.should.be.eql({ contains: "each value in someProperty must contain a hello string" }); - errors[0].value.should.be.equal(model.someProperty); - errors[0].target.should.be.equal(model); - errors[0].property.should.be.equal("someProperty"); - }); - }); + it('with valid text', () => { + return validator.validate(validText, { groups: ['text-validation'] }).then(errors => { + expect(errors.length).toEqual(0); + }); + }); + it('with both valid', () => { + return validator.validate(validBoth, { groups: ['text-validation'] }).then(errors => { + expect(errors.length).toEqual(0); + }); }); - describe("groups", function() { + it('with none valid', () => { + return validator.validate(validNone, { groups: ['text-validation'] }).then(errors => { + expect(errors.length).toEqual(1); + expectTextContains(errors[0]); + }); + }); + }); + + describe('should validate only properties of the given groups: both groups', () => { + it('with valid title', () => { + return validator.validate(validTitle, { groups: ['title-validation', 'text-validation'] }).then(errors => { + expect(errors.length).toEqual(1); + expectTextContains(errors[0]); + }); + }); - class MyClass { - @Contains("hello", { - groups: ["title-validation"] - }) - title: string; + it('with valid text', () => { + return validator.validate(validText, { groups: ['title-validation', 'text-validation'] }).then(errors => { + expect(errors.length).toEqual(1); + expectTitleContains(errors[0]); + }); + }); - @Contains("bye", { - groups: ["text-validation"] - }) - text: string; - } + it('with both valid', () => { + return validator.validate(validBoth, { groups: ['title-validation', 'text-validation'] }).then(errors => { + expect(errors.length).toEqual(0); + }); + }); - const model1 = new MyClass(); - model1.title = "hello world"; - model1.text = "hello world"; + it('with none valid', () => { + return validator.validate(validNone, { groups: ['title-validation', 'text-validation'] }).then(errors => { + expect(errors.length).toEqual(2); + expectTitleContains(errors[0]); + expectTextContains(errors[1]); + }); + }); + }); + + describe('should validate all if no group is given', () => { + it('with valid title', () => { + // todo: all or without? what is better expected behaviour? + return validator.validate(validTitle).then(errors => { + expect(errors.length).toEqual(1); + expectTextContains(errors[0]); + }); + }); - const model2 = new MyClass(); - model2.title = "bye world"; - model2.text = "bye world"; + it('with valid text', () => { + // todo: all or without? what is better expected behaviour? + return validator.validate(validText).then(errors => { + expect(errors.length).toEqual(1); + expectTitleContains(errors[0]); + }); + }); - const model3 = new MyClass(); - model3.title = "bye world"; - model3.text = "hello world"; + it('with both valid', () => { + // todo: all or without? what is better expected behaviour? + return validator.validate(validBoth).then(errors => { + expect(errors.length).toEqual(0); + }); + }); - const model4 = new MyClass(); - model4.title = "hello world"; - model4.text = "bye world"; + it('with none valid', () => { + // todo: all or without? what is better expected behaviour? + return validator.validate(validNone).then(errors => { + expect(errors.length).toEqual(2); + expectTitleContains(errors[0]); + expectTextContains(errors[1]); + }); + }); + }); + + describe('should validate all groups if empty group array is given', () => { + it('with valid title', () => { + return validator.validate(validTitle, { groups: [] }).then(errors => { + expect(errors.length).toEqual(1); + expectTextContains(errors[0]); + }); + }); - it("should validate only properties of the given group", function() { - return validator.validate(model1, { groups: ["title-validation"] }).then(errors => { - errors.length.should.be.equal(0); - }); - }); + it('with valid text', () => { + return validator.validate(validText, { groups: [] }).then(errors => { + expect(errors.length).toEqual(1); + expectTitleContains(errors[0]); + }); + }); - it("should validate only properties of the given group", function() { - return validator.validate(model2, { groups: ["title-validation"] }).then(errors => { - errors.length.should.be.equal(1); - }); + it('with both valid', () => { + return validator.validate(validBoth, { groups: [] }).then(errors => { + expect(errors.length).toEqual(0); + }); + }); + + it('with none valid', () => { + return validator.validate(validNone, { groups: [] }).then(errors => { + expect(errors.length).toEqual(2); + expectTitleContains(errors[0]); + expectTextContains(errors[1]); + }); + }); + }); + + describe('multiple groups per property', () => { + class MyClass { + @Contains('hello', { groups: ['contains'] }) + @Matches(/.*stranger.*/, { groups: ['matches'] }) + title: string; + } + + function expectTitleMatches(error: ValidationError): void { + expect(error.constraints).toEqual({ matches: 'title must match /.*stranger.*/ regular expression' }); + } + + const validContains = new MyClass(); + validContains.title = 'hello'; + + const validMatches = new MyClass(); + validMatches.title = 'stranger'; + + const validBoth = new MyClass(); + validBoth.title = 'hello stranger'; + + const validNone = new MyClass(); + validNone.title = 'howdy rowdy'; + + describe('group: contains', () => { + it('with valid contains', () => { + return validator.validate(validContains, { groups: ['contains'] }).then(errors => { + expect(errors.length).toEqual(0); }); + }); - it("should validate only properties of the given group", function() { - return validator.validate(model2, { groups: ["text-validation"] }).then(errors => { - errors.length.should.be.equal(0); - }); + it('with valid matches', () => { + return validator.validate(validMatches, { groups: ['contains'] }).then(errors => { + expect(errors.length).toEqual(1); + expectTitleContains(errors[0]); }); + }); - it("should validate only properties of the given group", function() { - return validator.validate(model1, { groups: ["text-validation"] }).then(errors => { - errors.length.should.be.equal(1); - }); + it('with valid both', () => { + return validator.validate(validBoth, { groups: ['contains'] }).then(errors => { + expect(errors.length).toEqual(0); }); + }); - it("should validate only properties of the given groups", function() { - return validator.validate(model1, { groups: ["title-validation", "text-validation"] }).then(errors => { - errors.length.should.be.equal(1); - }); + it('with valid none', () => { + return validator.validate(validNone, { groups: ['contains'] }).then(errors => { + expect(errors.length).toEqual(1); + expectTitleContains(errors[0]); }); + }); + }); - it("should validate only properties of the given groups", function() { - return validator.validate(model2, { groups: ["title-validation", "text-validation"] }).then(errors => { - errors.length.should.be.equal(1); - }); + describe('group: matches', () => { + it('with valid contains', () => { + return validator.validate(validContains, { groups: ['matches'] }).then(errors => { + expect(errors.length).toEqual(1); + expectTitleMatches(errors[0]); }); + }); - it("should validate only properties of the given groups", function() { - return validator.validate(model3, { groups: ["title-validation", "text-validation"] }).then(errors => { - errors.length.should.be.equal(2); - }); + it('with valid matches', () => { + return validator.validate(validMatches, { groups: ['matches'] }).then(errors => { + expect(errors.length).toEqual(0); }); + }); - it("should validate only properties of the given groups", function() { - return validator.validate(model4, { groups: ["title-validation", "text-validation"] }).then(errors => { - errors.length.should.be.equal(0); - }); + it('with valid both', () => { + return validator.validate(validBoth, { groups: ['matches'] }).then(errors => { + expect(errors.length).toEqual(0); }); + }); - it("should validate all if no group is given", function() { // todo: all or without? what is better expected behaviour? - return validator.validate(model1).then(errors => { - errors.length.should.be.equal(1); - }); + it('with valid none', () => { + return validator.validate(validNone, { groups: ['matches'] }).then(errors => { + expect(errors.length).toEqual(1); + expectTitleMatches(errors[0]); }); + }); + }); - it("should validate all if no group is given", function() { // todo: all or without? what is better expected behaviour? - return validator.validate(model2).then(errors => { - errors.length.should.be.equal(1); - }); + describe('groups: contains & matches', () => { + it('with valid contains', () => { + return validator.validate(validContains, { groups: ['contains', 'matches'] }).then(errors => { + expect(errors.length).toEqual(1); + expectTitleMatches(errors[0]); }); + }); - it("should validate all if no group is given", function() { // todo: all or without? what is better expected behaviour? - return validator.validate(model3).then(errors => { - errors.length.should.be.equal(2); - }); + it('with valid matches', () => { + return validator.validate(validMatches, { groups: ['contains', 'matches'] }).then(errors => { + expect(errors.length).toEqual(1); + expectTitleContains(errors[0]); }); + }); - it("should validate all if no group is given", function() { // todo: all or without? what is better expected behaviour? - return validator.validate(model4).then(errors => { - errors.length.should.be.equal(0); - }); + it('with valid both', () => { + return validator.validate(validBoth, { groups: ['contains', 'matches'] }).then(errors => { + expect(errors.length).toEqual(0); + }); + }); + + it('with valid none', () => { + return validator.validate(validNone, { groups: ['contains', 'matches'] }).then(errors => { + expect(errors.length).toEqual(1); + expect(errors[0].constraints).toEqual({ + contains: 'title must contain a hello string', + matches: 'title must match /.*stranger.*/ regular expression', + }); }); + }); + }); + }); + + describe('ValidationOptions.always', function () { + class MyClass { + @Contains('noOptions') + noOptions: string; + + @Contains('groupA', { + groups: ['A'], + }) + groupA: string; + + @Contains('alwaysFalse', { + always: false, + }) + alwaysFalse: string; + + @Contains('alwaysTrue', { + always: true, + }) + alwaysTrue: string; + } + + const model1 = new MyClass(); + model1.noOptions = 'XXX'; + model1.groupA = 'groupA'; + model1.alwaysFalse = 'alwaysFalse'; + model1.alwaysTrue = 'alwaysTrue'; + + const model2 = new MyClass(); + model2.noOptions = 'noOptions'; + model2.groupA = 'XXX'; + model2.alwaysFalse = 'alwaysFalse'; + model2.alwaysTrue = 'alwaysTrue'; + + const model3 = new MyClass(); + model3.noOptions = 'noOptions'; + model3.groupA = 'groupA'; + model3.alwaysFalse = 'XXX'; + model3.alwaysTrue = 'alwaysTrue'; + + const model4 = new MyClass(); + model4.noOptions = 'noOptions'; + model4.groupA = 'groupA'; + model4.alwaysFalse = 'alwaysFalse'; + model4.alwaysTrue = 'XXX'; + + it('should validate decorator without options', function () { + return validator.validate(model1, { always: true, groups: ['A'] }).then(errors => { + expect(errors).toHaveLength(1); + }); + }); + it('should not validate decorator with groups if validating without matching groups', function () { + return validator.validate(model2, { always: true, groups: ['B'] }).then(errors => { + expect(errors).toHaveLength(0); + }); }); - describe("always", function() { + it('should not validate decorator with always set to false', function () { + return validator.validate(model3, { always: true, groups: ['A'] }).then(errors => { + expect(errors).toHaveLength(0); + }); + }); - class MyClass { - @Contains("hello", { - groups: ["title-validation"] - }) - title: string; + it('should validate decorator with always set to true', function () { + return validator.validate(model4, { always: true, groups: ['A'] }).then(errors => { + expect(errors).toHaveLength(1); + }); + }); + }); + + describe('strictGroups', function () { + class MyPayload { + /** + * Since forbidUnknownValues defaults to true, we must add a property to + * register the class in the metadata storage. Otherwise the unknown value check + * would take priority (first check) and exit without running the grouping logic. + * + * To solve this we register this property with always: true, so at least a single + * metadata is returned for each validation preventing the unknown value was passed error. + */ + @IsOptional({ always: true }) + propertyToRegisterClass: string; + + @Contains('hello', { groups: ['A'] }) + title: string; + } + + const instance = new MyPayload(); + + it('should ignore decorators with groups if validating without groups', function () { + return validator.validate(instance, { strictGroups: true }).then(errors => { + expect(errors).toHaveLength(0); + }); + }); - @Contains("bye", { - groups: ["text-validation"], - always: true - }) - text: string; - } + it('should ignore decorators with groups if validating with empty groups array', function () { + return validator.validate(instance, { strictGroups: true, groups: [] }).then(errors => { + expect(errors).toHaveLength(0); + }); + }); - const model1 = new MyClass(); - model1.title = "hello world"; - model1.text = "hello world"; + it('should include decorators with groups if validating with matching groups', function () { + return validator.validate(instance, { strictGroups: true, groups: ['A'] }).then(errors => { + expect(errors).toHaveLength(1); + expectTitleContains(errors[0]); + }); + }); - const model2 = new MyClass(); - model2.title = "bye world"; - model2.text = "bye world"; + it('should not include decorators with groups if validating with different groups', function () { + return validator.validate(instance, { strictGroups: true, groups: ['B'] }).then(errors => { + expect(errors).toHaveLength(0); + }); + }); + }); + + describe('always', () => { + class MyClass { + @Contains('hello', { + groups: ['sometimes'], + }) + title: string; + + @Contains('bye', { + groups: ['always'], + always: true, + }) + text: string; + } + + const model = new MyClass(); + + it('should always validate a marked field even if another group is specified', () => { + return validator.validate(model, { groups: ['sometimes'] }).then(errors => { + expect(errors.length).toEqual(2); + expectTitleContains(errors[0]); + expectTextContains(errors[1]); + }); + }); - it("should always validate a marked field no matter if group is specified", function() { - return validator.validate(model1, { groups: ["title-validation"] }).then(errors => { - errors.length.should.be.equal(1); - }); - }); + it('should always validate a marked field if its group is specified also (doubly enabled)', () => { + return validator.validate(model, { groups: ['always'] }).then(errors => { + expect(errors.length).toEqual(1); + expectTextContains(errors[0]); + }); + }); - it("should always validate a marked field no matter if group is specified", function() { - return validator.validate(model2, { groups: ["text-validation"] }).then(errors => { - errors.length.should.be.equal(0); - }); - }); + it('should always validate *all* fields if group is not specified', () => { + return validator.validate(model, { groups: undefined }).then(errors => { + expect(errors.length).toEqual(2); + expectTitleContains(errors[0]); + expectTextContains(errors[1]); + }); + }); + it('should always validate *all* fields if groups array is empty', () => { + return validator.validate(model, { groups: [] }).then(errors => { + expect(errors.length).toEqual(2); + expectTitleContains(errors[0]); + expectTextContains(errors[1]); + }); + }); + }); + + describe('groups - nested', () => { + class Nested { + @Contains('hello', { + groups: ['always'], + always: true, + }) + text: string; + } + + class Root { + @ValidateNested({ groups: ['always'], always: true }) + always = new Nested(); + + @ValidateNested({ groups: ['sometimes'] }) + sometimes = new Nested(); + + @ValidateNested({ groups: ['other'] }) + other = new Nested(); + } + + const model = new Root(); + + function expectChildConstraint(error: ValidationError, childName: string): void { + expect(error.property).toEqual(childName); + expect(error.children.length).toEqual(1); + expect(error.children[0].property).toEqual('text'); + expect(error.children[0].constraints).toEqual({ contains: 'text must contain a hello string' }); + } + + it('should validate all children if no group is given', () => { + return validator.validate(model, { groups: undefined }).then(errors => { + expect(errors.length).toEqual(3); + expectChildConstraint(errors[0], 'always'); + expectChildConstraint(errors[1], 'sometimes'); + expectChildConstraint(errors[2], 'other'); + }); }); - describe("context", function() { - - it("should map context", function() { - class MyClass { - @Contains("hello", { - message: "String is not valid. You string must contain a hello word", - context: { - hi: "there" - } - }) - someProperty: string; - - @Contains("bye", { - message: "String is not valid. You string must contain a bye word", - context: { - bye: "now" - } - }) - someOtherProperty: string; - } - - const model = new MyClass(); - // model.someProperty = "hell no world"; - return validator.validate(model).then(errors => { - errors.length.should.be.equal(2); - errors[0].contexts["contains"].should.be.eql({ hi: "there" }); - errors[1].contexts["contains"].should.be.eql({ bye: "now" }); - }); - }); + it('should validate only the given group + always', () => { + return validator.validate(model, { groups: ['sometimes'] }).then(errors => { + expect(errors.length).toEqual(2); + expectChildConstraint(errors[0], 'always'); + expectChildConstraint(errors[1], 'sometimes'); + }); + }); - it("should map multiple context on a single property for different constraints", function() { - class MyClass { - @Contains("hello", { - message: "String is not valid. You string must contain a hello word", - context: { - hi: "there" - } - }) - @MinLength(20, { - context: { - whats: "up" - } - }) - someProperty: string; - } - - const model = new MyClass(); - model.someProperty = "bippity"; - return validator.validate(model).then(errors => { - errors.length.should.be.equal(1); - errors[0].contexts["contains"].should.be.eql({ hi: "there" }); - errors[0].contexts["minLength"].should.be.eql({ whats: "up" }); - }); - }); + it('should validate only the given group + always', () => { + return validator.validate(model, { groups: ['always'] }).then(errors => { + expect(errors.length).toEqual(1); + expectChildConstraint(errors[0], 'always'); + }); + }); + }); +}); - it("should not map no context", function() { - class MyClass { - @Contains("hello", { - message: "String is not valid. You string must contain a hello word" - }) - someProperty: string; - - @Contains("bye", { - message: "String is not valid. You string must contain a bye word", - context: { - bye: "now" - } - }) - someOtherProperty: string; - } - - const model = new MyClass(); - // model.someProperty = "hell no world"; - return validator.validate(model).then(errors => { - errors.length.should.be.equal(2); - should().equal(errors[0].contexts, undefined); - errors[1].contexts["contains"].should.be.eql({ bye: "now" }); - }); +describe('context', () => { + it('should map context', () => { + function IsLongerThan(property: string, validationOptions?: ValidationOptions) { + return function (object: object, propertyName: string): void { + registerDecorator({ + target: object.constructor, + propertyName: propertyName, + options: validationOptions, + constraints: [property], + name: 'isLongerThan', + validator: { + validate(value: any, args: ValidationArguments): boolean { + const [relatedPropertyName] = args.constraints; + const relatedValue = (args.object as any)[relatedPropertyName]; + if (relatedValue === undefined || relatedValue === null) return true; + + return ( + typeof value === 'string' && typeof relatedValue === 'string' && value.length > relatedValue.length + ); + }, + }, }); - + }; + } + + class MyClass { + @Contains('hello', { + message: 'String is not valid. You string must contain a hello word', + context: { + hi: 'there', + }, + }) + someProperty: string; + + @Contains('bye', { + message: 'String is not valid. You string must contain a bye word', + context: { + bye: 'now', + }, + }) + someOtherProperty: string; + + @IsDefined({ + context: { + foo: 'bar', + }, + }) + requiredProperty: string; + + @IsLongerThan('lastName', { + context: { baz: 'qux' }, + message: '$property must be longer then $constraint1. Given value: $value', + }) + firstName: string; + + lastName: string; + } + + const model = new MyClass(); + model.firstName = 'Short'; + model.lastName = 'LongerThanFirstName'; + + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(4); + expect(errors[0].contexts['contains']).toEqual({ hi: 'there' }); + expect(errors[1].contexts['contains']).toEqual({ bye: 'now' }); + expect(errors[2].contexts['isDefined']).toEqual({ foo: 'bar' }); + expect(errors[3].contexts['isLongerThan']).toEqual({ baz: 'qux' }); + }); + }); + + it('should map multiple context on a single property for different constraints', () => { + class MyClass { + @Contains('hello', { + message: 'String is not valid. You string must contain a hello word', + context: { + hi: 'there', + }, + }) + @MinLength(20, { + context: { + whats: 'up', + }, + }) + someProperty: string; + } + + const model = new MyClass(); + model.someProperty = 'bippity'; + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(1); + expect(errors[0].contexts['contains']).toEqual({ hi: 'there' }); + expect(errors[0].contexts['minLength']).toEqual({ whats: 'up' }); + }); + }); + + it('should not map no context', () => { + class MyClass { + @Contains('hello', { + message: 'String is not valid. You string must contain a hello word', + }) + someProperty: string; + + @Contains('bye', { + message: 'String is not valid. You string must contain a bye word', + context: { + bye: 'now', + }, + }) + someOtherProperty: string; + } + + const model = new MyClass(); + // model.someProperty = "hell no world"; + return validator.validate(model).then(errors => { + expect(errors.length).toEqual(2); + expect(errors[0].contexts).toBeUndefined(); + expect(errors[1].contexts['contains']).toEqual({ bye: 'now' }); + }); + }); + + it('should stop at first error.', () => { + class MySubClass { + @IsDefined({ + message: 'isDefined', + }) + name: string; + } + + class MyClass { + @IsDefined({ + message: 'isDefined', + }) + @Contains('hello', { + message: 'String is not valid. You string must contain a hello word', + }) + sameProperty: string; + + @ValidateNested() + @IsArray() + nestedWithPrimitiveValue: MySubClass[]; + } + + const model = new MyClass(); + model.nestedWithPrimitiveValue = 'invalid' as any; + + const hasStopAtFirstError = validator.validate(model, { stopAtFirstError: true }).then(errors => { + expect(errors.length).toEqual(2); + expect(Object.keys(errors[0].constraints).length).toBe(1); + expect(errors[0].constraints['isDefined']).toBe('isDefined'); + expect(Object.keys(errors[1].constraints).length).toBe(1); + expect(errors[1].constraints).toHaveProperty('isArray'); + }); + const hasNotStopAtFirstError = validator.validate(model, { stopAtFirstError: false }).then(errors => { + expect(errors.length).toEqual(2); + expect(Object.keys(errors[0].constraints).length).toBe(2); + expect(errors[0].constraints).toHaveProperty('contains'); + expect(errors[0].constraints).toHaveProperty('isDefined'); + expect(Object.keys(errors[1].constraints).length).toBe(2); + expect(errors[1].constraints).toHaveProperty('isArray'); + expect(errors[1].constraints).toHaveProperty('nestedValidation'); }); + return Promise.all([hasStopAtFirstError, hasNotStopAtFirstError]); + }); }); diff --git a/test/functional/validator-options.spec.ts b/test/functional/validator-options.spec.ts index 6cd6303609..b14fbf125b 100644 --- a/test/functional/validator-options.spec.ts +++ b/test/functional/validator-options.spec.ts @@ -1,66 +1,47 @@ -import "es6-shim"; -import {IsNotEmpty} from "../../src/decorator/decorators"; -import {Validator} from "../../src/validation/Validator"; -import {expect} from "chai"; - -import {should, use } from "chai"; - -import * as chaiAsPromised from "chai-as-promised"; - -should(); -use(chaiAsPromised); - -// ------------------------------------------------------------------------- -// Setup -// ------------------------------------------------------------------------- +import { IsNotEmpty } from '../../src/decorator/decorators'; +import { Validator } from '../../src/validation/Validator'; const validator = new Validator(); -// ------------------------------------------------------------------------- -// Specifications: common decorators -// ------------------------------------------------------------------------- - -describe("validator options", function() { - it("should not return target in validation error if validationError: { target: false } is set", function() { - class MyClass { - @IsNotEmpty() - title: string = ""; - isActive: boolean; - } - - const model = new MyClass(); - model.title = ""; - return validator.validate(model, { skipMissingProperties: true, validationError: { target: false } }).then(errors => { - errors.length.should.be.equal(1); - expect(errors[0].target).to.be.undefined; - errors[0].property.should.be.equal("title"); - errors[0].constraints.should.be.eql({ isNotEmpty: "title should not be empty" }); - errors[0].value.should.be.equal(""); - }); +describe('validator options', () => { + it('should not return target in validation error if validationError: { target: false } is set', () => { + class MyClass { + @IsNotEmpty() + title: string = ''; + isActive: boolean; + } + + const model = new MyClass(); + model.title = ''; + return validator + .validate(model, { skipMissingProperties: true, validationError: { target: false } }) + .then(errors => { + expect(errors.length).toEqual(1); + expect(errors[0].target).toBeUndefined(); + expect(errors[0].property).toEqual('title'); + expect(errors[0].constraints).toEqual({ isNotEmpty: 'title should not be empty' }); + expect(errors[0].value).toEqual(''); + }); + }); + + it('should returns error on unknown objects if forbidUnknownValues is true', function () { + const anonymousObject = { badKey: 'This should not pass.' }; + + return validator.validate(anonymousObject, { forbidUnknownValues: true }).then(errors => { + expect(errors.length).toEqual(1); + expect(errors[0].target).toEqual(anonymousObject); + expect(errors[0].property).toEqual(undefined); + expect(errors[0].value).toEqual(undefined); + expect(errors[0].children).toBeInstanceOf(Array); + expect(errors[0].constraints).toEqual({ unknownValue: 'an unknown value was passed to the validate function' }); }); + }); - it("should returns error on unknown objects if forbidUnknownValues is true", function () { - - const anonymousObject = { badKey: "This should not pass." }; + it('should return no error on unknown objects if forbidUnknownValues is false', function () { + const anonymousObject = { badKey: 'This should not pass.' }; - return validator.validate(anonymousObject, { forbidUnknownValues: true }).then(errors => { - errors.length.should.be.equal(1); - expect(errors[0].target).to.be.equal(anonymousObject); - expect(errors[0].property).to.be.equal(undefined); - expect(errors[0].value).to.be.equal(undefined); - errors[0].children.should.be.instanceof(Array); - errors[0].constraints.should.be.eql({ unknownValue: "an unknown value was passed to the validate function" }); - }); + return validator.validate(anonymousObject, { forbidUnknownValues: false }).then(errors => { + expect(errors.length).toEqual(0); }); - - it("should return no error on unknown objects if forbidUnknownValues is false", function () { - - const anonymousObject = { badKey: "This should not pass." }; - - return validator.validate(anonymousObject, { forbidUnknownValues: false }).then(errors => { - errors.length.should.be.equal(0); - }); - }); - - + }); }); diff --git a/test/functional/whitelist-validation.spec.ts b/test/functional/whitelist-validation.spec.ts index 3750ee3d55..2667bcb6a9 100644 --- a/test/functional/whitelist-validation.spec.ts +++ b/test/functional/whitelist-validation.spec.ts @@ -1,85 +1,75 @@ -import "es6-shim"; -import {Allow, IsDefined, Min, ValidateNested} from "../../src/decorator/decorators"; -import {Validator} from "../../src/validation/Validator"; -import {expect} from "chai"; -import {ValidationTypes} from "../../src/validation/ValidationTypes"; - -import {should, use } from "chai"; - -import * as chaiAsPromised from "chai-as-promised"; - -should(); -use(chaiAsPromised); - -// ------------------------------------------------------------------------- -// Setup -// ------------------------------------------------------------------------- +import { Allow, IsDefined, IsOptional, Min } from '../../src/decorator/decorators'; +import { Validator } from '../../src/validation/Validator'; +import { ValidationTypes } from '../../src'; const validator = new Validator(); -// ------------------------------------------------------------------------- -// Specifications: allowed validation -// ------------------------------------------------------------------------- - -describe("whitelist validation", function () { - - it("should strip non whitelisted properties, but leave whitelisted untouched", function () { - - class MyClass { - @IsDefined() - title: string; - - @Min(0) - views: number; - } - - const model: any = new MyClass(); - - model.title = "hello"; - model.views = 56; - model.unallowedProperty = 42; - return validator.validate(model, { whitelist: true }).then(errors => { - expect(errors.length).to.be.equal(0); - expect(model.unallowedProperty).be.undefined; - expect(model.title).to.equal("hello"); - expect(model.views).to.be.equal(56); - }); +describe('whitelist validation', () => { + it('should strip non whitelisted properties, but leave whitelisted untouched', () => { + class MyClass { + @IsDefined() + title: string; + + @Min(0) + views: number; + } + + const model: any = new MyClass(); + + model.title = 'hello'; + model.views = 56; + model.unallowedProperty = 42; + return validator.validate(model, { whitelist: true }).then(errors => { + expect(errors.length).toEqual(0); + expect(model.unallowedProperty).toBeUndefined(); + expect(model.title).toEqual('hello'); + expect(model.views).toEqual(56); }); + }); - it("should be able to whitelist with @Allow", function () { - - class MyClass { - @Allow() - views: number; - } + it('should be able to whitelist with @Allow', () => { + class MyClass { + @Allow() + views: number; + } - const model: any = new MyClass(); + const model: any = new MyClass(); - model.views = 420; - model.unallowedProperty = "non-whitelisted"; + model.views = 420; + model.unallowedProperty = 'non-whitelisted'; - return validator.validate(model, { whitelist: true }).then(errors => { - expect(errors.length).to.be.equal(0); - expect(model.unallowedProperty).be.undefined; - expect(model.views).to.be.equal(420); - }); + return validator.validate(model, { whitelist: true }).then(errors => { + expect(errors.length).toEqual(0); + expect(model.unallowedProperty).toBeUndefined(); + expect(model.views).toEqual(420); }); - - it("should throw an error when forbidNonWhitelisted flag is set", function () { - - class MyClass { - } - - const model: any = new MyClass(); - - model.unallowedProperty = "non-whitelisted"; - - return validator.validate(model, { whitelist: true, forbidNonWhitelisted: true }).then(errors => { - expect(errors.length).to.be.equal(1); - errors[0].target.should.be.equal(model); - errors[0].property.should.be.equal("unallowedProperty"); - errors[0].constraints.should.haveOwnProperty(ValidationTypes.WHITELIST); - }); + }); + + it('should throw an error when forbidNonWhitelisted flag is set', () => { + class MyPayload { + /** + * Since forbidUnknownValues defaults to true, we must add a property to + * register the class in the metadata storage. Otherwise the unknown value check + * would take priority (first check) and exit without running the whitelist logic. + */ + @IsOptional() + propertyToRegisterClass: string; + + nonDecorated: string; + + constructor(nonDecorated: string) { + this.nonDecorated = nonDecorated; + } + } + + const instance = new MyPayload('non-whitelisted'); + + return validator.validate(instance, { whitelist: true, forbidNonWhitelisted: true }).then(errors => { + expect(errors.length).toEqual(1); + expect(errors[0].target).toEqual(instance); + expect(errors[0].property).toEqual('nonDecorated'); + expect(errors[0].constraints).toHaveProperty(ValidationTypes.WHITELIST); + expect(() => errors[0].toString()).not.toThrow(); }); - + }); }); diff --git a/test/utils.spec.ts b/test/utils.spec.ts new file mode 100644 index 0000000000..04b92ec519 --- /dev/null +++ b/test/utils.spec.ts @@ -0,0 +1,33 @@ +import { convertToArray } from '../src/utils'; + +describe('convertToArray', () => { + it('convert Set into array', () => { + const setExample = new Set(); + setExample.add('hello'); + setExample.add('world'); + const newArr = convertToArray(setExample); + expect(newArr).toBeInstanceOf(Array); + expect(newArr.length).toEqual(2); + expect(newArr).toContain('hello'); + expect(newArr).toContain('world'); + }); + + it('convert Map into array of values', () => { + const map = new Map(); + map.set('key1', 'hello'); + map.set('key2', 'world'); + const newArr = convertToArray(map); + expect(newArr).toBeInstanceOf(Array); + expect(newArr.length).toEqual(2); + expect(newArr).toContain('hello'); + expect(newArr).toContain('world'); + }); + + it('should return array untouched', () => { + const arr = ['hello', 'world']; + expect(arr).toBeInstanceOf(Array); + expect(arr.length).toEqual(2); + expect(arr).toContain('hello'); + expect(arr).toContain('world'); + }); +}); diff --git a/tsconfig.json b/tsconfig.json index 0efa191ac2..c9f3bd3b42 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,19 +1,19 @@ { - "version": "2.0.3", - "compilerOptions": { - "outDir": "build/compiled", - "target": "es5", - "module": "commonjs", - "moduleResolution": "node", - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "sourceMap": true, - "noImplicitAny": true, - "declaration": true, - "lib": ["es6"] - }, - "exclude": [ - "build", - "node_modules" - ] + "compilerOptions": { + "module": "commonjs", + "moduleResolution": "node", + "target": "es2018", + "lib": ["es2018"], + "outDir": "build/node", + "rootDir": "./src", + "strict": true, + "sourceMap": true, + "inlineSources": true, + "removeComments": false, + "esModuleInterop": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "forceConsistentCasingInFileNames": true + }, + "exclude": ["build", "node_modules", "sample", "**/*.spec.ts", "test/**"] } diff --git a/tsconfig.prod.cjs.json b/tsconfig.prod.cjs.json new file mode 100644 index 0000000000..8f8b22e900 --- /dev/null +++ b/tsconfig.prod.cjs.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.prod.json", + "compilerOptions": { + "module": "CommonJS", + "outDir": "build/cjs" + }, +} diff --git a/tsconfig.prod.esm2015.json b/tsconfig.prod.esm2015.json new file mode 100644 index 0000000000..c109ec5a01 --- /dev/null +++ b/tsconfig.prod.esm2015.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.prod.json", + "compilerOptions": { + "module": "ES2015", + "outDir": "build/esm2015", + }, +} diff --git a/tsconfig.prod.esm5.json b/tsconfig.prod.esm5.json new file mode 100644 index 0000000000..ada41ec754 --- /dev/null +++ b/tsconfig.prod.esm5.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.prod.json", + "compilerOptions": { + "module": "ES2015", + "target": "ES5", + "outDir": "build/esm5", + "downlevelIteration": true, + }, +} diff --git a/tsconfig.prod.json b/tsconfig.prod.json new file mode 100644 index 0000000000..7daa5be4fe --- /dev/null +++ b/tsconfig.prod.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "strict": false, + "declaration": false, + }, +} diff --git a/tsconfig.prod.types.json b/tsconfig.prod.types.json new file mode 100644 index 0000000000..8de58dd290 --- /dev/null +++ b/tsconfig.prod.types.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.prod.json", + "compilerOptions": { + "declaration": true, + "emitDeclarationOnly": true, + "outDir": "build/types", + }, +} diff --git a/tsconfig.spec.json b/tsconfig.spec.json new file mode 100644 index 0000000000..c0215c96a0 --- /dev/null +++ b/tsconfig.spec.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "strict": false, + "strictPropertyInitialization": false, + "sourceMap": false, + "removeComments": true, + "noImplicitAny": false, + }, + "exclude": ["node_modules"] +} diff --git a/tslint.json b/tslint.json deleted file mode 100644 index 2186f83007..0000000000 --- a/tslint.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "rules": { - "class-name": true, - "comment-format": [ - true, - "check-space" - ], - "indent": [ - true, - "spaces" - ], - "no-duplicate-variable": true, - "no-eval": true, - "no-internal-module": true, - "no-var-keyword": true, - "one-line": [ - true, - "check-open-brace", - "check-whitespace" - ], - "quotemark": [ - true, - "double" - ], - "semicolon": true, - "triple-equals": [ - true, - "allow-null-check" - ], - "typedef-whitespace": [ - true, - { - "call-signature": "nospace", - "index-signature": "nospace", - "parameter": "nospace", - "property-declaration": "nospace", - "variable-declaration": "nospace" - } - ], - "variable-name": [ - true, - "ban-keywords" - ], - "whitespace": [ - true, - "check-branch", - "check-decl", - "check-operator", - "check-separator", - "check-type" - ] - } -} \ No newline at end of file diff --git a/typings.json b/typings.json deleted file mode 100644 index f74ca2c7a0..0000000000 --- a/typings.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "globalDevDependencies": { - "Q": "github:DefinitelyTyped/DefinitelyTyped/q/Q.d.ts#efd40e67ff323f7147651bdbef03c03ead7b1675", - "assertion-error": "registry:dt/assertion-error#1.0.0+20141105053942", - "chai": "registry:dt/chai#3.4.0+20160216071402", - "chai-as-promised": "registry:dt/chai-as-promised#0.0.0+20160219015317", - "gulp": "registry:dt/gulp#3.8.0+20150825164847", - "mocha": "registry:dt/mocha#2.2.5+20151023103246", - "orchestrator": "registry:dt/orchestrator#0.0.0+20150821143159", - "promises-a-plus": "registry:dt/promises-a-plus#0.0.0+20150118213706", - "sinon": "registry:dt/sinon#1.16.0+20151027143416" - }, - "globalDependencies": { - "es6-shim": "registry:dt/es6-shim#0.31.2+20160215162030", - "node": "registry:dt/node#4.0.0+20160226132328" - } -}